Skip to content

Commit 5bba01d

Browse files
Added variable o&m (#235)
* added variable into cost and finance models * added plant life input to cost model tests that were missing it * added varom output to wombat model * updated CostModelBaseClass doc string * updated changelog * added initial test for variable om in profast and updated profast script * updated added finance tests * updated units in new finance tests * added plant life to test plant configs * updated feedstock cost model and natgas test * fixed failing tests in feedstock and custom elec cost * updated variable om tests in test_finance.py * renamed attr_filter * Using create_years_of_operation in tests * Naming fix * Fixing order of args * Mods due to review feedback --------- Co-authored-by: John Jasa <johnjasa11@gmail.com>
1 parent 52a21a7 commit 5bba01d

15 files changed

Lines changed: 338 additions & 34 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
- Added `"custom_electrolyzer_cost"` model, an electrolyzer cost model that allows for user-defined capex and opex values
5252
- Made `pipe` and `cable` substance-agnostic rather than hard-coded for `hydrogen` and `electricity`
5353
- Change finance handling to use `finance_subgroups` and `finance_groups` defined in the `plant_config` rather than previous `financial_groups` in the `tech_config` and `technologies_to_include_in_metrics` in `plant_config`
54+
- Added variable O&M to `CostModelBaseClass` and integrated into finance-related models
5455
- Added generic storage model, useful for battery, hydrogen, CO2, or other resource storage.
5556
- Added wind resource model, API baseclasses, updated examples, and documentation.
5657

h2integrate/converters/ammonia/test/test_simple_ammonia_model.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@
4242

4343
def test_simple_ammonia_performance_model():
4444
plant_info = {
45+
"plant_life": 30,
4546
"simulation": {
4647
"n_timesteps": 2,
4748
"dt": 3600,
48-
}
49+
},
4950
}
5051

5152
prob = om.Problem()
@@ -67,10 +68,11 @@ def test_simple_ammonia_performance_model():
6768

6869
def test_simple_ammonia_cost_model(subtests):
6970
plant_info = {
71+
"plant_life": 30,
7072
"simulation": {
7173
"n_timesteps": 8760,
7274
"dt": 3600,
73-
}
75+
},
7476
}
7577

7678
prob = om.Problem()

h2integrate/converters/hydrogen/test/test_custom_electrolyzer_cost_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def test_custom_electrolyzer_cost_model(subtests):
2222

