From 6b980aaefd0a3adc2d7ea37f90a88c72d1ad1558 Mon Sep 17 00:00:00 2001 From: Jared Thomas Date: Mon, 12 Feb 2024 12:25:35 -0700 Subject: [PATCH] stop using hopp for h2 script --- greenheart/tools/eco/electrolysis.py | 113 ++--------- greenheart/tools/eco/hopp_mgmt.py | 192 +++--------------- greenheart/tools/eco/hybrid_system.py | 14 +- hopp/simulation/hybrid_simulation.py | 4 +- .../hopp_config_wind_wave_solar_battery.yaml | 2 +- tests/greenheart/test_hydrogen/test_eco.py | 4 +- 6 files changed, 60 insertions(+), 269 deletions(-) diff --git a/greenheart/tools/eco/electrolysis.py b/greenheart/tools/eco/electrolysis.py index 08701bf3f..410846e89 100644 --- a/greenheart/tools/eco/electrolysis.py +++ b/greenheart/tools/eco/electrolysis.py @@ -20,8 +20,7 @@ def run_electrolyzer_physics( hopp_results, - hopp_scenario, - hopp_h2_args, + useful_life, eco_config, wind_resource, design_scenario, @@ -29,110 +28,31 @@ def run_electrolyzer_physics( save_plots=False, verbose=False, ): - # parse inputs to provide to hopp_tools call - hybrid_plant = hopp_results["hybrid_plant"] + + electrolyzer_size_mw = eco_config["electrolyzer"]["rating"] + electrolyzer_capex_kw = eco_config["electrolyzer"]["electrolyzer_capex"] + if eco_config["project_parameters"]["grid_connection"]: - # print(np.ones(365*24)*(hopp_h2_args["electrolyzer_size"]*1E3)) - energy_to_electrolyzer_kw = np.ones(365 * 24 - 4*7*12) * ( - hopp_h2_args["electrolyzer_size"] * 1e3 + energy_to_electrolyzer_kw = np.ones(365 * 24 - 4*7*12) * ( # TODO why the subtraction here? + electrolyzer_size_mw * 1e3 ) else: energy_to_electrolyzer_kw = np.asarray(hopp_results[ "combined_hybrid_power_production_hopp" ]) - - scenario = hopp_scenario - wind_size_mw = hopp_h2_args["wind_size_mw"] - solar_size_mw = hopp_h2_args["solar_size_mw"] - electrolyzer_size_mw = hopp_h2_args["electrolyzer_size"] - kw_continuous = hopp_h2_args["kw_continuous"] - electrolyzer_capex_kw = eco_config["electrolyzer"]["electrolyzer_capex"] - lcoe = hopp_results["lcoe"] - useful_life = scenario['Useful Life'] - # n_pem_clusters = eco_config["n_"] -############### - - # adjusted_installed_cost = hybrid_plant.grid._financial_model.Outputs.adjusted_installed_cost - #NB: adjusted_installed_cost does NOT include the electrolyzer cost - # system_rating = electrolyzer_size_mw - # system_rating = wind_size_mw + solar_size_mw - # H2_Results, H2A_Results = run_h2_PEM( - # energy_to_electrolyzer_kw, - # electrolyzer_size_mw, - # kw_continuous, - # electrolyzer_capex_kw, - # lcoe, - # adjusted_installed_cost, - # useful_life - # ) - # H2_Results, H2A_Results = run_h2_PEM( - # energy_to_electrolyzer_kw, - # electrolyzer_size_mw, - # useful_life, - # n_pem_clusters, # new - # electrolysis_scale, # new and unused - # pem_control_type, # new, set to optimize if you want to run Sanjan's code that gets good results but is very slow - # electrolyzer_direct_cost_kw, # new - # user_defined_pem_param_dictionary, # new must have self.user_params = ( - # # user_defined_electrolyzer_params["Modify EOL Degradation Value"], - # # user_defined_electrolyzer_params["EOL Rated Efficiency Drop"], - # # user_defined_electrolyzer_params["Modify BOL Eff"], - # # user_defined_electrolyzer_params["BOL Eff [kWh/kg-H2]"], - # # ) - # use_degradation_penalty, # new, bool - # grid_connection_scenario, # new can be ["off-grid", or "on-grid"] - # hydrogen_production_capacity_required_kgphr, # new - # # The inputs below are not used - # kw_continuous, - # lcoe, - # adjusted_installed_cost, - - # ) - # inputs - # electrical_generation_timeseries, electrolyzer_size, - # useful_life, n_pem_clusters, electrolysis_scale, - # pem_control_type,electrolyzer_direct_cost_kw, user_defined_pem_param_dictionary, - # use_degradation_penalty, grid_connection_scenario, - # hydrogen_production_capacity_required_kgphr,debug_mode = False,turndown_ratio = 0.1, -############# - # # run electrolyzer model - # H2_Results, _, electrical_generation_timeseries = hopp_tools.run_H2_PEM_sim( - # hybrid_plant, - # energy_to_electrolyzer_kw, - # scenario, - # wind_size_mw, - # solar_size_mw, - # electrolyzer_size_mw, - # kw_continuous, - # electrolyzer_capex_kw, - # lcoe, - # ) - # calculate utilization rate - energy_capacity = hopp_h2_args["electrolyzer_size"] * 365 * 24 # MWh + energy_capacity = electrolyzer_size_mw * 365 * 24 # MWh energy_available = sum(energy_to_electrolyzer_kw) * 1e-3 # MWh capacity_factor_electrolyzer = energy_available / energy_capacity - - # # run using electrolyzer.py - # electrolyzer_config = eco_config["electrolyzer"]["config"] - # # energy_to_electrolyzer_kw = np.arange(0, 1E6, 1000) - # elec_sys, results_df = run_electrolyzer(electrolyzer_config, energy_to_electrolyzer_kw*1E3)#, optimize=eco_config["electrolyzer"]["optimize"]) - - # plt.scatter(energy_to_electrolyzer_kw, results_df['kg_rate'].values) - # plt.show() - # H2_Results = {} - # H2_Results.update({"hydrogen_annual_output": np.sum(results_df["kg_rate"]), - # "hydrogen_hourly_production": results_df["kg_rate"], - # "cap_factor": capacity_factor_electrolyzer}) - ## run using greensteel model pem_param_dict = {"Modify EOL Degradation Value": False, "EOL Rated Efficiency Drop": 0.1, "Modify BOL Eff": False, "BOL Eff [kWh/kg-H2]": 0.95} + #TODO get electrolyzer params from input yaml H2_Results, h2_ts, h2_tot, energy_input_to_electrolyzer = run_h2_PEM(electrical_generation_timeseries=energy_to_electrolyzer_kw, electrolyzer_size=electrolyzer_size_mw, useful_life=useful_life, # EG: should be in years for full plant life - only used in financial model @@ -150,7 +70,6 @@ def run_electrolyzer_physics( ) # calculate mass and foorprint of system - print(f"test{basic_H2_cost_model}") mass_kg = run_electrolyzer_mass(electrolyzer_size_mw) footprint_m2 = run_electrolyzer_footprint(electrolyzer_size_mw) @@ -294,10 +213,8 @@ def run_electrolyzer_physics( return electrolyzer_physics_results - def run_electrolyzer_cost( electrolyzer_physics_results, - hopp_scenario, orbit_config, hopp_config, eco_config, @@ -340,15 +257,15 @@ def run_electrolyzer_cost( h2_tax_credit, h2_itc, ) = basic_H2_cost_model( - plant_config["electrolyzer"]["electrolyzer_capex"], - plant_config["electrolyzer"]["time_between_replacement"], + eco_config["electrolyzer"]["electrolyzer_capex"], + eco_config["electrolyzer"]["time_between_replacement"], per_turb_electrolyzer_size_mw, useful_life, atb_year, per_turb_electrical_generation_timeseries, per_turb_h2_annual_output, - hopp_scenario["H2 PTC"], - hopp_scenario["Wind ITC"], + 0.0, + 0.0, include_refurb_in_opex=False, offshore=offshore, ) @@ -388,8 +305,8 @@ def run_electrolyzer_cost( atb_year, electrical_generation_timeseries, H2_Results["hydrogen_annual_output"], - hopp_scenario["H2 PTC"], - hopp_scenario["Wind ITC"], + 0.0, + 0.0, include_refurb_in_opex=False, offshore=offshore, ) diff --git a/greenheart/tools/eco/hopp_mgmt.py b/greenheart/tools/eco/hopp_mgmt.py index d96d4f7f1..ff5ab5fbb 100644 --- a/greenheart/tools/eco/hopp_mgmt.py +++ b/greenheart/tools/eco/hopp_mgmt.py @@ -1,15 +1,13 @@ import copy import numpy as np import matplotlib.pyplot as plt +import os + from hopp.simulation.technologies.sites import SiteInfo from hopp.simulation.technologies.sites import flatirons_site as sample_site -import os -from greenheart.to_organize.H2_Analysis.hopp_for_h2_floris import ( - hopp_for_h2_floris as hopp_for_h2, -) from hopp.simulation.hopp_interface import HoppInterface -# Funtion to set up the HOPP model +# Function to set up the HOPP model def setup_hopp( hopp_config, eco_config, @@ -20,15 +18,6 @@ def setup_hopp( show_plots=False, save_plots=False, ): - # set desired schedule based on electrolyzer capacity - desired_schedule = [eco_config["electrolyzer"]["rating"]] * 8760 - - # generate HOPP SiteInfo class instance - # hopp_site = SiteInfo( - # hub_height=turbine_config["hub_height"], - # desired_schedule=desired_schedule, - # **{k: hopp_config["site"][k] for k in hopp_config["site"].keys() - ["hub_height", "desired_schedule", "follow_desired_schedule"]} - # ) hopp_site = SiteInfo(**hopp_config["site"]) # adjust mean wind speed if desired @@ -91,87 +80,7 @@ def setup_hopp( hopp_config["technologies"]["wind"][key] = hopp_technologies["wind"][key] else: hopp_config["technologies"]["wind"].update(hopp_technologies["wind"][key]) - - hopp_technologies = hopp_config["technologies"] - print("HOPP TECH KEYS: ", hopp_config["technologies"]["grid"].keys()) - ################ set up scenario dict input for hopp_for_h2() - hopp_scenario = dict() - hopp_scenario["Wind ITC"] = eco_config["policy_parameters"]["option1"]["electricity_itc"] - hopp_scenario["Wind PTC"] = eco_config["policy_parameters"]["option1"]["electricity_ptc"] - hopp_scenario["H2 PTC"] = eco_config["policy_parameters"]["option1"]["h2_ptc"] - hopp_scenario["Useful Life"] = orbit_config["project_parameters"][ - "project_lifetime" - ] - hopp_scenario["Debt Equity"] = eco_config["finance_parameters"][ - "debt_equity_split" - ] - hopp_scenario["Discount Rate"] = eco_config["finance_parameters"]["discount_rate"] - hopp_scenario["Tower Height"] = turbine_config["hub_height"] - hopp_scenario["Powercurve File"] = turbine_config["turbine_type"] + ".csv" - - ############### prepare other HOPP for H2 inputs - - # get/set specific wind inputs - wind_size_mw = ( - orbit_config["plant"]["num_turbines"] * turbine_config["turbine_rating"] - ) - wind_om_cost_kw = orbit_config["project_parameters"]["opex_rate"] - - wind_cost_kw = (orbit_project.total_capex) / ( - wind_size_mw * 1e3 - ) # should be full plant installation and equipment costs etc minus the export costs - - custom_powercurve = False # flag to use powercurve file provided in hopp_scenario? - - # get/set specific solar inputs - solar_size_mw = 0.0 - solar_cost_kw = 0.0 - - # get/set specific storage inputs - storage_size_mw = 0.0 - storage_size_mwh = 0.0 - storage_hours = 0.0 - - storage_cost_kw = 0.0 - storage_cost_kwh = 0.0 - - # get/set specific electrolyzer inputs - electrolyzer_size_mw = eco_config["electrolyzer"]["rating"] - - # get/set specific load and source inputs - kw_continuous = electrolyzer_size_mw * 1e3 - load = [kw_continuous for x in range(0, 8760)] - grid_connected_hopp = eco_config["project_parameters"]["grid_connection"] - - - # add these specific inputs to a dictionary for transfer - # TODO may need to add this for solar somehow - # if "solar_om_cost_kw" in plant_config["project_parameters"]: - # solar_om_cost_kw = plant_config["project_parameters"]["solar_om_cost_kw"] - # else: - solar_om_cost_kw = 0.0 - - hopp_h2_args = { - "wind_size_mw": wind_size_mw, - "wind_om_cost_kw": wind_om_cost_kw, - "wind_cost_kw": wind_cost_kw, - "custom_powercurve": custom_powercurve, - "solar_size_mw": solar_size_mw, - "solar_cost_kw": solar_cost_kw, - "storage_size_mw": storage_size_mw, - "storage_size_mwh": storage_size_mwh, - "storage_hours": storage_hours, - "storage_cost_kw": storage_cost_kw, - "storage_cost_kwh": storage_cost_kwh, - "electrolyzer_size": electrolyzer_size_mw, - "kw_continuous": kw_continuous, - "load": load, - "grid_connected_hopp": grid_connected_hopp, - "turbine_parent_path": "../../input/turbines/", - "ppa_price": eco_config["project_parameters"]["ppa_price"], - "solar_om_cost_kw": solar_om_cost_kw - } - + if show_plots or save_plots: # plot wind resource if desired print("\nPlotting Wind Resource") @@ -194,77 +103,40 @@ def setup_hopp( print("\n") ################ return all the inputs for hopp - return hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args - + return hopp_config, hopp_site # Function to run hopp from provided inputs from setup_hopp() -def run_hopp(hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args, wave_cost_dict={}, verbose=False): +def run_hopp(hopp_config, hopp_site, project_lifetime, verbose=False): + hopp_config_internal = copy.deepcopy(hopp_config) + if "wave" in hopp_config_internal["technologies"].keys(): + wave_cost_dict = hopp_config_internal["technologies"]["wave"].pop("cost_inputs") + + hi = HoppInterface(hopp_config_internal) + hi.system.site = hopp_site + + # hi.system.setup_cost_calculator() + + if "wave" in hi.system.technologies.keys(): + hi.system.wave.create_mhk_cost_calculator(wave_cost_dict) + + hi.simulate(project_life=project_lifetime) - hopp_technologies_copy = copy.deepcopy(hopp_technologies) - if wave_cost_dict == {} and 'wave' in hopp_technologies.keys(): - wave_cost_dict = hopp_technologies_copy["wave"].pop("cost_inputs") - # wave_cost_dict = hopp_technologies["wave"]["cost_inputs"] - # run hopp for H2 - ( - hybrid_plant, - combined_hybrid_power_production_hopp, - combined_pv_wind_curtailment_hopp, - energy_shortfall_hopp, - annual_energies, - wind_plus_solar_npv, - npvs, - lcoe, - lcoe_nom, - ) = hopp_for_h2( - hopp_site, - hopp_scenario, - hopp_technologies_copy, - hopp_h2_args["wind_size_mw"], - hopp_h2_args["solar_size_mw"], - hopp_h2_args["storage_size_mw"], - hopp_h2_args["storage_size_mwh"], - hopp_h2_args["storage_hours"], - hopp_h2_args["wind_cost_kw"], - hopp_h2_args["solar_cost_kw"], - hopp_h2_args["storage_cost_kw"], - hopp_h2_args["storage_cost_kwh"], - hopp_h2_args["kw_continuous"], - hopp_h2_args["load"], - hopp_h2_args["custom_powercurve"], - hopp_h2_args["electrolyzer_size"], - solar_om_cost_kw=hopp_h2_args["solar_om_cost_kw"], - grid_connected_hopp=hopp_h2_args["grid_connected_hopp"], - wind_om_cost_kw=hopp_h2_args["wind_om_cost_kw"], - turbine_parent_path=hopp_h2_args["turbine_parent_path"], - ppa_price=hopp_h2_args["ppa_price"], - wave_cost_dict=wave_cost_dict - ) - # store results for later use hopp_results = { - "hybrid_plant": hybrid_plant, - "combined_hybrid_power_production_hopp": combined_hybrid_power_production_hopp, - "combined_pv_wind_curtailment_hopp": combined_pv_wind_curtailment_hopp, - "energy_shortfall_hopp": energy_shortfall_hopp, - "annual_energies": annual_energies, - "wind_plus_solar_npv": wind_plus_solar_npv, - "npvs": npvs, - "lcoe": lcoe, - "lcoe_nom": lcoe_nom, + "hybrid_plant": hi.system, + "combined_hybrid_power_production_hopp": hi.system.grid._system_model.Outputs.system_pre_interconnect_kwac[0:8759], + "combined_pv_wind_curtailment_hopp": hi.system.grid.generation_curtailed, + "energy_shortfall_hopp": hi.system.grid.missed_load, + "annual_energies": hi.system.annual_energies, + "hybrid_npv": hi.system.net_present_values.hybrid, + "npvs": hi.system.net_present_values, + "lcoe": hi.system.lcoe_real, + "lcoe_nom": hi.system.lcoe_nom, } if verbose: print("\nHOPP Results") - print("Annual Energies: ", annual_energies) - print( - "combined power production: ", sum(combined_hybrid_power_production_hopp) - ) - print("other ", hybrid_plant.wind.system_capacity_kw) - print("Theoretical capacity: ", hopp_h2_args["wind_size_mw"] * 1e3 * 365 * 24) - print( - "Capacity factor: ", - sum(combined_hybrid_power_production_hopp) - / (hopp_h2_args["wind_size_mw"] * 1e3 * 365 * 24), - ) - print("LCOE from HOPP: ", lcoe) + print("Hybrid Annual Energy: ", hopp_results["annual_energies"]) + print("Capacity factors: ", hi.system.capacity_factors) + print("Real LCOE from HOPP: ", hi.system.lcoe_real) - return hopp_results + return hopp_results \ No newline at end of file diff --git a/greenheart/tools/eco/hybrid_system.py b/greenheart/tools/eco/hybrid_system.py index ec3dcaa78..9a30c3f70 100644 --- a/greenheart/tools/eco/hybrid_system.py +++ b/greenheart/tools/eco/hybrid_system.py @@ -69,13 +69,15 @@ def run_simulation(filename_hopp_config, filename_eco_config, filename_turbine_c orbit_project, orbit_hybrid_electrical_export_project = he_fin.run_orbit(orbit_config, weather=None, verbose=verbose, orbit_hybrid_electrical_export_config=orbit_hybrid_electrical_export_config) # setup HOPP model - hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args = he_hopp.setup_hopp(hopp_config, eco_config, orbit_config, turbine_config, orbit_project, floris_config, show_plots=show_plots, save_plots=save_plots) + # hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args = he_hopp.setup_hopp(hopp_config, eco_config, orbit_config, turbine_config, orbit_project, floris_config, show_plots=show_plots, save_plots=save_plots) + hopp_config, hopp_site = he_hopp.setup_hopp(hopp_config, eco_config, orbit_config, turbine_config, orbit_project, floris_config, show_plots=show_plots, save_plots=save_plots) # run HOPP model - hopp_results = he_hopp.run_hopp(hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args, verbose=verbose) - # TODO pick up here? + # hopp_results = he_hopp.run_hopp(hopp_site, hopp_technologies, hopp_scenario, hopp_h2_args, verbose=verbose) + hopp_results = he_hopp.run_hopp(hopp_config, hopp_site, project_lifetime=orbit_config["project_parameters"]["project_lifetime"], verbose=verbose) + # this portion of the system is inside a function so we can use a solver to determine the correct energy availability for h2 production - def energy_internals(hopp_results=hopp_results, hopp_scenario=hopp_scenario, hopp_h2_args=hopp_h2_args, orbit_project=orbit_project, design_scenario=design_scenario, orbit_config=orbit_config, hopp_config=hopp_config, eco_config=eco_config, turbine_config=turbine_config, wind_resource=hopp_site.wind_resource, verbose=verbose, show_plots=show_plots, save_plots=save_plots, solver=True, power_for_peripherals_kw_in=0.0, breakdown=False): + def energy_internals(hopp_results=hopp_results, orbit_project=orbit_project, design_scenario=design_scenario, orbit_config=orbit_config, hopp_config=hopp_config, eco_config=eco_config, turbine_config=turbine_config, wind_resource=hopp_site.wind_resource, verbose=verbose, show_plots=show_plots, save_plots=save_plots, solver=True, power_for_peripherals_kw_in=0.0, breakdown=False): hopp_results_internal = dict(hopp_results) @@ -94,10 +96,10 @@ def energy_internals(hopp_results=hopp_results, hopp_scenario=hopp_scenario, hop hopp_results_internal["combined_hybrid_power_production_hopp"] = tuple(remaining_power_profile_in) # run electrolyzer physics model - electrolyzer_physics_results = he_elec.run_electrolyzer_physics(hopp_results_internal, hopp_scenario, hopp_h2_args, eco_config, wind_resource, design_scenario, show_plots=show_plots, save_plots=save_plots, verbose=verbose) + electrolyzer_physics_results = he_elec.run_electrolyzer_physics(hopp_results_internal, orbit_config["project_parameters"]["project_lifetime"], eco_config, wind_resource, design_scenario, show_plots=show_plots, save_plots=save_plots, verbose=verbose) # run electrolyzer cost model - electrolyzer_cost_results = he_elec.run_electrolyzer_cost(electrolyzer_physics_results, hopp_scenario, orbit_config, hopp_config, eco_config, design_scenario, verbose=verbose) + electrolyzer_cost_results = he_elec.run_electrolyzer_cost(electrolyzer_physics_results, orbit_config, hopp_config, eco_config, design_scenario, verbose=verbose) desal_results = he_elec.run_desal(orbit_config, electrolyzer_physics_results, design_scenario, verbose) diff --git a/hopp/simulation/hybrid_simulation.py b/hopp/simulation/hybrid_simulation.py index 914a4cb22..8837b50e2 100644 --- a/hopp/simulation/hybrid_simulation.py +++ b/hopp/simulation/hybrid_simulation.py @@ -281,6 +281,7 @@ def __attrs_post_init__(self): dispatch_options=self.dispatch_options or {}) # Default cost calculator, can be overwritten + print(f"cost_info: {self.cost_info}") self.cost_model = create_cost_calculator(self.interconnect_kw, **self.cost_info or {}) self.outputs_factory = HybridSimulationOutput(self.technologies) @@ -399,10 +400,9 @@ def calculate_installed_cost(self): if self.trough: self.trough.total_installed_cost = self.trough.calculate_total_installed_cost() total_cost += self.trough.total_installed_cost - self.grid.total_installed_cost = total_cost logger.info("HybridSystem set hybrid total installed cost to to {}".format(total_cost)) - + def calculate_financials(self): """ Prepare financial parameters from individual power plants for hybrid system financial metrics. diff --git a/tests/greenheart/test_hydrogen/input_files/plant/hopp_config_wind_wave_solar_battery.yaml b/tests/greenheart/test_hydrogen/input_files/plant/hopp_config_wind_wave_solar_battery.yaml index abfe87a3e..bb1d9403e 100644 --- a/tests/greenheart/test_hydrogen/input_files/plant/hopp_config_wind_wave_solar_battery.yaml +++ b/tests/greenheart/test_hydrogen/input_files/plant/hopp_config_wind_wave_solar_battery.yaml @@ -99,4 +99,4 @@ config: storage_installed_cost_mwh: 347000 # based on 2023 ATB moderate case for utility-scale battery energy costs storage_installed_cost_mw: 329000 # based on 2023 ATB moderate case for utility-scale battery power costs wind_installed_cost_mw: 3526000 # based on 2023 ATB moderate case for offshore wind - solar_installed_cost_per_kw: 1331353 # based on 2023 ATB moderate case for utility-scale pv \ No newline at end of file + solar_installed_cost_mw: 1331353 # based on 2023 ATB moderate case for utility-scale pv \ No newline at end of file diff --git a/tests/greenheart/test_hydrogen/test_eco.py b/tests/greenheart/test_hydrogen/test_eco.py index 820359a64..1c94c41dd 100644 --- a/tests/greenheart/test_hydrogen/test_eco.py +++ b/tests/greenheart/test_hydrogen/test_eco.py @@ -98,9 +98,9 @@ class TestSimulationWindWaveSolarBattery(): output_level=4) def test_lcoh(self): - assert self.lcoh == approx(11.427501448682067) #TODO base this test value on something. Currently just based on output at writing. + assert self.lcoh == approx(11.31588977975476) #TODO base this test value on something. Currently just based on output at writing. def test_lcoe(self): - assert self.lcoe == approx(0.10799072389369309) # TODO base this test value on something. Currently just based on output at writing. + assert self.lcoe == approx(0.10861038057539885) # TODO base this test value on something. Currently just based on output at writing. # run the stuff if __name__ == "__main__":