-
Notifications
You must be signed in to change notification settings - Fork 35
Compressed Gas H2 Storage #680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
259b895
302aef0
70d5496
b159329
ea3ac03
6483f76
e3d5d59
b27efa3
04aa292
0463477
e3bc32a
18bc662
595c3a5
81db005
ee7701e
70ba6a1
d56c333
c971250
93d23d0
86d0a4b
2e14c63
4f8be20
d66659a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,9 +12,21 @@ | |
| class HydrogenStorageBaseCostModelConfig(BaseConfig): | ||
| """Base config class for HydrogenStorageBaseCostModel. | ||
|
|
||
| Fields include `max_capacity`, `max_charge_rate`, `sizing_mode`, `commodity_name`, | ||
| `commodity_units`, `cost_year`, `labor_rate`, `insurance`, `property_taxes`, | ||
| `licensing_permits`, `compressor_om`, and `facility_om`. | ||
| Fields: | ||
| - `max_capacity` | ||
| - `max_charge_rate` | ||
| - `sizing_mode` | ||
| - `commodity_name`, | ||
| - `commodity_units` | ||
| - `cost_year` | ||
| - `labor_rate` | ||
| - `insurance` | ||
| - `property_taxes`, | ||
|
jmartin4u marked this conversation as resolved.
Outdated
|
||
| - `licensing_permits` | ||
| - `compressor_om` | ||
| - `facility_om` | ||
| - `inlet_pressure_bar` # Update in other modesl besides comp gas? | ||
| - `storage_pressure_bar` - max 700 # Update in other modesl besides comp gas? | ||
| """ | ||
|
|
||
| max_capacity: float | None = field(default=None) | ||
|
|
@@ -33,6 +45,8 @@ class HydrogenStorageBaseCostModelConfig(BaseConfig): | |
| licensing_permits: float = field(default=0.001, validator=range_val(0, 1)) | ||
| compressor_om: float = field(default=0.04, validator=range_val(0, 1)) | ||
| facility_om: float = field(default=0.01, validator=range_val(0, 1)) | ||
| inlet_pressure_bar: float = field(default=20, validator=gte_zero) | ||
| storage_pressure_bar: float = field(default=200, validator=range_val(0, 700)) | ||
|
|
||
| def __attrs_post_init__(self): | ||
| undefined_capacities = self.max_capacity is None or self.max_charge_rate is None | ||
|
|
@@ -227,8 +241,12 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): | |
| # ============================================================================ | ||
| outlet_pressure = 200 # Max outlet pressure of lined rock cavern in [1] [bar] | ||
| n_compressors = 2 | ||
| comp_type = "pipeline" | ||
| storage_compressor = Compressor( | ||
| outlet_pressure, system_flow_rate, n_compressors=n_compressors | ||
| outlet_pressure, | ||
| system_flow_rate, | ||
| n_compressors=n_compressors, | ||
| compressor_type=comp_type, | ||
| ) | ||
| storage_compressor.compressor_power() | ||
| motor_rating, power = storage_compressor.compressor_system_power() | ||
|
|
@@ -357,8 +375,12 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): | |
| # ============================================================================ | ||
| outlet_pressure = 120 # Max outlet pressure of salt cavern in [1] [bar] | ||
| n_compressors = 2 | ||
| comp_type = "pipeline" | ||
| storage_compressor = Compressor( | ||
| outlet_pressure, system_flow_rate, n_compressors=n_compressors | ||
| outlet_pressure, | ||
| system_flow_rate, | ||
| n_compressors=n_compressors, | ||
| compressor_type=comp_type, | ||
| ) | ||
| storage_compressor.compressor_power() | ||
| motor_rating, power = storage_compressor.compressor_system_power() | ||
|
|
@@ -501,8 +523,12 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): | |
| compressor_output_pressure # Max outlet pressure of underground pipe storage [1] [bar] | ||
| ) | ||
| n_compressors = 2 | ||
| comp_type = "pipeline" | ||
| storage_compressor = Compressor( | ||
| outlet_pressure, system_flow_rate, n_compressors=n_compressors | ||
| outlet_pressure, | ||
| system_flow_rate, | ||
| n_compressors=n_compressors, | ||
| compressor_type=comp_type, | ||
| ) | ||
| storage_compressor.compressor_power() | ||
| motor_rating, power = storage_compressor.compressor_system_power() | ||
|
|
@@ -546,3 +572,175 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): | |
|
|
||
| outputs["CapEx"] = installed_capex | ||
| outputs["OpEx"] = total_om | ||
|
|
||
|
|
||
| class CompressedGasStorageCostModel(HydrogenStorageBaseCostModel): | ||
| """Capital and operational cost model for compressed gas hydrogen storage. | ||
|
|
||
| This model is based on HDSAM's compressed gas hydrogen storage terminal cost model, which is | ||
| designed for loading of trucks with compressed H2. In this model, we isolate just the parts of | ||
| the HDSAM that relate to filling and storage, and ignoring the costs related to truck loading. | ||
|
|
||
| Costs have been converted to 2018 costs to match the models above using CEPCI values in HDSAM. | ||
|
|
||
| References: | ||
| [1] HDSAM V5.5 Compressed Gas H2 Terminal: https://hdsam.es.anl.gov/index.php?content=hdsam | ||
| """ | ||
|
|
||
| def compute(self, inputs, outputs, discrete_inputs, discrete_outputs): | ||
| """Calculate installed capital and O&M costs for lined rock cavern hydrogen storage. | ||
|
|
||
| Args: | ||
| inputs: OpenMDAO inputs containing ``max_capacity`` (total capacity [kg]), | ||
| ``max_charge_rate`` (charge rate [kg/h]), and ``hydrogen_in`` | ||
| (timeseries [kg/h]). | ||
| outputs: OpenMDAO outputs dict. | ||
| discrete_inputs: OpenMDAO discrete inputs dict. | ||
| discrete_outputs: OpenMDAO discrete outputs dict. | ||
|
|
||
| Sets: | ||
| outputs["CapEx"]: Installed capital cost in 2018 USD (including compressor). | ||
| outputs["OpEx"]: Annual fixed O&M in 2018 USD/yr (excluding electricity). | ||
| """ | ||
|
|
||
| # ============================================================================ | ||
| # Design inputs | ||
| # ============================================================================ | ||
| # Relevant design parameters (mostly rows 32-74 of "Compressed Gas H2 Terminal" in [1]) | ||
|
|
||
| h2_in_kg_d = units.convert_units( | ||
| inputs["hydrogen_in"], f"({self.config.commodity_units})", "kg/d" | ||
|
jmartin4u marked this conversation as resolved.
Outdated
|
||
| ) | ||
| terminal_capacity_kg_d = np.max(h2_in_kg_d) | ||
| storage_capacity_kg = units.convert_units( | ||
| inputs["storage_capacity"][0], f"({self.config.commodity_units})*h", "kg" | ||
| ) | ||
| n_compressors = np.ceil(terminal_capacity_kg_d / 24 / 50) # Cell B59 | ||
| # Not sure where the 50 comes from in HDSAM - using rule of thumb of 1 unit per 50 kg/hr? | ||
| storage_compressor = Compressor( | ||
| compressor_type="storage", | ||
| p_inlet=self.config.inlet_pressure_bar, | ||
| p_outlet=self.config.storage_pressure_bar, | ||
| flow_rate_kg_d=terminal_capacity_kg_d, | ||
| n_compressors=n_compressors, | ||
| ) | ||
|
|
||
| # ============================================================================ | ||
| # Calculate CAPEX | ||
| # ============================================================================ | ||
| # Installed capital cost per kg from rows 158-180 of "Compressed Gas H2 Terminal" in [1] | ||
| # Capex for compressor and storage scales with size | ||
| # Capex for piping, plumbing, electrical, instrumentation, and buildings is constant | ||
| # CEPCI data from HDSAM used to convert most costs to 2018, for those without a CEPCI index | ||
| # the BLS CPI calcualtor was used instead: https://data.bls.gov/cgi-bin/cpicalc.pl | ||
| # "Truck Loading Compressor" and "Truck Scale" from HDSAM are not included | ||
|
|
||
| # Storage Compressor | ||
| storage_compressor.compressor_power() | ||
| unit_power_kw, system_power_kw = storage_compressor.compressor_system_power() | ||
| comp_capex_2016, _ = storage_compressor.compressor_costs() | ||
| comp_capex = comp_capex_2016 * 1.36013289036545 / 1.2890365448505 | ||
| # Values taken from CEPCI table in "Feedstock & Utility Prices" | ||
|
|
||
| # Compressed Gas H2 Storage | ||
| # Currently using a linear fit between 350 and 700 bar (the two discrete HDSAM levels) | ||
| capex_per_kg_350_bar_2013 = 1560 # "Cost Data" row 89 | ||
| capex_per_kg_700_bar_2013 = 2340 # "Cost Data" row 96 | ||
| tank_capex_per_kg_2013 = ( | ||
| capex_per_kg_350_bar_2013 | ||
| + (capex_per_kg_700_bar_2013 - capex_per_kg_350_bar_2013) | ||
| * (self.config.storage_pressure_bar - 350) | ||
| / 350 | ||
| ) | ||
| tank_installation_factor = 1.3 | ||
| capex_2013 = tank_capex_per_kg_2013 * storage_capacity_kg * tank_installation_factor | ||
| tank_capex = capex_2013 * 1.36013289036545 / 1.22431893687708 | ||
|
|
||
| # Piping - simplifying a bit from HDSAM since this is a "drop in the bucket" | ||
| kg_d_per_pipe_m = 300 # Estimated by dividing B34 by B104 for many different values | ||
| pipe_length_m = terminal_capacity_kg_d / kg_d_per_pipe_m # Simplifying calc of B104 | ||
| pipe_capex_per_m_2005 = 300 # Using H2A "Estimate based on engineering judgement" | ||
| pipe_capex_2005 = pipe_length_m * pipe_capex_per_m_2005 | ||
| pipe_capex = pipe_capex_2005 * 1.53471220137887 / 1.0 | ||
|
|
||
| # Plumbing, electrical, instrumentation capex = "pei" - simplifying a bit from HDSAM | ||
| kg_d_per_bay = 1600 # Estimated by dividing B34 by B103 for many different values | ||
| num_bays = terminal_capacity_kg_d / kg_d_per_bay # Simplifying calc of B103 | ||
| pipe_capex_per_bay_2005 = 10000 # Using H2A "Estimate based on engineering judgement" | ||
| pei_capex_2005 = num_bays * pipe_capex_per_bay_2005 | ||
| pei_capex = pei_capex_2005 * 2.0454178984144 / 1.0 | ||
|
|
||
| # Buildings and structures | ||
| buildings_capex_2022 = 370029 | ||
| buildings_capex = buildings_capex_2022 * 1.33340822287126 / 1.85014603459897 | ||
|
|
||
| # Land - simplifying | ||
| kg_d_per_land_m2 = 4 # Estimated by dividing B34 by B192 for many different values | ||
| land_required_m2 = terminal_capacity_kg_d / kg_d_per_land_m2 | ||
| land_capex_per_m2_2022 = 12.35 | ||
| land_capex_2022 = land_required_m2 * land_capex_per_m2_2022 | ||
| land_capex = land_capex_2022 * 0.88 # Using CPI to convert to 2018 no CEPCI for land) | ||
|
|
||
| # Other | ||
| depreciable_capex = comp_capex + tank_capex + pipe_capex + pei_capex + buildings_capex | ||
| site_preparation_pct = 0.05 | ||
| engineering_design_pct = 0.1 | ||
| project_contingency_pct = 0.1 | ||
| licensing_pct = 0.0 | ||
|
jmartin4u marked this conversation as resolved.
|
||
| permitting_pct = 0.03 | ||
| owner_cost_pct = 0.12 | ||
| total_other_capex_pct = ( | ||
| site_preparation_pct | ||
| + engineering_design_pct | ||
| + project_contingency_pct | ||
| + licensing_pct | ||
| + permitting_pct | ||
| + owner_cost_pct | ||
| ) | ||
| other_capex_2022 = depreciable_capex * total_other_capex_pct | ||
| other_capex = other_capex_2022 * 0.88 # Using CPI to convert to 2018 | ||
|
|
||
| # Final, total installed cost: | ||
| installed_capex = depreciable_capex + land_capex + other_capex | ||
|
|
||
| # ============================================================================ | ||
| # Calculate OPEX - NEEDS TO BE UPDATED | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this "NEEDS TO BE UPDATED" note still relevant? part of this PR or follow-on work?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope - I put that there in an intermediate commit where I had just copy-pasted code from the geologic storage. Now it is updated, I'll remove the note |
||
| # ============================================================================ | ||
| # Operations and Maintenance costs [1] | ||
|
|
||
| # Labor | ||
| # Base case is 2 operators, 24 hours a day, 7 days a week for a 100,000 kg/day | ||
| # average capacity facility. Scaling factor of 0.25 is used for other sized facilities | ||
| # See equation on HDSAM "Cost Data" tab, row 12 | ||
| # Cost corrected to 2018 using HDSAM "Feedstock & Utility Prices" tab, Table B3 | ||
| system_flow_rate = terminal_capacity_kg_d | ||
| annual_hours = 2 * 8760 * (system_flow_rate / 100000) ** 0.25 | ||
| labor_rate_2013 = 27.51 | ||
| labor_rate = labor_rate_2013 * 1.29 / 1.09 | ||
| overhead = 0.5 | ||
| labor_om = annual_hours * labor_rate * overhead | ||
|
|
||
| # Other O&M | ||
| insurance_pct = 0.01 # "Compressed Gas H2 Terminal" tab, cell B229 | ||
| property_taxes_pct = 0.01 # "Compressed Gas H2 Terminal" tab, cell B229 | ||
| licensing_permits_pct = 0.001 | ||
| comp_om_pct = 0.04 | ||
| facility_om_pct = 0.01 | ||
| insurance_om = insurance_pct * installed_capex | ||
|
jmartin4u marked this conversation as resolved.
|
||
| property_taxes_om = property_taxes_pct * installed_capex | ||
| licensing_permits_om = licensing_permits_pct * installed_capex | ||
| comp_om = comp_om_pct * comp_capex | ||
| facility_om = facility_om_pct * (installed_capex - comp_capex) | ||
|
|
||
| # O&M excludes electricity requirements | ||
| total_om = ( | ||
| labor_om | ||
| + insurance_om | ||
| + licensing_permits_om | ||
| + property_taxes_om | ||
| + comp_om | ||
| + facility_om | ||
| ) | ||
|
|
||
| outputs["CapEx"] = installed_capex | ||
| outputs["OpEx"] = total_om | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,16 +15,20 @@ def __init__( | |
| self, | ||
| p_outlet, | ||
| flow_rate_kg_d, | ||
| compressor_type="pipeline", | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The compressor cost model works different for pipeline compressors and storage compressors - see HDSAM, "Cost Data" tab, "Compressor Costs" section (lines 120-131) |
||
| p_inlet=20, | ||
| n_compressors=2, | ||
| sizing_safety_factor=1.1, | ||
| ): | ||
| """ | ||
| Parameters: | ||
| --------------- | ||
| compressor_type: "pipeline" or "storage" | ||
| p_inlet: inlet pressure (bar) | ||
| p_outlet: outlet pressure (bar) | ||
| flow_rate_kg_d: mass flow rate in kg/day | ||
| """ | ||
| self.compressor_type = compressor_type | ||
| self.p_inlet = p_inlet # bar | ||
| self.p_outlet = p_outlet # bar | ||
| self.flow_rate_kg_d = flow_rate_kg_d # kg/day | ||
|
|
@@ -121,15 +125,23 @@ def compressor_costs(self): | |
| n_comp_total = ( | ||
| self.n_compressors + self.n_comp_back_up | ||
| ) # 2 compressors + 1 backup for reliability | ||
| production_volume_factor = 0.55 # Assume high production volume | ||
| CEPCI = 1.29 / 1.1 # Convert from 2007 to 2016$ | ||
| production_volume_factor = 0.55 # Assume high production volume - HDSAM "Scenario" tab Q7 | ||
|
|
||
| # From HDSAM "Cost Data" tab, rows 120-131 | ||
| if self.compressor_type == "pipeline": | ||
| cost_per_unit_2007 = 1962.2 * self.motor_rating**0.8225 * production_volume_factor | ||
| CEPCI_2007_to_2016 = 1.29 / 1.1 # Convert from 2007 to 2016$ | ||
| cost_per_unit = cost_per_unit_2007 * CEPCI_2007_to_2016 | ||
| install_cost_factor = 2 | ||
| elif self.compressor_type == "storage": | ||
| cost_per_unit_2013 = (3758.2 * self.motor_rating + 107562) * production_volume_factor | ||
| CEPCI_2013_to_2016 = 1.29 / 1.22 # Convert from 2013 to 2016$ | ||
| cost_per_unit = cost_per_unit_2013 * CEPCI_2013_to_2016 | ||
| install_cost_factor = 1.3 | ||
|
|
||
| cost_per_unit = 1962.2 * self.motor_rating**0.8225 * production_volume_factor * CEPCI | ||
| if self.stages > 2: | ||
| cost_per_unit = cost_per_unit * (1 + 0.2 * (self.stages - 2)) | ||
|
|
||
| install_cost_factor = 2 | ||
|
|
||
| direct_capex = cost_per_unit * n_comp_total * install_cost_factor | ||
|
|
||
| land_required = 10000 # m^2 This doesn't change at all in HDSAM...? | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.