ahuora-builder 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. ahuora_builder/__init__.py +0 -0
  2. ahuora_builder/arc_manager.py +33 -0
  3. ahuora_builder/build_state.py +57 -0
  4. ahuora_builder/custom/PIDController.py +494 -0
  5. ahuora_builder/custom/PySMOModel.py +178 -0
  6. ahuora_builder/custom/SimpleEffectivenessHX_DH.py +727 -0
  7. ahuora_builder/custom/__init__.py +0 -0
  8. ahuora_builder/custom/add_initial_dynamics.py +35 -0
  9. ahuora_builder/custom/custom_compressor.py +107 -0
  10. ahuora_builder/custom/custom_cooler.py +33 -0
  11. ahuora_builder/custom/custom_heat_exchanger.py +183 -0
  12. ahuora_builder/custom/custom_heat_exchanger_1d.py +258 -0
  13. ahuora_builder/custom/custom_heater.py +41 -0
  14. ahuora_builder/custom/custom_pressure_changer.py +34 -0
  15. ahuora_builder/custom/custom_pump.py +107 -0
  16. ahuora_builder/custom/custom_separator.py +371 -0
  17. ahuora_builder/custom/custom_tank.py +133 -0
  18. ahuora_builder/custom/custom_turbine.py +132 -0
  19. ahuora_builder/custom/custom_valve.py +300 -0
  20. ahuora_builder/custom/custom_variable.py +29 -0
  21. ahuora_builder/custom/direct_steam_injection.py +371 -0
  22. ahuora_builder/custom/energy/__init__.py +0 -0
  23. ahuora_builder/custom/energy/acBus.py +280 -0
  24. ahuora_builder/custom/energy/ac_property_package.py +279 -0
  25. ahuora_builder/custom/energy/battery.py +170 -0
  26. ahuora_builder/custom/energy/bus.py +182 -0
  27. ahuora_builder/custom/energy/energy_mixer.py +195 -0
  28. ahuora_builder/custom/energy/energy_splitter.py +228 -0
  29. ahuora_builder/custom/energy/grid.py +173 -0
  30. ahuora_builder/custom/energy/hydro.py +169 -0
  31. ahuora_builder/custom/energy/link.py +137 -0
  32. ahuora_builder/custom/energy/load.py +155 -0
  33. ahuora_builder/custom/energy/mainDistributionBoard.py +257 -0
  34. ahuora_builder/custom/energy/power_property_package.py +253 -0
  35. ahuora_builder/custom/energy/solar.py +176 -0
  36. ahuora_builder/custom/energy/storage.py +230 -0
  37. ahuora_builder/custom/energy/storage_wrapper +0 -0
  38. ahuora_builder/custom/energy/tests/__init__.py +0 -0
  39. ahuora_builder/custom/energy/tests/test_bus.py +44 -0
  40. ahuora_builder/custom/energy/tests/test_energy_mixer.py +46 -0
  41. ahuora_builder/custom/energy/tests/test_mdb.py +49 -0
  42. ahuora_builder/custom/energy/transformer.py +187 -0
  43. ahuora_builder/custom/energy/transformer_property_package.py +267 -0
  44. ahuora_builder/custom/energy/transmissionLine.py +228 -0
  45. ahuora_builder/custom/energy/wind.py +206 -0
  46. ahuora_builder/custom/hda_ideal_VLE.py +1341 -0
  47. ahuora_builder/custom/hda_reaction.py +182 -0
  48. ahuora_builder/custom/heat_exchanger_1d_wrapper.py +31 -0
  49. ahuora_builder/custom/integration_block.py +106 -0
  50. ahuora_builder/custom/inverted.py +81 -0
  51. ahuora_builder/custom/performance_curves.py +1 -0
  52. ahuora_builder/custom/reactions/__init__.py +0 -0
  53. ahuora_builder/custom/reactions/hda_stoich.py +10 -0
  54. ahuora_builder/custom/simple_separator.py +680 -0
  55. ahuora_builder/custom/tests/__init__.py +0 -0
  56. ahuora_builder/custom/tests/test_SimpleEffectivenessHX_DH.py +91 -0
  57. ahuora_builder/custom/tests/test_custom_tank.py +70 -0
  58. ahuora_builder/custom/tests/test_direct_steam_injection.py +41 -0
  59. ahuora_builder/custom/tests/test_simple_separator.py +46 -0
  60. ahuora_builder/custom/tests/test_waterpipe.py +46 -0
  61. ahuora_builder/custom/thermal_utility_systems/desuperheater.py +624 -0
  62. ahuora_builder/custom/thermal_utility_systems/header.py +889 -0
  63. ahuora_builder/custom/thermal_utility_systems/simple_heat_pump.py +567 -0
  64. ahuora_builder/custom/thermal_utility_systems/steam_header.py +353 -0
  65. ahuora_builder/custom/thermal_utility_systems/steam_user.py +944 -0
  66. ahuora_builder/custom/thermal_utility_systems/temp.py +349 -0
  67. ahuora_builder/custom/thermal_utility_systems/tests/test_desuperheater.py +142 -0
  68. ahuora_builder/custom/thermal_utility_systems/tests/test_header.py +998 -0
  69. ahuora_builder/custom/thermal_utility_systems/tests/test_ntu_hx.py +129 -0
  70. ahuora_builder/custom/thermal_utility_systems/tests/test_simple_heat_pump.py +120 -0
  71. ahuora_builder/custom/thermal_utility_systems/tests/test_steam_header.py +703 -0
  72. ahuora_builder/custom/thermal_utility_systems/tests/test_steam_user.py +277 -0
  73. ahuora_builder/custom/thermal_utility_systems/tests/test_waterpipe.py +36 -0
  74. ahuora_builder/custom/thermal_utility_systems/tests/test_willans_turbine.py +253 -0
  75. ahuora_builder/custom/thermal_utility_systems/willans_turbine.py +804 -0
  76. ahuora_builder/custom/translator.py +129 -0
  77. ahuora_builder/custom/updated_pressure_changer.py +1404 -0
  78. ahuora_builder/custom/valve_wrapper.py +38 -0
  79. ahuora_builder/custom/water_tank_with_units.py +456 -0
  80. ahuora_builder/diagnostics/__init__.py +0 -0
  81. ahuora_builder/diagnostics/infeasibilities.py +40 -0
  82. ahuora_builder/diagnostics/tests/__init__.py +0 -0
  83. ahuora_builder/diagnostics/tests/test_infeasibilities.py +28 -0
  84. ahuora_builder/flowsheet_manager.py +542 -0
  85. ahuora_builder/flowsheet_manager_type.py +20 -0
  86. ahuora_builder/generate_python_file.py +440 -0
  87. ahuora_builder/methods/BlockContext.py +84 -0
  88. ahuora_builder/methods/__init__.py +0 -0
  89. ahuora_builder/methods/adapter.py +355 -0
  90. ahuora_builder/methods/adapter_library.py +549 -0
  91. ahuora_builder/methods/adapter_methods.py +80 -0
  92. ahuora_builder/methods/expression_parsing.py +105 -0
  93. ahuora_builder/methods/load_unit_model.py +147 -0
  94. ahuora_builder/methods/slice_manipulation.py +7 -0
  95. ahuora_builder/methods/tests/__init__.py +0 -0
  96. ahuora_builder/methods/tests/test_expression_parsing.py +15 -0
  97. ahuora_builder/methods/units_handler.py +129 -0
  98. ahuora_builder/ml_wizard.py +101 -0
  99. ahuora_builder/port_manager.py +20 -0
  100. ahuora_builder/properties_manager.py +44 -0
  101. ahuora_builder/property_package_manager.py +78 -0
  102. ahuora_builder/solver.py +38 -0
  103. ahuora_builder/tear_manager.py +98 -0
  104. ahuora_builder/tests/__init__.py +0 -0
  105. ahuora_builder/tests/test_generate_python_file/__init__.py +0 -0
  106. ahuora_builder/tests/test_generate_python_file/configurations/compressor_generated.py +63 -0
  107. ahuora_builder/tests/test_generate_python_file/configurations/heat_exchanger_generated.py +70 -0
  108. ahuora_builder/tests/test_generate_python_file/configurations/pump_generated.py +84 -0
  109. ahuora_builder/tests/test_generate_python_file/configurations/recycle_generated.py +73 -0
  110. ahuora_builder/tests/test_generate_python_file/test_generate_python_file.py +108 -0
  111. ahuora_builder/tests/test_solver/__init__.py +0 -0
  112. ahuora_builder/tests/test_solver/configurations/BT_PR.json +59 -0
  113. ahuora_builder/tests/test_solver/configurations/BT_PR_solved.json +59 -0
  114. ahuora_builder/tests/test_solver/configurations/bus.json +99 -0
  115. ahuora_builder/tests/test_solver/configurations/bus_solved.json +50 -0
  116. ahuora_builder/tests/test_solver/configurations/compound_separator.json +377 -0
  117. ahuora_builder/tests/test_solver/configurations/compound_separator_solved.json +374 -0
  118. ahuora_builder/tests/test_solver/configurations/compressor.json +38 -0
  119. ahuora_builder/tests/test_solver/configurations/compressor_solved.json +68 -0
  120. ahuora_builder/tests/test_solver/configurations/constraints.json +44 -0
  121. ahuora_builder/tests/test_solver/configurations/constraints_solved.json +59 -0
  122. ahuora_builder/tests/test_solver/configurations/control.json +39 -0
  123. ahuora_builder/tests/test_solver/configurations/control_solved.json +68 -0
  124. ahuora_builder/tests/test_solver/configurations/dynamic_tank.json +733 -0
  125. ahuora_builder/tests/test_solver/configurations/dynamic_tank_solved.json +846 -0
  126. ahuora_builder/tests/test_solver/configurations/elimination.json +39 -0
  127. ahuora_builder/tests/test_solver/configurations/elimination_solved.json +68 -0
  128. ahuora_builder/tests/test_solver/configurations/expressions.json +68 -0
  129. ahuora_builder/tests/test_solver/configurations/expressions_solved.json +104 -0
  130. ahuora_builder/tests/test_solver/configurations/header.json +1192 -0
  131. ahuora_builder/tests/test_solver/configurations/header_solved.json +761 -0
  132. ahuora_builder/tests/test_solver/configurations/heat_exchanger.json +63 -0
  133. ahuora_builder/tests/test_solver/configurations/heat_exchanger_solved.json +104 -0
  134. ahuora_builder/tests/test_solver/configurations/heat_pump.json +137 -0
  135. ahuora_builder/tests/test_solver/configurations/heat_pump_solved.json +104 -0
  136. ahuora_builder/tests/test_solver/configurations/machine_learning.json +2156 -0
  137. ahuora_builder/tests/test_solver/configurations/machine_learning_solved.json +266 -0
  138. ahuora_builder/tests/test_solver/configurations/mass_flow_tear.json +77 -0
  139. ahuora_builder/tests/test_solver/configurations/mass_flow_tear_solved.json +68 -0
  140. ahuora_builder/tests/test_solver/configurations/milk_heater.json +521 -0
  141. ahuora_builder/tests/test_solver/configurations/milk_heater_solved.json +311 -0
  142. ahuora_builder/tests/test_solver/configurations/mixer.json +44 -0
  143. ahuora_builder/tests/test_solver/configurations/mixer_solved.json +86 -0
  144. ahuora_builder/tests/test_solver/configurations/optimization.json +62 -0
  145. ahuora_builder/tests/test_solver/configurations/optimization_solved.json +59 -0
  146. ahuora_builder/tests/test_solver/configurations/propane_heat_pump.json +167 -0
  147. ahuora_builder/tests/test_solver/configurations/propane_heat_pump_solved.json +158 -0
  148. ahuora_builder/tests/test_solver/configurations/propane_recycle.json +141 -0
  149. ahuora_builder/tests/test_solver/configurations/propane_recycle_solved.json +104 -0
  150. ahuora_builder/tests/test_solver/configurations/pump.json +64 -0
  151. ahuora_builder/tests/test_solver/configurations/pump_solved.json +59 -0
  152. ahuora_builder/tests/test_solver/configurations/pump_unit_conversions.json +63 -0
  153. ahuora_builder/tests/test_solver/configurations/recycle.json +49 -0
  154. ahuora_builder/tests/test_solver/configurations/recycle_solved.json +50 -0
  155. ahuora_builder/tests/test_solver/configurations/sb_vapor_frac.json +29 -0
  156. ahuora_builder/tests/test_solver/configurations/sb_vapor_frac_solved.json +29 -0
  157. ahuora_builder/tests/test_solver/configurations/solar.json +67 -0
  158. ahuora_builder/tests/test_solver/configurations/solar_solved.json +50 -0
  159. ahuora_builder/tests/test_solver/configurations/vapor_frac_target.json +67 -0
  160. ahuora_builder/tests/test_solver/configurations/vapor_frac_target_solved.json +68 -0
  161. ahuora_builder/tests/test_solver/test_solve_models.py +250 -0
  162. ahuora_builder/timing.py +65 -0
  163. ahuora_builder/types/__init__.py +1 -0
  164. ahuora_builder/unit_model_manager.py +48 -0
  165. ahuora_builder-0.1.0.dist-info/METADATA +14 -0
  166. ahuora_builder-0.1.0.dist-info/RECORD +167 -0
  167. ahuora_builder-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,944 @@
