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,371 @@
|
|
|
1
|
+
# Import Pyomo libraries
|
|
2
|
+
from pyomo.environ import (
|
|
3
|
+
Var,
|
|
4
|
+
Suffix,
|
|
5
|
+
units as pyunits,
|
|
6
|
+
)
|
|
7
|
+
from pyomo.common.config import ConfigBlock, ConfigValue, In
|
|
8
|
+
from idaes.core.util.tables import create_stream_table_dataframe
|
|
9
|
+
from idaes.core.util.exceptions import ConfigurationError
|
|
10
|
+
|
|
11
|
+
# Import IDAES cores
|
|
12
|
+
from idaes.core import (
|
|
13
|
+
declare_process_block_class,
|
|
14
|
+
UnitModelBlockData,
|
|
15
|
+
useDefault,
|
|
16
|
+
)
|
|
17
|
+
from idaes.core.util.config import is_physical_parameter_block
|
|
18
|
+
import idaes.core.util.scaling as iscale
|
|
19
|
+
import idaes.logger as idaeslog
|
|
20
|
+
|
|
21
|
+
# Set up logger
|
|
22
|
+
_log = idaeslog.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# When using this file the name "Load" is what is imported
|
|
26
|
+
@declare_process_block_class("Dsi")
|
|
27
|
+
class dsiData(UnitModelBlockData):
|
|
28
|
+
"""
|
|
29
|
+
Direct Steam Injection Unit Model
|
|
30
|
+
|
|
31
|
+
This unit model is used to represent a direct steam injection
|
|
32
|
+
process. There are no degrees of freedom, but the steam is mixed with the inlet fluid to heat it up.
|
|
33
|
+
It is assumed that the pressure of the fluid doesn't change, i.e the steam loses its pressure.
|
|
34
|
+
However, the enthalpy of the steam remains the same.
|
|
35
|
+
This allows to use two different property packages for the steam and for the inlet fluid, however,
|
|
36
|
+
it only works if the reference enthalpy of the steam and the inlet fluid are the same.
|
|
37
|
+
|
|
38
|
+
It's basically a combination of a mixer and a translator.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# CONFIG are options for the unit model
|
|
42
|
+
CONFIG = ConfigBlock()
|
|
43
|
+
|
|
44
|
+
CONFIG.declare(
|
|
45
|
+
"dynamic",
|
|
46
|
+
ConfigValue(
|
|
47
|
+
domain=In([False]),
|
|
48
|
+
default=False,
|
|
49
|
+
description="Dynamic model flag - must be False",
|
|
50
|
+
doc="""Indicates whether this model will be dynamic or not,
|
|
51
|
+
**default** = False. The Bus unit does not support dynamic
|
|
52
|
+
behavior, thus this must be False.""",
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
CONFIG.declare(
|
|
56
|
+
"has_holdup",
|
|
57
|
+
ConfigValue(
|
|
58
|
+
default=False,
|
|
59
|
+
domain=In([False]),
|
|
60
|
+
description="Holdup construction flag - must be False",
|
|
61
|
+
doc="""Indicates whether holdup terms should be constructed or not.
|
|
62
|
+
**default** - False. The Bus unit does not have defined volume, thus
|
|
63
|
+
this must be False.""",
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
CONFIG.declare(
|
|
67
|
+
"property_package",
|
|
68
|
+
ConfigValue(
|
|
69
|
+
default=useDefault,
|
|
70
|
+
domain=is_physical_parameter_block,
|
|
71
|
+
description="Property package to use for control volume",
|
|
72
|
+
doc="""Property parameter object used to define property calculations,
|
|
73
|
+
**default** - useDefault.
|
|
74
|
+
**Valid values:** {
|
|
75
|
+
**useDefault** - use default package from parent model or flowsheet,
|
|
76
|
+
**PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
CONFIG.declare(
|
|
80
|
+
"property_package_args",
|
|
81
|
+
ConfigBlock(
|
|
82
|
+
implicit=True,
|
|
83
|
+
description="Arguments to use for constructing property packages",
|
|
84
|
+
doc="""A ConfigBlock with arguments to be passed to a property block(s)
|
|
85
|
+
and used when constructing these,
|
|
86
|
+
**default** - None.
|
|
87
|
+
**Valid values:** {
|
|
88
|
+
see property package for documentation.}""",
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
CONFIG.declare(
|
|
92
|
+
"steam_property_package",
|
|
93
|
+
ConfigValue(
|
|
94
|
+
default=useDefault,
|
|
95
|
+
domain=is_physical_parameter_block,
|
|
96
|
+
description="Property package to use for control volume",
|
|
97
|
+
doc="""Property parameter object used to define property calculations,
|
|
98
|
+
**default** - useDefault.
|
|
99
|
+
**Valid values:** {
|
|
100
|
+
**useDefault** - use default package from parent model or flowsheet,
|
|
101
|
+
**PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
CONFIG.declare(
|
|
105
|
+
"steam_property_package_args",
|
|
106
|
+
ConfigBlock(
|
|
107
|
+
implicit=True,
|
|
108
|
+
description="Arguments to use for constructing property packages",
|
|
109
|
+
doc="""A ConfigBlock with arguments to be passed to a property block(s)
|
|
110
|
+
and used when constructing these,
|
|
111
|
+
**default** - None.
|
|
112
|
+
**Valid values:** {
|
|
113
|
+
see property package for documentation.}""",
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def build(self):
|
|
118
|
+
# build always starts by calling super().build()
|
|
119
|
+
# This triggers a lot of boilerplate in the background for you
|
|
120
|
+
super().build()
|
|
121
|
+
|
|
122
|
+
# This creates blank scaling factors, which are populated later
|
|
123
|
+
self.scaling_factor = Suffix(direction=Suffix.EXPORT)
|
|
124
|
+
|
|
125
|
+
# Add state blocks for inlet, outlet, and waste
|
|
126
|
+
# These include the state variables and any other properties on demand
|
|
127
|
+
# Add inlet block
|
|
128
|
+
tmp_dict = dict(**self.config.property_package_args)
|
|
129
|
+
tmp_dict["parameters"] = self.config.property_package
|
|
130
|
+
tmp_dict["defined_state"] = True # inlet block is an inlet
|
|
131
|
+
self.properties_milk_in = self.config.property_package.state_block_class(
|
|
132
|
+
self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# We need to calculate the enthalpy of the composition, before adding additional enthalpy from the temperature difference.
|
|
136
|
+
# so we'll add another state block to do that.
|
|
137
|
+
tmp_dict["defined_state"] = False
|
|
138
|
+
tmp_dict["has_phase_equilibrium"] = False
|
|
139
|
+
self.properties_mixed_unheated = self.config.property_package.state_block_class(
|
|
140
|
+
self.flowsheet().config.time,
|
|
141
|
+
doc="Material properties of mixture, before accounting for temperature difference",
|
|
142
|
+
**tmp_dict,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Add outlet block
|
|
146
|
+
tmp_dict["defined_state"] = False
|
|
147
|
+
tmp_dict["has_phase_equilibrium"] = False
|
|
148
|
+
self.properties_out = self.config.property_package.state_block_class(
|
|
149
|
+
self.flowsheet().config.time,
|
|
150
|
+
doc="Material properties of outlet",
|
|
151
|
+
**tmp_dict,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Add steam inlet block
|
|
155
|
+
steam_dict = dict(**self.config.steam_property_package_args)
|
|
156
|
+
steam_dict["parameters"] = self.config.steam_property_package
|
|
157
|
+
steam_dict["defined_state"] = True
|
|
158
|
+
tmp_dict["has_phase_equilibrium"] = True
|
|
159
|
+
|
|
160
|
+
self.properties_steam_in = self.config.steam_property_package.state_block_class(
|
|
161
|
+
self.flowsheet().config.time,
|
|
162
|
+
doc="Material properties of steam inlet",
|
|
163
|
+
**steam_dict,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# To calculate the amount of enthalpy to add to the inlet fluid, we need to know the difference in enthalpy between steam at that T and P
|
|
167
|
+
# and steam at its inlet conditions. Note this is assuming that effects of composition (the steam will no longer be pure water) are negligible.
|
|
168
|
+
# Note that this state block is just for calcuating, and not an actual inlet or outlet.
|
|
169
|
+
|
|
170
|
+
steam_dict["defined_state"] = False # This doesn't affect pure components.
|
|
171
|
+
steam_dict["has_phase_equilibrium"] = True
|
|
172
|
+
self.properties_steam_cooled = (
|
|
173
|
+
self.config.steam_property_package.state_block_class(
|
|
174
|
+
self.flowsheet().config.time,
|
|
175
|
+
doc="Material properties of cooled steam",
|
|
176
|
+
**steam_dict,
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Add ports
|
|
181
|
+
self.add_port(name="outlet", block=self.properties_out)
|
|
182
|
+
self.add_port(name="inlet", block=self.properties_milk_in, doc="Inlet port")
|
|
183
|
+
self.add_port(
|
|
184
|
+
name="steam_inlet", block=self.properties_steam_in, doc="Steam inlet port"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# CONDITIONS
|
|
188
|
+
|
|
189
|
+
# STEAM INTERMEDIATE BLOCK
|
|
190
|
+
|
|
191
|
+
# Temperature (= other inlet temperature)
|
|
192
|
+
@self.Constraint(
|
|
193
|
+
self.flowsheet().time,
|
|
194
|
+
doc="Set the temperature of the cooled steam to be the same as the inlet fluid",
|
|
195
|
+
)
|
|
196
|
+
def eq_steam_cooled_temperature(b, t):
|
|
197
|
+
return (
|
|
198
|
+
b.properties_steam_cooled[t].temperature
|
|
199
|
+
== b.properties_milk_in[t].temperature
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Pressure (= other inlet pressure)
|
|
203
|
+
@self.Constraint(
|
|
204
|
+
self.flowsheet().time,
|
|
205
|
+
doc="Set the pressure of the cooled steam to be the same as the inlet fluid",
|
|
206
|
+
)
|
|
207
|
+
def eq_steam_cooled_pressure(b, t):
|
|
208
|
+
return (
|
|
209
|
+
b.properties_steam_cooled[t].pressure
|
|
210
|
+
== b.properties_milk_in[t].pressure
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Flow = steam_flow
|
|
214
|
+
@self.Constraint(
|
|
215
|
+
self.flowsheet().time,
|
|
216
|
+
self.config.steam_property_package.component_list,
|
|
217
|
+
doc="Set the composition of the cooled steam to be the same as the steam inlet",
|
|
218
|
+
)
|
|
219
|
+
def eq_steam_cooled_composition(b, t, c):
|
|
220
|
+
return 0 == sum(
|
|
221
|
+
b.properties_steam_cooled[t].get_material_flow_terms(p, c)
|
|
222
|
+
- b.properties_steam_in[t].get_material_flow_terms(p, c)
|
|
223
|
+
for p in b.properties_steam_in[t].phase_list
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# CALCULATE ENTHALPY DIFFERENCE
|
|
227
|
+
@self.Expression(
|
|
228
|
+
self.flowsheet().time,
|
|
229
|
+
)
|
|
230
|
+
def steam_delta_h(b, t):
|
|
231
|
+
"""
|
|
232
|
+
Calculate the difference in enthalpy between the steam inlet and the cooled steam.
|
|
233
|
+
This is used to calculate the amount of enthalpy to add to the inlet fluid.
|
|
234
|
+
"""
|
|
235
|
+
return (
|
|
236
|
+
b.properties_steam_in[t].enth_mol
|
|
237
|
+
- b.properties_steam_cooled[t].enth_mol
|
|
238
|
+
) * b.properties_steam_in[t].flow_mol
|
|
239
|
+
|
|
240
|
+
# MIXING (without changing temperature)
|
|
241
|
+
|
|
242
|
+
# Pressure (= inlet pressure)
|
|
243
|
+
@self.Constraint(
|
|
244
|
+
self.flowsheet().time,
|
|
245
|
+
doc="Equivalent pressure balance",
|
|
246
|
+
)
|
|
247
|
+
def eq_mixed_pressure(b, t):
|
|
248
|
+
return (
|
|
249
|
+
b.properties_mixed_unheated[t].pressure
|
|
250
|
+
== b.properties_milk_in[t].pressure
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Temperature (= inlet temperature)
|
|
254
|
+
@self.Constraint(
|
|
255
|
+
self.flowsheet().time,
|
|
256
|
+
doc="Equivalent temperature balance",
|
|
257
|
+
)
|
|
258
|
+
def eq_mixed_temperature(b, t):
|
|
259
|
+
return (
|
|
260
|
+
b.properties_mixed_unheated[t].temperature
|
|
261
|
+
== b.properties_milk_in[t].temperature
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Flow = inlet flow + steam flow
|
|
265
|
+
@self.Constraint(
|
|
266
|
+
self.flowsheet().time,
|
|
267
|
+
self.config.property_package.component_list,
|
|
268
|
+
doc="Mass balance",
|
|
269
|
+
)
|
|
270
|
+
def eq_mixed_composition(b, t, c):
|
|
271
|
+
return 0 == sum(
|
|
272
|
+
b.properties_milk_in[t].get_material_flow_terms(p, c)
|
|
273
|
+
+ (
|
|
274
|
+
b.properties_steam_in[t].get_material_flow_terms(p, c)
|
|
275
|
+
if c
|
|
276
|
+
in b.properties_steam_in[
|
|
277
|
+
t
|
|
278
|
+
].component_list # handle the case where a component isn't in the steam inlet (e.g no milk in helmholtz)
|
|
279
|
+
else 0
|
|
280
|
+
)
|
|
281
|
+
- b.properties_mixed_unheated[t].get_material_flow_terms(p, c)
|
|
282
|
+
for p in b.properties_milk_in[t].phase_list
|
|
283
|
+
if (p, c) in b.properties_milk_in[t].phase_component_set
|
|
284
|
+
) # handle the case where a component is not in that phase (e.g no milk vapor)
|
|
285
|
+
|
|
286
|
+
# OUTLET BLOCK
|
|
287
|
+
|
|
288
|
+
# Pressure (= inlet pressure)
|
|
289
|
+
@self.Constraint(
|
|
290
|
+
self.flowsheet().time,
|
|
291
|
+
doc="Pressure balance",
|
|
292
|
+
)
|
|
293
|
+
def eq_outlet_pressure(b, t):
|
|
294
|
+
return b.properties_out[t].pressure == b.properties_milk_in[t].pressure
|
|
295
|
+
|
|
296
|
+
# Enthalpy (= mixed enthalpy + delta steam enthalpy)
|
|
297
|
+
@self.Constraint(
|
|
298
|
+
self.flowsheet().time,
|
|
299
|
+
doc="Energy balance",
|
|
300
|
+
)
|
|
301
|
+
def eq_outlet_combined_enthalpy(b, t):
|
|
302
|
+
return b.properties_out[t].enth_mol == b.properties_mixed_unheated[
|
|
303
|
+
t
|
|
304
|
+
].enth_mol + (b.steam_delta_h[t] / b.properties_mixed_unheated[t].flow_mol)
|
|
305
|
+
|
|
306
|
+
# Flow = mixed flow
|
|
307
|
+
|
|
308
|
+
@self.Constraint(
|
|
309
|
+
self.flowsheet().time,
|
|
310
|
+
self.config.property_package.component_list,
|
|
311
|
+
doc="Mass balance for the outlet",
|
|
312
|
+
)
|
|
313
|
+
def eq_outlet_composition(b, t, c):
|
|
314
|
+
return 0 == sum(
|
|
315
|
+
b.properties_out[t].get_material_flow_terms(p, c)
|
|
316
|
+
- b.properties_mixed_unheated[t].get_material_flow_terms(p, c)
|
|
317
|
+
for p in b.properties_out[t].phase_list
|
|
318
|
+
if (p, c) in b.properties_out[t].phase_component_set
|
|
319
|
+
) # handle the case where a component is not in that phase (e.g no milk vapor)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def calculate_scaling_factors(self):
|
|
324
|
+
super().calculate_scaling_factors()
|
|
325
|
+
|
|
326
|
+
def initialize(blk, *args, **kwargs):
|
|
327
|
+
blk.properties_milk_in.initialize()
|
|
328
|
+
blk.properties_steam_in.initialize()
|
|
329
|
+
|
|
330
|
+
for t in blk.flowsheet().time:
|
|
331
|
+
# copy temperature and pressure from properties_milk_in to properties_steam_cooled
|
|
332
|
+
# blk.properties_steam_cooled[t].temperature.set_value(
|
|
333
|
+
# blk.properties_milk_in[t].temperature.value
|
|
334
|
+
# )
|
|
335
|
+
blk.properties_steam_cooled[t].pressure.set_value(
|
|
336
|
+
blk.properties_milk_in[t].pressure.value
|
|
337
|
+
)
|
|
338
|
+
# Copy composition from properties_steam_in to properties_steam_cooled
|
|
339
|
+
blk.properties_steam_cooled[t].flow_mol.set_value(
|
|
340
|
+
blk.properties_steam_in[t].flow_mol.value
|
|
341
|
+
)
|
|
342
|
+
# If it's steam, there's only one component, so we prolly don't need to worry about composition.
|
|
343
|
+
# But may want TODO this for other cases.
|
|
344
|
+
|
|
345
|
+
blk.properties_steam_cooled.initialize()
|
|
346
|
+
blk.properties_mixed_unheated.initialize()
|
|
347
|
+
|
|
348
|
+
blk.properties_out.initialize()
|
|
349
|
+
pass
|
|
350
|
+
|
|
351
|
+
def _get_stream_table_contents(self, time_point=0):
|
|
352
|
+
"""
|
|
353
|
+
Assume unit has standard configuration of 1 inlet and 1 outlet.
|
|
354
|
+
|
|
355
|
+
Developers should overload this as appropriate.
|
|
356
|
+
"""
|
|
357
|
+
try:
|
|
358
|
+
return create_stream_table_dataframe(
|
|
359
|
+
{
|
|
360
|
+
"outlet": self.outlet,
|
|
361
|
+
"inlet": self.inlet,
|
|
362
|
+
"steam_inlet": self.steam_inlet,
|
|
363
|
+
},
|
|
364
|
+
time_point=time_point,
|
|
365
|
+
)
|
|
366
|
+
except AttributeError:
|
|
367
|
+
raise ConfigurationError(
|
|
368
|
+
f"Unit model {self.name} does not have the standard Port "
|
|
369
|
+
f"names (inlet and outlet). Please contact the unit model "
|
|
370
|
+
f"developer to develop a unit specific stream table."
|
|
371
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Import Pyomo libraries
|
|
2
|
+
from stringprep import in_table_a1
|
|
3
|
+
from pyomo.environ import (
|
|
4
|
+
Constraint,
|
|
5
|
+
Set,
|
|
6
|
+
Var,
|
|
7
|
+
Suffix,
|
|
8
|
+
units as pyunits,
|
|
9
|
+
)
|
|
10
|
+
from pyomo.environ import Reals
|
|
11
|
+
from pyomo.common.config import ConfigBlock, ConfigValue, In
|
|
12
|
+
from idaes.core.util.model_statistics import degrees_of_freedom
|
|
13
|
+
# Import IDAES cores
|
|
14
|
+
from idaes.core import (
|
|
15
|
+
declare_process_block_class,
|
|
16
|
+
UnitModelBlockData,
|
|
17
|
+
useDefault,
|
|
18
|
+
MaterialBalanceType,
|
|
19
|
+
MaterialFlowBasis,
|
|
20
|
+
)
|
|
21
|
+
from idaes.core.util.config import is_physical_parameter_block
|
|
22
|
+
import idaes.core.util.scaling as iscale
|
|
23
|
+
import idaes.logger as idaeslog
|
|
24
|
+
from idaes.models.unit_models import separator
|
|
25
|
+
from idaes.core.util.tables import create_stream_table_dataframe
|
|
26
|
+
from idaes.core.util.exceptions import ConfigurationError, BurntToast, PropertyNotSupportedError
|
|
27
|
+
|
|
28
|
+
from idaes.models.unit_models import separator
|
|
29
|
+
#Import enum
|
|
30
|
+
from enum import Enum
|
|
31
|
+
|
|
32
|
+
# Set up logger
|
|
33
|
+
_log = idaeslog.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# Enumerate options for balances
|
|
36
|
+
class SplittingType(Enum):
|
|
37
|
+
"""
|
|
38
|
+
Enum of supported material split types.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
totalFlow = 1
|
|
42
|
+
phaseFlow = 2
|
|
43
|
+
componentFlow = 3
|
|
44
|
+
phaseComponentFlow = 4
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# When using this file the name "acBus" is what is imported
|
|
49
|
+
@declare_process_block_class("acBus")
|
|
50
|
+
class acBusData(UnitModelBlockData):
|
|
51
|
+
"""
|
|
52
|
+
Zero order acbus model
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# CONFIG are options for the unit model, this simple model only has the mandatory config options
|
|
56
|
+
CONFIG = ConfigBlock()
|
|
57
|
+
|
|
58
|
+
CONFIG.declare(
|
|
59
|
+
"dynamic",
|
|
60
|
+
ConfigValue(
|
|
61
|
+
domain=In([False]),
|
|
62
|
+
default=False,
|
|
63
|
+
description="Dynamic model flag - must be False",
|
|
64
|
+
doc="""Indicates whether this model will be dynamic or not,
|
|
65
|
+
**default** = False. The Bus unit does not support dynamic
|
|
66
|
+
behavior, thus this must be False.""",
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
CONFIG.declare(
|
|
70
|
+
"has_holdup",
|
|
71
|
+
ConfigValue(
|
|
72
|
+
default=False,
|
|
73
|
+
domain=In([False]),
|
|
74
|
+
description="Holdup construction flag - must be False",
|
|
75
|
+
doc="""Indicates whether holdup terms should be constructed or not.
|
|
76
|
+
**default** - False. The Bus unit does not have defined volume, thus
|
|
77
|
+
this must be False.""",
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
CONFIG.declare(
|
|
81
|
+
"property_package",
|
|
82
|
+
ConfigValue(
|
|
83
|
+
default=useDefault,
|
|
84
|
+
domain=is_physical_parameter_block,
|
|
85
|
+
description="Property package to use for control volume",
|
|
86
|
+
doc="""Property parameter object used to define property calculations,
|
|
87
|
+
**default** - useDefault.
|
|
88
|
+
**Valid values:** {
|
|
89
|
+
**useDefault** - use default package from parent model or flowsheet,
|
|
90
|
+
**PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
CONFIG.declare(
|
|
94
|
+
"property_package_args",
|
|
95
|
+
ConfigBlock(
|
|
96
|
+
implicit=True,
|
|
97
|
+
description="Arguments to use for constructing property packages",
|
|
98
|
+
doc="""A ConfigBlock with arguments to be passed to a property block(s)
|
|
99
|
+
and used when constructing these,
|
|
100
|
+
**default** - None.
|
|
101
|
+
**Valid values:** {
|
|
102
|
+
see property package for documentation.}""",
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
CONFIG.declare(
|
|
106
|
+
"num_inlets",
|
|
107
|
+
ConfigValue(
|
|
108
|
+
default=False,
|
|
109
|
+
domain=int,
|
|
110
|
+
description="Number of inlets to add",
|
|
111
|
+
doc="Number of inlets to add",
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
CONFIG.declare(
|
|
115
|
+
"num_outlets",
|
|
116
|
+
ConfigValue(
|
|
117
|
+
default=False,
|
|
118
|
+
domain=int,
|
|
119
|
+
description="Number of outlets to add",
|
|
120
|
+
doc="Number of outlets to add",
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
CONFIG.declare(
|
|
124
|
+
"material_balance_type",
|
|
125
|
+
ConfigValue(
|
|
126
|
+
default=MaterialBalanceType.useDefault,
|
|
127
|
+
domain=In(MaterialBalanceType),
|
|
128
|
+
description="Material balance construction flag",
|
|
129
|
+
doc="""Indicates what type of mass balance should be constructed,
|
|
130
|
+
**default** - MaterialBalanceType.useDefault.
|
|
131
|
+
**Valid values:** {
|
|
132
|
+
**MaterialBalanceType.useDefault - refer to property package for default
|
|
133
|
+
balance type
|
|
134
|
+
**MaterialBalanceType.none** - exclude material balances,
|
|
135
|
+
**MaterialBalanceType.componentPhase** - use phase component balances,
|
|
136
|
+
**MaterialBalanceType.componentTotal** - use total component balances,
|
|
137
|
+
**MaterialBalanceType.elementTotal** - use total element balances,
|
|
138
|
+
**MaterialBalanceType.total** - use total material balance.}""",
|
|
139
|
+
),
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def build(self):
|
|
144
|
+
# build always starts by calling super().build()
|
|
145
|
+
# This triggers a lot of boilerplate in the background for you
|
|
146
|
+
super().build()
|
|
147
|
+
|
|
148
|
+
# This creates blank scaling factors, which are populated later
|
|
149
|
+
self.scaling_factor = Suffix(direction=Suffix.EXPORT)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Defining parameters of state block class
|
|
153
|
+
tmp_dict = dict(**self.config.property_package_args)
|
|
154
|
+
tmp_dict["parameters"] = self.config.property_package
|
|
155
|
+
tmp_dict["defined_state"] = True # inlet block is an inlet
|
|
156
|
+
|
|
157
|
+
# Add state blocks for inlet, outlet, and waste
|
|
158
|
+
# These include the state variables and any other properties on demand
|
|
159
|
+
num_inlets = self.config.num_inlets
|
|
160
|
+
self.inlet_list = [ "inlet_" + str(i+1) for i in range(num_inlets) ]
|
|
161
|
+
self.inlet_set = Set(initialize=self.inlet_list)
|
|
162
|
+
self.inlet_blocks = []
|
|
163
|
+
|
|
164
|
+
for name in self.inlet_list:
|
|
165
|
+
# add properties_inlet_1, properties_inlet2 etc
|
|
166
|
+
state_block = self.config.property_package.state_block_class(
|
|
167
|
+
self.flowsheet().config.time, doc="inlet power", **tmp_dict
|
|
168
|
+
)
|
|
169
|
+
self.inlet_blocks.append(state_block)
|
|
170
|
+
# Dynamic equivalent to self.properties_inlet_1 = stateblock
|
|
171
|
+
setattr(self,"properties_" + name, state_block)
|
|
172
|
+
# also add the port
|
|
173
|
+
self.add_port(name=name,block=state_block)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Add outlet state blocks
|
|
177
|
+
|
|
178
|
+
num_outlets = self.config.num_outlets
|
|
179
|
+
self.outlet_list = [ "outlet_" + str(i+1) for i in range(num_outlets) ]
|
|
180
|
+
self.outlet_set = Set(initialize=self.outlet_list)
|
|
181
|
+
self.outlet_blocks = []
|
|
182
|
+
|
|
183
|
+
for name in self.outlet_list:
|
|
184
|
+
tmp_dict["defined_state"] = False
|
|
185
|
+
state_block = self.config.property_package.state_block_class(
|
|
186
|
+
self.flowsheet().config.time, doc="outlet power", **tmp_dict
|
|
187
|
+
)
|
|
188
|
+
self.outlet_blocks.append(state_block)
|
|
189
|
+
setattr(self,"properties_" + name, state_block)
|
|
190
|
+
self.add_port(name=name,block=state_block)
|
|
191
|
+
|
|
192
|
+
# Add variable for power splitting
|
|
193
|
+
self.split_fraction = Var(
|
|
194
|
+
self.flowsheet().time,
|
|
195
|
+
self.outlet_set,
|
|
196
|
+
initialize=1.0,
|
|
197
|
+
#units = pyunits.dimensionless,
|
|
198
|
+
doc="How the power is split between outlets",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
#Obtain the power components from the inlets
|
|
202
|
+
@self.Expression(
|
|
203
|
+
self.flowsheet().time,
|
|
204
|
+
)
|
|
205
|
+
def total_active_power(b,t):
|
|
206
|
+
return sum(state_block[t].active_power for state_block in self.inlet_blocks)
|
|
207
|
+
|
|
208
|
+
@self.Expression(
|
|
209
|
+
self.flowsheet().time,
|
|
210
|
+
)
|
|
211
|
+
def total_reactive_power(b,t):
|
|
212
|
+
return sum(state_block[t].reactive_power for state_block in self.inlet_blocks)
|
|
213
|
+
|
|
214
|
+
@self.Expression(
|
|
215
|
+
self.flowsheet().time,
|
|
216
|
+
)
|
|
217
|
+
def total_voltage(b,t):
|
|
218
|
+
return sum(state_block[t].voltage for state_block in self.inlet_blocks)
|
|
219
|
+
|
|
220
|
+
#Add constraints
|
|
221
|
+
|
|
222
|
+
@self.Constraint(
|
|
223
|
+
self.flowsheet().time,
|
|
224
|
+
self.outlet_list,
|
|
225
|
+
doc="active power split",
|
|
226
|
+
)
|
|
227
|
+
def eq_active_power(b,t,o):
|
|
228
|
+
outlet_block = getattr(self,"properties_" + o)
|
|
229
|
+
return outlet_block[t].active_power == (
|
|
230
|
+
self.total_active_power[t] * b.split_fraction[t,o])
|
|
231
|
+
|
|
232
|
+
@self.Constraint(
|
|
233
|
+
self.flowsheet().time,
|
|
234
|
+
self.outlet_list,
|
|
235
|
+
doc="reactive power split",
|
|
236
|
+
)
|
|
237
|
+
def eq_reactive_power(b,t,o):
|
|
238
|
+
outlet_block = getattr(self,"properties_" + o)
|
|
239
|
+
return outlet_block[t].reactive_power == (
|
|
240
|
+
self.total_reactive_power[t] * b.split_fraction[t,o])
|
|
241
|
+
|
|
242
|
+
@self.Constraint(
|
|
243
|
+
self.flowsheet().time,
|
|
244
|
+
self.outlet_list,
|
|
245
|
+
doc="voltage split",
|
|
246
|
+
)
|
|
247
|
+
def eq_voltage(b,t,o):
|
|
248
|
+
outlet_block = getattr(self,"properties_" + o)
|
|
249
|
+
return outlet_block[t].voltage == (
|
|
250
|
+
self.total_voltage[t] * b.split_fraction[t,o])
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@self.Constraint(
|
|
254
|
+
self.flowsheet().time,
|
|
255
|
+
doc="Split fraction sum to 1",
|
|
256
|
+
)
|
|
257
|
+
def eq_split_fraction_sum(b, t):
|
|
258
|
+
return sum(b.split_fraction[t, o] for o in self.outlet_list) == 1.0
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def calculate_scaling_factors(self):
|
|
262
|
+
super().calculate_scaling_factors()
|
|
263
|
+
|
|
264
|
+
def initialize(blk, *args, **kwargs):
|
|
265
|
+
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _get_stream_table_contents(self, time_point=0):
|
|
270
|
+
|
|
271
|
+
io_dict = {}
|
|
272
|
+
for inlet_name in self.inlet_list:
|
|
273
|
+
io_dict[inlet_name] = getattr(self, inlet_name) # get a reference to the port
|
|
274
|
+
|
|
275
|
+
out_dict = {}
|
|
276
|
+
for outlet_name in self.outlet_list:
|
|
277
|
+
out_dict[outlet_name] = getattr(self, outlet_name) # get a reference to the port
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|