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.
- ahuora_builder/__init__.py +0 -0
- ahuora_builder/arc_manager.py +33 -0
- ahuora_builder/build_state.py +57 -0
- ahuora_builder/custom/PIDController.py +494 -0
- ahuora_builder/custom/PySMOModel.py +178 -0
- ahuora_builder/custom/SimpleEffectivenessHX_DH.py +727 -0
- ahuora_builder/custom/__init__.py +0 -0
- ahuora_builder/custom/add_initial_dynamics.py +35 -0
- ahuora_builder/custom/custom_compressor.py +107 -0
- ahuora_builder/custom/custom_cooler.py +33 -0
- ahuora_builder/custom/custom_heat_exchanger.py +183 -0
- ahuora_builder/custom/custom_heat_exchanger_1d.py +258 -0
- ahuora_builder/custom/custom_heater.py +41 -0
- ahuora_builder/custom/custom_pressure_changer.py +34 -0
- ahuora_builder/custom/custom_pump.py +107 -0
- ahuora_builder/custom/custom_separator.py +371 -0
- ahuora_builder/custom/custom_tank.py +133 -0
- ahuora_builder/custom/custom_turbine.py +132 -0
- ahuora_builder/custom/custom_valve.py +300 -0
- ahuora_builder/custom/custom_variable.py +29 -0
- ahuora_builder/custom/direct_steam_injection.py +371 -0
- ahuora_builder/custom/energy/__init__.py +0 -0
- ahuora_builder/custom/energy/acBus.py +280 -0
- ahuora_builder/custom/energy/ac_property_package.py +279 -0
- ahuora_builder/custom/energy/battery.py +170 -0
- ahuora_builder/custom/energy/bus.py +182 -0
- ahuora_builder/custom/energy/energy_mixer.py +195 -0
- ahuora_builder/custom/energy/energy_splitter.py +228 -0
- ahuora_builder/custom/energy/grid.py +173 -0
- ahuora_builder/custom/energy/hydro.py +169 -0
- ahuora_builder/custom/energy/link.py +137 -0
- ahuora_builder/custom/energy/load.py +155 -0
- ahuora_builder/custom/energy/mainDistributionBoard.py +257 -0
- ahuora_builder/custom/energy/power_property_package.py +253 -0
- ahuora_builder/custom/energy/solar.py +176 -0
- ahuora_builder/custom/energy/storage.py +230 -0
- ahuora_builder/custom/energy/storage_wrapper +0 -0
- ahuora_builder/custom/energy/tests/__init__.py +0 -0
- ahuora_builder/custom/energy/tests/test_bus.py +44 -0
- ahuora_builder/custom/energy/tests/test_energy_mixer.py +46 -0
- ahuora_builder/custom/energy/tests/test_mdb.py +49 -0
- ahuora_builder/custom/energy/transformer.py +187 -0
- ahuora_builder/custom/energy/transformer_property_package.py +267 -0
- ahuora_builder/custom/energy/transmissionLine.py +228 -0
- ahuora_builder/custom/energy/wind.py +206 -0
- ahuora_builder/custom/hda_ideal_VLE.py +1341 -0
- ahuora_builder/custom/hda_reaction.py +182 -0
- ahuora_builder/custom/heat_exchanger_1d_wrapper.py +31 -0
- ahuora_builder/custom/integration_block.py +106 -0
- ahuora_builder/custom/inverted.py +81 -0
- ahuora_builder/custom/performance_curves.py +1 -0
- ahuora_builder/custom/reactions/__init__.py +0 -0
- ahuora_builder/custom/reactions/hda_stoich.py +10 -0
- ahuora_builder/custom/simple_separator.py +680 -0
- ahuora_builder/custom/tests/__init__.py +0 -0
- ahuora_builder/custom/tests/test_SimpleEffectivenessHX_DH.py +91 -0
- ahuora_builder/custom/tests/test_custom_tank.py +70 -0
- ahuora_builder/custom/tests/test_direct_steam_injection.py +41 -0
- ahuora_builder/custom/tests/test_simple_separator.py +46 -0
- ahuora_builder/custom/tests/test_waterpipe.py +46 -0
- ahuora_builder/custom/thermal_utility_systems/desuperheater.py +624 -0
- ahuora_builder/custom/thermal_utility_systems/header.py +889 -0
- ahuora_builder/custom/thermal_utility_systems/simple_heat_pump.py +567 -0
- ahuora_builder/custom/thermal_utility_systems/steam_header.py +353 -0
- ahuora_builder/custom/thermal_utility_systems/steam_user.py +944 -0
- ahuora_builder/custom/thermal_utility_systems/temp.py +349 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_desuperheater.py +142 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_header.py +998 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_ntu_hx.py +129 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_simple_heat_pump.py +120 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_steam_header.py +703 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_steam_user.py +277 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_waterpipe.py +36 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_willans_turbine.py +253 -0
- ahuora_builder/custom/thermal_utility_systems/willans_turbine.py +804 -0
- ahuora_builder/custom/translator.py +129 -0
- ahuora_builder/custom/updated_pressure_changer.py +1404 -0
- ahuora_builder/custom/valve_wrapper.py +38 -0
- ahuora_builder/custom/water_tank_with_units.py +456 -0
- ahuora_builder/diagnostics/__init__.py +0 -0
- ahuora_builder/diagnostics/infeasibilities.py +40 -0
- ahuora_builder/diagnostics/tests/__init__.py +0 -0
- ahuora_builder/diagnostics/tests/test_infeasibilities.py +28 -0
- ahuora_builder/flowsheet_manager.py +542 -0
- ahuora_builder/flowsheet_manager_type.py +20 -0
- ahuora_builder/generate_python_file.py +440 -0
- ahuora_builder/methods/BlockContext.py +84 -0
- ahuora_builder/methods/__init__.py +0 -0
- ahuora_builder/methods/adapter.py +355 -0
- ahuora_builder/methods/adapter_library.py +549 -0
- ahuora_builder/methods/adapter_methods.py +80 -0
- ahuora_builder/methods/expression_parsing.py +105 -0
- ahuora_builder/methods/load_unit_model.py +147 -0
- ahuora_builder/methods/slice_manipulation.py +7 -0
- ahuora_builder/methods/tests/__init__.py +0 -0
- ahuora_builder/methods/tests/test_expression_parsing.py +15 -0
- ahuora_builder/methods/units_handler.py +129 -0
- ahuora_builder/ml_wizard.py +101 -0
- ahuora_builder/port_manager.py +20 -0
- ahuora_builder/properties_manager.py +44 -0
- ahuora_builder/property_package_manager.py +78 -0
- ahuora_builder/solver.py +38 -0
- ahuora_builder/tear_manager.py +98 -0
- ahuora_builder/tests/__init__.py +0 -0
- ahuora_builder/tests/test_generate_python_file/__init__.py +0 -0
- ahuora_builder/tests/test_generate_python_file/configurations/compressor_generated.py +63 -0
- ahuora_builder/tests/test_generate_python_file/configurations/heat_exchanger_generated.py +70 -0
- ahuora_builder/tests/test_generate_python_file/configurations/pump_generated.py +84 -0
- ahuora_builder/tests/test_generate_python_file/configurations/recycle_generated.py +73 -0
- ahuora_builder/tests/test_generate_python_file/test_generate_python_file.py +108 -0
- ahuora_builder/tests/test_solver/__init__.py +0 -0
- ahuora_builder/tests/test_solver/configurations/BT_PR.json +59 -0
- ahuora_builder/tests/test_solver/configurations/BT_PR_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/bus.json +99 -0
- ahuora_builder/tests/test_solver/configurations/bus_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/compound_separator.json +377 -0
- ahuora_builder/tests/test_solver/configurations/compound_separator_solved.json +374 -0
- ahuora_builder/tests/test_solver/configurations/compressor.json +38 -0
- ahuora_builder/tests/test_solver/configurations/compressor_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/constraints.json +44 -0
- ahuora_builder/tests/test_solver/configurations/constraints_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/control.json +39 -0
- ahuora_builder/tests/test_solver/configurations/control_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/dynamic_tank.json +733 -0
- ahuora_builder/tests/test_solver/configurations/dynamic_tank_solved.json +846 -0
- ahuora_builder/tests/test_solver/configurations/elimination.json +39 -0
- ahuora_builder/tests/test_solver/configurations/elimination_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/expressions.json +68 -0
- ahuora_builder/tests/test_solver/configurations/expressions_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/header.json +1192 -0
- ahuora_builder/tests/test_solver/configurations/header_solved.json +761 -0
- ahuora_builder/tests/test_solver/configurations/heat_exchanger.json +63 -0
- ahuora_builder/tests/test_solver/configurations/heat_exchanger_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/heat_pump.json +137 -0
- ahuora_builder/tests/test_solver/configurations/heat_pump_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/machine_learning.json +2156 -0
- ahuora_builder/tests/test_solver/configurations/machine_learning_solved.json +266 -0
- ahuora_builder/tests/test_solver/configurations/mass_flow_tear.json +77 -0
- ahuora_builder/tests/test_solver/configurations/mass_flow_tear_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/milk_heater.json +521 -0
- ahuora_builder/tests/test_solver/configurations/milk_heater_solved.json +311 -0
- ahuora_builder/tests/test_solver/configurations/mixer.json +44 -0
- ahuora_builder/tests/test_solver/configurations/mixer_solved.json +86 -0
- ahuora_builder/tests/test_solver/configurations/optimization.json +62 -0
- ahuora_builder/tests/test_solver/configurations/optimization_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/propane_heat_pump.json +167 -0
- ahuora_builder/tests/test_solver/configurations/propane_heat_pump_solved.json +158 -0
- ahuora_builder/tests/test_solver/configurations/propane_recycle.json +141 -0
- ahuora_builder/tests/test_solver/configurations/propane_recycle_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/pump.json +64 -0
- ahuora_builder/tests/test_solver/configurations/pump_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/pump_unit_conversions.json +63 -0
- ahuora_builder/tests/test_solver/configurations/recycle.json +49 -0
- ahuora_builder/tests/test_solver/configurations/recycle_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/sb_vapor_frac.json +29 -0
- ahuora_builder/tests/test_solver/configurations/sb_vapor_frac_solved.json +29 -0
- ahuora_builder/tests/test_solver/configurations/solar.json +67 -0
- ahuora_builder/tests/test_solver/configurations/solar_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/vapor_frac_target.json +67 -0
- ahuora_builder/tests/test_solver/configurations/vapor_frac_target_solved.json +68 -0
- ahuora_builder/tests/test_solver/test_solve_models.py +250 -0
- ahuora_builder/timing.py +65 -0
- ahuora_builder/types/__init__.py +1 -0
- ahuora_builder/unit_model_manager.py +48 -0
- ahuora_builder-0.1.0.dist-info/METADATA +14 -0
- ahuora_builder-0.1.0.dist-info/RECORD +167 -0
- 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
|