2323
plant_config = {
2424
"plant": {
25+
"plant_life": 30,
2526
"simulation": {
2627
"n_timesteps": 8760, # Default number of timesteps for the simulation
2728
},

h2integrate/converters/hydrogen/wombat_model.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,17 @@ def setup(self):
4040
self.config = WOMBATModelConfig.from_dict(
4141
merge_shared_inputs(self.options["tech_config"]["model_inputs"], "performance")
4242
)
43-
43+
plant_life = int(self.options["plant_config"]["plant"]["plant_life"])
4444
self.add_output("capacity_factor", val=0.0, units=None)
4545
self.add_output("CapEx", val=0.0, units="USD", desc="Capital expenditure")
4646
self.add_output("OpEx", val=0.0, units="USD/year", desc="Operational expenditure")
47+
self.add_output(
48+
"VarOpEx",
49+
val=0.0,
50+
shape=plant_life,
51+
units="USD/year",
52+
desc="Variable operational expenditure",
53+
)
4754
self.add_discrete_output("cost_year", val=0, desc="Dollar year for costs")
4855
self.add_output(
4956
"percent_hydrogen_lost",

h2integrate/converters/nitrogen/test/test_simple_asu_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
plant_config = {
99
"plant": {
10+
"plant_life": 30,
1011
"simulation": {
1112
"n_timesteps": 8760, # Default number of timesteps for the simulation
1213
},

h2integrate/converters/solar/test/test_pysam_with_atb_costs.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,11 @@ def test_utility_pv_cost(
132132
}
133133

134134
plant_info = {
135+
"plant_life": 30,
135136
"simulation": {
136137
"n_timesteps": 8760,
137138
"dt": 3600,
138-
}
139+
},
139140
}
140141

141142
prob = om.Problem()
@@ -189,10 +190,11 @@ def test_commercial_pv_cost(
189190
}
190191

191192
plant_info = {
193+
"plant_life": 30,
192194
"simulation": {
193195
"n_timesteps": 8760,
194196
"dt": 3600,
195-
}
197+
},
196198
}
197199

198200
prob = om.Problem()
@@ -243,10 +245,11 @@ def test_residential_pv_cost(
243245
}
244246

245247
plant_info = {
248+
"plant_life": 30,
246249
"simulation": {
247250
"n_timesteps": 8760,
248251
"dt": 3600,
249-
}
252+
},
250253
}
251254

252255
prob = om.Problem()

h2integrate/core/feedstocks.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,5 @@ def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
9292
cost_per_year = sum(price * hourly_consumption)
9393

9494
outputs["CapEx"] = self.config.start_up_cost
95-
outputs["OpEx"] = self.config.annual_cost + cost_per_year
95+
outputs["OpEx"] = self.config.annual_cost
96+
outputs["VarOpEx"] = cost_per_year

h2integrate/core/h2integrate_model.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,9 @@ def connect_technologies(self):
751751
self.plant.connect(
752752
f"{tech_name}.OpEx", f"finance_subgroup_{group_id}.opex_{tech_name}"
753753
)
754+
self.plant.connect(
755+
f"{tech_name}.VarOpEx", f"finance_subgroup_{group_id}.varopex_{tech_name}"
756+
)
754757
self.plant.connect(
755758
f"{tech_name}.cost_year",
756759
f"finance_subgroup_{group_id}.cost_year_{tech_name}",

h2integrate/core/model_baseclasses.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ class CostModelBaseClass(om.ExplicitComponent):
77
88
Outputs:
99
- CapEx (float): capital expenditure costs in $
10-
- OpEx (float): annual operating expenditure costs in $/year
10+
- OpEx (float): annual fixed operating expenditure costs in $/year
11+
- VarOpEx (float): annual variable operating expenditure costs in $/year
1112
1213
Discrete Outputs:
1314
- cost_year (int): dollar-year corresponding to CapEx and OpEx values.
@@ -20,15 +21,17 @@ def initialize(self):
2021
self.options.declare("tech_config", types=dict)
2122

2223
def setup(self):
24+
plant_life = int(self.options["plant_config"]["plant"]["plant_life"])
2325
# Define outputs: CapEx and OpEx costs
2426
self.add_output("CapEx", val=0.0, units="USD", desc="Capital expenditure")
27+
self.add_output("OpEx", val=0.0, units="USD/year", desc="Fixed operational expenditure")
2528
self.add_output(
26-
"OpEx",
29+
"VarOpEx",
2730
val=0.0,
31+
shape=plant_life,
2832
units="USD/year",
29-
desc="Total (fixed and variable) operational expenditure",
33+
desc="Variable operational expenditure",
3034
)
31-
3235
# Define discrete outputs: cost_year
3336
self.add_discrete_output(
3437
"cost_year", val=self.config.cost_year, desc="Dollar year for costs"

h2integrate/core/test/test_feedstocks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def test_single_feedstock_natural_gas(self):
100100

101101
# Check outputs
102102
capex = prob_cost.get_val("feedstock_cost.CapEx")[0]
103-
opex = prob_cost.get_val("feedstock_cost.OpEx")[0]
103+
opex = prob_cost.get_val("feedstock_cost.VarOpEx")[0]
104104

105105
self.assertEqual(capex, 100000.0) # start_up_cost
106106
expected_opex = 0.0 + 4.2 * consumption.sum() # annual_cost + price * consumption
@@ -235,7 +235,7 @@ def test_variable_pricing(self):
235235
prob.run_model()
236236

237237
# Check that OpEx reflects variable pricing
238-
opex = prob.get_val("feedstock_cost.OpEx")[0]
238+
opex = prob.get_val("feedstock_cost.VarOpEx")[0]
239239
expected_opex = 0.0 + np.sum(hourly_prices * consumption)
240240
self.assertAlmostEqual(opex, expected_opex, places=5)
241241

0 commit comments

Comments
 (0)