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,727 @@
|
|
|
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
|
+
Ben Lincolns heat exchanger model using effectiveness
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Import Pyomo libraries
|
|
19
|
+
from pyomo.environ import (
|
|
20
|
+
Block,
|
|
21
|
+
check_optimal_termination,
|
|
22
|
+
Constraint,
|
|
23
|
+
Expression,
|
|
24
|
+
Param,
|
|
25
|
+
PositiveReals,
|
|
26
|
+
Reference,
|
|
27
|
+
units as pyunits,
|
|
28
|
+
Var,
|
|
29
|
+
)
|
|
30
|
+
from pyomo.common.config import Bool, ConfigBlock, ConfigValue, In
|
|
31
|
+
|
|
32
|
+
# Import IDAES cores
|
|
33
|
+
from idaes.core import (
|
|
34
|
+
ControlVolume0DBlock,
|
|
35
|
+
declare_process_block_class,
|
|
36
|
+
MaterialBalanceType,
|
|
37
|
+
EnergyBalanceType,
|
|
38
|
+
MomentumBalanceType,
|
|
39
|
+
UnitModelBlockData,
|
|
40
|
+
useDefault,
|
|
41
|
+
)
|
|
42
|
+
from idaes.models.unit_models.heat_exchanger import hx_process_config, add_hx_references
|
|
43
|
+
from idaes.core.util.config import is_physical_parameter_block
|
|
44
|
+
from idaes.core.util.tables import create_stream_table_dataframe
|
|
45
|
+
from idaes.core.util.math import smooth_min, smooth_max
|
|
46
|
+
from idaes.core.solvers import get_solver
|
|
47
|
+
from idaes.core.util.exceptions import InitializationError
|
|
48
|
+
import idaes.logger as idaeslog
|
|
49
|
+
from idaes.core.initialization import SingleControlVolumeUnitInitializer
|
|
50
|
+
from idaes.core.util.model_statistics import degrees_of_freedom
|
|
51
|
+
from .inverted import add_inverted, initialise_inverted
|
|
52
|
+
|
|
53
|
+
__author__ = "Paul Akula, Andrew Lee, Ben Lincoln"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Set up logger
|
|
57
|
+
_log = idaeslog.getLogger(__name__)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class HXEFFInitializer(SingleControlVolumeUnitInitializer):
|
|
61
|
+
"""
|
|
62
|
+
Initializer for NTU Heat Exchanger units.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def initialization_routine(
|
|
67
|
+
self,
|
|
68
|
+
model: Block,
|
|
69
|
+
plugin_initializer_args: dict = None,
|
|
70
|
+
copy_inlet_state: bool = False,
|
|
71
|
+
duty=1000 * pyunits.W,
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Common initialization routine for NTU Heat Exchangers.
|
|
75
|
+
|
|
76
|
+
This routine starts by initializing the hot and cold side properties. Next, the heat
|
|
77
|
+
transfer between the two sides is fixed to an initial guess for the heat duty (provided by the duty
|
|
78
|
+
argument), the associated constraint deactivated, and the model is then solved. Finally, the heat
|
|
79
|
+
duty is unfixed and the heat transfer constraint reactivated followed by a final solve of the model.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
model: Pyomo Block to be initialized
|
|
83
|
+
plugin_initializer_args: dict-of-dicts containing arguments to be passed to plug-in Initializers.
|
|
84
|
+
Keys should be submodel components.
|
|
85
|
+
copy_inlet_state: bool (default=False). Whether to copy inlet state to other states or not
|
|
86
|
+
(0-D control volumes only). Copying will generally be faster, but inlet states may not contain
|
|
87
|
+
all properties required elsewhere.
|
|
88
|
+
duty: initial guess for heat duty to assist with initialization. Can be a Pyomo expression with units.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Pyomo solver results object
|
|
92
|
+
"""
|
|
93
|
+
return super(SingleControlVolumeUnitInitializer, self).initialization_routine(
|
|
94
|
+
model=model,
|
|
95
|
+
plugin_initializer_args=plugin_initializer_args,
|
|
96
|
+
copy_inlet_state=copy_inlet_state,
|
|
97
|
+
duty=duty,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def initialize_main_model(
|
|
101
|
+
self,
|
|
102
|
+
model: Block,
|
|
103
|
+
copy_inlet_state: bool = False,
|
|
104
|
+
duty=1000 * pyunits.W,
|
|
105
|
+
):
|
|
106
|
+
"""
|
|
107
|
+
Initialization routine for main NTU HX models.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
model: Pyomo Block to be initialized.
|
|
111
|
+
copy_inlet_state: bool (default=False). Whether to copy inlet state to other states or not
|
|
112
|
+
(0-D control volumes only). Copying will generally be faster, but inlet states may not contain
|
|
113
|
+
all properties required elsewhere.
|
|
114
|
+
duty: initial guess for heat duty to assist with initialization. Can be a Pyomo expression with units.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Pyomo solver results object.
|
|
118
|
+
|
|
119
|
+
"""
|
|
120
|
+
initialise_inverted(model.hot_side,"deltaP")
|
|
121
|
+
initialise_inverted(model.cold_side,"deltaP")
|
|
122
|
+
# TODO: Aside from one differences in constraint names, this is
|
|
123
|
+
# identical to the Initializer for the 0D HX unit.
|
|
124
|
+
# Set solver options
|
|
125
|
+
init_log = idaeslog.getInitLogger(
|
|
126
|
+
model.name, self.get_output_level(), tag="unit"
|
|
127
|
+
)
|
|
128
|
+
solve_log = idaeslog.getSolveLogger(
|
|
129
|
+
model.name, self.get_output_level(), tag="unit"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Create solver
|
|
133
|
+
solver = self._get_solver()
|
|
134
|
+
|
|
135
|
+
self.initialize_control_volume(model.hot_side, copy_inlet_state)
|
|
136
|
+
init_log.info_high("Initialization Step 1a (hot side) Complete.")
|
|
137
|
+
|
|
138
|
+
self.initialize_control_volume(model.cold_side, copy_inlet_state)
|
|
139
|
+
init_log.info_high("Initialization Step 1b (cold side) Complete.")
|
|
140
|
+
|
|
141
|
+
# ---------------------------------------------------------------------
|
|
142
|
+
# Solve unit without heat transfer equation
|
|
143
|
+
model.energy_balance_constraint.deactivate()
|
|
144
|
+
|
|
145
|
+
model.cold_side.heat.fix(duty)
|
|
146
|
+
for i in model.hot_side.heat:
|
|
147
|
+
model.hot_side.heat[i].set_value(-duty)
|
|
148
|
+
|
|
149
|
+
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
|
|
150
|
+
res = solver.solve(model, tee=slc.tee)
|
|
151
|
+
|
|
152
|
+
init_log.info_high("Initialization Step 2 {}.".format(idaeslog.condition(res)))
|
|
153
|
+
|
|
154
|
+
model.cold_side.heat.unfix()
|
|
155
|
+
model.energy_balance_constraint.activate()
|
|
156
|
+
# ---------------------------------------------------------------------
|
|
157
|
+
# Solve unit
|
|
158
|
+
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
|
|
159
|
+
res = solver.solve(model, tee=slc.tee)
|
|
160
|
+
init_log.info("Initialization Completed, {}".format(idaeslog.condition(res)))
|
|
161
|
+
|
|
162
|
+
return res
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@declare_process_block_class("HeatExchangerEffectiveness")
|
|
166
|
+
class HeatExchangerEFFData(UnitModelBlockData):
|
|
167
|
+
"""Heat Exchanger Unit Model using NTU method."""
|
|
168
|
+
|
|
169
|
+
default_initializer = HXEFFInitializer
|
|
170
|
+
|
|
171
|
+
CONFIG = UnitModelBlockData.CONFIG(implicit=True)
|
|
172
|
+
|
|
173
|
+
# Configuration template for fluid specific arguments
|
|
174
|
+
_SideCONFIG = ConfigBlock()
|
|
175
|
+
_SideCONFIG.declare(
|
|
176
|
+
"has_phase_equilibrium",
|
|
177
|
+
ConfigValue(
|
|
178
|
+
default=False,
|
|
179
|
+
domain=Bool,
|
|
180
|
+
description="Phase equilibrium construction flag",
|
|
181
|
+
doc="""Indicates whether terms for phase equilibrium should be
|
|
182
|
+
constructed, **default** = False.
|
|
183
|
+
**Valid values:** {
|
|
184
|
+
**True** - include phase equilibrium terms
|
|
185
|
+
**False** - exclude phase equilibrium terms.}""",
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
_SideCONFIG.declare(
|
|
189
|
+
"material_balance_type",
|
|
190
|
+
ConfigValue(
|
|
191
|
+
default=MaterialBalanceType.useDefault,
|
|
192
|
+
domain=In(MaterialBalanceType),
|
|
193
|
+
description="Material balance construction flag",
|
|
194
|
+
doc="""Indicates what type of mass balance should be constructed,
|
|
195
|
+
**default** - MaterialBalanceType.useDefault.
|
|
196
|
+
**Valid values:** {
|
|
197
|
+
**MaterialBalanceType.useDefault - refer to property package for default
|
|
198
|
+
balance type
|
|
199
|
+
**MaterialBalanceType.none** - exclude material balances,
|
|
200
|
+
**MaterialBalanceType.componentPhase** - use phase component balances,
|
|
201
|
+
**MaterialBalanceType.componentTotal** - use total component balances,
|
|
202
|
+
**MaterialBalanceType.elementTotal** - use total element balances,
|
|
203
|
+
**MaterialBalanceType.total** - use total material balance.}""",
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
_SideCONFIG.declare(
|
|
207
|
+
"energy_balance_type",
|
|
208
|
+
ConfigValue(
|
|
209
|
+
default=EnergyBalanceType.useDefault,
|
|
210
|
+
domain=In(EnergyBalanceType),
|
|
211
|
+
description="Energy balance construction flag",
|
|
212
|
+
doc="""Indicates what type of energy balance should be constructed,
|
|
213
|
+
**default** - EnergyBalanceType.useDefault.
|
|
214
|
+
**Valid values:** {
|
|
215
|
+
**EnergyBalanceType.useDefault - refer to property package for default
|
|
216
|
+
balance type
|
|
217
|
+
**EnergyBalanceType.none** - exclude energy balances,
|
|
218
|
+
**EnergyBalanceType.enthalpyTotal** - single enthalpy balance for material,
|
|
219
|
+
**EnergyBalanceType.enthalpyPhase** - enthalpy balances for each phase,
|
|
220
|
+
**EnergyBalanceType.energyTotal** - single energy balance for material,
|
|
221
|
+
**EnergyBalanceType.energyPhase** - energy balances for each phase.}""",
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
_SideCONFIG.declare(
|
|
225
|
+
"momentum_balance_type",
|
|
226
|
+
ConfigValue(
|
|
227
|
+
default=MomentumBalanceType.pressureTotal,
|
|
228
|
+
domain=In(MomentumBalanceType),
|
|
229
|
+
description="Momentum balance construction flag",
|
|
230
|
+
doc="""Indicates what type of momentum balance should be constructed,
|
|
231
|
+
**default** - MomentumBalanceType.pressureTotal.
|
|
232
|
+
**Valid values:** {
|
|
233
|
+
**MomentumBalanceType.none** - exclude momentum balances,
|
|
234
|
+
**MomentumBalanceType.pressureTotal** - single pressure balance for material,
|
|
235
|
+
**MomentumBalanceType.pressurePhase** - pressure balances for each phase,
|
|
236
|
+
**MomentumBalanceType.momentumTotal** - single momentum balance for material,
|
|
237
|
+
**MomentumBalanceType.momentumPhase** - momentum balances for each phase.}""",
|
|
238
|
+
),
|
|
239
|
+
)
|
|
240
|
+
_SideCONFIG.declare(
|
|
241
|
+
"has_pressure_change",
|
|
242
|
+
ConfigValue(
|
|
243
|
+
default=False,
|
|
244
|
+
domain=Bool,
|
|
245
|
+
description="Pressure change term construction flag",
|
|
246
|
+
doc="""Indicates whether terms for pressure change should be
|
|
247
|
+
constructed,
|
|
248
|
+
**default** - False.
|
|
249
|
+
**Valid values:** {
|
|
250
|
+
**True** - include pressure change terms,
|
|
251
|
+
**False** - exclude pressure change terms.}""",
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
_SideCONFIG.declare(
|
|
255
|
+
"property_package",
|
|
256
|
+
ConfigValue(
|
|
257
|
+
default=useDefault,
|
|
258
|
+
domain=is_physical_parameter_block,
|
|
259
|
+
description="Property package to use ",
|
|
260
|
+
doc="""Property parameter object used to define property calculations
|
|
261
|
+
**default** - useDefault.
|
|
262
|
+
**Valid values:** {
|
|
263
|
+
**useDefault** - use default package from parent model or flowsheet,
|
|
264
|
+
**PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
_SideCONFIG.declare(
|
|
268
|
+
"property_package_args",
|
|
269
|
+
ConfigBlock(
|
|
270
|
+
implicit=True,
|
|
271
|
+
description="Arguments to use for constructing property package",
|
|
272
|
+
doc="""A ConfigBlock with arguments to be passed to
|
|
273
|
+
property block(s) and used when constructing these,
|
|
274
|
+
**default** - None.
|
|
275
|
+
**Valid values:** {
|
|
276
|
+
see property package for documentation.}""",
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Create individual config blocks for hot and cold sides
|
|
281
|
+
CONFIG.declare("hot_side", _SideCONFIG(doc="Hot fluid config arguments"))
|
|
282
|
+
CONFIG.declare("cold_side", _SideCONFIG(doc="Cold fluid config arguments"))
|
|
283
|
+
CONFIG.declare(
|
|
284
|
+
"hot_side_name",
|
|
285
|
+
ConfigValue(
|
|
286
|
+
default=None,
|
|
287
|
+
domain=str,
|
|
288
|
+
doc="Hot side name, sets control volume and inlet and outlet names",
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
CONFIG.declare(
|
|
292
|
+
"cold_side_name",
|
|
293
|
+
ConfigValue(
|
|
294
|
+
default=None,
|
|
295
|
+
domain=str,
|
|
296
|
+
doc="Cold side name, sets control volume and inlet and outlet names",
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
def build(self):
|
|
301
|
+
# Call UnitModel.build to setup model
|
|
302
|
+
super().build()
|
|
303
|
+
hx_process_config(self)
|
|
304
|
+
|
|
305
|
+
# ---------------------------------------------------------------------
|
|
306
|
+
# Build hot-side control volume
|
|
307
|
+
self.hot_side = ControlVolume0DBlock(
|
|
308
|
+
dynamic=self.config.dynamic,
|
|
309
|
+
has_holdup=self.config.has_holdup,
|
|
310
|
+
property_package=self.config.hot_side.property_package,
|
|
311
|
+
property_package_args=self.config.hot_side.property_package_args,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# TODO : Add support for phase equilibrium?
|
|
315
|
+
self.hot_side.add_state_blocks(has_phase_equilibrium=self.config.hot_side.has_phase_equilibrium)
|
|
316
|
+
|
|
317
|
+
self.hot_side.add_material_balances(
|
|
318
|
+
balance_type=self.config.hot_side.material_balance_type,
|
|
319
|
+
has_phase_equilibrium=self.config.hot_side.has_phase_equilibrium,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
self.hot_side.add_energy_balances(
|
|
323
|
+
balance_type=self.config.hot_side.energy_balance_type,
|
|
324
|
+
has_heat_transfer=True,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
self.hot_side.add_momentum_balances(
|
|
328
|
+
balance_type=self.config.hot_side.momentum_balance_type,
|
|
329
|
+
has_pressure_change=self.config.hot_side.has_pressure_change,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# ---------------------------------------------------------------------
|
|
333
|
+
# Build cold-side control volume
|
|
334
|
+
self.cold_side = ControlVolume0DBlock(
|
|
335
|
+
dynamic=self.config.dynamic,
|
|
336
|
+
has_holdup=self.config.has_holdup,
|
|
337
|
+
property_package=self.config.cold_side.property_package,
|
|
338
|
+
property_package_args=self.config.cold_side.property_package_args,
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
self.cold_side.add_state_blocks(has_phase_equilibrium=self.config.cold_side.has_phase_equilibrium)
|
|
342
|
+
|
|
343
|
+
self.cold_side.add_material_balances(
|
|
344
|
+
balance_type=self.config.cold_side.material_balance_type,
|
|
345
|
+
has_phase_equilibrium=self.config.cold_side.has_phase_equilibrium,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
self.cold_side.add_energy_balances(
|
|
349
|
+
balance_type=self.config.cold_side.energy_balance_type,
|
|
350
|
+
has_heat_transfer=True,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
self.cold_side.add_momentum_balances(
|
|
354
|
+
balance_type=self.config.cold_side.momentum_balance_type,
|
|
355
|
+
has_pressure_change=self.config.cold_side.has_pressure_change,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# ---------------------------------------------------------------------
|
|
359
|
+
# Add Ports to control volumes
|
|
360
|
+
self.add_inlet_port(
|
|
361
|
+
name="hot_side_inlet", block=self.hot_side, doc="Hot side inlet port"
|
|
362
|
+
)
|
|
363
|
+
self.add_outlet_port(
|
|
364
|
+
name="hot_side_outlet", block=self.hot_side, doc="Hot side outlet port"
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
self.add_inlet_port(
|
|
368
|
+
name="cold_side_inlet", block=self.cold_side, doc="Cold side inlet port"
|
|
369
|
+
)
|
|
370
|
+
self.add_outlet_port(
|
|
371
|
+
name="cold_side_outlet", block=self.cold_side, doc="Cold side outlet port"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# ---------------------------------------------------------------------
|
|
375
|
+
# Add unit level References
|
|
376
|
+
# Set references to balance terms at unit level
|
|
377
|
+
self.heat_duty = Reference(self.cold_side.heat[:])
|
|
378
|
+
|
|
379
|
+
# Add references to the user provided aliases (if applicable).
|
|
380
|
+
add_hx_references(self)
|
|
381
|
+
|
|
382
|
+
# ---------------------------------------------------------------------
|
|
383
|
+
# Add performance equations
|
|
384
|
+
# All units of measurement will be based on hot side
|
|
385
|
+
hunits = self.hot_side.config.property_package.get_metadata().get_derived_units
|
|
386
|
+
|
|
387
|
+
# Overall energy balance
|
|
388
|
+
def rule_energy_balance(blk, t):
|
|
389
|
+
return blk.hot_side.heat[t] == -pyunits.convert(
|
|
390
|
+
blk.cold_side.heat[t], to_units=hunits("power")
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
self.energy_balance_constraint = Constraint(
|
|
394
|
+
self.flowsheet().time, rule=rule_energy_balance
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Add e-NTU variables
|
|
398
|
+
self.effectiveness = Var(
|
|
399
|
+
self.flowsheet().time,
|
|
400
|
+
initialize=1,
|
|
401
|
+
units=pyunits.dimensionless,
|
|
402
|
+
domain=PositiveReals,
|
|
403
|
+
doc="Effectiveness factor",
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
# Minimum heat capacitance ratio for e-NTU method
|
|
407
|
+
self.eps_cmin = Param(
|
|
408
|
+
initialize=1e-3,
|
|
409
|
+
mutable=True,
|
|
410
|
+
units=hunits("power") / hunits("temperature"),
|
|
411
|
+
doc="Epsilon parameter for smooth Cmin and Cmax",
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
###########
|
|
415
|
+
|
|
416
|
+
# Todo: Will not work for a pure component system in the two phase region
|
|
417
|
+
|
|
418
|
+
###########
|
|
419
|
+
|
|
420
|
+
#Build hotside State Block
|
|
421
|
+
tmp_dict = dict(**self.config.hot_side.property_package_args)
|
|
422
|
+
tmp_dict["has_phase_equilibrium"] = self.config.hot_side.has_phase_equilibrium
|
|
423
|
+
tmp_dict["defined_state"] = False
|
|
424
|
+
|
|
425
|
+
self.properties_hotside = self.config.hot_side.property_package.build_state_block(
|
|
426
|
+
self.flowsheet().time, doc="Hot side properties at outlet under Qmax", **tmp_dict
|
|
427
|
+
)
|
|
428
|
+
#Connect Properties to Hot Side
|
|
429
|
+
@self.Constraint(
|
|
430
|
+
self.flowsheet().time, doc="Hot Side Temperature at Qmax = Inlet Cold Temp")
|
|
431
|
+
def constraint_hotside_temp(self, t):
|
|
432
|
+
return self.properties_hotside[t].temperature == self.cold_side.properties_in[t].temperature
|
|
433
|
+
@self.Constraint(
|
|
434
|
+
self.flowsheet().time, doc="Hot Side Pressure at Qmax = Inlet Hot Pressure")
|
|
435
|
+
def constraint_hotside_pres(self, t):
|
|
436
|
+
return self.properties_hotside[t].pressure == self.hot_side.properties_in[t].pressure
|
|
437
|
+
@self.Constraint(
|
|
438
|
+
self.flowsheet().time, doc="Hot Side Flowrate at Qmax = Inlet Hot Flowrate")
|
|
439
|
+
def constraint_hotside_flow(self, t):
|
|
440
|
+
return self.properties_hotside[t].flow_mol == self.hot_side.properties_in[t].flow_mol
|
|
441
|
+
|
|
442
|
+
#Build Coldside State Block
|
|
443
|
+
tmp_dict = dict(**self.config.cold_side.property_package_args)
|
|
444
|
+
tmp_dict["has_phase_equilibrium"] = self.config.cold_side.has_phase_equilibrium
|
|
445
|
+
tmp_dict["defined_state"] = False
|
|
446
|
+
|
|
447
|
+
self.properties_coldside = self.config.cold_side.property_package.build_state_block(
|
|
448
|
+
self.flowsheet().time, doc="Cold side properties at outlet under Qmax", **tmp_dict
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
########
|
|
452
|
+
|
|
453
|
+
# Todo: Will not work for a pure component system in the two phase region
|
|
454
|
+
|
|
455
|
+
###########
|
|
456
|
+
|
|
457
|
+
#Connect Properties to Cold Side
|
|
458
|
+
@self.Constraint(
|
|
459
|
+
self.flowsheet().time, doc="Cold Side Temperature at Qmax = Inlet Hot Temp")
|
|
460
|
+
def constraint_coldside_temp(self, t):
|
|
461
|
+
return self.properties_coldside[t].temperature == self.hot_side.properties_in[t].temperature
|
|
462
|
+
@self.Constraint(
|
|
463
|
+
self.flowsheet().time, doc="Cold Side Pressure at Qmax = Inlet Cold Pressure")
|
|
464
|
+
def constraint_coldside_pres(self, t):
|
|
465
|
+
return self.properties_coldside[t].pressure == self.cold_side.properties_in[t].pressure
|
|
466
|
+
@self.Constraint(
|
|
467
|
+
self.flowsheet().time, doc="Cold Side Flowrate at Qmax = Inlet Cold Flowrate")
|
|
468
|
+
def constraint_coldside_flow(self, t):
|
|
469
|
+
return self.properties_coldside[t].flow_mol == self.cold_side.properties_in[t].flow_mol
|
|
470
|
+
|
|
471
|
+
# Delta h hot at Qmax
|
|
472
|
+
def rule_deltah_hot_qmax(blk, t):
|
|
473
|
+
return blk.hot_side.properties_in[t].enth_mol - blk.properties_hotside[t].enth_mol
|
|
474
|
+
|
|
475
|
+
self.delta_h_hot_qmax = Expression(
|
|
476
|
+
self.flowsheet().time, rule=rule_deltah_hot_qmax, doc="Delta h Hot Side at Qmax"
|
|
477
|
+
)
|
|
478
|
+
# Delta h cold at Qmax
|
|
479
|
+
def rule_deltah_cold_qmax(blk, t):
|
|
480
|
+
return blk.properties_coldside[t].enth_mol - blk.cold_side.properties_in[t].enth_mol
|
|
481
|
+
self.delta_h_cold_qmax = Expression(
|
|
482
|
+
self.flowsheet().time, rule=rule_deltah_cold_qmax, doc="Delta h Cold Side at Qmax"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# TODO : Support both mass and mole based flows
|
|
486
|
+
# Minimum heat transfer rate
|
|
487
|
+
def rule_Hmin(blk, t):
|
|
488
|
+
Hhot = pyunits.convert(
|
|
489
|
+
blk.hot_side.properties_in[t].flow_mol
|
|
490
|
+
* blk.delta_h_hot_qmax[t],
|
|
491
|
+
to_units=hunits("power"),
|
|
492
|
+
)
|
|
493
|
+
Hcold = pyunits.convert(
|
|
494
|
+
blk.cold_side.properties_in[t].flow_mol
|
|
495
|
+
* blk.delta_h_cold_qmax[t],
|
|
496
|
+
to_units=hunits("power"),
|
|
497
|
+
)
|
|
498
|
+
return smooth_min(Hhot, Hcold, eps=blk.eps_cmin)
|
|
499
|
+
|
|
500
|
+
self.Qmax = Expression(
|
|
501
|
+
self.flowsheet().time, rule=rule_Hmin, doc="Max heat transfer rate"
|
|
502
|
+
)
|
|
503
|
+
#Max heat transfer rate
|
|
504
|
+
def rule_Hmax(blk, t):
|
|
505
|
+
Hhot = pyunits.convert(
|
|
506
|
+
blk.hot_side.properties_in[t].flow_mol
|
|
507
|
+
* blk.delta_h_hot_qmax[t],
|
|
508
|
+
to_units=hunits("power"),
|
|
509
|
+
)
|
|
510
|
+
Hcold = pyunits.convert(
|
|
511
|
+
blk.cold_side.properties_in[t].flow_mol
|
|
512
|
+
* blk.delta_h_cold_qmax[t],
|
|
513
|
+
to_units=hunits("power"),
|
|
514
|
+
)
|
|
515
|
+
return smooth_max(Hhot, Hcold, eps=blk.eps_cmin)
|
|
516
|
+
self.Hmax_theory = Expression(
|
|
517
|
+
self.flowsheet().time,
|
|
518
|
+
rule=rule_Hmax, doc="Maximum heat transfer rate"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
# Add effectiveness relation
|
|
522
|
+
def rule_effectiveness(blk, t):
|
|
523
|
+
return blk.hot_side.heat[t] == -(
|
|
524
|
+
blk.effectiveness[t]
|
|
525
|
+
* blk.Qmax[t]
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
self.heat_duty_constraint = Constraint(self.flowsheet().time, rule=rule_effectiveness)
|
|
529
|
+
|
|
530
|
+
add_inverted(self.hot_side,"deltaP")
|
|
531
|
+
add_inverted(self.cold_side,"deltaP")
|
|
532
|
+
# TODO : Add scaling methods
|
|
533
|
+
|
|
534
|
+
def initialize_build(
|
|
535
|
+
self,
|
|
536
|
+
hot_side_state_args=None,
|
|
537
|
+
cold_side_state_args=None,
|
|
538
|
+
outlvl=idaeslog.NOTSET,
|
|
539
|
+
solver=None,
|
|
540
|
+
optarg=None,
|
|
541
|
+
duty=None,
|
|
542
|
+
):
|
|
543
|
+
"""
|
|
544
|
+
Heat exchanger initialization method.
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
hot_side_state_args : a dict of arguments to be passed to the
|
|
548
|
+
property initialization for the hot side (see documentation of
|
|
549
|
+
the specific property package) (default = None).
|
|
550
|
+
cold_side_state_args : a dict of arguments to be passed to the
|
|
551
|
+
property initialization for the cold side (see documentation of
|
|
552
|
+
the specific property package) (default = None).
|
|
553
|
+
outlvl : sets output level of initialization routine
|
|
554
|
+
optarg : solver options dictionary object (default=None, use
|
|
555
|
+
default solver options)
|
|
556
|
+
solver : str indicating which solver to use during
|
|
557
|
+
initialization (default = None, use default solver)
|
|
558
|
+
duty : an initial guess for the amount of heat transferred. This
|
|
559
|
+
should be a tuple in the form (value, units), (default
|
|
560
|
+
= (1000 J/s))
|
|
561
|
+
|
|
562
|
+
Returns:
|
|
563
|
+
None
|
|
564
|
+
|
|
565
|
+
"""
|
|
566
|
+
initialise_inverted(self.hot_side,"deltaP")
|
|
567
|
+
initialise_inverted(self.cold_side,"deltaP")
|
|
568
|
+
# Set solver options
|
|
569
|
+
init_log = idaeslog.getInitLogger(self.name, outlvl, tag="unit")
|
|
570
|
+
solve_log = idaeslog.getSolveLogger(self.name, outlvl, tag="unit")
|
|
571
|
+
|
|
572
|
+
hot_side = self.hot_side
|
|
573
|
+
cold_side = self.cold_side
|
|
574
|
+
hot_side_max = self.properties_hotside
|
|
575
|
+
cold_side_max = self.properties_coldside
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
# Create solver
|
|
579
|
+
opt = get_solver(solver, optarg)
|
|
580
|
+
|
|
581
|
+
flags1 = hot_side.initialize(
|
|
582
|
+
outlvl=outlvl, optarg=optarg, solver=solver, state_args=hot_side_state_args
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
init_log.info_high("Initialization Step 1a (hot side) Complete.")
|
|
586
|
+
|
|
587
|
+
flags2 = cold_side.initialize(
|
|
588
|
+
outlvl=outlvl, optarg=optarg, solver=solver, state_args=cold_side_state_args
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
init_log.info_high("Initialization Step 1b (cold side) Complete.")
|
|
592
|
+
|
|
593
|
+
# cold_side_max[0].pressure.set_value(cold_side.properties_in[0].pressure.value)
|
|
594
|
+
# hot_side_max[0].pressure.set_value(hot_side.properties_in[0].pressure.value)
|
|
595
|
+
|
|
596
|
+
# cold_side_max[0].flow_mol.set_value(cold_side.properties_in[0].flow_mol.value)
|
|
597
|
+
# hot_side_max[0].flow_mol.set_value(hot_side.properties_in[0].flow_mol.value)
|
|
598
|
+
|
|
599
|
+
# cold_side_max[0].enth_mol.set_value(cold_side.properties_in[0].enth_mol.value)
|
|
600
|
+
# hot_side_max[0].enth_mol.set_value(hot_side.properties_in[0].enth_mol.value)
|
|
601
|
+
|
|
602
|
+
flags3 = hot_side_max.initialize(
|
|
603
|
+
outlvl=outlvl
|
|
604
|
+
)
|
|
605
|
+
init_log.info_high("Initialization Step 1c (hot side max) Complete.")
|
|
606
|
+
|
|
607
|
+
flags4 = cold_side_max.initialize(
|
|
608
|
+
outlvl=outlvl
|
|
609
|
+
)
|
|
610
|
+
init_log.info_high("Initialization Step 1d (cold side max) Complete.")
|
|
611
|
+
|
|
612
|
+
hot_side_max[0].display()
|
|
613
|
+
cold_side_max[0].display()
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
#----------------------------------------------------------------------
|
|
617
|
+
|
|
618
|
+
self.energy_balance_constraint.deactivate()
|
|
619
|
+
self.heat_duty_constraint.deactivate()
|
|
620
|
+
self.hot_side.enthalpy_balances.deactivate()
|
|
621
|
+
self.cold_side.enthalpy_balances.deactivate()
|
|
622
|
+
self.cold_side.material_balances.deactivate()
|
|
623
|
+
self.hot_side.material_balances.deactivate()
|
|
624
|
+
self.hot_side.properties_out.deactivate()
|
|
625
|
+
self.cold_side.properties_out.deactivate()
|
|
626
|
+
|
|
627
|
+
print("DOF:", degrees_of_freedom(self))
|
|
628
|
+
from idaes.core.util import DiagnosticsToolbox
|
|
629
|
+
dt = DiagnosticsToolbox(self)
|
|
630
|
+
dt.display_underconstrained_set()
|
|
631
|
+
dt.display_overconstrained_set()
|
|
632
|
+
dt.report_structural_issues()
|
|
633
|
+
dt.report_numerical_issues()
|
|
634
|
+
dt.display_constraints_with_large_residuals()
|
|
635
|
+
#dt.compute_infeasibility_explanation()
|
|
636
|
+
dt.display_variables_at_or_outside_bounds()
|
|
637
|
+
|
|
638
|
+
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
|
|
639
|
+
res = opt.solve(self, tee=slc.tee)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
init_log.info_high("Initialization Step 1.5 {}.".format(idaeslog.condition(res)))
|
|
643
|
+
|
|
644
|
+
# ---------------------------------------------------------------------
|
|
645
|
+
# Solve unit without heat transfer equation
|
|
646
|
+
self.heat_duty_constraint.activate()
|
|
647
|
+
self.heat_duty.unfix()
|
|
648
|
+
self.hot_side.enthalpy_balances.activate()
|
|
649
|
+
self.cold_side.enthalpy_balances.activate()
|
|
650
|
+
self.cold_side.material_balances.activate()
|
|
651
|
+
self.hot_side.material_balances.activate()
|
|
652
|
+
self.hot_side.properties_out.activate()
|
|
653
|
+
self.cold_side.properties_out.activate()
|
|
654
|
+
|
|
655
|
+
# self.Qmax[0].set_value(1000)
|
|
656
|
+
|
|
657
|
+
# Get side 1 and side 2 heat units, and convert duty as needed
|
|
658
|
+
s1_units = hot_side.heat.get_units()
|
|
659
|
+
s2_units = cold_side.heat.get_units()
|
|
660
|
+
|
|
661
|
+
if duty is None:
|
|
662
|
+
# Assume 1000 J/s and check for unitless properties
|
|
663
|
+
if s1_units is None and s2_units is None:
|
|
664
|
+
# Backwards compatibility for unitless properties
|
|
665
|
+
s1_duty = -1000
|
|
666
|
+
s2_duty = 1000
|
|
667
|
+
else:
|
|
668
|
+
s1_duty = pyunits.convert_value(
|
|
669
|
+
-1000, from_units=pyunits.W, to_units=s1_units
|
|
670
|
+
)
|
|
671
|
+
s2_duty = pyunits.convert_value(
|
|
672
|
+
1000, from_units=pyunits.W, to_units=s2_units
|
|
673
|
+
)
|
|
674
|
+
else:
|
|
675
|
+
# Duty provided with explicit units
|
|
676
|
+
s1_duty = -pyunits.convert_value(
|
|
677
|
+
duty[0], from_units=duty[1], to_units=s1_units
|
|
678
|
+
)
|
|
679
|
+
s2_duty = pyunits.convert_value(
|
|
680
|
+
duty[0], from_units=duty[1], to_units=s2_units
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
cold_side.heat.fix(s2_duty)
|
|
684
|
+
for i in hot_side.heat:
|
|
685
|
+
hot_side.heat[i].value = s1_duty
|
|
686
|
+
|
|
687
|
+
print("DOF:", degrees_of_freedom(self))
|
|
688
|
+
|
|
689
|
+
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
|
|
690
|
+
res = opt.solve(self, tee=slc.tee)
|
|
691
|
+
|
|
692
|
+
init_log.info_high("Initialization Step 2 {}.".format(idaeslog.condition(res)))
|
|
693
|
+
|
|
694
|
+
cold_side.heat.unfix()
|
|
695
|
+
self.energy_balance_constraint.activate()
|
|
696
|
+
|
|
697
|
+
# ---------------------------------------------------------------------
|
|
698
|
+
# Solve unit
|
|
699
|
+
with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
|
|
700
|
+
res = opt.solve(self, tee=slc.tee)
|
|
701
|
+
init_log.info_high("Initialization Step 3 {}.".format(idaeslog.condition(res)))
|
|
702
|
+
|
|
703
|
+
# ---------------------------------------------------------------------
|
|
704
|
+
# Release Inlet state
|
|
705
|
+
hot_side.release_state(flags1, outlvl=outlvl)
|
|
706
|
+
cold_side.release_state(flags2, outlvl=outlvl)
|
|
707
|
+
|
|
708
|
+
init_log.info("Initialization Completed, {}".format(idaeslog.condition(res)))
|
|
709
|
+
|
|
710
|
+
if not check_optimal_termination(res):
|
|
711
|
+
raise InitializationError(
|
|
712
|
+
f"{self.name} failed to initialize successfully. Please check "
|
|
713
|
+
f"the output logs for more information."
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
return res
|
|
717
|
+
|
|
718
|
+
def _get_stream_table_contents(self, time_point=0):
|
|
719
|
+
return create_stream_table_dataframe(
|
|
720
|
+
{
|
|
721
|
+
"Hot Inlet": self.hot_side_inlet,
|
|
722
|
+
"Hot Outlet": self.hot_side_outlet,
|
|
723
|
+
"Cold Inlet": self.cold_side_inlet,
|
|
724
|
+
"Cold Outlet": self.cold_side_outlet,
|
|
725
|
+
},
|
|
726
|
+
time_point=time_point,
|
|
727
|
+
)
|