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,680 @@
1
+ #################################################################################
2
+ # The Institute for the Design of Advanced Energy Systems Integrated Platform
3
+ # Framework (IDAES IP) was produced under the DOE Institute for the
4
+ # Design of Advanced Energy Systems (IDAES).
5
+ #
6
+ # Copyright (c) 2018-2024 by the software owners: The Regents of the
7
+ # University of California, through Lawrence Berkeley National Laboratory,
8
+ # National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon
9
+ # University, West Virginia University Research Corporation, et al.
10
+ # All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md
11
+ # for full copyright and license information.
12
+ #################################################################################
13
+ """
14
+ General purpose separator block for IDAES models
15
+ """
16
+
17
+ from enum import Enum
18
+ from pandas import DataFrame
19
+
20
+ from pyomo.environ import (Block, Set)
21
+ from pyomo.network import Port
22
+ from pyomo.common.config import ConfigBlock, ConfigValue, In, ListOf, Bool
23
+
24
+ from idaes.core import (
25
+ declare_process_block_class,
26
+ UnitModelBlockData,
27
+ useDefault,
28
+ MaterialBalanceType,
29
+ MomentumBalanceType,
30
+ )
31
+ from idaes.core.util.config import (
32
+ is_physical_parameter_block,
33
+ is_state_block,
34
+ )
35
+ from idaes.core.util.exceptions import (
36
+ BurntToast,
37
+ ConfigurationError,
38
+ )
39
+ from idaes.core.solvers import get_solver
40
+ from idaes.core.util.tables import create_stream_table_dataframe
41
+ from idaes.core.util.model_statistics import degrees_of_freedom
42
+ import idaes.logger as idaeslog
43
+ import idaes.core.util.scaling as iscale
44
+ from idaes.core.util.units_of_measurement import report_quantity
45
+ from idaes.core.initialization import ModularInitializerBase
46
+
47
+ __author__ = "Team Ahuora"
48
+
49
+
50
+ # Set up logger
51
+ _log = idaeslog.getLogger(__name__)
52
+
53
+
54
+ # Enumerate options for balances
55
+ class SplittingType(Enum):
56
+ """
57
+ Enum of supported material split types.
58
+ """
59
+
60
+ totalFlow = 1
61
+ phaseFlow = 2
62
+ componentFlow = 3
63
+ phaseComponentFlow = 4
64
+
65
+
66
+ class EnergySplittingType(Enum):
67
+ """
68
+ Enum of support energy split types.
69
+ """
70
+
71
+ none = 0
72
+ equal_molar_enthalpy = 2
73
+
74
+
75
+ class SimpleSeparatorInitializer(ModularInitializerBase):
76
+ """
77
+ Initializer for Separator blocks.
78
+
79
+ """
80
+
81
+ def initialization_routine(
82
+ self,
83
+ model: Block,
84
+ ):
85
+ """
86
+ Initialization routine for Separator Blocks.
87
+
88
+ This routine starts by initializing the feed and outlet streams using simple rules.
89
+
90
+ Args:
91
+ model: model to be initialized
92
+
93
+ Returns:
94
+ None
95
+
96
+ """
97
+ init_log = idaeslog.getInitLogger(
98
+ model.name, self.get_output_level(), tag="unit"
99
+ )
100
+ solve_log = idaeslog.getSolveLogger(
101
+ model.name, self.get_output_level(), tag="unit"
102
+ )
103
+
104
+ # Create solver
105
+ solver = self._get_solver()
106
+ # Initialize mixed state block
107
+
108
+ mblock = model.mixed_state
109
+ self.get_submodel_initializer(mblock).initialize(mblock)
110
+
111
+ res = None
112
+ if degrees_of_freedom(model) != 0:
113
+ with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
114
+ res = solver.solve(model, tee=slc.tee)
115
+ init_log.info(
116
+ "Initialization Step 1 Complete: {}".format(idaeslog.condition(res))
117
+ )
118
+
119
+ for c, s in component_status.items():
120
+ if s:
121
+ c.activate()
122
+
123
+
124
+ # Initialize outlet StateBlocks
125
+ outlet_list = model._create_outlet_list()
126
+
127
+ # Initializing outlet states
128
+ for o in outlet_list:
129
+ # Get corresponding outlet StateBlock
130
+ o_block = getattr(model, o + "_state")
131
+
132
+ # Create dict to store fixed status of state variables
133
+ for t in model.flowsheet().time:
134
+ # Calculate values for state variables
135
+ s_vars = o_block[t].define_state_vars()
136
+ for var_name_port, var_obj in s_vars.items():
137
+ for k in var_obj:
138
+ # If fixed, use current value
139
+ # otherwise calculate guess from mixed state and fix
140
+ if not var_obj[k].fixed:
141
+ m_var = getattr(mblock[t], var_obj.local_name)
142
+ if "flow" in var_name_port:
143
+ # Leave initial value
144
+ pass
145
+ else:
146
+ # Otherwise intensive, equate to mixed stream
147
+ var_obj[k].set_value(m_var[k].value)
148
+
149
+ # Call initialization routine for outlet StateBlock
150
+ self.get_submodel_initializer(o_block).initialize(o_block)
151
+
152
+ init_log.info("Initialization Complete.")
153
+
154
+ return res
155
+
156
+
157
+ @declare_process_block_class("SimpleSeparator")
158
+ class SimpleSeparatorData(UnitModelBlockData):
159
+ """
160
+ This is a simple Splitter block with the IDAES modeling framework.
161
+ Unlike the generic Separator, this block avoids use of split fractions.
162
+
163
+ This model creates a number of StateBlocks to represent the outgoing
164
+ streams, then writes a set of phase-component material balances, an
165
+ overall enthalpy balance (2 options), and a momentum balance (2 options)
166
+ linked to a mixed-state StateBlock. The mixed-state StateBlock can either
167
+ be specified by the user (allowing use as a sub-model), or created by the
168
+ Separator.
169
+ """
170
+
171
+ default_initializer = SimpleSeparatorInitializer
172
+
173
+ CONFIG = UnitModelBlockData.CONFIG()
174
+ CONFIG.declare(
175
+ "property_package",
176
+ ConfigValue(
177
+ default=useDefault,
178
+ domain=is_physical_parameter_block,
179
+ description="Property package to use for mixer",
180
+ doc="""Property parameter object used to define property
181
+ calculations,
182
+ **default** - useDefault.
183
+ **Valid values:** {
184
+ **useDefault** - use default package from parent model or flowsheet,
185
+ **PropertyParameterObject** - a PropertyParameterBlock object.}""",
186
+ ),
187
+ )
188
+ CONFIG.declare(
189
+ "property_package_args",
190
+ ConfigBlock(
191
+ implicit=True,
192
+ description="Arguments to use for constructing property packages",
193
+ doc="""A ConfigBlock with arguments to be passed to a property
194
+ block(s) and used when constructing these,
195
+ **default** - None.
196
+ **Valid values:** {
197
+ see property package for documentation.}""",
198
+ ),
199
+ )
200
+ CONFIG.declare(
201
+ "outlet_list",
202
+ ConfigValue(
203
+ domain=ListOf(str),
204
+ description="List of outlet names",
205
+ doc="""A list containing names of outlets,
206
+ **default** - None.
207
+ **Valid values:** {
208
+ **None** - use num_outlets argument,
209
+ **list** - a list of names to use for outlets.}""",
210
+ ),
211
+ )
212
+ CONFIG.declare(
213
+ "num_outlets",
214
+ ConfigValue(
215
+ domain=int,
216
+ description="Number of outlets to unit",
217
+ doc="""Argument indicating number (int) of outlets to construct,
218
+ not used if outlet_list arg is provided,
219
+ **default** - None.
220
+ **Valid values:** {
221
+ **None** - use outlet_list arg instead, or default to 2 if neither argument
222
+ provided,
223
+ **int** - number of outlets to create (will be named with sequential integers
224
+ from 1 to num_outlets).}""",
225
+ ),
226
+ )
227
+ CONFIG.declare(
228
+ "material_balance_type",
229
+ ConfigValue(
230
+ default=MaterialBalanceType.useDefault,
231
+ domain=In(MaterialBalanceType),
232
+ description="Material balance construction flag",
233
+ doc="""Indicates what type of mass balance should be constructed,
234
+ **default** - MaterialBalanceType.useDefault.
235
+ **Valid values:** {
236
+ **MaterialBalanceType.useDefault - refer to property package for default
237
+ balance type
238
+ **MaterialBalanceType.none** - exclude material balances,
239
+ **MaterialBalanceType.componentPhase** - use phase component balances,
240
+ **MaterialBalanceType.componentTotal** - use total component balances,
241
+ **MaterialBalanceType.elementTotal** - use total element balances,
242
+ **MaterialBalanceType.total** - use total material balance.}""",
243
+ ),
244
+ )
245
+ CONFIG.declare(
246
+ "momentum_balance_type",
247
+ ConfigValue(
248
+ default=MomentumBalanceType.pressureTotal,
249
+ domain=In(MomentumBalanceType),
250
+ description="Momentum balance construction flag",
251
+ doc="""Indicates what type of momentum balance should be constructed,
252
+ **default** - MomentumBalanceType.pressureTotal.
253
+ **Valid values:** {
254
+ **MomentumBalanceType.none** - exclude momentum balances,
255
+ **MomentumBalanceType.pressureTotal** - pressure in all outlets is equal,
256
+ **MomentumBalanceType.pressurePhase** - not yet supported,
257
+ **MomentumBalanceType.momentumTotal** - not yet supported,
258
+ **MomentumBalanceType.momentumPhase** - not yet supported.}""",
259
+ ),
260
+ )
261
+ CONFIG.declare(
262
+ "has_phase_equilibrium",
263
+ ConfigValue(
264
+ default=False,
265
+ domain=Bool,
266
+ description="Calculate phase equilibrium in mixed stream",
267
+ doc="""Argument indicating whether phase equilibrium should be
268
+ calculated for the resulting mixed stream,
269
+ **default** - False.
270
+ **Valid values:** {
271
+ **True** - calculate phase equilibrium in mixed stream,
272
+ **False** - do not calculate equilibrium in mixed stream.}""",
273
+ ),
274
+ )
275
+
276
+ def build(self):
277
+ """
278
+ General build method for SeparatorData. This method calls a number
279
+ of sub-methods which automate the construction of expected attributes
280
+ of unit models.
281
+
282
+ Inheriting models should call `super().build`.
283
+
284
+ Args:
285
+ None
286
+
287
+ Returns:
288
+ None
289
+ """
290
+ # Call super.build()
291
+ super(SimpleSeparatorData, self).build()
292
+
293
+ # Call setup methods from ControlVolumeBlockData
294
+ self._get_property_package()
295
+ self._get_indexing_sets()
296
+
297
+ # Create list of inlet names
298
+ outlet_list = self._create_outlet_list()
299
+
300
+ mixed_block = self._add_mixed_state_block()
301
+
302
+ # Add inlet port
303
+ self._add_inlet_port_objects(mixed_block)
304
+
305
+ # Build StateBlocks for outlet
306
+ outlet_blocks = self._add_outlet_state_blocks(outlet_list)
307
+ self.outlet_idx = Set(initialize=outlet_list)
308
+
309
+ # Construct splitting equations
310
+ self._add_material_balance(mixed_block, outlet_blocks)
311
+ self._add_energy_balance(mixed_block, outlet_blocks)
312
+ self._add_momentum_balance(mixed_block, outlet_blocks)
313
+
314
+ # Construct outlet port objects
315
+ self._add_outlet_port_objects(outlet_list)
316
+
317
+ def _create_outlet_list(self):
318
+ """
319
+ Create list of outlet stream names based on config arguments.
320
+
321
+ Returns:
322
+ list of strings
323
+ """
324
+ if self.config.outlet_list is not None and self.config.num_outlets is not None:
325
+ # If both arguments provided and not consistent, raise Exception
326
+ if len(self.config.outlet_list) != self.config.num_outlets:
327
+ raise ConfigurationError(
328
+ "{} Separator provided with both outlet_list and "
329
+ "num_outlets arguments, which were not consistent ("
330
+ "length of outlet_list was not equal to num_outlets). "
331
+ "Please check your arguments for consistency, and "
332
+ "note that it is only necessry to provide one of "
333
+ "these arguments.".format(self.name)
334
+ )
335
+ elif self.config.outlet_list is None and self.config.num_outlets is None:
336
+ # If no arguments provided for outlets, default to num_outlets = 2
337
+ self.config.num_outlets = 2
338
+
339
+ # Create a list of names for outlet StateBlocks
340
+ if self.config.outlet_list is not None:
341
+ outlet_list = self.config.outlet_list
342
+ else:
343
+ outlet_list = [
344
+ "outlet_" + str(n) for n in range(1, self.config.num_outlets + 1)
345
+ ]
346
+
347
+ return outlet_list
348
+
349
+ def _add_outlet_state_blocks(self, outlet_list):
350
+ """
351
+ Construct StateBlocks for all outlet streams.
352
+
353
+ Args:
354
+ list of strings to use as StateBlock names
355
+
356
+ Returns:
357
+ list of StateBlocks
358
+ """
359
+ # Setup StateBlock argument dict
360
+ tmp_dict = dict(**self.config.property_package_args)
361
+ tmp_dict["has_phase_equilibrium"] = False
362
+ tmp_dict["defined_state"] = False
363
+
364
+ # Create empty list to hold StateBlocks for return
365
+ outlet_blocks = []
366
+
367
+ # Create an instance of StateBlock for all outlets
368
+ for o in outlet_list:
369
+ o_obj = self.config.property_package.build_state_block(
370
+ self.flowsheet().time, doc="Material properties at outlet", **tmp_dict
371
+ )
372
+
373
+ setattr(self, o + "_state", o_obj)
374
+
375
+ outlet_blocks.append(getattr(self, o + "_state"))
376
+
377
+ return outlet_blocks
378
+
379
+ def _add_mixed_state_block(self):
380
+ """
381
+ Constructs StateBlock to represent mixed stream.
382
+
383
+ Returns:
384
+ New StateBlock object
385
+ """
386
+ # Setup StateBlock argument dict
387
+ tmp_dict = dict(**self.config.property_package_args)
388
+ tmp_dict["has_phase_equilibrium"] = False
389
+ tmp_dict["defined_state"] = True
390
+
391
+ self.mixed_state = self.config.property_package.build_state_block(
392
+ self.flowsheet().time, doc="Material properties of mixed stream", **tmp_dict
393
+ )
394
+
395
+ return self.mixed_state
396
+
397
+ def _add_inlet_port_objects(self, mixed_block):
398
+ """ Adds inlet Port object."""
399
+ self.add_port(name="inlet", block=mixed_block, doc="Inlet Port")
400
+
401
+ def _add_outlet_port_objects(self, outlet_list):
402
+ """Adds outlet Port objects."""
403
+ for p in outlet_list:
404
+ o_state = getattr(self, p + "_state")
405
+ self.add_port(name=p, block=o_state, doc="Outlet Port")
406
+
407
+ def _add_material_balance(self, mixed_block, outlet_blocks):
408
+ """Add overall material balance equation."""
409
+ # Get phase component list(s)
410
+ pc_set = mixed_block.phase_component_set
411
+
412
+ # Write phase-component balances
413
+ @self.Constraint(self.flowsheet().time, doc="Material balance equation")
414
+ def material_balance_equation(b, t):
415
+ return 0 == sum(
416
+ sum(
417
+ mixed_block[t].get_material_flow_terms(p, j)
418
+ -
419
+ sum(
420
+ o[t].get_material_flow_terms(p, j)
421
+ for o in outlet_blocks
422
+ )
423
+ for j in mixed_block.component_list
424
+ if (p, j) in pc_set
425
+ )
426
+ for p in mixed_block.phase_list
427
+ )
428
+
429
+ def _add_energy_balance(self, mixed_block, outlet_blocks):
430
+ """
431
+ Creates constraints for splitting the energy flows.
432
+ """
433
+ # split basis is equal_molar_enthalpy
434
+ @self.Constraint(
435
+ self.flowsheet().time,
436
+ self.outlet_idx,
437
+ doc="Molar enthalpy equality constraint",
438
+ )
439
+ def molar_enthalpy_equality_eqn(b, t, o):
440
+ o_block = getattr(self, o + "_state")
441
+ return mixed_block[t].enth_mol == o_block[t].enth_mol
442
+
443
+ def _add_momentum_balance(self, mixed_block, outlet_blocks):
444
+ """
445
+ Creates constraints for splitting the momentum flows - done by equating
446
+ pressures in outlets.
447
+ """
448
+ if self.config.momentum_balance_type is MomentumBalanceType.pressureTotal:
449
+ @self.Constraint(
450
+ self.flowsheet().time,
451
+ self.outlet_idx,
452
+ doc="Pressure equality constraint",
453
+ )
454
+ def pressure_equality_eqn(b, t, o):
455
+ o_block = getattr(self, o + "_state")
456
+ return mixed_block[t].pressure == o_block[t].pressure
457
+
458
+ def model_check(blk):
459
+ """
460
+ This method executes the model_check methods on the associated state
461
+ blocks (if they exist). This method is generally called by a unit model
462
+ as part of the unit's model_check method.
463
+
464
+ Args:
465
+ None
466
+
467
+ Returns:
468
+ None
469
+ """
470
+ # Try property block model check
471
+ for t in blk.flowsheet().time:
472
+ try:
473
+ blk.mixed_state[t].model_check()
474
+ except AttributeError:
475
+ _log.warning(
476
+ "{} Separator inlet state block has no "
477
+ "model check. To correct this, add a "
478
+ "model_check method to the associated "
479
+ "StateBlock class.".format(blk.name)
480
+ )
481
+
482
+ try:
483
+ outlet_list = blk._create_outlet_list()
484
+ for o in outlet_list:
485
+ o_block = getattr(blk, o + "_state")
486
+ o_block[t].model_check()
487
+ except AttributeError:
488
+ _log.warning(
489
+ "{} Separator outlet state block has no "
490
+ "model checks. To correct this, add a model_check"
491
+ " method to the associated StateBlock class.".format(blk.name)
492
+ )
493
+
494
+ def initialize_build(
495
+ blk, outlvl=idaeslog.NOTSET, optarg=None, solver=None, hold_state=False
496
+ ):
497
+ """
498
+ Initialization routine for separator
499
+
500
+ Keyword Arguments:
501
+ outlvl : sets output level of initialization routine
502
+ optarg : solver options dictionary object (default=None, use
503
+ default solver options)
504
+ solver : str indicating which solver to use during
505
+ initialization (default = None, use default solver)
506
+ hold_state : flag indicating whether the initialization routine
507
+ should unfix any state variables fixed during
508
+ initialization, **default** - False. **Valid values:**
509
+ **True** - states variables are not unfixed, and a dict of
510
+ returned containing flags for which states were fixed
511
+ during initialization, **False** - state variables are
512
+ unfixed after initialization by calling the release_state
513
+ method.
514
+
515
+ Returns:
516
+ If hold_states is True, returns a dict containing flags for which
517
+ states were fixed during initialization.
518
+ """
519
+ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit")
520
+ solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit")
521
+
522
+ # Create solver
523
+ opt = get_solver(solver, optarg)
524
+
525
+ mblock = blk.mixed_state
526
+ flags = mblock.initialize(
527
+ outlvl=outlvl,
528
+ optarg=optarg,
529
+ solver=solver,
530
+ hold_state=True,
531
+ )
532
+
533
+ if degrees_of_freedom(blk) != 0:
534
+ with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
535
+ res = opt.solve(blk, tee=slc.tee)
536
+ init_log.info(
537
+ "Initialization Step 1 Complete: {}".format(idaeslog.condition(res))
538
+ )
539
+
540
+ # Initialize outlet StateBlocks
541
+ outlet_list = blk._create_outlet_list()
542
+
543
+ # Premises for initializing outlet states:
544
+ for o in outlet_list:
545
+ # Get corresponding outlet StateBlock
546
+ o_block = getattr(blk, o + "_state")
547
+
548
+ # Create dict to store fixed status of state variables
549
+ o_flags = {}
550
+ for t in blk.flowsheet().time:
551
+
552
+ # Calculate values for state variables
553
+ s_vars = o_block[t].define_state_vars()
554
+ for v in s_vars:
555
+ for k in s_vars[v]:
556
+ # Record whether variable was fixed or not
557
+ o_flags[t, v, k] = s_vars[v][k].fixed
558
+
559
+ # If fixed, use current value
560
+ # otherwise calculate guess from mixed state and fix
561
+ if not s_vars[v][k].fixed:
562
+ m_var = getattr(mblock[t], s_vars[v].local_name)
563
+ if "flow" in v:
564
+ # Leave initial value, but avoid negative flows
565
+ if s_vars[v][k].value < 1e-4:
566
+ s_vars[v][k].set_value(1e-2)
567
+ else:
568
+ # Otherwise intensive, equate to mixed stream
569
+ s_vars[v][k].set_value(m_var[k].value)
570
+
571
+ # Call initialization routine for outlet StateBlock
572
+ o_block.initialize(
573
+ outlvl=outlvl,
574
+ optarg=optarg,
575
+ solver=solver,
576
+ hold_state=False,
577
+ )
578
+
579
+ # Revert fixed status of variables to what they were before
580
+ for t in blk.flowsheet().time:
581
+ s_vars = o_block[t].define_state_vars()
582
+ for v in s_vars:
583
+ for k in s_vars[v]:
584
+ s_vars[v][k].fixed = o_flags[t, v, k]
585
+
586
+ init_log.info("Initialization Complete.")
587
+ return flags
588
+
589
+ def release_state(blk, flags, outlvl=idaeslog.NOTSET):
590
+ """
591
+ Method to release state variables fixed during initialization.
592
+
593
+ Keyword Arguments:
594
+ flags : dict containing information of which state variables
595
+ were fixed during initialization, and should now be
596
+ unfixed. This dict is returned by initialize if
597
+ hold_state = True.
598
+ outlvl : sets output level of logging
599
+
600
+ Returns:
601
+ None
602
+ """
603
+ mblock = blk.mixed_state
604
+ mblock.release_state(flags, outlvl=outlvl)
605
+
606
+ def calculate_scaling_factors(self):
607
+ mb_type = self.config.material_balance_type
608
+ mixed_state = self.mixed_state
609
+ if mb_type == MaterialBalanceType.useDefault:
610
+ t_ref = self.flowsheet().time.first()
611
+ mb_type = mixed_state[t_ref].default_material_balance_type()
612
+ super().calculate_scaling_factors()
613
+
614
+ if hasattr(self, "temperature_equality_eqn"):
615
+ for (t, i), c in self.temperature_equality_eqn.items():
616
+ s = iscale.get_scaling_factor(
617
+ mixed_state[t].temperature, default=1, warning=True
618
+ )
619
+ iscale.constraint_scaling_transform(c, s)
620
+
621
+ if hasattr(self, "pressure_equality_eqn"):
622
+ for (t, i), c in self.pressure_equality_eqn.items():
623
+ s = iscale.get_scaling_factor(
624
+ mixed_state[t].pressure, default=1, warning=True
625
+ )
626
+ iscale.constraint_scaling_transform(c, s)
627
+
628
+ if hasattr(self, "material_splitting_eqn"):
629
+ if mb_type == MaterialBalanceType.componentPhase:
630
+ for (t, _, p, j), c in self.material_splitting_eqn.items():
631
+ flow_term = mixed_state[t].get_material_flow_terms(p, j)
632
+ s = iscale.get_scaling_factor(flow_term, default=1)
633
+ iscale.constraint_scaling_transform(c, s)
634
+ elif mb_type == MaterialBalanceType.componentTotal:
635
+ for (t, _, j), c in self.material_splitting_eqn.items():
636
+ s = None
637
+ for p in mixed_state.phase_list:
638
+ try:
639
+ ft = mixed_state[t].get_material_flow_terms(p, j)
640
+ except KeyError:
641
+ # This component does not exist in this phase
642
+ continue
643
+ if s is None:
644
+ s = iscale.get_scaling_factor(ft, default=1)
645
+ else:
646
+ _s = iscale.get_scaling_factor(ft, default=1)
647
+ s = _s if _s < s else s
648
+ iscale.constraint_scaling_transform(c, s)
649
+ elif mb_type == MaterialBalanceType.total:
650
+ pc_set = mixed_state.phase_component_set
651
+ for (t, _), c in self.material_splitting_eqn.items():
652
+ for i, (p, j) in enumerate(pc_set):
653
+ ft = mixed_state[t].get_material_flow_terms(p, j)
654
+ if i == 0:
655
+ s = iscale.get_scaling_factor(ft, default=1)
656
+ else:
657
+ _s = iscale.get_scaling_factor(ft, default=1)
658
+ s = _s if _s < s else s
659
+ iscale.constraint_scaling_transform(c, s)
660
+
661
+ def _get_performance_contents(self, time_point=0):
662
+ if hasattr(self, "split_fraction"):
663
+ var_dict = {}
664
+ for k, v in self.split_fraction.items():
665
+ if k[0] == time_point:
666
+ var_dict[f"Split Fraction [{str(k[1:])}]"] = v
667
+ return {"vars": var_dict}
668
+ else:
669
+ return None
670
+
671
+ def _get_stream_table_contents(self, time_point=0):
672
+ outlet_list = self._create_outlet_list()
673
+
674
+ io_dict = {}
675
+ io_dict["Inlet"] = self.mixed_state
676
+
677
+ for o in outlet_list:
678
+ io_dict[o] = getattr(self, o + "_state")
679
+
680
+ return create_stream_table_dataframe(io_dict, time_point=time_point)
File without changes