1
+ # Pyomo core
2
+ from pyomo.environ import (
3
+ Constraint,
4
+ Expression,
5
+ NonNegativeReals,
6
+ Suffix,
7
+ Var,
8
+ value,
9
+ units as UNIT,
10
+ )
11
+ from pyomo.core.base.reference import Reference
12
+ from pyomo.common.config import ConfigBlock, ConfigValue, Bool
13
+
14
+ # IDAES core
15
+ from idaes.core import (
16
+ declare_process_block_class,
17
+ UnitModelBlockData,
18
+ useDefault,
19
+ StateBlock,
20
+ )
21
+ from idaes.core.util import scaling
22
+ from idaes.core.util.config import is_physical_parameter_block
23
+ from idaes.core.util.tables import create_stream_table_dataframe
24
+ from idaes.core.solvers import get_solver
25
+ from idaes.core.initialization import ModularInitializerBase
26
+
27
+ # Other
28
+ from property_packages.build_package import build_package
29
+
30
+ # Logger
31
+ import idaes.logger as idaeslog
32
+
33
+ # Typing
34
+ from typing import List
35
+
36
+
37
+ __author__ = "Ahuora Centre for Smart Energy Systems, University of Waikato, New Zealand"
38
+
39
+ # Set up logger
40
+ _log = idaeslog.getLogger(__name__)
41
+
42
+ class SteamUserInitializer(ModularInitializerBase):
43
+ """Initializer for ``SteamUser``.
44
+
45
+ Parameters
46
+ ----------
47
+ blk : SteamUser
48
+ The unit model block to initialize.
49
+ solver : optional
50
+ A Pyomo/IDAES solver instance. Defaults to :func:`idaes.core.solvers.get_solver`.
51
+ solver_options : dict, optional
52
+ Options to set on the solver, e.g. tolerances.
53
+ outlvl : int, optional
54
+ IDAES log level (e.g. :data:`idaes.logger.WARNING`).
55
+
56
+ Returns
57
+ -------
58
+ pyomo.opt.results.results_.SolverResults
59
+ Result from the final solve.
60
+ """
61
+
62
+ def initialize(self, blk, **kwargs):
63
+ # --- Solver setup
64
+ solver = kwargs.get("solver", None) or get_solver()
65
+ solver_options = kwargs.get("solver_options", {})
66
+ for k, v in solver_options.items():
67
+ solver.options[k] = v
68
+
69
+ outlvl = kwargs.get("outlvl", idaeslog.WARNING)
70
+ log = idaeslog.getLogger(__name__)
71
+
72
+ # --- Time index
73
+ t0 = blk.flowsheet().time.first()
74
+
75
+ # --- 1) Initialize inlet state blocks
76
+ inlet_blocks = list(blk.inlet_blocks)
77
+ if blk.config.has_desuperheating:
78
+ inlet_steam, inlet_water = inlet_blocks
79
+ inlet_water[t0].pressure.set_value(
80
+ inlet_steam[t0].pressure
81
+ )
82
+ inlet_water[t0].enth_mol.set_value(
83
+ blk.water.htpx(
84
+ blk.bfw_temperature[t0],
85
+ inlet_water[t0].pressure,
86
+ )
87
+ )
88
+ else:
89
+ inlet_steam = inlet_blocks[0]
90
+
91
+ for sb in inlet_blocks:
92
+ if hasattr(sb, "initialize"):
93
+ sb.initialize(outlvl=outlvl)
94
+
95
+ # --- 2) Seed satuarate inlet-related state
96
+ if blk.config.has_desuperheating:
97
+ sb = blk._int_inlet_sat_vap_state
98
+ sb[t0].pressure.set_value(
99
+ inlet_steam[t0].pressure
100
+ )
101
+ sb[t0].enth_mol.set_value(
102
+ inlet_steam[t0].enth_mol_sat_phase["Vap"]
103
+ )
104
+ if hasattr(sb, "initialize"):
105
+ sb.initialize(outlvl=outlvl)
106
+
107
+ # --- 3) Aggregate inlet info for seeding mixed state block
108
+ ms = blk._int_mixed_inlet_state
109
+ ms[t0].pressure.set_value(
110
+ inlet_steam[t0].pressure
111
+ )
112
+ if blk.config.has_desuperheating:
113
+ if value(blk.deltaT_superheat[t0]) > 0:
114
+ ms[t0].enth_mol.set_value(
115
+ blk.water.htpx(
116
+ T=blk._int_inlet_sat_vap_state[t0].temperature + blk.deltaT_superheat[t0],
117
+ p=inlet_steam[t0].pressure,
118
+ )
119
+ )
120
+ else:
121
+ ms[t0].enth_mol.set_value(
122
+ blk._int_inlet_sat_vap_state[t0].enth_mol
123
+ )
124
+
125
+ if abs(value(ms[t0].enth_mol - inlet_water[t0].enth_mol)) > 0:
126
+ ms[t0].flow_mol.set_value(
127
+ inlet_steam[t0].flow_mol * (inlet_steam[t0].enth_mol - ms[t0].enth_mol) / (ms[t0].enth_mol - inlet_water[t0].enth_mol)
128
+ )
129
+ else:
130
+ ms[t0].flow_mol.set_value(
131
+ inlet_steam[t0].flow_mol
132
+ )
133
+ else:
134
+ ms[t0].enth_mol.set_value(
135
+ inlet_steam[t0].enth_mol
136
+ )
137
+ ms[t0].flow_mol.set_value(
138
+ inlet_steam[t0].flow_mol
139
+ )
140
+
141
+ if hasattr(ms, "initialize"):
142
+ ms.initialize(outlvl=outlvl)
143
+
144
+ # --- 4) Seed outlet-related states
145
+ # Condensate after heating (internal)
146
+ ios = blk._int_outlet_cond_state
147
+ ios[t0].flow_mol.set_value(
148
+ ms[t0].flow_mol
149
+ )
150
+ ios[t0].pressure.set_value(
151
+ ms[t0].pressure - blk.pressure_loss[t0]
152
+ )
153
+ ios[t0].enth_mol.set_value(
154
+ blk.water.htpx(
155
+ T=blk._int_outlet_cond_state[t0].temperature_sat - blk.deltaT_subcool[t0],
156
+ p=ios[t0].pressure,
157
+ )
158
+ )
159
+ if hasattr(ios, "initialize"):
160
+ ios.initialize(outlvl=outlvl)
161
+
162
+ # --- 5) Seed satuarate outlet-related state
163
+ sb = blk._int_outlet_sat_liq_state
164
+ sb[t0].pressure.set_value(
165
+ ios[t0].pressure
166
+ )
167
+ sb[t0].enth_mol.set_value(
168
+ ios[t0].enth_mol_sat_phase["Vap"]
169
+ )
170
+ if hasattr(sb, "initialize"):
171
+ sb.initialize(outlvl=outlvl)
172
+
173
+ # --- 5) Seed external outlets
174
+ # Return line properties at user-specified temperature
175
+ ors = blk.outlet_return_state
176
+ ors[t0].flow_mol.set_value(ms[t0].flow_mol * value(blk.cond_return_rate[t0]))
177
+ ors[t0].pressure.set_value(
178
+ ms[t0].pressure - blk.pressure_loss[t0]
179
+ )
180
+ ors[t0].enth_mol.set_value(
181
+ blk.water.htpx(
182
+ T=blk.cond_return_temperature[t0],
183
+ p=ors[t0].pressure,
184
+ )
185
+ )
186
+ if hasattr(ors, "initialize"):
187
+ ors.initialize(outlvl=outlvl)
188
+
189
+ # --- 6) Drain/blowdown outlet uses reference enthalpy (fixed later in build)
190
+ # Flow/pressure will be solved by constraints
191
+ if hasattr(blk.outlet_drain_state, "initialize"):
192
+ blk.outlet_drain_state.initialize(outlvl=outlvl)
193
+
194
+ # --- 7) Solve
195
+ res = solver.solve(blk, tee=False)
196
+ log.info(f"SteamUser init status: {res.solver.termination_condition}")
197
+ return res
198
+
199
+ def _make_config_block(config):
200
+ """Declare configuration options for the SteamUser unit.
201
+
202
+ Declares property package references and integer counts for inlets and outlets.
203
+
204
+ Args:
205
+ config (ConfigBlock): The mutable configuration block to populate.
206
+ """
207
+
208
+ config.declare(
209
+ "property_package",
210
+ ConfigValue(
211
+ default=useDefault,
212
+ domain=is_physical_parameter_block,
213
+ description="Property package to use for control volume",
214
+ ),
215
+ )
216
+ config.declare(
217
+ "property_package_args",
218
+ ConfigBlock(
219
+ implicit=True,
220
+ description="Arguments to use for constructing property packages",
221
+ ),
222
+ )
223
+ config.declare(
224
+ "has_desuperheating",
225
+ ConfigValue(
226
+ default=False,
227
+ domain=Bool,
228
+ description="If true, include desuperheating prior to use as process heat. " \
229
+ "Adds the state variable of the degree of superheat after desuperheating. Default: 0.",
230
+ ),
231
+ )
232
+
233
+ @declare_process_block_class("SteamUser")
234
+ class SteamUserData(UnitModelBlockData):
235
+ """Steam user unit operation.
236
+
237
+ The SteamUser aggregates thermal loads from multiple sub-users (heaters) within a site.
238
+ Desuperheating the flow is optional. Heat loss and pressure loss may be defined.
239
+ A mixed (intermediate) states are used for balances.
240
+
241
+ Key features:
242
+ - Material, energy, and momentum balances around the user
243
+ - Optional desuperheating step prior to process use
244
+ - User-specified condensate return rate and temperature
245
+ - Optional heat and pressure losses.
246
+
247
+ Attributes:
248
+ inlet_list (list[str]): Names for inlet ports.
249
+ outlet_list (list[str]): Names for outlet ports (incl. condensate/ and vent).
250
+ inlet_blocks (list): StateBlocks for all inlets.
251
+ outlet_blocks (list): StateBlocks for all outlets.
252
+ _int_mixed_inlet_state: Intermediate mixture StateBlock.
253
+
254
+ heat_loss (Var): Heat loss from the header (W).
255
+ pressure_loss (Var): Pressure drop from inlet minimum to mixed state (Pa).
256
+ bfw_flow_mol (Var): Required inlet boiler feed water flow for desuperheating (mol/s).
257
+ """
258
+
259
+ default_initializer=SteamUserInitializer
260
+ CONFIG = UnitModelBlockData.CONFIG()
261
+ _make_config_block(CONFIG)
262
+
263
+ def build(self) -> None:
264
+ """Build the unit model structure (ports, states, constraints)."""
265
+ # 1. Inherit standard UnitModelBlockData properties and functions
266
+ super().build()
267
+
268
+ # 2. Validate input parameters are valid
269
+ self._validate_model_config()
270
+
271
+ # 3. Create lists of ports with state blocks to add
272
+ self.inlet_list = self._create_inlet_port_name_list()
273
+ self.outlet_list = self._create_outlet_port_name_list()
274
+
275
+ # 4. Declare ports, state blocks and state property bounds
276
+ self.inlet_blocks = self._add_ports_with_state_blocks(
277
+ stream_list=self.inlet_list,
278
+ is_inlet=True,
279
+ has_phase_equilibrium=False,
280
+ is_defined_state=True,
281
+ )
282
+ self.outlet_blocks = self._add_ports_with_state_blocks(
283
+ stream_list=self.outlet_list,
284
+ is_inlet=False,
285
+ has_phase_equilibrium=False,
286
+ is_defined_state=False
287
+ )
288
+ self._internal_blocks = self._add_internal_state_blocks()
289
+ self._ref_enth = self._add_environmental_reference_enth()
290
+ self._add_bounds_to_state_properties()
291
+
292
+ # 4. Declare references, variables and expressions for external and internal use
293
+ self._create_references()
294
+ self._create_variables()
295
+ self._create_expressions()
296
+
297
+ # 5. Set balance equations
298
+ self._add_material_balances()
299
+ self._add_energy_balances()
300
+ self._add_momentum_balances()
301
+ self._add_additional_constraints()
302
+
303
+ # 6. Other
304
+ self.scaling_factor = Suffix(direction=Suffix.EXPORT)
305
+
306
+ # ------------------------------------------------------------------
307
+ # Helpers & construction utilities
308
+ # ------------------------------------------------------------------
309
+ def _validate_model_config(self) -> bool:
310
+ """Validate configuration for inlet and outlet counts.
311
+
312
+ Raises:
313
+ ValueError: If ``property_package is None``.
314
+ """
315
+ if self.config.property_package is None:
316
+ raise ValueError("SteamUser: Property package not defined.")
317
+ return True
318
+
319
+ def _create_inlet_port_name_list(self) -> List[str]:
320
+ """Build ordered inlet port names.
321
+
322
+ Returns:
323
+ list[str]: Names
324
+ """
325
+
326
+ return (
327
+ [
328
+ "inlet_steam", "inlet_water"
329
+ ]
330
+ if self.config.has_desuperheating else
331
+ [
332
+ "inlet_steam"
333
+ ]
334
+ )
335
+
336
+ def _create_outlet_port_name_list(self) -> List[str]:
337
+ """Build ordered outlet port names.
338
+
339
+ Returns:
340
+ list[str]: Names
341
+ """
342
+ return [
343
+ "outlet_return",
344
+ "outlet_drain",
345
+ ]
346
+
347
+ def _add_ports_with_state_blocks(self,
348
+ stream_list: List[str],
349
+ is_inlet: List[str],
350
+ has_phase_equilibrium: bool=False,
351
+ is_defined_state: bool=None,
352
+ ) -> List[StateBlock]:
353
+ """Construct StateBlocks and expose them as ports.
354
+
355
+ Creates a StateBlock per named stream and attaches a corresponding inlet or
356
+ outlet Port. Inlet blocks are defined states; outlet blocks are calculated states.
357
+
358
+ Args:
359
+ stream_list (list[str]): Port/StateBlock base names to create.
360
+ is_inlet (bool): If True, create inlet ports with ``defined_state=True``;
361
+ otherwise create outlet ports with ``defined_state=False``.
362
+ has_phase_equilibrium (bool)
363
+
364
+ Returns:
365
+ list: The created StateBlocks, in the same order as ``stream_list``.
366
+ """
367
+ # Create empty list to hold StateBlocks for return
368
+ state_block_ls = []
369
+
370
+ # Setup StateBlock argument dict
371
+ tmp_dict = dict(**self.config.property_package_args)
372
+ tmp_dict["has_phase_equilibrium"] = has_phase_equilibrium
373
+ if is_defined_state == None:
374
+ tmp_dict["defined_state"] = True if is_inlet else False
375
+ else:
376
+ tmp_dict["defined_state"] = is_defined_state
377
+
378
+ # Create an instance of StateBlock for all streams
379
+ for s in stream_list:
380
+ sb = self.config.property_package.build_state_block(
381
+ self.flowsheet().time, doc=f"Thermophysical properties at {s}", **tmp_dict
382
+ )
383
+ setattr(
384
+ self, s + "_state",
385
+ sb
386
+ )
387
+ state_block_ls.append(sb)
388
+ add_fn = self.add_inlet_port if is_inlet else self.add_outlet_port
389
+ add_fn(
390
+ name=s,
391
+ block=sb,
392
+ )
393
+
394
+ return state_block_ls
395
+
396
+ def _add_internal_state_blocks(self) -> List[StateBlock]:
397
+ """Create the intermediate StateBlock(s)."""
398
+ # The _int_outlet_cond_state:
399
+ # - Is not a defined state (solved from balances).
400
+ # - Represents the state of the condensate after delivering process heating.
401
+ tmp_dict = dict(**self.config.property_package_args)
402
+ tmp_dict["has_phase_equilibrium"] = False
403
+ tmp_dict["defined_state"] = False
404
+
405
+ self._int_outlet_cond_state = self.config.property_package.build_state_block(
406
+ self.flowsheet().time,
407
+ doc="Thermophysical properties of condensate after process heating.",
408
+ **tmp_dict
409
+ )
410
+ # The _int_mixed_inlet_state:
411
+ # - Has phase equilibrium enabled.
412
+ # - Is not a defined state (solved from balances).
413
+ # - Always exists even when not desuperheating
414
+ tmp_dict["has_phase_equilibrium"] = True
415
+ tmp_dict["defined_state"] = False
416
+ self._int_mixed_inlet_state = self.config.property_package.build_state_block(
417
+ self.flowsheet().time,
418
+ doc="Thermophysical properties internal mixed inlet state after desuperheating (if applicable).",
419
+ **tmp_dict
420
+ )
421
+ # The _int_outlet_sat_liq_state:
422
+ # - Has phase equilibrium enabled.
423
+ # - Is not a defined state (solved from balances).
424
+ # - Always exists even when not desuperheating
425
+ tmp_dict["has_phase_equilibrium"] = True
426
+ tmp_dict["defined_state"] = False
427
+ self._int_outlet_sat_liq_state = self.config.property_package.build_state_block(
428
+ self.flowsheet().time,
429
+ doc="Thermophysical properties internal mixed saturate state.",
430
+ **tmp_dict
431
+ )
432
+ self._int_outlet_sat_liq_state[:].flow_mol.fix(1)
433
+
434
+ if self.config.has_desuperheating:
435
+ # The _int_inlet_sat_vap_state:
436
+ # - Has phase equilibrium enabled.
437
+ # - Is not a defined state (solved from balances).
438
+ # - Only exists when desuperheating
439
+ tmp_dict["has_phase_equilibrium"] = True
440
+ tmp_dict["defined_state"] = False
441
+ self._int_inlet_sat_vap_state = self.config.property_package.build_state_block(
442
+ self.flowsheet().time,
443
+ doc="Thermophysical properties internal mixed saturate state.",
444
+ **tmp_dict
445
+ )
446
+ self._int_inlet_sat_vap_state[:].flow_mol.fix(1)
447
+
448
+ return [
449
+ self._int_mixed_inlet_state,
450
+ self._int_outlet_cond_state,
451
+ self._int_outlet_sat_liq_state,
452
+ self._int_inlet_sat_vap_state,
453
+ ] if self.config.has_desuperheating else [
454
+ self._int_mixed_inlet_state,
455
+ self._int_outlet_cond_state,
456
+ self._int_outlet_sat_liq_state,
457
+ ]
458
+
459
+ def _add_environmental_reference_enth(self) -> None:
460
+ """Create a helper to compute reference enthalpy at 15°C, 1 atm (water)."""
461
+ self.water = build_package("helmholtz", ["water"], ["Liq"])
462
+ return self.water.htpx(
463
+ (15 + 273.15) * UNIT.K,
464
+ 101325 * UNIT.Pa
465
+ )
466
+
467
+ def _add_bounds_to_state_properties(self) -> None:
468
+ """Add lower and/or upper bounds to state properties.
469
+
470
+ - Set nonnegativity lower bounds on all inlet/intermediate/outlet flows.
471
+ """
472
+ for sb in (self.inlet_blocks + self.outlet_blocks + self._internal_blocks):
473
+ for t in sb:
474
+ sb[t].flow_mol.setlb(0.0)
475
+
476
+ def _create_references(self) -> None:
477
+ """Create convenient References.
478
+
479
+ Creates references to _int_mixed_inlet_state properties:
480
+ - ``bfw_temperature``
481
+ - ``bfw_flow_mass``
482
+ - ``bfw_flow_mol``
483
+ """
484
+ # Read only variables, only applicable if desuperheating is active
485
+ if self.config.has_desuperheating:
486
+ self.bfw_flow_mass = Reference(
487
+ self.inlet_water_state[:].flow_mass
488
+ )
489
+ self.bfw_flow_mol = Reference(
490
+ self.inlet_water_state[:].flow_mol
491
+ )
492
+ self.inlet_water_state[:].flow_mol.unfix()
493
+
494
+ def _create_variables(self) -> None:
495
+ """Declare decision/parameter variables for the unit.
496
+
497
+ Creates:
498
+ - ``heat_demand``
499
+ - ``cond_return_rate``
500
+ - ``cond_return_temperature``
501
+ - ``deltaT_subcool``
502
+ - ``heat_loss``
503
+ - ``pressure_loss``
504
+ If desuperheating:
505
+ - ``bfw_temperature``
506
+ - ``deltaT_superheat``
507
+ """
508
+ # Get units consistent with the property package
509
+ units_meta = self.config.property_package.get_metadata()
510
+
511
+ # Calculated: Process heat demand (kW) — user typically fixes
512
+ self.heat_demand = Var(
513
+ self.flowsheet().time,
514
+ domain=NonNegativeReals,
515
+ doc="Process heat demand of the users. Default: 0 kW.",
516
+ units=units_meta.get_derived_units("power"),
517
+ )
518
+ self.heat_demand[:].set_value(
519
+ 0 # Default value
520
+ )
521
+ # User defined: Fraction of total condensate that returns to boiler (dimensionless)
522
+ self.cond_return_rate = Var(
523
+ self.flowsheet().time,
524
+ domain=NonNegativeReals,
525
+ bounds=(0,1),
526
+ doc="Fraction of condensate returned to the boiler. Default: 0.7."
527
+ )
528
+ self.cond_return_rate.fix(
529
+ 0.7 # Default value
530
+ )
531
+ # User defined: Condensate return temperature (degC or K)
532
+ self.cond_return_temperature = Var(
533
+ self.flowsheet().time,
534
+ domain=NonNegativeReals,
535
+ doc="Temperature at which the condensate returns to the boiler. Default: 80 degC.",
536
+ units=units_meta.get_derived_units("temperature"),
537
+ )
538
+ self.cond_return_temperature.fix(
539
+ (80 + 273.15) * UNIT.K # Default fixed value
540
+ )
541
+ # User defined: Subcooling target delta T for condensate after process heating (K)
542
+ self.deltaT_subcool = Var(
543
+ self.flowsheet().time,
544
+ domain=NonNegativeReals,
545
+ doc="The target amount of subcooling of the condensate after process heating. Default: 0 K.",
546
+ units=units_meta.get_derived_units("temperature"),
547
+ )
548
+ self.deltaT_subcool.fix(
549
+ 0 # Default fixed value
550
+ )
551
+ # User defined: Heat and pressure losses
552
+ self.heat_loss = Var(
553
+ self.flowsheet().time,
554
+ domain=NonNegativeReals,
555
+ doc="Heat loss. Default: 0 kW.",
556
+ units=units_meta.get_derived_units("power")
557
+ )
558
+ self.heat_loss.fix(
559
+ 0 # Default fixed value
560
+ )
561
+ self.pressure_loss = Var(
562
+ self.flowsheet().time,
563
+ domain=NonNegativeReals,
564
+ doc="Pressure loss. Default: 0 Pa.",
565
+ units=units_meta.get_derived_units("pressure")
566
+ )
567
+ self.pressure_loss.fix(
568
+ 0 # Default fixed value
569
+ )
570
+
571
+ # User defined when desuperheating is active, otherwise do not show
572
+ if self.config.has_desuperheating:
573
+ # User defined: Boiler feed water temperature entering the desuperheater
574
+ self.bfw_temperature = Var(
575
+ self.flowsheet().time,
576
+ domain=NonNegativeReals,
577
+ doc="The target amount of subcooling of the condensate after process heating. Default: 0 K.",
578
+ units=units_meta.get_derived_units("temperature"),
579
+ )
580
+ self.bfw_temperature.fix(
581
+ (110 + 273.15) * UNIT.K # Default fixed value
582
+ )
583
+ # User defined: Target degree of superheat at the outlet of the desuperheater
584
+ self.deltaT_superheat = Var(
585
+ self.flowsheet().time,
586
+ domain=NonNegativeReals,
587
+ doc="The target amount of superheat present in the steam after desuperheating before use. Default: 0 K.",
588
+ units=units_meta.get_derived_units("temperature"),
589
+ )
590
+ self.deltaT_superheat.fix(
591
+ 0 # Default fixed value
592
+ )
593
+
594
+ def _create_expressions(self) -> None:
595
+ """Create helper Expressions.
596
+
597
+ Creates:
598
+ - ``energy_lost``
599
+ """
600
+ # Calculated, always show
601
+ self.energy_lost = Expression(
602
+ self.flowsheet().time,
603
+ rule=lambda b, t: (
604
+ b._int_outlet_cond_state[t].flow_mol
605
+ * (b._int_outlet_cond_state[t].enth_mol - b._ref_enth)
606
+ -
607
+ b.outlet_return_state[t].flow_mol
608
+ * (b.outlet_return_state[t].enth_mol - b._ref_enth)
609
+ ),
610
+ doc="Energy lost from condensate cooling and condensate to drain.",
611
+ )
612
+
613
+ # ------------------------------------------------------------------
614
+ # Balances
615
+ # ------------------------------------------------------------------
616
+ def _add_material_balances(self) -> None:
617
+ """Material balance equations summary.
618
+
619
+ Balances / Constraints:
620
+ - ``overall_material_balance``
621
+ - ``condensate_return_material_eq``
622
+ - ``intermediate_material_balance_post_heating``
623
+ - ``intermediate_material_balance_pre_heating``
624
+ """
625
+ @self.Constraint(
626
+ self.flowsheet().time,
627
+ doc="Overall material balance",
628
+ )
629
+ def overall_material_balance(b, t):
630
+ return (
631
+ sum(
632
+ o[t].flow_mol
633
+ for o in b.outlet_blocks
634
+ )
635
+ ==
636
+ sum(
637
+ i[t].flow_mol
638
+ for i in b.inlet_blocks
639
+ )
640
+ )
641
+ @self.Constraint(
642
+ self.flowsheet().time,
643
+ doc="Intermediate material balance",
644
+ )
645
+ def intermediate_material_balance_pre_heating(b, t):
646
+ return (
647
+ b._int_mixed_inlet_state[t].flow_mol
648
+ ==
649
+ sum(
650
+ i[t].flow_mol
651
+ for i in b.inlet_blocks
652
+ )
653
+ )
654
+ @self.Constraint(
655
+ self.flowsheet().time,
656
+ doc="Intermediate material balance",
657
+ )
658
+ def intermediate_material_balance_post_heating(b, t):
659
+ return (
660
+ b._int_outlet_cond_state[t].flow_mol
661
+ ==
662
+ sum(
663
+ i[t].flow_mol
664
+ for i in b.inlet_blocks
665
+ )
666
+ )
667
+ @self.Constraint(
668
+ self.flowsheet().time,
669
+ doc="Condensate return material equation",
670
+ )
671
+ def condensate_return_material_eq(b, t):
672
+ return (
673
+ b.outlet_return_state[t].flow_mol
674
+ ==
675
+ sum(
676
+ i[t].flow_mol
677
+ for i in b.inlet_blocks
678
+ )
679
+ *
680
+ b.cond_return_rate[t]
681
+ )
682
+
683
+ def _add_energy_balances(self) -> None:
684
+ """Energy balance equations summary.
685
+
686
+ Balances / Constraints:
687
+ - ``mixing_energy_balance``
688
+ - ``heating_energy_balance``
689
+ """
690
+ @self.Constraint(
691
+ self.flowsheet().time,
692
+ doc="Inlet mixing energy balance",
693
+ )
694
+ def mixing_energy_balance(b, t):
695
+ return (
696
+ b._int_mixed_inlet_state[t].flow_mol * b._int_mixed_inlet_state[t].enth_mol
697
+ ==
698
+ sum(
699
+ i[t].flow_mol * i[t].enth_mol
700
+ for i in b.inlet_blocks
701
+ )
702
+ )
703
+ @self.Constraint(
704
+ self.flowsheet().time,
705
+ doc="Process heating energy balance",
706
+ )
707
+ def heating_energy_balance(b, t):
708
+ return (
709
+ b._int_mixed_inlet_state[t].flow_mol * b._int_mixed_inlet_state[t].enth_mol
710
+ ==
711
+ b._int_outlet_cond_state[t].flow_mol * b._int_outlet_cond_state[t].enth_mol
712
+ +
713
+ b.heat_loss[t]
714
+ +
715
+ b.heat_demand[t]
716
+ )
717
+ self.outlet_drain_state[:].enth_mol.fix(
718
+ value(
719
+ self._ref_enth
720
+ )
721
+ )
722
+ @self.Constraint(
723
+ self.flowsheet().time,
724
+ doc="Saturated liquid enthalpy",
725
+ )
726
+ def saturated_liq_enthalpy_eq(b, t):
727
+ return (
728
+ b._int_outlet_sat_liq_state[t].enth_mol_sat_phase["Liq"]
729
+ ==
730
+ b._int_outlet_sat_liq_state[t].enth_mol
731
+ )
732
+ if self.config.has_desuperheating:
733
+ @self.Constraint(
734
+ self.flowsheet().time,
735
+ doc="Saturated vapour enthalpy",
736
+ )
737
+ def saturated_vap_enthalpy_eq(b, t):
738
+ return (
739
+ b._int_inlet_sat_vap_state[t].enth_mol_sat_phase["Vap"]
740
+ ==
741
+ b._int_inlet_sat_vap_state[t].enth_mol
742
+ )
743
+
744
+ def _add_momentum_balances(self) -> None:
745
+ """Momentum balance equations summary.
746
+
747
+ Balances / Constraints:
748
+ - ``mixing_momentum_balance``
749
+ - ``heating_momentum_balance``
750
+ - ``outlet_momentum_balance``
751
+ If desuperheating:
752
+ - ``intlet_water_momentum_balance``
753
+ """
754
+ @self.Constraint(
755
+ self.flowsheet().time,
756
+ doc="Momentum equalities",
757
+ )
758
+ def mixing_momentum_balance(b, t):
759
+ return (
760
+ b.inlet_steam_state[t].pressure
761
+ ==
762
+ b._int_mixed_inlet_state[t].pressure
763
+ )
764
+ @self.Constraint(
765
+ self.flowsheet().time,
766
+ doc="Process heating momentum balance",
767
+ )
768
+ def heating_momentum_balance(b, t):
769
+ return (
770
+ b._int_mixed_inlet_state[t].pressure
771
+ ==
772
+ b._int_outlet_cond_state[t].pressure
773
+ +
774
+ b.pressure_loss[t]
775
+ )
776
+ @self.Constraint(
777
+ self.flowsheet().time,
778
+ doc="Saturated liq pressure",
779
+ )
780
+ def saturated_liq_pressure_eq(b, t):
781
+ return (
782
+ b._int_outlet_sat_liq_state[t].pressure
783
+ ==
784
+ b._int_outlet_cond_state[t].pressure
785
+ )
786
+ @self.Constraint(
787
+ self.flowsheet().time,
788
+ doc="Outlet momentum equality",
789
+ )
790
+ def outlet_momentum_balance(b, t):
791
+ return (
792
+ b.outlet_return_state[t].pressure
793
+ ==
794
+ b.outlet_drain_state[t].pressure
795
+ )
796
+ self.outlet_return_state[:].pressure.fix(
797
+ 101325 * UNIT.Pa # Fixed value, hidden from the user
798
+ )
799
+ if self.config.has_desuperheating:
800
+ @self.Constraint(
801
+ self.flowsheet().time,
802
+ doc="Inlet water momentum balance",
803
+ )
804
+ def intlet_water_momentum_balance(b, t):
805
+ return (
806
+ b.inlet_water_state[t].pressure
807
+ ==
808
+ b.inlet_steam_state[t].pressure
809
+ )
810
+ @self.Constraint(
811
+ self.flowsheet().time,
812
+ doc="Saturated vapour pressure",
813
+ )
814
+ def saturated_vap_pressure_eq(b, t):
815
+ return (
816
+ b.inlet_steam_state[t].pressure
817
+ ==
818
+ b._int_inlet_sat_vap_state[t].pressure
819
+ )
820
+
821
+ def _add_additional_constraints(self) -> None:
822
+ """Add auxiliary constraints and bounds.
823
+
824
+ Constraints:
825
+ - ``condensate_temperature_eq``
826
+ - ``subcooling_temperature_eq``
827
+ If desuperheating:
828
+ - ``desuperheating_mixed_temperature_eq``
829
+ """
830
+ @self.Constraint(
831
+ self.flowsheet().time,
832
+ doc="Condensate return temperature",
833
+ )
834
+ def condensate_temperature_eq(b, t):
835
+ return (
836
+ b.outlet_return_state[t].temperature
837
+ ==
838
+ b.cond_return_temperature[t]
839
+ )
840
+ @self.Constraint(
841
+ self.flowsheet().time,
842
+ doc="Subcool temperature",
843
+ )
844
+ def subcooling_temperature_eq(b, t):
845
+ return (
846
+ b._int_outlet_cond_state[t].temperature
847
+ ==
848
+ b._int_outlet_sat_liq_state[t].temperature - b.deltaT_subcool[t]
849
+ )
850
+ if self.config.has_desuperheating:
851
+ @self.Constraint(
852
+ self.flowsheet().time,
853
+ doc="Inlet water temperature",
854
+ )
855
+ def inlet_water_temperature_eq(b, t):
856
+ return (
857
+ b.inlet_water_state[t].temperature
858
+ ==
859
+ b.bfw_temperature[t]
860
+ )
861
+ @self.Constraint(
862
+ self.flowsheet().time,
863
+ doc="Mixed temperature after desuperheating",
864
+ )
865
+ def desuperheating_mixed_temperature_eq(b, t):
866
+ return (
867
+ b._int_mixed_inlet_state[t].temperature
868
+ ==
869
+ b._int_inlet_sat_vap_state[t].temperature + b.deltaT_superheat[t]
870
+ )
871
+
872
+ def calculate_scaling_factors(self):
873
+ """Assign scaling factors to improve numerical conditioning.
874
+
875
+ Sets scaling factors for performance and auxiliary variables.
876
+ """
877
+ super().calculate_scaling_factors()
878
+ scaling.set_scaling_factor(self.heat_loss, 1e-6) # kW scale
879
+ scaling.set_scaling_factor(self.pressure_loss, 1e-6) # Pa scale
880
+
881
+ def _get_stream_table_contents(self, time_point=0):
882
+ """Create a stream table for all inlets and outlets.
883
+
884
+ Args:
885
+ time_point (int | float): Time index at which to extract stream data.
886
+
887
+ Returns:
888
+ pandas.DataFrame: A tabular view suitable for reporting via
889
+ ``create_stream_table_dataframe``.
890
+ """
891
+ io_dict = {}
892
+
893
+ for inlet_name in self.inlet_list:
894
+ io_dict[inlet_name] = getattr(self, inlet_name)
895
+
896
+ for outlet_name in self.outlet_list:
897
+ io_dict[outlet_name] = getattr(self, outlet_name)
898
+
899
+ return create_stream_table_dataframe(io_dict, time_point=time_point)
900
+
901
+ def _get_performance_contents(self, time_point=0):
902
+ """Collect performance variables for reporting.
903
+
904
+ Args:
905
+ time_point (int | float): Time index at which to report values.
906
+
907
+ Returns:
908
+ dict: Mapping used by IDAES reporters, containing human-friendly labels
909
+ to Vars/References (e.g., heat/pressure loss, mixed-state properties).
910
+ """
911
+ perf = {
912
+ "vars": {
913
+ "Heat demand [W]": self.heat_demand[time_point],
914
+ "Degree of subcooling target [K]": self.deltaT_subcool[time_point],
915
+ "Heat loss [W]": self.heat_loss[time_point],
916
+ "Pressure loss [Pa]": self.pressure_loss[time_point],
917
+ "Condensate return rate [-]:": self.cond_return_rate[time_point],
918
+ "Condensate return temperature [degC]": UNIT.convert_temp_K_to_C(self.cond_return_temperature[time_point]),
919
+ },
920
+ "exprs": {
921
+ "Energy lost to return network [W]": self.energy_lost[time_point],
922
+ },
923
+ }
924
+ if self.config.has_desuperheating:
925
+ perf["vars"].update(
926
+ {
927
+ "BFW temperature [K]": self.bfw_temperature[time_point],
928
+ "Degree of superheat target [K]": self.deltaT_superheat[time_point],
929
+ }
930
+ )
931
+ return perf
932
+
933
+ def initialize(self, *args, **kwargs):
934
+ """Initialize the SteamUser unit using :class:`SteamUserInitializer`.
935
+
936
+ Args:
937
+ *args: Forwarded to ``SteamUserInitializer.initialize``.
938
+ **kwargs: Forwarded to ``SteamUserInitializer.initialize`` (e.g., solver, options).
939
+
940
+ Returns:
941
+ pyomo.opt.results.results_.SolverResults: Results from the initializer's solve.
942
+ """
943
+ init = SteamUserInitializer()
944
+ return init.initialize(self, *args, **kwargs)