Skip to content

Commit 85ee3d1

Browse files
johnjasajmartin4u
andauthored
Removed some shape_by_conn calls due to OM issues (#632)
* Removed some shape_by_conn calls due to OM issues * Moved last calls of copy_shape * Fixing test_pipe fail --------- Co-authored-by: jmartin4 <jonathan.martin@nrel.gov>
1 parent 21d109b commit 85ee3d1

7 files changed

Lines changed: 97 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- Added base class (`StorageOpenLoopControlBase`) and base configuration class (`StorageOpenLoopControlBaseConfig`) for open-loop storage control strategies and updated the existing open-loop storage control strategies to inherit these [PR 619](https://github.com/NatLabRockies/H2Integrate/pull/619)
2929
- Added a generic cost model for converters [PR 622](https://github.com/NatLabRockies/H2Integrate/pull/622)
3030
- Updated the `StorageAutoSizingModel` model to be compatible with Pyomo control strategies [PR 621](https://github.com/NatLabRockies/H2Integrate/pull/621)
31+
- Removed a few usages of `shape_by_conn` due to issues with OpenMDAO v3.43.0 release on some computers [PR 632](https://github.com/NatLabRockies/H2Integrate/pull/632)
3132

3233
## 0.7.1 [March 13, 2026]
3334

h2integrate/core/h2integrate_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,7 @@ def connect_technologies(self):
10521052
pass
10531053
else:
10541054
connection_component = self.supported_models[transport_type](
1055-
transport_item=transport_item
1055+
transport_item=transport_item, plant_config=self.plant_config
10561056
)
10571057

10581058
# Add the connection component to the model

h2integrate/transporters/cable.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@ class CablePerformanceModel(om.ExplicitComponent):
88

99
def initialize(self):
1010
self.options.declare("transport_item", values=["electricity"])
11+
self.options.declare("plant_config", types=dict)
1112

1213
def setup(self):
14+
n_timesteps = int(self.options["plant_config"]["plant"]["simulation"]["n_timesteps"])
1315
self.input_name = self.options["transport_item"] + "_in"
1416
self.output_name = self.options["transport_item"] + "_out"
1517
self.add_input(
1618
self.input_name,
1719
val=-1.0,
18-
shape_by_conn=True,
19-
copy_shape=self.output_name,
20+
shape=n_timesteps,
2021
units="kW",
2122
)
2223
self.add_output(
2324
self.output_name,
2425
val=-1.0,
25-
shape_by_conn=True,
26-
copy_shape=self.input_name,
26+
shape=n_timesteps,
2727
units="kW",
2828
)
2929

h2integrate/transporters/generic_splitter.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ def setup(self):
7575
additional_cls_name=self.__class__.__name__,
7676
)
7777

78+
n_timesteps = int(self.options["plant_config"]["plant"]["simulation"]["n_timesteps"])
79+
7880
self.add_input(
7981
f"{self.config.commodity}_in",
8082
val=0.0,
81-
shape_by_conn=True,
83+
shape=n_timesteps,
8284
units=self.config.commodity_rate_units,
8385
)
8486

@@ -94,22 +96,22 @@ def setup(self):
9496
self.add_input(
9597
"prescribed_commodity_to_priority_tech",
9698
val=self.config.prescribed_commodity_to_priority_tech,
97-
copy_shape=f"{self.config.commodity}_in",
99+
shape=n_timesteps,
98100
units=self.config.commodity_rate_units,
99101
desc="Prescribed amount of commodity to send to the priority technology",
100102
)
101103

102104
self.add_output(
103105
f"{self.config.commodity}_out1",
104106
val=0.0,
105-
copy_shape=f"{self.config.commodity}_in",
107+
shape=n_timesteps,
106108
units=self.config.commodity_rate_units,
107109
desc=f"{self.config.commodity} output to the first technology",
108110
)
109111
self.add_output(
110112
f"{self.config.commodity}_out2",
111113
val=0.0,
112-
copy_shape=f"{self.config.commodity}_in",
114+
shape=n_timesteps,
113115
units=self.config.commodity_rate_units,
114116
desc=f"{self.config.commodity} output to the second technology",
115117
)

h2integrate/transporters/pipe.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ def initialize(self):
2020
"water",
2121
],
2222
)
23+
self.options.declare("plant_config", types=dict)
2324

2425
def setup(self):
2526
transport_item = self.options["transport_item"]
2627
self.input_name = transport_item + "_in"
2728
self.output_name = transport_item + "_out"
2829

30+
n_timesteps = int(self.options["plant_config"]["plant"]["simulation"]["n_timesteps"])
31+
2932
if transport_item == "natural_gas":
3033
units = "MMBtu/h"
3134
elif transport_item == "water":
@@ -38,15 +41,13 @@ def setup(self):
3841
self.add_input(
3942
self.input_name,
4043
val=-1.0,
41-
shape_by_conn=True,
42-
copy_shape=self.output_name,
44+
shape=n_timesteps,
4345
units=units,
4446
)
4547
self.add_output(
4648
self.output_name,
4749
val=-1.0,
48-
shape_by_conn=True,
49-
copy_shape=self.input_name,
50+
shape=n_timesteps,
5051
units=units,
5152
)
5253

h2integrate/transporters/test/test_generic_splitter.py

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,16 @@ def splitter_tech_config_hydrogen():
2525

2626

2727
rng = np.random.default_rng(seed=0)
28+
N_TIMESTEPS = 10
29+
30+
31+
@fixture
32+
def plant_config():
33+
return {"plant": {"simulation": {"n_timesteps": N_TIMESTEPS}}}
2834

2935

3036
@pytest.mark.regression
31-
def test_splitter_ratio_mode_edge_cases_electricity(splitter_tech_config_electricity):
37+
def test_splitter_ratio_mode_edge_cases_electricity(splitter_tech_config_electricity, plant_config):
3238
"""Test the splitter in fraction mode with edge case fractions."""
3339
performance_config = {
3440
"split_mode": "fraction",
@@ -40,45 +46,45 @@ def test_splitter_ratio_mode_edge_cases_electricity(splitter_tech_config_electri
4046
tech_config = {"model_inputs": {"performance_parameters": performance_config}}
4147

4248
prob = om.Problem()
43-
comp = GenericSplitterPerformanceModel(tech_config=tech_config)
49+
comp = GenericSplitterPerformanceModel(tech_config=tech_config, plant_config=plant_config)
4450
prob.model.add_subsystem("comp", comp, promotes=["*"])
4551
ivc = om.IndepVarComp()
46-
ivc.add_output("electricity_in", val=100.0, units="kW")
52+
ivc.add_output("electricity_in", val=np.full(N_TIMESTEPS, 100.0), units="kW")
4753
ivc.add_output("fraction_to_priority_tech", val=0.0)
4854
prob.model.add_subsystem("ivc", ivc, promotes=["*"])
4955

5056
prob.setup()
5157

52-
electricity_input = 100.0
58+
electricity_input = np.full(N_TIMESTEPS, 100.0)
5359

5460
prob.set_val("electricity_in", electricity_input, units="kW")
5561
prob.set_val("fraction_to_priority_tech", 0.0)
5662
prob.run_model()
5763

58-
assert prob.get_val("electricity_out1", units="kW") == approx(0.0, abs=1e-10)
64+
assert prob.get_val("electricity_out1", units="kW") == approx(np.zeros(N_TIMESTEPS), abs=1e-10)
5965
assert prob.get_val("electricity_out2", units="kW") == approx(electricity_input, rel=1e-5)
6066

6167
prob.set_val("fraction_to_priority_tech", 1.0)
6268
prob.run_model()
6369

6470
assert prob.get_val("electricity_out1", units="kW") == approx(electricity_input, rel=1e-5)
65-
assert prob.get_val("electricity_out2", units="kW") == approx(0.0, abs=1e-10)
71+
assert prob.get_val("electricity_out2", units="kW") == approx(np.zeros(N_TIMESTEPS), abs=1e-10)
6672

6773
prob.set_val("fraction_to_priority_tech", 1.5)
6874
prob.run_model()
6975

7076
assert prob.get_val("electricity_out1", units="kW") == approx(electricity_input, rel=1e-5)
71-
assert prob.get_val("electricity_out2", units="kW") == approx(0.0, abs=1e-10)
77+
assert prob.get_val("electricity_out2", units="kW") == approx(np.zeros(N_TIMESTEPS), abs=1e-10)
7278

7379
prob.set_val("fraction_to_priority_tech", -0.5)
7480
prob.run_model()
7581

76-
assert prob.get_val("electricity_out1", units="kW") == approx(0.0, abs=1e-10)
82+
assert prob.get_val("electricity_out1", units="kW") == approx(np.zeros(N_TIMESTEPS), abs=1e-10)
7783
assert prob.get_val("electricity_out2", units="kW") == approx(electricity_input, rel=1e-5)
7884

7985

8086
@pytest.mark.regression
81-
def test_splitter_prescribed_electricity_mode(splitter_tech_config_electricity):
87+
def test_splitter_prescribed_electricity_mode(splitter_tech_config_electricity, plant_config):
8288
"""Test the splitter in prescribed_electricity mode."""
8389
performance_config = {
8490
"split_mode": "prescribed_commodity",
@@ -90,17 +96,17 @@ def test_splitter_prescribed_electricity_mode(splitter_tech_config_electricity):
9096
tech_config = {"model_inputs": {"performance_parameters": performance_config}}
9197

9298
prob = om.Problem()
93-
comp = GenericSplitterPerformanceModel(tech_config=tech_config)
99+
comp = GenericSplitterPerformanceModel(tech_config=tech_config, plant_config=plant_config)
94100
prob.model.add_subsystem("comp", comp, promotes=["*"])
95101
ivc = om.IndepVarComp()
96-
ivc.add_output("electricity_in", val=np.zeros(8760), units="kW")
97-
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(8760), units="kW")
102+
ivc.add_output("electricity_in", val=np.zeros(N_TIMESTEPS), units="kW")
103+
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(N_TIMESTEPS), units="kW")
98104
prob.model.add_subsystem("ivc", ivc, promotes=["*"])
99105

100106
prob.setup()
101107

102-
electricity_input = rng.random(8760) * 500 + 300
103-
prescribed_electricity = np.full(8760, 200.0)
108+
electricity_input = rng.random(N_TIMESTEPS) * 500 + 300
109+
prescribed_electricity = np.full(N_TIMESTEPS, 200.0)
104110

105111
prob.set_val("electricity_in", electricity_input, units="kW")
106112
prob.set_val("prescribed_commodity_to_priority_tech", prescribed_electricity, units="kW")
@@ -119,7 +125,9 @@ def test_splitter_prescribed_electricity_mode(splitter_tech_config_electricity):
119125

120126

121127
@pytest.mark.regression
122-
def test_splitter_prescribed_electricity_mode_limited_input(splitter_tech_config_electricity):
128+
def test_splitter_prescribed_electricity_mode_limited_input(
129+
splitter_tech_config_electricity, plant_config
130+
):
123131
"""
124132
Test the splitter in prescribed_electricity mode
125133
when input is less than prescribed electricity.
@@ -135,31 +143,31 @@ def test_splitter_prescribed_electricity_mode_limited_input(splitter_tech_config
135143
tech_config = {"model_inputs": {"performance_parameters": performance_config}}
136144

137145
prob = om.Problem()
138-
comp = GenericSplitterPerformanceModel(tech_config=tech_config)
146+
comp = GenericSplitterPerformanceModel(tech_config=tech_config, plant_config=plant_config)
139147
prob.model.add_subsystem("comp", comp, promotes=["*"])
140148
ivc = om.IndepVarComp()
141-
ivc.add_output("electricity_in", val=np.zeros(8760), units="kW")
142-
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(8760), units="kW")
149+
ivc.add_output("electricity_in", val=np.zeros(N_TIMESTEPS), units="kW")
150+
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(N_TIMESTEPS), units="kW")
143151
prob.model.add_subsystem("ivc", ivc, promotes=["*"])
144152

145153
prob.setup()
146154

147-
electricity_input = np.full(8760, 100.0)
148-
prescribed_electricity = np.full(8760, 150.0)
155+
electricity_input = np.full(N_TIMESTEPS, 100.0)
156+
prescribed_electricity = np.full(N_TIMESTEPS, 150.0)
149157

150158
prob.set_val("electricity_in", electricity_input, units="kW")
151159
prob.set_val("prescribed_commodity_to_priority_tech", prescribed_electricity, units="kW")
152160
prob.run_model()
153161

154162
expected_output1 = electricity_input
155-
expected_output2 = np.zeros(8760)
163+
expected_output2 = np.zeros(N_TIMESTEPS)
156164

157165
assert prob.get_val("electricity_out1", units="kW") == approx(expected_output1, rel=1e-5)
158166
assert prob.get_val("electricity_out2", units="kW") == approx(expected_output2, abs=1e-10)
159167

160168

161169
@pytest.mark.unit
162-
def test_splitter_invalid_mode(splitter_tech_config_electricity):
170+
def test_splitter_invalid_mode(splitter_tech_config_electricity, plant_config):
163171
"""Test that an invalid split mode raises an error."""
164172
performance_config = {
165173
"split_mode": "invalid_mode",
@@ -174,13 +182,13 @@ def test_splitter_invalid_mode(splitter_tech_config_electricity):
174182
match="Item invalid_mode not found in list",
175183
):
176184
prob = om.Problem()
177-
comp = GenericSplitterPerformanceModel(tech_config=tech_config)
185+
comp = GenericSplitterPerformanceModel(tech_config=tech_config, plant_config=plant_config)
178186
prob.model.add_subsystem("comp", comp, promotes=["*"])
179187
prob.setup()
180188

181189

182190
@pytest.mark.regression
183-
def test_splitter_scalar_inputs(splitter_tech_config_electricity):
191+
def test_splitter_scalar_inputs(splitter_tech_config_electricity, plant_config):
184192
"""Test the splitter with scalar inputs instead of arrays."""
185193
performance_config_ratio = {
186194
"split_mode": "fraction",
@@ -191,18 +199,22 @@ def test_splitter_scalar_inputs(splitter_tech_config_electricity):
191199
tech_config_ratio = {"model_inputs": {"performance_parameters": performance_config_ratio}}
192200

193201
prob = om.Problem()
194-
comp = GenericSplitterPerformanceModel(tech_config=tech_config_ratio)
202+
comp = GenericSplitterPerformanceModel(tech_config=tech_config_ratio, plant_config=plant_config)
195203
prob.model.add_subsystem("comp", comp, promotes=["*"])
196204
ivc = om.IndepVarComp()
197-
ivc.add_output("electricity_in", val=100.0, units="kW")
205+
ivc.add_output("electricity_in", val=np.full(N_TIMESTEPS, 100.0), units="kW")
198206
ivc.add_output("fraction_to_priority_tech", val=0.4)
199207
prob.model.add_subsystem("ivc", ivc, promotes=["*"])
200208

201209
prob.setup()
202210
prob.run_model()
203211

204-
assert prob.get_val("electricity_out1", units="kW") == approx(40.0, rel=1e-5)
205-
assert prob.get_val("electricity_out2", units="kW") == approx(60.0, rel=1e-5)
212+
assert prob.get_val("electricity_out1", units="kW") == approx(
213+
np.full(N_TIMESTEPS, 40.0), rel=1e-5
214+
)
215+
assert prob.get_val("electricity_out2", units="kW") == approx(
216+
np.full(N_TIMESTEPS, 60.0), rel=1e-5
217+
)
206218

207219
performance_config_prescribed = {
208220
"split_mode": "prescribed_commodity",
@@ -216,22 +228,32 @@ def test_splitter_scalar_inputs(splitter_tech_config_electricity):
216228
}
217229

218230
prob2 = om.Problem()
219-
comp2 = GenericSplitterPerformanceModel(tech_config=tech_config_prescribed)
231+
comp2 = GenericSplitterPerformanceModel(
232+
tech_config=tech_config_prescribed, plant_config=plant_config
233+
)
220234
prob2.model.add_subsystem("comp", comp2, promotes=["*"])
221235
ivc2 = om.IndepVarComp()
222-
ivc2.add_output("electricity_in", val=100.0, units="kW")
223-
ivc2.add_output("prescribed_commodity_to_priority_tech", val=30.0, units="kW")
236+
ivc2.add_output("electricity_in", val=np.full(N_TIMESTEPS, 100.0), units="kW")
237+
ivc2.add_output(
238+
"prescribed_commodity_to_priority_tech", val=np.full(N_TIMESTEPS, 30.0), units="kW"
239+
)
224240
prob2.model.add_subsystem("ivc", ivc2, promotes=["*"])
225241

226242
prob2.setup()
227243
prob2.run_model()
228244

229-
assert prob2.get_val("electricity_out1", units="kW") == approx(30.0, rel=1e-5)
230-
assert prob2.get_val("electricity_out2", units="kW") == approx(70.0, rel=1e-5)
245+
assert prob2.get_val("electricity_out1", units="kW") == approx(
246+
np.full(N_TIMESTEPS, 30.0), rel=1e-5
247+
)
248+
assert prob2.get_val("electricity_out2", units="kW") == approx(
249+
np.full(N_TIMESTEPS, 70.0), rel=1e-5
250+
)
231251

232252

233253
@pytest.mark.regression
234-
def test_splitter_prescribed_electricity_varied_array(splitter_tech_config_electricity):
254+
def test_splitter_prescribed_electricity_varied_array(
255+
splitter_tech_config_electricity, plant_config
256+
):
235257
"""Test the splitter in prescribed_electricity mode with a varied array (50-100 MW)."""
236258
performance_config = {
237259
"split_mode": "prescribed_commodity",
@@ -245,20 +267,20 @@ def test_splitter_prescribed_electricity_varied_array(splitter_tech_config_elect
245267
tech_config.update(splitter_tech_config_electricity)
246268

247269
prob = om.Problem()
248-
comp = GenericSplitterPerformanceModel(tech_config=tech_config)
270+
comp = GenericSplitterPerformanceModel(tech_config=tech_config, plant_config=plant_config)
249271
prob.model.add_subsystem("comp", comp, promotes=["*"])
250272
ivc = om.IndepVarComp()
251-
ivc.add_output("electricity_in", val=np.zeros(8760), units="kW")
252-
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(8760), units="kW")
273+
ivc.add_output("electricity_in", val=np.zeros(N_TIMESTEPS), units="kW")
274+
ivc.add_output("prescribed_commodity_to_priority_tech", val=np.zeros(N_TIMESTEPS), units="kW")
253275
prob.model.add_subsystem("ivc", ivc, promotes=["*"])
254276

255277
prob.setup()
256278

257279
# Generate varied prescribed electricity array between 50-100 MW (50,000-100,000 kW)
258-
prescribed_electricity = rng.random(8760) * 50000 + 50000 # 50-100 MW range
280+
prescribed_electricity = rng.random(N_TIMESTEPS) * 50000 + 50000 # 50-100 MW range
259281

260282
# Input electricity should be higher than prescribed to test both scenarios
261-
electricity_input = rng.random(8760) * 30000 + 120000 # 120-150 MW range
283+
electricity_input = rng.random(N_TIMESTEPS) * 30000 + 120000 # 120-150 MW range
262284

263285
prob.set_val("electricity_in", electricity_input, units="kW")
264286
prob.set_val("prescribed_commodity_to_priority_tech", prescribed_electricity, units="kW")
@@ -278,7 +300,7 @@ def test_splitter_prescribed_electricity_varied_array(splitter_tech_config_elect
278300
assert total_output == approx(electricity_input, rel=1e-5)
279301

280302
# Test with some time steps where prescribed > available
281-
electricity_input_limited = rng.random(8760) * 30000 + 20000 # 20-50 MW range
303+
electricity_input_limited = rng.random(N_TIMESTEPS) * 30000 + 20000 # 20-50 MW range
282304
prob.set_val("electricity_in", electricity_input_limited, units="kW")
283305
prob.run_model()
284306

0 commit comments

Comments
 (0)