Skip to content

Commit dda9dc8

Browse files
authored
Update Natural GeoH2 with Yearly CF (#552)
1 parent fb15c67 commit dda9dc8

5 files changed

Lines changed: 144 additions & 35 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
all tests must be marked via `@pytest.mark.<test-type>`.
2121
- Partial testing suite refactor to parameterize many of the common fixtures and test routines.
2222
- `unittest` style tests are refactored to be `pytest` style tests for test consistency.
23+
- Update finance models to use annual capacity factor and rated production rather than annual production.
24+
- Update `NaturalGeoH2PerformanceModel` to output yearly metrics.
2325
- Added a pre-commit hook for `yamlfix` to auto-format YAML files and `yamlfix`'d all YAML files for consistent formatting
2426
- Insert model names for technologies with control strategies to simplify Pyomo workflows.
2527
- Refactored pyomo code by splitting apart classes into separate files and removing unused properties

examples/04_geo_h2/run_geo_h2.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212

1313

1414
hydrogen_out = h2i_nat.prob.model.plant.geoh2_well_subsurface.NaturalGeoH2PerformanceModel.get_val(
15-
"hydrogen_out", units="kg/h"
15+
"annual_hydrogen_produced", units="t/year"
1616
)
17-
plt.plot(hydrogen_out)
18-
plt.xlabel("Time (hours)")
19-
plt.ylabel("Wellhead Gas Flow (kg/h)")
20-
plt.title("Wellhead Gas Flow Profile Over First Year")
17+
start_year = (
18+
h2i_nat.prob.model.plant.finance_subgroup_h2.hydrogen_finance_default.params.analysis_start_year
19+
)
20+
years = range(start_year, start_year + len(hydrogen_out))
21+
plt.plot(years, hydrogen_out)
22+
plt.xlabel("Year")
23+
plt.ylabel("Hydrogen Production (tonne per annum)")
24+
plt.title("Hydrogen Production over Well Lifetime")
2125
plt.grid()
2226
plt.show()
2327

examples/test/test_all_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2115,7 +2115,7 @@ def test_natural_geoh2(subtests, temp_copy_of_example):
21152115
pytest.approx(
21162116
h2i_nat.prob.get_val("finance_subgroup_h2.LCOH", units="USD/kg"), rel=1e-6
21172117
)
2118-
== 1.5870496689
2118+
== 1.3089029
21192119
)
21202120
with subtests.test("subsurface Capex"):
21212121
assert (

h2integrate/converters/hydrogen/geologic/simple_natural_geoh2.py

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,27 @@ class NaturalGeoH2PerformanceModel(GeoH2SubsurfacePerformanceBaseClass):
118118
Hourly wellhead gas production profile from natural accumulations,
119119
covering one simulated year (8760 hours), in kg/h.
120120
121+
wellhead_gas_out (ndarray):
122+
Hourly wellhead gas production profile used for downstream modeling, in kg/h.
123+
121124
max_wellhead_gas (float):
122125
Maximum wellhead gas output over the system lifetime, in kg/h.
126+
127+
rated_hydrogen_production (float):
128+
Rated hydrogen production at the wellhead, in kg/h.
129+
130+
total_wellhead_gas_produced (float):
131+
Total mass of gas produced at the wellhead over the simulation period, in kg/year.
132+
133+
total_hydrogen_produced (float):
134+
Total mass of hydrogen produced at the wellhead over the simulation period, in kg/year.
135+
136+
annual_hydrogen_produced (list):
137+
List of total hydrogen produced for each year of the simulation, in kg/year.
138+
139+
capacity_factor (list):
140+
List of capacity factors for each year of the simulation, calculated as the ratio
141+
of annual hydrogen production to the maximum hydrogen production of the well.
123142
"""
124143

125144
def setup(self):
@@ -229,40 +248,42 @@ def compute(self, inputs, outputs):
229248
h2_mw = 2.016
230249
x_h2 = wh_h2_conc / 100
231250
w_h2 = x_h2 * h2_mw / (x_h2 * h2_mw + (1 - x_h2) * balance_mw)
232-
avg_h2_flow = w_h2 * wh_flow_profile
251+
h2_flow = w_h2 * wh_flow_profile
252+
253+
# calculate the yearly capacity factors and add to dict
254+
yearly_h2_cf = []
255+
yearly_h2 = []
256+
# for each 8760 in the flow profile, calculate the capacity factor for
257+
# that year and add to array
258+
for year in range(self.options["plant_config"]["plant"]["plant_life"]):
259+
start_idx = year * 8760
260+
end_idx = start_idx + 8760
261+
max_h2_produced = ramp_up_flow * w_h2 * 8760
262+
yearly_h2_produced = np.sum(h2_flow[start_idx:end_idx])
263+
yearly_h2.append(yearly_h2_produced)
264+
yearly_h2_cf.append(yearly_h2_produced / max_h2_produced)
233265

234266
# Parse outputs
235267
outputs["wellhead_h2_concentration_mass"] = w_h2 * 100
236268
outputs["wellhead_h2_concentration_mol"] = wh_h2_conc
237269
outputs["lifetime_wellhead_flow"] = np.average(wh_flow_profile)
238-
# fill "wellhead_gas_out_natural" with first year profile from wh_flow_profile
239-
# updated to average value over 8760 until simulation length handling is improved
240-
# commented code on lines 240 to 246 have the original intended functionality
241-
outputs["wellhead_gas_out_natural"] = np.full(
242-
n_timesteps, np.average(wh_flow_profile)
243-
) # wh_flow_profile[:n_timesteps]
244-
outputs["wellhead_gas_out"] = np.full(
245-
n_timesteps, np.average(wh_flow_profile)
246-
) # wh_flow_profile[:n_timesteps]
247-
outputs["hydrogen_out"] = np.full(
248-
n_timesteps, np.average(avg_h2_flow)
249-
) # avg_h2_flow[:n_timesteps]
270+
# use lifetime average because H2I is unable to handle multiyear
271+
# commodity_out. Noted in issue #475.
272+
outputs["wellhead_gas_out_natural"] = np.full(n_timesteps, np.average(wh_flow_profile))
273+
outputs["wellhead_gas_out"] = np.full(n_timesteps, np.average(wh_flow_profile))
274+
outputs["hydrogen_out"] = np.full(n_timesteps, np.average(h2_flow))
275+
250276
outputs["max_wellhead_gas"] = ramp_up_flow
251-
# this is lifetime flow which decreases over time
252-
outputs["total_wellhead_gas_produced"] = (
253-
np.average(wh_flow_profile) * n_timesteps
254-
) # np.sum(wh_flow_profile)
255-
outputs["total_hydrogen_produced"] = (
256-
np.average(avg_h2_flow) * n_timesteps
257-
) # np.sum(avg_h2_flow)
258-
259-
outputs["annual_hydrogen_produced"] = (
260-
outputs["total_hydrogen_produced"] / self.fraction_of_year_simulated
261-
)
262277
outputs["rated_hydrogen_production"] = ramp_up_flow * w_h2
263-
outputs["capacity_factor"] = outputs["total_hydrogen_produced"] / (
264-
outputs["rated_hydrogen_production"] * self.n_timesteps
265-
)
278+
# total is amount produced over simulation, which is a single year
279+
# for now so lifetime average is more accurate for model
280+
outputs["total_wellhead_gas_produced"] = np.average(wh_flow_profile) * n_timesteps
281+
outputs["total_hydrogen_produced"] = np.average(h2_flow) * n_timesteps
282+
283+
# output array of hydrogen produced and capacity factors
284+
# for each year of the simulation
285+
outputs["annual_hydrogen_produced"] = yearly_h2
286+
outputs["capacity_factor"] = yearly_h2_cf
266287

267288
def arps_decline_curve_fit(self, t, qi, Di, b):
268289
"""Arps decline curve model based on Arps (1945)

h2integrate/converters/hydrogen/geologic/test/test_geologic_h2.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,17 +142,99 @@ def test_natural_geoh2_well_performance(subtests, plant_config):
142142
prob.setup()
143143
prob.run_model()
144144

145+
with subtests.test("max wellhead gas"):
146+
assert (
147+
pytest.approx(prob.model.get_val("perf.max_wellhead_gas", units="kg/h"), rel=1e-6)
148+
== 42000.0
149+
)
150+
# test wellhead_h2_concentration_mass wellhead_h2_concentration_mol well_head_gas_out_natural
151+
with subtests.test("wellhead_h2_concentration_mass"):
152+
assert (
153+
pytest.approx(
154+
prob.model.get_val("perf.wellhead_h2_concentration_mass", units="percent"), rel=1e-6
155+
)
156+
== 62.15760093
157+
)
158+
159+
with subtests.test("wellhead_h2_concentration_mol"):
160+
assert (
161+
pytest.approx(
162+
prob.model.get_val("perf.wellhead_h2_concentration_mol", units="percent"), rel=1e-6
163+
)
164+
== 95.0
165+
)
166+
with subtests.test("well_head_gas_out_natural"):
167+
assert (
168+
pytest.approx(
169+
np.sum(prob.model.get_val("perf.wellhead_gas_out_natural", units="kg/h")), rel=1e-6
170+
)
171+
== 85426105.21216634
172+
)
173+
with subtests.test("wellhead_gas_out"):
174+
assert (
175+
pytest.approx(
176+
np.sum(prob.model.get_val("perf.wellhead_gas_out", units="kg/h")), rel=1e-6
177+
)
178+
== 85426105.21217233
179+
)
145180
with subtests.test("Well hydrogen production"):
146181
assert (
147182
pytest.approx(np.mean(prob.model.get_val("perf.hydrogen_out", units="kg/h")), rel=1e-6)
148183
== 6061.508855232839
149-
), 1e-6
184+
)
150185

