From 2f429472be4fc768ab5fc0fec8eaa3733dd0c578 Mon Sep 17 00:00:00 2001 From: kbrunik Date: Thu, 6 Jun 2024 08:52:44 -0700 Subject: [PATCH] fixed h2 storage sizing bug --- .../plant/greenheart_config_onshore_mn.yaml | 4 ++-- .../plant/greenheart_config_onshore_tx.yaml | 6 ++--- .../plant/greenheart_config_offshore_gom.yaml | 6 ++--- .../plant/greenheart_config_offshore_ny.yaml | 6 ++--- .../plant/greenheart_config_offshore_ca.yaml | 6 ++--- greenheart/tools/eco/hydrogen_mgmt.py | 24 ++++++++++--------- tests/greenheart/test_greenheart_system.py | 6 ++--- 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/examples/reference_plants/01-onshore-steel-mn/input/plant/greenheart_config_onshore_mn.yaml b/examples/reference_plants/01-onshore-steel-mn/input/plant/greenheart_config_onshore_mn.yaml index 596e98a02..7b98f4f20 100644 --- a/examples/reference_plants/01-onshore-steel-mn/input/plant/greenheart_config_onshore_mn.yaml +++ b/examples/reference_plants/01-onshore-steel-mn/input/plant/greenheart_config_onshore_mn.yaml @@ -82,10 +82,10 @@ h2_transport_pipe: outlet_pressure: 10 # bar - from example in code from Jamie #TODO check this value h2_storage: size_capacity_from_demand: - flag: False # If True, then storage is sized to provide steady-state storage + flag: True # If True, then storage is sized to provide steady-state storage capacity_from_max_on_turbine_storage: False # if True, then days of storage is ignored and storage capacity is based on how much h2 storage fits on the turbines in the plant using Kottenstete 2003. type: "lined_rock_cavern" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] - days: 8.57267 # from `hydrogen_storage_duration_hr` = 205.74419987482239 [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) + days: -1 #8.57267 # from `hydrogen_storage_duration_hr` = 205.74419987482239 [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, #then prevailing wage multiplier if applicable, then inflation diff --git a/examples/reference_plants/02-onshore-ammonia-tx/input/plant/greenheart_config_onshore_tx.yaml b/examples/reference_plants/02-onshore-ammonia-tx/input/plant/greenheart_config_onshore_tx.yaml index d23442c05..8ea5ecf52 100644 --- a/examples/reference_plants/02-onshore-ammonia-tx/input/plant/greenheart_config_onshore_tx.yaml +++ b/examples/reference_plants/02-onshore-ammonia-tx/input/plant/greenheart_config_onshore_tx.yaml @@ -82,10 +82,10 @@ h2_transport_pipe: outlet_pressure: 10 # bar - from example in code from Jamie #TODO check this value h2_storage: size_capacity_from_demand: - flag: False # If True, then storage is sized to provide steady-state storage + flag: True # If True, then storage is sized to provide steady-state storage capacity_from_max_on_turbine_storage: False # if True, then days of storage is ignored and storage capacity is based on how much h2 storage fits on the turbines in the plant using Kottenstete 2003. - type: "none" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] - days: 19.783 # from `hydrogen_storage_duration_hr` = 474.7948370015298 [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) + type: "salt_cavern" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] + days: -1 #19.783 # from `hydrogen_storage_duration_hr` = 474.7948370015298 [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) policy_parameters: # these should be adjusted for inflation prior to application - order of operations: rate in 1992 $, #then prevailing wage multiplier if applicable, then inflation diff --git a/examples/reference_plants/03-offshore-hydrogen-gom/input/plant/greenheart_config_offshore_gom.yaml b/examples/reference_plants/03-offshore-hydrogen-gom/input/plant/greenheart_config_offshore_gom.yaml index ef443d0e1..9daf5d2d0 100644 --- a/examples/reference_plants/03-offshore-hydrogen-gom/input/plant/greenheart_config_offshore_gom.yaml +++ b/examples/reference_plants/03-offshore-hydrogen-gom/input/plant/greenheart_config_offshore_gom.yaml @@ -81,10 +81,10 @@ h2_transport_pipe: outlet_pressure: 10 # bar - from example in code from Jamie #TODO check this value h2_storage: size_capacity_from_demand: - flag: False # If True, then storage is sized to provide steady-state storage + flag: True # If True, then storage is sized to provide steady-state storage capacity_from_max_on_turbine_storage: False # if True, then days of storage is ignored and storage capacity is based on how much h2 storage fits on the turbines in the plant using Kottenstete 2003. - type: "none" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] - days: 3 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) + type: "salt_cavern" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] + days: -1 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) # platform: # opex_rate: 0.0111 # % of capex to determine opex (see table 5 in https://www.acm.nl/sites/default/files/documents/study-on-estimation-method-for-additional-efficient-offshore-grid-opex.pdf) # installation_days: 14 # days diff --git a/examples/reference_plants/04-offshore-hydrogen-ny/input/plant/greenheart_config_offshore_ny.yaml b/examples/reference_plants/04-offshore-hydrogen-ny/input/plant/greenheart_config_offshore_ny.yaml index 8d747decc..980947a4f 100644 --- a/examples/reference_plants/04-offshore-hydrogen-ny/input/plant/greenheart_config_offshore_ny.yaml +++ b/examples/reference_plants/04-offshore-hydrogen-ny/input/plant/greenheart_config_offshore_ny.yaml @@ -81,10 +81,10 @@ h2_transport_pipe: outlet_pressure: 10 # bar - from example in code from Jamie #TODO check this value h2_storage: size_capacity_from_demand: - flag: False # If True, then storage is sized to provide steady-state storage + flag: True # If True, then storage is sized to provide steady-state storage capacity_from_max_on_turbine_storage: False # if True, then days of storage is ignored and storage capacity is based on how much h2 storage fits on the turbines in the plant using Kottenstete 2003. - type: "none" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] - days: 3 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) + type: "lined_rock_cavern" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] + days: 0 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) # platform: # opex_rate: 0.0111 # % of capex to determine opex (see table 5 in https://www.acm.nl/sites/default/files/documents/study-on-estimation-method-for-additional-efficient-offshore-grid-opex.pdf) # installation_days: 14 # days diff --git a/examples/reference_plants/05-offshore-hydrogen-ca/input/plant/greenheart_config_offshore_ca.yaml b/examples/reference_plants/05-offshore-hydrogen-ca/input/plant/greenheart_config_offshore_ca.yaml index 136eab3f0..17255059e 100644 --- a/examples/reference_plants/05-offshore-hydrogen-ca/input/plant/greenheart_config_offshore_ca.yaml +++ b/examples/reference_plants/05-offshore-hydrogen-ca/input/plant/greenheart_config_offshore_ca.yaml @@ -81,10 +81,10 @@ h2_transport_pipe: outlet_pressure: 10 # bar - from example in code from Jamie #TODO check this value h2_storage: size_capacity_from_demand: - flag: False # If True, then storage is sized to provide steady-state storage + flag: True # If True, then storage is sized to provide steady-state storage capacity_from_max_on_turbine_storage: False # if True, then days of storage is ignored and storage capacity is based on how much h2 storage fits on the turbines in the plant using Kottenstete 2003. - type: "none" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] - days: 3 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) + type: "lined_rock_cavern" # can be one of ["none", "pipe", "turbine", "pressure_vessel", "salt_cavern", "lined_rock_cavern"] + days: 0 # [days] how many days worth of production we should be able to store (this is ignored if `capacity_from_max_on_turbine_storage` is set to True) # platform: # opex_rate: 0.0111 # % of capex to determine opex (see table 5 in https://www.acm.nl/sites/default/files/documents/study-on-estimation-method-for-additional-efficient-offshore-grid-opex.pdf) # installation_days: 14 # days diff --git a/greenheart/tools/eco/hydrogen_mgmt.py b/greenheart/tools/eco/hydrogen_mgmt.py index 46201c447..44a82e678 100644 --- a/greenheart/tools/eco/hydrogen_mgmt.py +++ b/greenheart/tools/eco/hydrogen_mgmt.py @@ -298,6 +298,7 @@ def run_h2_storage( hydrogen_storage_capacity_kg, hydrogen_storage_duration_hr, hydrogen_storage_soc = hydrogen_storage_capacity(electrolyzer_physics_results['H2_Results'], greenheart_config['electrolyzer']['rating'], hydrogen_storage_demand) greenheart_config["h2_capacity"] = hydrogen_storage_capacity_kg h2_storage_results["h2_storage_kg"] = hydrogen_storage_capacity_kg + else: greenheart_config["h2_capacity"] = h2_capacity h2_storage_results["h2_storage_kg"] = h2_capacity @@ -355,7 +356,7 @@ def run_h2_storage( area_site, mass_tank_empty_site, _, - ) = h2_storage.distributed_storage_vessels(h2_capacity, 1) + ) = h2_storage.distributed_storage_vessels(greenheart_config["h2_capacity"], 1) # ) = h2_storage.distributed_storage_vessels(h2_capacity, nturbines) # capex, opex, energy = h2_storage.calculate_from_fit(h2_capacity) @@ -368,15 +369,15 @@ def run_h2_storage( ] ) # total in kWh h2_storage_results["tank_mass_full_kg"] = ( - h2_storage.get_tank_mass(h2_capacity)[1] + h2_capacity + h2_storage.get_tank_mass(greenheart_config["h2_capacity"])[1] + greenheart_config["h2_capacity"] ) h2_storage_results["tank_footprint_m2"] = h2_storage.get_tank_footprint( - h2_capacity, upright=True + greenheart_config["h2_capacity"], upright=True )[1] h2_storage_results[ "tank volume (m^3)" ] = h2_storage.compressed_gas_function.Vtank - h2_storage_results["Number of tanks"] = h2_storage.get_tanks(h2_capacity) + h2_storage_results["Number of tanks"] = h2_storage.get_tanks(greenheart_config["h2_capacity"]) if verbose: print("ENERGY FOR STORAGE: ", energy * 1e-3 / (365 * 24), " MW") print("Tank volume (M^3): ", h2_storage_results["tank volume (m^3)"]) @@ -397,7 +398,7 @@ def run_h2_storage( storage_input = dict() # pull parameters from plat_config file - storage_input["h2_storage_kg"] = h2_capacity + storage_input["h2_storage_kg"] = greenheart_config["h2_capacity"] storage_input["compressor_output_pressure"] = greenheart_config[ "h2_storage_compressor" ]["output_pressure"] @@ -426,7 +427,7 @@ def run_h2_storage( h2_storage = PressureVessel(Energy_cost=energy_cost) h2_storage.run() - capex, opex, energy = h2_storage.calculate_from_fit(h2_capacity) + capex, opex, energy = h2_storage.calculate_from_fit(greenheart_config["h2_capacity"]) h2_storage_results["storage_capex"] = capex h2_storage_results["storage_opex"] = opex @@ -437,10 +438,10 @@ def run_h2_storage( ] ) # total in kWh h2_storage_results["tank_mass_full_kg"] = ( - h2_storage.get_tank_mass(h2_capacity)[1] + h2_capacity + h2_storage.get_tank_mass(greenheart_config["h2_capacity"])[1] + greenheart_config["h2_capacity"] ) h2_storage_results["tank_footprint_m2"] = h2_storage.get_tank_footprint( - h2_capacity, upright=True + greenheart_config["h2_capacity"], upright=True )[1] h2_storage_results[ "tank volume (m^3)" @@ -461,8 +462,8 @@ def run_h2_storage( # initialize dictionary for salt cavern storage parameters storage_input = dict() - # pull parameters from plat_config file - storage_input["h2_storage_kg"] = h2_capacity + # pull parameters from plant_config file + storage_input["h2_storage_kg"] = greenheart_config["h2_capacity"] storage_input["system_flow_rate"] = storage_max_fill_rate storage_input["model"] = "papadias" @@ -495,7 +496,7 @@ def run_h2_storage( storage_input = dict() # pull parameters from plat_config file - storage_input["h2_storage_kg"] = h2_capacity + storage_input["h2_storage_kg"] = greenheart_config["h2_capacity"] storage_input["system_flow_rate"] = storage_max_fill_rate storage_input["model"] = "papadias" @@ -521,6 +522,7 @@ def run_h2_storage( if verbose: print("\nH2 Storage Results:") + print("H2 Storage capacity (kg): ",greenheart_config["h2_capacity"]) print("H2 storage capex: ${0:,.0f}".format(h2_storage_results["storage_capex"])) print( "H2 storage annual opex: ${0:,.0f}/yr".format( diff --git a/tests/greenheart/test_greenheart_system.py b/tests/greenheart/test_greenheart_system.py index 912889931..2291cffcc 100644 --- a/tests/greenheart/test_greenheart_system.py +++ b/tests/greenheart/test_greenheart_system.py @@ -165,7 +165,7 @@ def test_simulation_wind_wave_solar(subtests): # prior to 20240207 value was approx(10.823798551850347) # TODO base this test value on something. Currently just based on output at writing. with subtests.test("lcoh"): - assert lcoh == approx(12.71510378132601, rel=rtol) + assert lcoh == approx(12.715103781326011, rel=rtol) # prior to 20240207 value was approx(0.11035426429749774) # TODO base this test value on something. Currently just based on output at writing. @@ -397,7 +397,7 @@ def test_simulation_wind_onshore_steel_ammonia_ss_h2storage(subtests): # TODO base this test value on something with subtests.test("lcoh"): - assert lcoh == approx(4.023687007795485, rel=rtol) + assert lcoh == approx(10.0064010897151, rel=rtol) # TODO base this test value on something with subtests.test("lcoe"): @@ -405,7 +405,7 @@ def test_simulation_wind_onshore_steel_ammonia_ss_h2storage(subtests): # TODO base this test value on something with subtests.test("steel_finance"): - lcos_expected = 1414.0330270955506 + lcos_expected = 1812.985744428756 assert steel_finance.sol.get("price") == approx(lcos_expected, rel=rtol)