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,624 @@
|
|
|
1
|
+
# Pyomo core
|
|
2
|
+
from pyomo.environ import (
|
|
3
|
+
Constraint,
|
|
4
|
+
Expression,
|
|
5
|
+
NonNegativeReals,
|
|
6
|
+
Suffix,
|
|
7
|
+
Var,
|
|
8
|
+
value,
|
|
9
|
+
units as UNIT,
|
|
10
|
+
)
|
|
11
|
+
from pyomo.core.base.reference import Reference
|
|
12
|
+
from pyomo.common.config import ConfigBlock, ConfigValue, Bool
|
|
13
|
+
|
|
14
|
+
# IDAES core
|
|
15
|
+
from idaes.core import (
|
|
16
|
+
declare_process_block_class,
|
|
17
|
+
UnitModelBlockData,
|
|
18
|
+
useDefault,
|
|
19
|
+
StateBlock,
|
|
20
|
+
)
|
|
21
|
+
from idaes.core.util import scaling
|
|
22
|
+
from idaes.core.util.config import is_physical_parameter_block
|
|
23
|
+
from idaes.core.util.tables import create_stream_table_dataframe
|
|
24
|
+
from idaes.core.solvers import get_solver
|
|
25
|
+
from idaes.core.initialization import ModularInitializerBase
|
|
26
|
+
|
|
27
|
+
# Other
|
|
28
|
+
from property_packages.build_package import build_package
|
|
29
|
+
|
|
30
|
+
# Logger
|
|
31
|
+
import idaes.logger as idaeslog
|
|
32
|
+
|
|
33
|
+
# Typing
|
|
34
|
+
from typing import List
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__author__ = "Ahuora Centre for Smart Energy Systems, University of Waikato, New Zealand"
|
|
38
|
+
|
|
39
|
+
# Set up logger
|
|
40
|
+
_log = idaeslog.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
class DesuperheaterInitializer(ModularInitializerBase):
|
|
43
|
+
"""Initializer for ``Desuperheater``.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
blk : Desuperheater
|
|
48
|
+
The unit model block to initialize.
|
|
49
|
+
solver : optional
|
|
50
|
+
A Pyomo/IDAES solver instance. Defaults to :func:`idaes.core.solvers.get_solver`.
|
|
51
|
+
solver_options : dict, optional
|
|
52
|
+
Options to set on the solver, e.g. tolerances.
|
|
53
|
+
outlvl : int, optional
|
|
54
|
+
IDAES log level (e.g. :data:`idaes.logger.WARNING`).
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
pyomo.opt.results.results_.SolverResults
|
|
59
|
+
Result from the final solve.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def initialize(self, blk, **kwargs):
|
|
63
|
+
# --- Solver setup
|
|
64
|
+
solver = kwargs.get("solver", None) or get_solver()
|
|
65
|
+
solver_options = kwargs.get("solver_options", {})
|
|
66
|
+
for k, v in solver_options.items():
|
|
67
|
+
solver.options[k] = v
|
|
68
|
+
|
|
69
|
+
outlvl = kwargs.get("outlvl", idaeslog.WARNING)
|
|
70
|
+
log = idaeslog.getLogger(__name__)
|
|
71
|
+
|
|
72
|
+
# --- Time index
|
|
73
|
+
t0 = blk.flowsheet().time.first()
|
|
74
|
+
|
|
75
|
+
# --- 1) Initialize inlet state blocks
|
|
76
|
+
inlet_blocks = list(blk.inlet_blocks)
|
|
77
|
+
|
|
78
|
+
inlet_steam, inlet_water = inlet_blocks
|
|
79
|
+
inlet_water[t0].pressure.set_value(
|
|
80
|
+
inlet_steam[t0].pressure
|
|
81
|
+
)
|
|
82
|
+
inlet_water[t0].enth_mol.set_value(
|
|
83
|
+
blk.water.htpx(
|
|
84
|
+
blk.bfw_temperature[t0],
|
|
85
|
+
inlet_water[t0].pressure,
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
for sb in inlet_blocks:
|
|
89
|
+
if hasattr(sb, "initialize"):
|
|
90
|
+
sb.initialize(outlvl=outlvl)
|
|
91
|
+
|
|
92
|
+
# --- 2) Seed satuarate inlet-related state
|
|
93
|
+
sb = blk._int_sat_vap_state
|
|
94
|
+
sb[t0].pressure.set_value(
|
|
95
|
+
inlet_steam[t0].pressure
|
|
96
|
+
)
|
|
97
|
+
sb[t0].enth_mol.set_value(
|
|
98
|
+
inlet_steam[t0].enth_mol_sat_phase["Vap"]
|
|
99
|
+
)
|
|
100
|
+
if hasattr(sb, "initialize"):
|
|
101
|
+
sb.initialize(outlvl=outlvl)
|
|
102
|
+
|
|
103
|
+
# --- 3) Aggregate inlet info for seeding mixed state block
|
|
104
|
+
ms = blk.outlet_state
|
|
105
|
+
ms[t0].pressure.set_value(
|
|
106
|
+
inlet_steam[t0].pressure
|
|
107
|
+
)
|
|
108
|
+
if value(blk.deltaT_superheat[t0]) > 0:
|
|
109
|
+
ms[t0].enth_mol.set_value(
|
|
110
|
+
blk.water.htpx(
|
|
111
|
+
T=blk._int_sat_vap_state[t0].temperature + blk.deltaT_superheat[t0],
|
|
112
|
+
p=inlet_steam[t0].pressure,
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
else:
|
|
116
|
+
ms[t0].enth_mol.set_value(
|
|
117
|
+
blk._int_sat_vap_state[t0].enth_mol
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if value(inlet_steam[t0].enth_mol) > value(ms[t0].enth_mol) > value(inlet_water[t0].enth_mol):
|
|
121
|
+
ms[t0].flow_mol.set_value(
|
|
122
|
+
inlet_steam[t0].flow_mol * (inlet_steam[t0].enth_mol - ms[t0].enth_mol) / (ms[t0].enth_mol - inlet_water[t0].enth_mol)
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
ms[t0].flow_mol.set_value(
|
|
126
|
+
inlet_steam[t0].flow_mol
|
|
127
|
+
)
|
|
128
|
+
if hasattr(ms, "initialize"):
|
|
129
|
+
ms.initialize(outlvl=outlvl)
|
|
130
|
+
|
|
131
|
+
# --- 4) Solve
|
|
132
|
+
res = solver.solve(blk, tee=False)
|
|
133
|
+
log.info(f"Desuperheater init status: {res.solver.termination_condition}")
|
|
134
|
+
return res
|
|
135
|
+
|
|
136
|
+
def _make_config_block(config):
|
|
137
|
+
"""Declare configuration options for the Desuperheater unit.
|
|
138
|
+
|
|
139
|
+
Declares property package references and integer counts for inlets and outlets.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
config (ConfigBlock): The mutable configuration block to populate.
|
|
143
|
+
"""
|
|
144
|
+
config.declare(
|
|
145
|
+
"property_package",
|
|
146
|
+
ConfigValue(
|
|
147
|
+
default=useDefault,
|
|
148
|
+
domain=is_physical_parameter_block,
|
|
149
|
+
description="Property package to use for control volume",
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
config.declare(
|
|
153
|
+
"property_package_args",
|
|
154
|
+
ConfigBlock(
|
|
155
|
+
implicit=True,
|
|
156
|
+
description="Arguments to use for constructing property packages",
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@declare_process_block_class("Desuperheater")
|
|
161
|
+
class DesuperheaterData(UnitModelBlockData):
|
|
162
|
+
"""Desuperheater unit operation.
|
|
163
|
+
|
|
164
|
+
The Desuperheater injects a small water flow into a superheated steam flow to reduce superheat.
|
|
165
|
+
Desuperheating is common before using steam. Heat loss and pressure loss may be defined.
|
|
166
|
+
An intermediate saturated state is used for a key reference point.
|
|
167
|
+
|
|
168
|
+
Key features:
|
|
169
|
+
- Material, energy, and momentum balances around the desuperheater
|
|
170
|
+
- User-specified target amount of superheat at the exit of the desuperheater
|
|
171
|
+
- Optional heat and pressure losses.
|
|
172
|
+
|
|
173
|
+
Attributes:
|
|
174
|
+
inlet_list (list[str]): Names for inlet ports.
|
|
175
|
+
outlet_list (list[str]): Names for outlet ports (incl. condensate/ and vent).
|
|
176
|
+
inlet_blocks (list): StateBlocks for all inlets.
|
|
177
|
+
outlet_blocks (list): StateBlocks for all outlets.
|
|
178
|
+
_int_sat_vap_state: Intermediate saturated vapour StateBlock.
|
|
179
|
+
|
|
180
|
+
State variables:
|
|
181
|
+
deltaT_superheat (Var): Target degree of superheat in the steam at the exit of the process.
|
|
182
|
+
bfw_temperature (Var): Temperature of the inlet boiler feed water flow for desuperheating (degC).
|
|
183
|
+
heat_loss (Var): Heat loss from the header (W).
|
|
184
|
+
pressure_loss (Var): Pressure drop from inlet minimum to mixed state (Pa).
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
default_initializer=DesuperheaterInitializer
|
|
188
|
+
CONFIG = UnitModelBlockData.CONFIG()
|
|
189
|
+
_make_config_block(CONFIG)
|
|
190
|
+
|
|
191
|
+
def build(self) -> None:
|
|
192
|
+
"""Build the unit model structure (ports, states, constraints)."""
|
|
193
|
+
# 1. Inherit standard UnitModelBlockData properties and functions
|
|
194
|
+
super().build()
|
|
195
|
+
|
|
196
|
+
# 2. Validate input parameters are valid
|
|
197
|
+
self._validate_model_config()
|
|
198
|
+
|
|
199
|
+
# 3. Create lists of ports with state blocks to add
|
|
200
|
+
self.inlet_list = self._create_inlet_port_name_list()
|
|
201
|
+
self.outlet_list = self._create_outlet_port_name_list()
|
|
202
|
+
|
|
203
|
+
# 4. Declare ports, state blocks and state property bounds
|
|
204
|
+
self.inlet_blocks = self._add_ports_with_state_blocks(
|
|
205
|
+
stream_list=self.inlet_list,
|
|
206
|
+
is_inlet=True,
|
|
207
|
+
has_phase_equilibrium=False,
|
|
208
|
+
is_defined_state=True,
|
|
209
|
+
)
|
|
210
|
+
self.outlet_blocks = self._add_ports_with_state_blocks(
|
|
211
|
+
stream_list=self.outlet_list,
|
|
212
|
+
is_inlet=False,
|
|
213
|
+
has_phase_equilibrium=False,
|
|
214
|
+
is_defined_state=False
|
|
215
|
+
)
|
|
216
|
+
self._internal_blocks = self._add_internal_state_blocks()
|
|
217
|
+
self._add_bounds_to_state_properties()
|
|
218
|
+
|
|
219
|
+
# 4. Declare references, variables and expressions for external and internal use
|
|
220
|
+
self._create_references()
|
|
221
|
+
self._create_variables()
|
|
222
|
+
self._create_expressions()
|
|
223
|
+
|
|
224
|
+
# 5. Set balance equations
|
|
225
|
+
self._add_material_balances()
|
|
226
|
+
self._add_energy_balances()
|
|
227
|
+
self._add_momentum_balances()
|
|
228
|
+
self._add_additional_constraints()
|
|
229
|
+
|
|
230
|
+
# 6. Other
|
|
231
|
+
self.scaling_factor = Suffix(direction=Suffix.EXPORT)
|
|
232
|
+
|
|
233
|
+
# ------------------------------------------------------------------
|
|
234
|
+
# Helpers & construction utilities
|
|
235
|
+
# ------------------------------------------------------------------
|
|
236
|
+
def _validate_model_config(self) -> bool:
|
|
237
|
+
"""Validate configuration for inlet and outlet counts.
|
|
238
|
+
|
|
239
|
+
Raises:
|
|
240
|
+
ValueError: If ``property_package is None``.
|
|
241
|
+
"""
|
|
242
|
+
if self.config.property_package is None:
|
|
243
|
+
raise ValueError("Desuperheater: Property package not defined.")
|
|
244
|
+
return True
|
|
245
|
+
|
|
246
|
+
def _create_inlet_port_name_list(self) -> List[str]:
|
|
247
|
+
"""Build ordered inlet port names.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
list[str]: Names
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
[
|
|
255
|
+
"inlet_steam", "inlet_water"
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def _create_outlet_port_name_list(self) -> List[str]:
|
|
260
|
+
"""Build ordered outlet port names.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
list[str]: Names
|
|
264
|
+
"""
|
|
265
|
+
return [
|
|
266
|
+
"outlet",
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
def _add_ports_with_state_blocks(self,
|
|
270
|
+
stream_list: List[str],
|
|
271
|
+
is_inlet: List[str],
|
|
272
|
+
has_phase_equilibrium: bool=False,
|
|
273
|
+
is_defined_state: bool=None,
|
|
274
|
+
) -> List[StateBlock]:
|
|
275
|
+
"""Construct StateBlocks and expose them as ports.
|
|
276
|
+
|
|
277
|
+
Creates a StateBlock per named stream and attaches a corresponding inlet or
|
|
278
|
+
outlet Port. Inlet blocks are defined states; outlet blocks are calculated states.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
stream_list (list[str]): Port/StateBlock base names to create.
|
|
282
|
+
is_inlet (bool): If True, create inlet ports with ``defined_state=True``;
|
|
283
|
+
otherwise create outlet ports with ``defined_state=False``.
|
|
284
|
+
has_phase_equilibrium (bool)
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
list: The created StateBlocks, in the same order as ``stream_list``.
|
|
288
|
+
"""
|
|
289
|
+
# Create empty list to hold StateBlocks for return
|
|
290
|
+
state_block_ls = []
|
|
291
|
+
|
|
292
|
+
# Setup StateBlock argument dict
|
|
293
|
+
tmp_dict = dict(**self.config.property_package_args)
|
|
294
|
+
tmp_dict["has_phase_equilibrium"] = has_phase_equilibrium
|
|
295
|
+
if is_defined_state == None:
|
|
296
|
+
tmp_dict["defined_state"] = True if is_inlet else False
|
|
297
|
+
else:
|
|
298
|
+
tmp_dict["defined_state"] = is_defined_state
|
|
299
|
+
|
|
300
|
+
# Create an instance of StateBlock for all streams
|
|
301
|
+
for s in stream_list:
|
|
302
|
+
sb = self.config.property_package.build_state_block(
|
|
303
|
+
self.flowsheet().time, doc=f"Thermophysical properties at {s}", **tmp_dict
|
|
304
|
+
)
|
|
305
|
+
setattr(
|
|
306
|
+
self, s + "_state",
|
|
307
|
+
sb
|
|
308
|
+
)
|
|
309
|
+
state_block_ls.append(sb)
|
|
310
|
+
add_fn = self.add_inlet_port if is_inlet else self.add_outlet_port
|
|
311
|
+
add_fn(
|
|
312
|
+
name=s,
|
|
313
|
+
block=sb,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
return state_block_ls
|
|
317
|
+
|
|
318
|
+
def _add_internal_state_blocks(self) -> List[StateBlock]:
|
|
319
|
+
"""Create the intermediate StateBlock(s)."""
|
|
320
|
+
# The _int_sat_vap_state:
|
|
321
|
+
# - Has phase equilibrium enabled.
|
|
322
|
+
# - Is not a defined state (solved from balances).
|
|
323
|
+
tmp_dict = dict(**self.config.property_package_args)
|
|
324
|
+
tmp_dict["has_phase_equilibrium"] = True
|
|
325
|
+
tmp_dict["defined_state"] = False
|
|
326
|
+
self._int_sat_vap_state = self.config.property_package.build_state_block(
|
|
327
|
+
self.flowsheet().time,
|
|
328
|
+
doc="Thermophysical properties internal saturated vapour state.",
|
|
329
|
+
**tmp_dict
|
|
330
|
+
)
|
|
331
|
+
self._int_sat_vap_state[:].flow_mol.fix(1)
|
|
332
|
+
|
|
333
|
+
return [
|
|
334
|
+
self._int_sat_vap_state,
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
def _add_bounds_to_state_properties(self) -> None:
|
|
338
|
+
"""Add lower and/or upper bounds to state properties.
|
|
339
|
+
|
|
340
|
+
- Set nonnegativity lower bounds on all inlet/intermediate/outlet flows.
|
|
341
|
+
"""
|
|
342
|
+
for sb in (self.inlet_blocks + self.outlet_blocks):
|
|
343
|
+
for t in sb:
|
|
344
|
+
sb[t].flow_mol.setlb(0.0)
|
|
345
|
+
|
|
346
|
+
def _create_references(self) -> None:
|
|
347
|
+
"""Create convenient References.
|
|
348
|
+
|
|
349
|
+
Creates references to _int_mixed_inlet_state properties:
|
|
350
|
+
- ``bfw_flow_mass``
|
|
351
|
+
- ``bfw_flow_mol``
|
|
352
|
+
"""
|
|
353
|
+
self.bfw_flow_mass = Reference(
|
|
354
|
+
self.inlet_water_state[:].flow_mass
|
|
355
|
+
)
|
|
356
|
+
self.bfw_flow_mol = Reference(
|
|
357
|
+
self.inlet_water_state[:].flow_mol
|
|
358
|
+
)
|
|
359
|
+
self.inlet_water_state[:].flow_mol.unfix()
|
|
360
|
+
self.water = build_package("helmholtz", ["water"], ["Liq"])
|
|
361
|
+
|
|
362
|
+
def _create_variables(self) -> None:
|
|
363
|
+
"""Declare decision/parameter variables for the unit.
|
|
364
|
+
|
|
365
|
+
Creates:
|
|
366
|
+
- ``heat_loss``
|
|
367
|
+
- ``pressure_loss``
|
|
368
|
+
- ``bfw_temperature``
|
|
369
|
+
- ``deltaT_superheat``
|
|
370
|
+
"""
|
|
371
|
+
# Get units consistent with the property package
|
|
372
|
+
units_meta = self.config.property_package.get_metadata()
|
|
373
|
+
|
|
374
|
+
# User defined: Heat and pressure losses
|
|
375
|
+
self.heat_loss = Var(
|
|
376
|
+
self.flowsheet().time,
|
|
377
|
+
domain=NonNegativeReals,
|
|
378
|
+
doc="Heat loss. Default: 0 kW.",
|
|
379
|
+
units=units_meta.get_derived_units("power")
|
|
380
|
+
)
|
|
381
|
+
self.heat_loss.fix(
|
|
382
|
+
0 # Default fixed value
|
|
383
|
+
)
|
|
384
|
+
self.pressure_loss = Var(
|
|
385
|
+
self.flowsheet().time,
|
|
386
|
+
domain=NonNegativeReals,
|
|
387
|
+
doc="Pressure loss. Default: 0 Pa.",
|
|
388
|
+
units=units_meta.get_derived_units("pressure")
|
|
389
|
+
)
|
|
390
|
+
self.pressure_loss.fix(
|
|
391
|
+
0 # Default fixed value
|
|
392
|
+
)
|
|
393
|
+
# User defined: Boiler feed water temperature entering the desuperheater
|
|
394
|
+
self.bfw_temperature = Var(
|
|
395
|
+
self.flowsheet().time,
|
|
396
|
+
domain=NonNegativeReals,
|
|
397
|
+
doc="The target amount of subcooling of the condensate after process heating. Default: 0 K.",
|
|
398
|
+
units=units_meta.get_derived_units("temperature"),
|
|
399
|
+
)
|
|
400
|
+
self.bfw_temperature.fix(
|
|
401
|
+
(110 + 273.15) * UNIT.K # Default fixed value
|
|
402
|
+
)
|
|
403
|
+
# User defined: Target degree of superheat at the outlet of the desuperheater
|
|
404
|
+
self.deltaT_superheat = Var(
|
|
405
|
+
self.flowsheet().time,
|
|
406
|
+
domain=NonNegativeReals,
|
|
407
|
+
doc="The target amount of superheat present in the steam after desuperheating before use. Default: 0 K.",
|
|
408
|
+
units=units_meta.get_derived_units("temperature"),
|
|
409
|
+
)
|
|
410
|
+
self.deltaT_superheat.fix(
|
|
411
|
+
0 # Default fixed value
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
def _create_expressions(self) -> None:
|
|
415
|
+
"""Create helper Expressions.
|
|
416
|
+
|
|
417
|
+
Creates:
|
|
418
|
+
- ``flow_ratio``
|
|
419
|
+
"""
|
|
420
|
+
# Calculated, always show
|
|
421
|
+
self.flow_ratio = Expression(
|
|
422
|
+
self.flowsheet().time,
|
|
423
|
+
rule=lambda b, t: (
|
|
424
|
+
b.inlet_water_state[t].flow_mol
|
|
425
|
+
/
|
|
426
|
+
(b.inlet_steam_state[t].flow_mol + 1e-9)
|
|
427
|
+
),
|
|
428
|
+
doc="Ratio of water to steam flows.",
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# ------------------------------------------------------------------
|
|
432
|
+
# Balances
|
|
433
|
+
# ------------------------------------------------------------------
|
|
434
|
+
def _add_material_balances(self) -> None:
|
|
435
|
+
"""Material balance equations summary.
|
|
436
|
+
|
|
437
|
+
Balances / Constraints:
|
|
438
|
+
- ``overall_material_balance``
|
|
439
|
+
"""
|
|
440
|
+
@self.Constraint(
|
|
441
|
+
self.flowsheet().time,
|
|
442
|
+
doc="Overall material balance",
|
|
443
|
+
)
|
|
444
|
+
def overall_material_balance(b, t):
|
|
445
|
+
return (
|
|
446
|
+
sum(
|
|
447
|
+
o[t].flow_mol
|
|
448
|
+
for o in b.outlet_blocks
|
|
449
|
+
)
|
|
450
|
+
==
|
|
451
|
+
sum(
|
|
452
|
+
i[t].flow_mol
|
|
453
|
+
for i in b.inlet_blocks
|
|
454
|
+
)
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
def _add_energy_balances(self) -> None:
|
|
458
|
+
"""Energy balance equations summary.
|
|
459
|
+
|
|
460
|
+
Balances / Constraints:
|
|
461
|
+
- ``overall_energy_balance``
|
|
462
|
+
- ``saturated_vap_enthalpy_eq``
|
|
463
|
+
"""
|
|
464
|
+
@self.Constraint(
|
|
465
|
+
self.flowsheet().time,
|
|
466
|
+
doc="Overall energy balance",
|
|
467
|
+
)
|
|
468
|
+
def overall_energy_balance(b, t):
|
|
469
|
+
return (
|
|
470
|
+
sum(
|
|
471
|
+
i[t].flow_mol * i[t].enth_mol
|
|
472
|
+
for i in b.inlet_blocks
|
|
473
|
+
)
|
|
474
|
+
==
|
|
475
|
+
sum(
|
|
476
|
+
i[t].flow_mol * i[t].enth_mol
|
|
477
|
+
for i in b.outlet_blocks
|
|
478
|
+
)
|
|
479
|
+
+
|
|
480
|
+
b.heat_loss[t]
|
|
481
|
+
)
|
|
482
|
+
@self.Constraint(
|
|
483
|
+
self.flowsheet().time,
|
|
484
|
+
doc="Saturated vapour enthalpy",
|
|
485
|
+
)
|
|
486
|
+
def saturated_vap_enthalpy_eq(b, t):
|
|
487
|
+
return (
|
|
488
|
+
b.outlet_state[t].enth_mol_sat_phase["Vap"]
|
|
489
|
+
==
|
|
490
|
+
b._int_sat_vap_state[t].enth_mol
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
def _add_momentum_balances(self) -> None:
|
|
494
|
+
"""Momentum balance equations summary.
|
|
495
|
+
|
|
496
|
+
Balances / Constraints:
|
|
497
|
+
- ``overall_momentum_balance``
|
|
498
|
+
- ``intlet_water_momentum_balance``
|
|
499
|
+
- ``saturated_vap_pressure_eq``
|
|
500
|
+
"""
|
|
501
|
+
@self.Constraint(
|
|
502
|
+
self.flowsheet().time,
|
|
503
|
+
doc="Overall momentum balance",
|
|
504
|
+
)
|
|
505
|
+
def overall_momentum_balance(b, t):
|
|
506
|
+
return (
|
|
507
|
+
b.inlet_steam_state[t].pressure
|
|
508
|
+
==
|
|
509
|
+
b.outlet_state[t].pressure
|
|
510
|
+
+
|
|
511
|
+
b.pressure_loss[t]
|
|
512
|
+
)
|
|
513
|
+
@self.Constraint(
|
|
514
|
+
self.flowsheet().time,
|
|
515
|
+
doc="Inlet water momentum balance",
|
|
516
|
+
)
|
|
517
|
+
def intlet_water_momentum_balance(b, t):
|
|
518
|
+
return (
|
|
519
|
+
b.inlet_water_state[t].pressure
|
|
520
|
+
==
|
|
521
|
+
b.inlet_steam_state[t].pressure
|
|
522
|
+
)
|
|
523
|
+
@self.Constraint(
|
|
524
|
+
self.flowsheet().time,
|
|
525
|
+
doc="Saturated vapour pressure",
|
|
526
|
+
)
|
|
527
|
+
def saturated_vap_pressure_eq(b, t):
|
|
528
|
+
return (
|
|
529
|
+
b._int_sat_vap_state[t].pressure
|
|
530
|
+
==
|
|
531
|
+
b.outlet_state[t].pressure
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
def _add_additional_constraints(self) -> None:
|
|
535
|
+
"""Add auxiliary constraints and bounds.
|
|
536
|
+
|
|
537
|
+
Constraints:
|
|
538
|
+
- ``inlet_water_temperature_eq``
|
|
539
|
+
- ``desuperheating_temperature_eq``
|
|
540
|
+
"""
|
|
541
|
+
@self.Constraint(
|
|
542
|
+
self.flowsheet().time,
|
|
543
|
+
doc="Inlet water temperature",
|
|
544
|
+
)
|
|
545
|
+
def inlet_water_temperature_eq(b, t):
|
|
546
|
+
return (
|
|
547
|
+
b.inlet_water_state[t].temperature
|
|
548
|
+
==
|
|
549
|
+
b.bfw_temperature[t]
|
|
550
|
+
)
|
|
551
|
+
@self.Constraint(
|
|
552
|
+
self.flowsheet().time,
|
|
553
|
+
doc="Temperature after desuperheating",
|
|
554
|
+
)
|
|
555
|
+
def desuperheating_temperature_eq(b, t):
|
|
556
|
+
return (
|
|
557
|
+
b.outlet_state[t].temperature
|
|
558
|
+
==
|
|
559
|
+
b._int_sat_vap_state[t].temperature + b.deltaT_superheat[t]
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
def calculate_scaling_factors(self):
|
|
563
|
+
"""Assign scaling factors to improve numerical conditioning.
|
|
564
|
+
|
|
565
|
+
Sets scaling factors for performance and auxiliary variables.
|
|
566
|
+
"""
|
|
567
|
+
super().calculate_scaling_factors()
|
|
568
|
+
scaling.set_scaling_factor(self.heat_loss, 1e-3) # kW scale
|
|
569
|
+
scaling.set_scaling_factor(self.pressure_loss, 1e-3) # kPa scale
|
|
570
|
+
|
|
571
|
+
def _get_stream_table_contents(self, time_point=0):
|
|
572
|
+
"""Create a stream table for all inlets and outlets.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
time_point (int | float): Time index at which to extract stream data.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
pandas.DataFrame: A tabular view suitable for reporting via
|
|
579
|
+
``create_stream_table_dataframe``.
|
|
580
|
+
"""
|
|
581
|
+
io_dict = {}
|
|
582
|
+
|
|
583
|
+
for inlet_name in self.inlet_list:
|
|
584
|
+
io_dict[inlet_name] = getattr(self, inlet_name)
|
|
585
|
+
|
|
586
|
+
for outlet_name in self.outlet_list:
|
|
587
|
+
io_dict[outlet_name] = getattr(self, outlet_name)
|
|
588
|
+
|
|
589
|
+
return create_stream_table_dataframe(io_dict, time_point=time_point)
|
|
590
|
+
|
|
591
|
+
def _get_performance_contents(self, time_point=0):
|
|
592
|
+
"""Collect performance variables for reporting.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
time_point (int | float): Time index at which to report values.
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
dict: Mapping used by IDAES reporters, containing human-friendly labels
|
|
599
|
+
to Vars/References (e.g., heat/pressure loss, mixed-state properties).
|
|
600
|
+
"""
|
|
601
|
+
return {
|
|
602
|
+
"vars": {
|
|
603
|
+
"BFW temperature [K]": self.bfw_temperature[time_point],
|
|
604
|
+
"Degree of superheat target [K]": self.deltaT_superheat[time_point],
|
|
605
|
+
"Heat loss [W]": self.heat_loss[time_point],
|
|
606
|
+
"Pressure loss [Pa]": self.pressure_loss[time_point],
|
|
607
|
+
},
|
|
608
|
+
"exprs": {
|
|
609
|
+
"Water-to-steam flow ratio": self.flow_ratio[time_point],
|
|
610
|
+
},
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
def initialize(self, *args, **kwargs):
|
|
614
|
+
"""Initialize the Desuperheater unit using :class:`DesuperheaterInitializer`.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
*args: Forwarded to ``DesuperheaterInitializer.initialize``.
|
|
618
|
+
**kwargs: Forwarded to ``DesuperheaterInitializer.initialize`` (e.g., solver, options).
|
|
619
|
+
|
|
620
|
+
Returns:
|
|
621
|
+
pyomo.opt.results.results_.SolverResults: Results from the initializer's solve.
|
|
622
|
+
"""
|
|
623
|
+
init = DesuperheaterInitializer()
|
|
624
|
+
return init.initialize(self, *args, **kwargs)
|