151186
with subtests.test("total h2 out"):
152187
assert (
153188
pytest.approx(prob.model.get_val("perf.total_hydrogen_produced", units="kg"), rel=1e-6)
154189
== 53098817.57183966
155-
), 1e-6
190+
)
191+
with subtests.test("total_wellhead_gas_produced"):
192+
assert (
193+
pytest.approx(
194+
prob.model.get_val("perf.total_wellhead_gas_produced", units="kg/year"), rel=1e-6
195+
)
196+
== 85426105.21217233
197+
)
198+
199+
with subtests.test("cf"):
200+
assert pytest.approx(
201+
prob.model.get_val("perf.capacity_factor", units="unitless"), rel=1e-6
202+
) == [
203+
0.8675885,
204+
0.42536644,
205+
0.25890337,
206+
0.18514585,
207+
0.14357352,
208+
0.1169487,
209+
0.09847076,
210+
0.08491557,
211+
0.07455868,
212+
0.06639494,
213+
]
214+
215+
with subtests.test("annual_hydrogen_production"):
216+
assert pytest.approx(
217+
prob.model.get_val("perf.annual_hydrogen_produced", units="kg/yr"), rel=1e-6
218+
) == [
219+
198409026.01277646,
220+
97277155.59721786,
221+
59208675.263037845,
222+
42341049.07389179,
223+
32833862.72029864,
224+
26745026.520159103,
225+
22519302.237744503,
226+
19419361.600134037,
227+
17050841.849094056,
228+
15183874.84404233,
229+
]
230+
231+
with subtests.test("rated h2 production"):
232+
assert (
233+
pytest.approx(
234+
prob.model.get_val("perf.rated_hydrogen_production", units="kg/h"), rel=1e-6
235+
)
236+
== 26106.19239257
237+
)
156238

157239

158240
@pytest.mark.unit

0 commit comments

Comments
 (0)