ahuora-builder 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. ahuora_builder/__init__.py +0 -0
  2. ahuora_builder/arc_manager.py +33 -0
  3. ahuora_builder/build_state.py +57 -0
  4. ahuora_builder/custom/PIDController.py +494 -0
  5. ahuora_builder/custom/PySMOModel.py +178 -0
  6. ahuora_builder/custom/SimpleEffectivenessHX_DH.py +727 -0
  7. ahuora_builder/custom/__init__.py +0 -0
  8. ahuora_builder/custom/add_initial_dynamics.py +35 -0
  9. ahuora_builder/custom/custom_compressor.py +107 -0
  10. ahuora_builder/custom/custom_cooler.py +33 -0
  11. ahuora_builder/custom/custom_heat_exchanger.py +183 -0
  12. ahuora_builder/custom/custom_heat_exchanger_1d.py +258 -0
  13. ahuora_builder/custom/custom_heater.py +41 -0
  14. ahuora_builder/custom/custom_pressure_changer.py +34 -0
  15. ahuora_builder/custom/custom_pump.py +107 -0
  16. ahuora_builder/custom/custom_separator.py +371 -0
  17. ahuora_builder/custom/custom_tank.py +133 -0
  18. ahuora_builder/custom/custom_turbine.py +132 -0
  19. ahuora_builder/custom/custom_valve.py +300 -0
  20. ahuora_builder/custom/custom_variable.py +29 -0
  21. ahuora_builder/custom/direct_steam_injection.py +371 -0
  22. ahuora_builder/custom/energy/__init__.py +0 -0
  23. ahuora_builder/custom/energy/acBus.py +280 -0
  24. ahuora_builder/custom/energy/ac_property_package.py +279 -0
  25. ahuora_builder/custom/energy/battery.py +170 -0
  26. ahuora_builder/custom/energy/bus.py +182 -0
  27. ahuora_builder/custom/energy/energy_mixer.py +195 -0
  28. ahuora_builder/custom/energy/energy_splitter.py +228 -0
  29. ahuora_builder/custom/energy/grid.py +173 -0
  30. ahuora_builder/custom/energy/hydro.py +169 -0
  31. ahuora_builder/custom/energy/link.py +137 -0
  32. ahuora_builder/custom/energy/load.py +155 -0
  33. ahuora_builder/custom/energy/mainDistributionBoard.py +257 -0
  34. ahuora_builder/custom/energy/power_property_package.py +253 -0
  35. ahuora_builder/custom/energy/solar.py +176 -0
  36. ahuora_builder/custom/energy/storage.py +230 -0
  37. ahuora_builder/custom/energy/storage_wrapper +0 -0
  38. ahuora_builder/custom/energy/tests/__init__.py +0 -0
  39. ahuora_builder/custom/energy/tests/test_bus.py +44 -0
  40. ahuora_builder/custom/energy/tests/test_energy_mixer.py +46 -0
  41. ahuora_builder/custom/energy/tests/test_mdb.py +49 -0
  42. ahuora_builder/custom/energy/transformer.py +187 -0
  43. ahuora_builder/custom/energy/transformer_property_package.py +267 -0
  44. ahuora_builder/custom/energy/transmissionLine.py +228 -0
  45. ahuora_builder/custom/energy/wind.py +206 -0
  46. ahuora_builder/custom/hda_ideal_VLE.py +1341 -0
  47. ahuora_builder/custom/hda_reaction.py +182 -0
  48. ahuora_builder/custom/heat_exchanger_1d_wrapper.py +31 -0
  49. ahuora_builder/custom/integration_block.py +106 -0
  50. ahuora_builder/custom/inverted.py +81 -0
  51. ahuora_builder/custom/performance_curves.py +1 -0
  52. ahuora_builder/custom/reactions/__init__.py +0 -0
  53. ahuora_builder/custom/reactions/hda_stoich.py +10 -0
  54. ahuora_builder/custom/simple_separator.py +680 -0
  55. ahuora_builder/custom/tests/__init__.py +0 -0
  56. ahuora_builder/custom/tests/test_SimpleEffectivenessHX_DH.py +91 -0
  57. ahuora_builder/custom/tests/test_custom_tank.py +70 -0
  58. ahuora_builder/custom/tests/test_direct_steam_injection.py +41 -0
  59. ahuora_builder/custom/tests/test_simple_separator.py +46 -0
  60. ahuora_builder/custom/tests/test_waterpipe.py +46 -0
  61. ahuora_builder/custom/thermal_utility_systems/desuperheater.py +624 -0
  62. ahuora_builder/custom/thermal_utility_systems/header.py +889 -0
  63. ahuora_builder/custom/thermal_utility_systems/simple_heat_pump.py +567 -0
  64. ahuora_builder/custom/thermal_utility_systems/steam_header.py +353 -0
  65. ahuora_builder/custom/thermal_utility_systems/steam_user.py +944 -0
  66. ahuora_builder/custom/thermal_utility_systems/temp.py +349 -0
  67. ahuora_builder/custom/thermal_utility_systems/tests/test_desuperheater.py +142 -0
  68. ahuora_builder/custom/thermal_utility_systems/tests/test_header.py +998 -0
  69. ahuora_builder/custom/thermal_utility_systems/tests/test_ntu_hx.py +129 -0
  70. ahuora_builder/custom/thermal_utility_systems/tests/test_simple_heat_pump.py +120 -0
  71. ahuora_builder/custom/thermal_utility_systems/tests/test_steam_header.py +703 -0
  72. ahuora_builder/custom/thermal_utility_systems/tests/test_steam_user.py +277 -0
  73. ahuora_builder/custom/thermal_utility_systems/tests/test_waterpipe.py +36 -0
  74. ahuora_builder/custom/thermal_utility_systems/tests/test_willans_turbine.py +253 -0
  75. ahuora_builder/custom/thermal_utility_systems/willans_turbine.py +804 -0
  76. ahuora_builder/custom/translator.py +129 -0
  77. ahuora_builder/custom/updated_pressure_changer.py +1404 -0
  78. ahuora_builder/custom/valve_wrapper.py +38 -0
  79. ahuora_builder/custom/water_tank_with_units.py +456 -0
  80. ahuora_builder/diagnostics/__init__.py +0 -0
  81. ahuora_builder/diagnostics/infeasibilities.py +40 -0
  82. ahuora_builder/diagnostics/tests/__init__.py +0 -0
  83. ahuora_builder/diagnostics/tests/test_infeasibilities.py +28 -0
  84. ahuora_builder/flowsheet_manager.py +542 -0
  85. ahuora_builder/flowsheet_manager_type.py +20 -0
  86. ahuora_builder/generate_python_file.py +440 -0
  87. ahuora_builder/methods/BlockContext.py +84 -0
  88. ahuora_builder/methods/__init__.py +0 -0
  89. ahuora_builder/methods/adapter.py +355 -0
  90. ahuora_builder/methods/adapter_library.py +549 -0
  91. ahuora_builder/methods/adapter_methods.py +80 -0
  92. ahuora_builder/methods/expression_parsing.py +105 -0
  93. ahuora_builder/methods/load_unit_model.py +147 -0
  94. ahuora_builder/methods/slice_manipulation.py +7 -0
  95. ahuora_builder/methods/tests/__init__.py +0 -0
  96. ahuora_builder/methods/tests/test_expression_parsing.py +15 -0
  97. ahuora_builder/methods/units_handler.py +129 -0
  98. ahuora_builder/ml_wizard.py +101 -0
  99. ahuora_builder/port_manager.py +20 -0
  100. ahuora_builder/properties_manager.py +44 -0
  101. ahuora_builder/property_package_manager.py +78 -0
  102. ahuora_builder/solver.py +38 -0
  103. ahuora_builder/tear_manager.py +98 -0
  104. ahuora_builder/tests/__init__.py +0 -0
  105. ahuora_builder/tests/test_generate_python_file/__init__.py +0 -0
  106. ahuora_builder/tests/test_generate_python_file/configurations/compressor_generated.py +63 -0
  107. ahuora_builder/tests/test_generate_python_file/configurations/heat_exchanger_generated.py +70 -0
  108. ahuora_builder/tests/test_generate_python_file/configurations/pump_generated.py +84 -0
  109. ahuora_builder/tests/test_generate_python_file/configurations/recycle_generated.py +73 -0
  110. ahuora_builder/tests/test_generate_python_file/test_generate_python_file.py +108 -0
  111. ahuora_builder/tests/test_solver/__init__.py +0 -0
  112. ahuora_builder/tests/test_solver/configurations/BT_PR.json +59 -0
  113. ahuora_builder/tests/test_solver/configurations/BT_PR_solved.json +59 -0
  114. ahuora_builder/tests/test_solver/configurations/bus.json +99 -0
  115. ahuora_builder/tests/test_solver/configurations/bus_solved.json +50 -0
  116. ahuora_builder/tests/test_solver/configurations/compound_separator.json +377 -0
  117. ahuora_builder/tests/test_solver/configurations/compound_separator_solved.json +374 -0
  118. ahuora_builder/tests/test_solver/configurations/compressor.json +38 -0
  119. ahuora_builder/tests/test_solver/configurations/compressor_solved.json +68 -0
  120. ahuora_builder/tests/test_solver/configurations/constraints.json +44 -0
  121. ahuora_builder/tests/test_solver/configurations/constraints_solved.json +59 -0
  122. ahuora_builder/tests/test_solver/configurations/control.json +39 -0
  123. ahuora_builder/tests/test_solver/configurations/control_solved.json +68 -0
  124. ahuora_builder/tests/test_solver/configurations/dynamic_tank.json +733 -0
  125. ahuora_builder/tests/test_solver/configurations/dynamic_tank_solved.json +846 -0
  126. ahuora_builder/tests/test_solver/configurations/elimination.json +39 -0
  127. ahuora_builder/tests/test_solver/configurations/elimination_solved.json +68 -0
  128. ahuora_builder/tests/test_solver/configurations/expressions.json +68 -0
  129. ahuora_builder/tests/test_solver/configurations/expressions_solved.json +104 -0
  130. ahuora_builder/tests/test_solver/configurations/header.json +1192 -0
  131. ahuora_builder/tests/test_solver/configurations/header_solved.json +761 -0
  132. ahuora_builder/tests/test_solver/configurations/heat_exchanger.json +63 -0
  133. ahuora_builder/tests/test_solver/configurations/heat_exchanger_solved.json +104 -0
  134. ahuora_builder/tests/test_solver/configurations/heat_pump.json +137 -0
  135. ahuora_builder/tests/test_solver/configurations/heat_pump_solved.json +104 -0
  136. ahuora_builder/tests/test_solver/configurations/machine_learning.json +2156 -0
  137. ahuora_builder/tests/test_solver/configurations/machine_learning_solved.json +266 -0
  138. ahuora_builder/tests/test_solver/configurations/mass_flow_tear.json +77 -0
  139. ahuora_builder/tests/test_solver/configurations/mass_flow_tear_solved.json +68 -0
  140. ahuora_builder/tests/test_solver/configurations/milk_heater.json +521 -0
  141. ahuora_builder/tests/test_solver/configurations/milk_heater_solved.json +311 -0
  142. ahuora_builder/tests/test_solver/configurations/mixer.json +44 -0
  143. ahuora_builder/tests/test_solver/configurations/mixer_solved.json +86 -0
  144. ahuora_builder/tests/test_solver/configurations/optimization.json +62 -0
  145. ahuora_builder/tests/test_solver/configurations/optimization_solved.json +59 -0
  146. ahuora_builder/tests/test_solver/configurations/propane_heat_pump.json +167 -0
  147. ahuora_builder/tests/test_solver/configurations/propane_heat_pump_solved.json +158 -0
  148. ahuora_builder/tests/test_solver/configurations/propane_recycle.json +141 -0
  149. ahuora_builder/tests/test_solver/configurations/propane_recycle_solved.json +104 -0
  150. ahuora_builder/tests/test_solver/configurations/pump.json +64 -0
  151. ahuora_builder/tests/test_solver/configurations/pump_solved.json +59 -0
  152. ahuora_builder/tests/test_solver/configurations/pump_unit_conversions.json +63 -0
  153. ahuora_builder/tests/test_solver/configurations/recycle.json +49 -0
  154. ahuora_builder/tests/test_solver/configurations/recycle_solved.json +50 -0
  155. ahuora_builder/tests/test_solver/configurations/sb_vapor_frac.json +29 -0
  156. ahuora_builder/tests/test_solver/configurations/sb_vapor_frac_solved.json +29 -0
  157. ahuora_builder/tests/test_solver/configurations/solar.json +67 -0
  158. ahuora_builder/tests/test_solver/configurations/solar_solved.json +50 -0
  159. ahuora_builder/tests/test_solver/configurations/vapor_frac_target.json +67 -0
  160. ahuora_builder/tests/test_solver/configurations/vapor_frac_target_solved.json +68 -0
  161. ahuora_builder/tests/test_solver/test_solve_models.py +250 -0
  162. ahuora_builder/timing.py +65 -0
  163. ahuora_builder/types/__init__.py +1 -0
  164. ahuora_builder/unit_model_manager.py +48 -0
  165. ahuora_builder-0.1.0.dist-info/METADATA +14 -0
  166. ahuora_builder-0.1.0.dist-info/RECORD +167 -0
  167. ahuora_builder-0.1.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,1341 @@
1
+ ##############################################################################
2
+ # Institute for the Design of Advanced Energy Systems Process Systems
3
+ # Engineering Framework (IDAES PSE Framework) Copyright (c) 2018-2019, by the
4
+ # software owners: The Regents of the University of California, through
5
+ # Lawrence Berkeley National Laboratory, National Technology & Engineering
6
+ # Solutions of Sandia, LLC, Carnegie Mellon University, West Virginia
7
+ # University Research Corporation, et al. All rights reserved.
8
+ #
9
+ # Please see the files COPYRIGHT.txt and LICENSE.txt for full copyright and
10
+ # license information, respectively. Both files are also available online
11
+ # at the URL "https://github.com/IDAES/idaes-pse".
12
+ ##############################################################################
13
+ """
14
+ Example ideal parameter block for the VLE calucations for a
15
+ Benzene-Toluene-o-Xylene system.
16
+ """
17
+
18
+
19
+ # Import Pyomo libraries
20
+ from pyomo.environ import (
21
+ Constraint,
22
+ Expression,
23
+ log,
24
+ NonNegativeReals,
25
+ Var,
26
+ Set,
27
+ Param,
28
+ sqrt,
29
+ log10,
30
+ units as pyunits,
31
+ )
32
+ from pyomo.util.calc_var_value import calculate_variable_from_constraint
33
+ from pyomo.common.config import ConfigValue
34
+
35
+ # Import IDAES cores
36
+ from idaes.core import (
37
+ declare_process_block_class,
38
+ MaterialFlowBasis,
39
+ PhysicalParameterBlock,
40
+ StateBlockData,
41
+ StateBlock,
42
+ MaterialBalanceType,
43
+ EnergyBalanceType,
44
+ Component,
45
+ LiquidPhase,
46
+ VaporPhase,
47
+ )
48
+ from idaes.core.util.constants import Constants as const
49
+ from idaes.core.util.initialization import fix_state_vars, solve_indexed_blocks
50
+ from idaes.core.initialization import InitializerBase
51
+ from idaes.core.util.misc import add_object_reference
52
+ from idaes.core.util.model_statistics import number_unfixed_variables
53
+ from idaes.core.util.misc import extract_data
54
+ from idaes.core.solvers import get_solver
55
+ import idaes.core.util.scaling as iscale
56
+ import idaes.logger as idaeslog
57
+
58
+ # Set up logger
59
+ _log = idaeslog.getLogger(__name__)
60
+
61
+
62
+ class HDAInitializer(InitializerBase):
63
+ """
64
+ Initializer for HDA Property package.
65
+
66
+ """
67
+
68
+ CONFIG = InitializerBase.CONFIG()
69
+ CONFIG.declare(
70
+ "solver",
71
+ ConfigValue(default=None, domain=str, description="Initialization solver"),
72
+ )
73
+ CONFIG.declare(
74
+ "solver_options",
75
+ ConfigValue(default=None, description="Initialization solver options"),
76
+ )
77
+
78
+ def initialization_routine(self, blk):
79
+ init_log = idaeslog.getInitLogger(
80
+ blk.name, self.config.output_level, tag="properties"
81
+ )
82
+ solve_log = idaeslog.getSolveLogger(
83
+ blk.name, self.config.output_level, tag="properties"
84
+ )
85
+
86
+ # Set solver
87
+ solver = get_solver(self.config.solver, self.config.solver_options)
88
+
89
+ # ---------------------------------------------------------------------
90
+ # If present, initialize bubble and dew point calculations
91
+ for k in blk.keys():
92
+ if hasattr(blk[k], "eq_temperature_dew"):
93
+ calculate_variable_from_constraint(
94
+ blk[k].temperature_dew, blk[k].eq_temperature_dew
95
+ )
96
+
97
+ if hasattr(blk[k], "eq_pressure_dew"):
98
+ calculate_variable_from_constraint(
99
+ blk[k].pressure_dew, blk[k].eq_pressure_dew
100
+ )
101
+
102
+ init_log.info_high(
103
+ "Initialization Step 1 - Dew and bubble points " "calculation completed."
104
+ )
105
+
106
+ # ---------------------------------------------------------------------
107
+ # If flash, initialize T1 and Teq
108
+ for k in blk.keys():
109
+ if blk[k].config.has_phase_equilibrium and not blk[k].config.defined_state:
110
+ blk[k]._t1.value = max(
111
+ blk[k].temperature.value, blk[k].temperature_bubble.value
112
+ )
113
+ blk[k]._teq.value = min(blk[k]._t1.value, blk[k].temperature_dew.value)
114
+
115
+ init_log.info_high(
116
+ "Initialization Step 2 - Equilibrium temperature " " calculation completed."
117
+ )
118
+
119
+ # ---------------------------------------------------------------------
120
+ # Initialize flow rates and compositions
121
+ free_vars = 0
122
+ for k in blk.keys():
123
+ free_vars += number_unfixed_variables(blk[k])
124
+ if free_vars > 0:
125
+ try:
126
+ with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
127
+ res = solve_indexed_blocks(solver, [blk], tee=slc.tee)
128
+ except:
129
+ res = None
130
+ else:
131
+ res = None
132
+
133
+ init_log.info("Initialization Complete")
134
+
135
+ return res
136
+
137
+
138
+ @declare_process_block_class("HDAParameterBlock")
139
+ class HDAParameterData(PhysicalParameterBlock):
140
+ CONFIG = PhysicalParameterBlock.CONFIG()
141
+
142
+ def build(self):
143
+ """
144
+ Callable method for Block construction.
145
+ """
146
+ super(HDAParameterData, self).build()
147
+
148
+ self._state_block_class = IdealStateBlock
149
+
150
+ self.benzene = Component()
151
+ self.toluene = Component()
152
+ self.methane = Component()
153
+ self.hydrogen = Component()
154
+
155
+ self.Liq = LiquidPhase()
156
+ self.Vap = VaporPhase()
157
+
158
+ # List of components in each phase (optional)
159
+ self.phase_comp = {"Liq": self.component_list, "Vap": self.component_list}
160
+
161
+ # List of phase equilibrium index
162
+ self.phase_equilibrium_idx = Set(initialize=[1, 2, 3, 4])
163
+
164
+ self.phase_equilibrium_list = {
165
+ 1: ["benzene", ("Vap", "Liq")],
166
+ 2: ["toluene", ("Vap", "Liq")],
167
+ 3: ["hydrogen", ("Vap", "Liq")],
168
+ 4: ["methane", ("Vap", "Liq")],
169
+ }
170
+
171
+ # Thermodynamic reference state
172
+ self.pressure_ref = Param(
173
+ mutable=True, default=101325, units=pyunits.Pa, doc="Reference pressure"
174
+ )
175
+ self.temperature_ref = Param(
176
+ mutable=True, default=298.15, units=pyunits.K, doc="Reference temperature"
177
+ )
178
+
179
+ # Source: The Properties of Gases and Liquids (1987)
180
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
181
+ pressure_crit_data = {
182
+ "benzene": 48.9e5,
183
+ "toluene": 41e5,
184
+ "hydrogen": 12.9e5,
185
+ "methane": 46e5,
186
+ }
187
+
188
+ self.pressure_crit = Param(
189
+ self.component_list,
190
+ within=NonNegativeReals,
191
+ mutable=True,
192
+ units=pyunits.Pa,
193
+ initialize=extract_data(pressure_crit_data),
194
+ doc="Critical pressure",
195
+ )
196
+
197
+ # Source: The Properties of Gases and Liquids (1987)
198
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
199
+ temperature_crit_data = {
200
+ "benzene": 562.2,
201
+ "toluene": 591.8,
202
+ "hydrogen": 33.0,
203
+ "methane": 190.4,
204
+ }
205
+
206
+ self.temperature_crit = Param(
207
+ self.component_list,
208
+ within=NonNegativeReals,
209
+ mutable=True,
210
+ units=pyunits.K,
211
+ initialize=extract_data(temperature_crit_data),
212
+ doc="Critical temperature",
213
+ )
214
+
215
+ # Source: The Properties of Gases and Liquids (1987)
216
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
217
+ mw_comp_data = {
218
+ "benzene": 78.1136e-3,
219
+ "toluene": 92.1405e-3,
220
+ "hydrogen": 2.016e-3,
221
+ "methane": 16.043e-3,
222
+ }
223
+
224
+ self.mw_comp = Param(
225
+ self.component_list,
226
+ mutable=True,
227
+ units=pyunits.kg / pyunits.mol,
228
+ initialize=extract_data(mw_comp_data),
229
+ doc="molecular weight",
230
+ )
231
+
232
+ # Constants for liquid densities
233
+ # Source: Perry's Chemical Engineers Handbook
234
+ # - Robert H. Perry (Cp_liq)
235
+ dens_liq_data = {
236
+ ("benzene", "1"): 1.0162,
237
+ ("benzene", "2"): 0.2655,
238
+ ("benzene", "3"): 562.16,
239
+ ("benzene", "4"): 0.28212,
240
+ ("toluene", "1"): 0.8488,
241
+ ("toluene", "2"): 0.26655,
242
+ ("toluene", "3"): 591.8,
243
+ ("toluene", "4"): 0.2878,
244
+ ("hydrogen", "1"): 5.414,
245
+ ("hydrogen", "2"): 0.34893,
246
+ ("hydrogen", "3"): 33.19,
247
+ ("hydrogen", "4"): 0.2706,
248
+ ("methane", "1"): 2.9214,
249
+ ("methane", "2"): 0.28976,
250
+ ("methane", "3"): 190.56,
251
+ ("methane", "4"): 0.28881,
252
+ }
253
+
254
+ self.dens_liq_param_1 = Param(
255
+ self.component_list,
256
+ mutable=True,
257
+ initialize={c: v for (c, j), v in dens_liq_data.items() if j == "1"},
258
+ doc="Parameter 1 to compute liquid densities",
259
+ units=pyunits.kmol * pyunits.m**-3,
260
+ )
261
+
262
+ self.dens_liq_param_2 = Param(
263
+ self.component_list,
264
+ mutable=True,
265
+ initialize={c: v for (c, j), v in dens_liq_data.items() if j == "2"},
266
+ doc="Parameter 2 to compute liquid densities",
267
+ units=pyunits.dimensionless,
268
+ )
269
+
270
+ self.dens_liq_param_3 = Param(
271
+ self.component_list,
272
+ mutable=True,
273
+ initialize={c: v for (c, j), v in dens_liq_data.items() if j == "3"},
274
+ doc="Parameter 3 to compute liquid densities",
275
+ units=pyunits.K,
276
+ )
277
+
278
+ self.dens_liq_param_4 = Param(
279
+ self.component_list,
280
+ mutable=True,
281
+ initialize={c: v for (c, j), v in dens_liq_data.items() if j == "4"},
282
+ doc="Parameter 4 to compute liquid densities",
283
+ units=pyunits.dimensionless,
284
+ )
285
+
286
+ # Boiling point at standard pressure
287
+ # Source: Perry's Chemical Engineers Handbook
288
+ # - Robert H. Perry (Cp_liq)
289
+ bp_data = {
290
+ ("benzene"): 353.25,
291
+ ("toluene"): 383.95,
292
+ ("hydrogen"): 20.45,
293
+ ("methane"): 111.75,
294
+ }
295
+
296
+ self.temperature_boil = Param(
297
+ self.component_list,
298
+ mutable=True,
299
+ units=pyunits.K,
300
+ initialize=extract_data(bp_data),
301
+ doc="Pure component boiling points at standard pressure",
302
+ )
303
+
304
+ # Constants for specific heat capacity, enthalpy
305
+ # Sources: The Properties of Gases and Liquids (1987)
306
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
307
+ # Perry's Chemical Engineers Handbook
308
+ # - Robert H. Perry (Cp_liq)
309
+ cp_ig_data = {
310
+ ("Liq", "benzene", "1"): 1.29e5,
311
+ ("Liq", "benzene", "2"): -1.7e2,
312
+ ("Liq", "benzene", "3"): 6.48e-1,
313
+ ("Liq", "benzene", "4"): 0,
314
+ ("Liq", "benzene", "5"): 0,
315
+ ("Vap", "benzene", "1"): -3.392e1,
316
+ ("Vap", "benzene", "2"): 4.739e-1,
317
+ ("Vap", "benzene", "3"): -3.017e-4,
318
+ ("Vap", "benzene", "4"): 7.130e-8,
319
+ ("Vap", "benzene", "5"): 0,
320
+ ("Liq", "toluene", "1"): 1.40e5,
321
+ ("Liq", "toluene", "2"): -1.52e2,
322
+ ("Liq", "toluene", "3"): 6.95e-1,
323
+ ("Liq", "toluene", "4"): 0,
324
+ ("Liq", "toluene", "5"): 0,
325
+ ("Vap", "toluene", "1"): -2.435e1,
326
+ ("Vap", "toluene", "2"): 5.125e-1,
327
+ ("Vap", "toluene", "3"): -2.765e-4,
328
+ ("Vap", "toluene", "4"): 4.911e-8,
329
+ ("Vap", "toluene", "5"): 0,
330
+ ("Liq", "hydrogen", "1"): 0, # 6.6653e1,
331
+ ("Liq", "hydrogen", "2"): 0, # 6.7659e3,
332
+ ("Liq", "hydrogen", "3"): 0, # -1.2363e2,
333
+ ("Liq", "hydrogen", "4"): 0, # 4.7827e2, # Eqn 2
334
+ ("Liq", "hydrogen", "5"): 0,
335
+ ("Vap", "hydrogen", "1"): 2.714e1,
336
+ ("Vap", "hydrogen", "2"): 9.274e-3,
337
+ ("Vap", "hydrogen", "3"): -1.381e-5,
338
+ ("Vap", "hydrogen", "4"): 7.645e-9,
339
+ ("Vap", "hydrogen", "5"): 0,
340
+ ("Liq", "methane", "1"): 0, # 6.5708e1,
341
+ ("Liq", "methane", "2"): 0, # 3.8883e4,
342
+ ("Liq", "methane", "3"): 0, # -2.5795e2,
343
+ ("Liq", "methane", "4"): 0, # 6.1407e2, # Eqn 2
344
+ ("Liq", "methane", "5"): 0,
345
+ ("Vap", "methane", "1"): 1.925e1,
346
+ ("Vap", "methane", "2"): 5.213e-2,
347
+ ("Vap", "methane", "3"): 1.197e-5,
348
+ ("Vap", "methane", "4"): -1.132e-8,
349
+ ("Vap", "methane", "5"): 0,
350
+ }
351
+
352
+ self.cp_ig_1 = Param(
353
+ self.phase_list,
354
+ self.component_list,
355
+ mutable=True,
356
+ initialize={(p, c): v for (p, c, j), v in cp_ig_data.items() if j == "1"},
357
+ doc="Parameter 1 to compute Cp_comp",
358
+ units=pyunits.J / pyunits.mol / pyunits.K,
359
+ )
360
+
361
+ self.cp_ig_2 = Param(
362
+ self.phase_list,
363
+ self.component_list,
364
+ mutable=True,
365
+ initialize={(p, c): v for (p, c, j), v in cp_ig_data.items() if j == "2"},
366
+ doc="Parameter 2 to compute Cp_comp",
367
+ units=pyunits.J / pyunits.mol / pyunits.K**2,
368
+ )
369
+
370
+ self.cp_ig_3 = Param(
371
+ self.phase_list,
372
+ self.component_list,
373
+ mutable=True,
374
+ initialize={(p, c): v for (p, c, j), v in cp_ig_data.items() if j == "3"},
375
+ doc="Parameter 3 to compute Cp_comp",
376
+ units=pyunits.J / pyunits.mol / pyunits.K**3,
377
+ )
378
+
379
+ self.cp_ig_4 = Param(
380
+ self.phase_list,
381
+ self.component_list,
382
+ mutable=True,
383
+ initialize={(p, c): v for (p, c, j), v in cp_ig_data.items() if j == "4"},
384
+ doc="Parameter 4 to compute Cp_comp",
385
+ units=pyunits.J / pyunits.mol / pyunits.K**4,
386
+ )
387
+
388
+ self.cp_ig_5 = Param(
389
+ self.phase_list,
390
+ self.component_list,
391
+ mutable=True,
392
+ initialize={(p, c): v for (p, c, j), v in cp_ig_data.items() if j == "5"},
393
+ doc="Parameter 5 to compute Cp_comp",
394
+ units=pyunits.J / pyunits.mol / pyunits.K**5,
395
+ )
396
+
397
+ # Source: The Properties of Gases and Liquids (1987)
398
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
399
+ # fitted to Antoine form
400
+ # H2, Methane from NIST webbook
401
+ pressure_sat_coeff_data = {
402
+ ("benzene", "A"): 4.202,
403
+ ("benzene", "B"): 1322,
404
+ ("benzene", "C"): -38.56,
405
+ ("toluene", "A"): 4.216,
406
+ ("toluene", "B"): 1435,
407
+ ("toluene", "C"): -43.33,
408
+ ("hydrogen", "A"): 3.543,
409
+ ("hydrogen", "B"): 99.40,
410
+ ("hydrogen", "C"): 7.726,
411
+ ("methane", "A"): 3.990,
412
+ ("methane", "B"): 443.0,
413
+ ("methane", "C"): -0.49,
414
+ }
415
+
416
+ self.pressure_sat_coeff_A = Param(
417
+ self.component_list,
418
+ mutable=True,
419
+ initialize={
420
+ c: v for (c, j), v in pressure_sat_coeff_data.items() if j == "A"
421
+ },
422
+ doc="Parameter A to compute saturated pressure",
423
+ units=pyunits.dimensionless,
424
+ )
425
+
426
+ self.pressure_sat_coeff_B = Param(
427
+ self.component_list,
428
+ mutable=True,
429
+ initialize={
430
+ c: v for (c, j), v in pressure_sat_coeff_data.items() if j == "B"
431
+ },
432
+ doc="Parameter B to compute saturated pressure",
433
+ units=pyunits.K,
434
+ )
435
+
436
+ self.pressure_sat_coeff_C = Param(
437
+ self.component_list,
438
+ mutable=True,
439
+ initialize={
440
+ c: v for (c, j), v in pressure_sat_coeff_data.items() if j == "C"
441
+ },
442
+ doc="Parameter C to compute saturated pressure",
443
+ units=pyunits.K,
444
+ )
445
+
446
+ # Source: The Properties of Gases and Liquids (1987)
447
+ # 4th edition, Chemical Engineering Series - Robert C. Reid
448
+ dh_vap = {"benzene": 3.387e4, "toluene": 3.8262e4, "hydrogen": 0, "methane": 0}
449
+
450
+ self.dh_vap = Param(
451
+ self.component_list,
452
+ mutable=True,
453
+ units=pyunits.J / pyunits.mol,
454
+ initialize=extract_data(dh_vap),
455
+ doc="heat of vaporization",
456
+ )
457
+
458
+ # Set default scaling factors
459
+ self.set_default_scaling("flow_mol", 1e3)
460
+ self.set_default_scaling("flow_mol_phase_comp", 1e3)
461
+ self.set_default_scaling("flow_mol_phase", 1e3)
462
+ self.set_default_scaling("material_flow_terms", 1e3)
463
+ self.set_default_scaling("enthalpy_flow_terms", 1e-2)
464
+ self.set_default_scaling("mole_frac_comp", 1e1)
465
+ self.set_default_scaling("temperature", 1e-2)
466
+ self.set_default_scaling("temperature_dew", 1e-2)
467
+ self.set_default_scaling("temperature_bubble", 1e-2)
468
+ self.set_default_scaling("pressure", 1e-5)
469
+ self.set_default_scaling("pressure_sat", 1e-5)
470
+ self.set_default_scaling("pressure_dew", 1e-5)
471
+ self.set_default_scaling("pressure_bubble", 1e-5)
472
+ self.set_default_scaling("mole_frac_phase_comp", 1e1)
473
+ self.set_default_scaling("enth_mol_phase", 1e-3, index="Liq")
474
+ self.set_default_scaling("enth_mol_phase", 1e-4, index="Vap")
475
+ self.set_default_scaling("enth_mol", 1e-3)
476
+ self.set_default_scaling("entr_mol_phase", 1e-2)
477
+ self.set_default_scaling("entr_mol", 1e-2)
478
+
479
+ @classmethod
480
+ def define_metadata(cls, obj):
481
+ """Define properties supported and units."""
482
+ obj.add_properties(
483
+ {
484
+ "flow_mol": {"method": None},
485
+ "flow_mol_phase_comp": {"method": None},
486
+ "mole_frac_comp": {"method": None},
487
+ "temperature": {"method": None},
488
+ "pressure": {"method": None},
489
+ "flow_mol_phase": {"method": None},
490
+ "dens_mol_phase": {"method": "_dens_mol_phase"},
491
+ "pressure_sat": {"method": "_pressure_sat"},
492
+ "mole_frac_phase_comp": {"method": "_mole_frac_phase"},
493
+ "energy_internal_mol_phase_comp": {
494
+ "method": "_energy_internal_mol_phase_comp"
495
+ },
496
+ "energy_internal_mol_phase": {"method": "_energy_internal_mol_phase"},
497
+ "enth_mol_phase_comp": {"method": "_enth_mol_phase_comp"},
498
+ "enth_mol_phase": {"method": "_enth_mol_phase"},
499
+ "entr_mol_phase_comp": {"method": "_entr_mol_phase_comp"},
500
+ "entr_mol_phase": {"method": "_entr_mol_phase"},
501
+ "temperature_bubble": {"method": "_temperature_bubble"},
502
+ "temperature_dew": {"method": "_temperature_dew"},
503
+ "pressure_bubble": {"method": "_pressure_bubble"},
504
+ "pressure_dew": {"method": "_pressure_dew"},
505
+ "fug_phase_comp": {"method": "_fug_phase_comp"},
506
+ }
507
+ )
508
+
509
+ obj.define_custom_properties(
510
+ {
511
+ # Enthalpy of vaporization
512
+ "dh_vap": {"method": "_dh_vap", "units": obj.derived_units.ENERGY_MOLE},
513
+ # Entropy of vaporization
514
+ "ds_vap": {
515
+ "method": "_ds_vap",
516
+ "units": obj.derived_units.ENTROPY_MOLE,
517
+ },
518
+ }
519
+ )
520
+
521
+ obj.add_default_units(
522
+ {
523
+ "time": pyunits.s,
524
+ "length": pyunits.m,
525
+ "mass": pyunits.kg,
526
+ "amount": pyunits.mol,
527
+ "temperature": pyunits.K,
528
+ }
529
+ )
530
+
531
+
532
+ class _IdealStateBlock(StateBlock):
533
+ """
534
+ This Class contains methods which should be applied to Property Blocks as a
535
+ whole, rather than individual elements of indexed Property Blocks.
536
+ """
537
+
538
+ default_initializer = HDAInitializer
539
+
540
+ def fix_initialization_states(blk):
541
+ """
542
+ Fixes state variables for state blocks.
543
+
544
+ Returns:
545
+ None
546
+ """
547
+
548
+ # Fix state variables
549
+ fix_state_vars(blk)
550
+
551
+ # Also need to deactivate sum of mole fraction constraint
552
+ for k in blk.values():
553
+ if not k.config.defined_state:
554
+ k.equilibrium_constraint.deactivate()
555
+
556
+
557
+ @declare_process_block_class("IdealStateBlock", block_class=_IdealStateBlock)
558
+ class IdealStateBlockData(StateBlockData):
559
+ """An example property package for ideal VLE."""
560
+
561
+ def build(self):
562
+ """Callable method for Block construction."""
563
+ super().build()
564
+
565
+ # Add state variables
566
+ self.flow_mol_phase_comp = Var(
567
+ self._params.phase_list,
568
+ self._params.component_list,
569
+ initialize=0.5,
570
+ units=pyunits.mol / pyunits.s,
571
+ bounds=(1e-12, 100),
572
+ doc="Phase-component molar flow rates",
573
+ )
574
+
575
+ self.pressure = Var(
576
+ initialize=101325,
577
+ bounds=(100000, 1000000),
578
+ units=pyunits.Pa,
579
+ domain=NonNegativeReals,
580
+ doc="State pressure",
581
+ )
582
+ self.temperature = Var(
583
+ initialize=298.15,
584
+ units=pyunits.K,
585
+ bounds=(298, 1000),
586
+ domain=NonNegativeReals,
587
+ doc="State temperature",
588
+ )
589
+
590
+ # Add supporting variables
591
+ def flow_mol_phase(b, p):
592
+ return sum(b.flow_mol_phase_comp[p, j] for j in b._params.component_list)
593
+
594
+ self.flow_mol_phase = Expression(
595
+ self._params.phase_list, rule=flow_mol_phase, doc="Phase molar flow rates"
596
+ )
597
+
598
+ def flow_mol(b):
599
+ return sum(
600
+ b.flow_mol_phase_comp[p, j]
601
+ for j in b._params.component_list
602
+ for p in b._params.phase_list
603
+ )
604
+
605
+ self.flow_mol = Expression(rule=flow_mol, doc="Total molar flowrate")
606
+
607
+ def mole_frac_phase_comp(b, p, j):
608
+ return b.flow_mol_phase_comp[p, j] / b.flow_mol_phase[p]
609
+
610
+ self.mole_frac_phase_comp = Expression(
611
+ self._params.phase_list,
612
+ self._params.component_list,
613
+ rule=mole_frac_phase_comp,
614
+ doc="Phase mole fractions",
615
+ )
616
+
617
+ def mole_frac_comp(b, j):
618
+ return (
619
+ sum(b.flow_mol_phase_comp[p, j] for p in b._params.phase_list)
620
+ / b.flow_mol
621
+ )
622
+
623
+ self.mole_frac_comp = Expression(
624
+ self._params.component_list,
625
+ rule=mole_frac_comp,
626
+ doc="Mixture mole fractions",
627
+ )
628
+
629
+ # Reaction Stoichiometry
630
+ add_object_reference(
631
+ self, "phase_equilibrium_list_ref", self._params.phase_equilibrium_list
632
+ )
633
+
634
+ if self.config.has_phase_equilibrium and self.config.defined_state is False:
635
+ # Definition of equilibrium temperature for smooth VLE
636
+ self._teq = Var(
637
+ initialize=self.temperature.value,
638
+ units=pyunits.K,
639
+ doc="Temperature for calculating phase equilibrium",
640
+ )
641
+ self._t1 = Var(
642
+ initialize=self.temperature.value,
643
+ units=pyunits.K,
644
+ doc="Intermediate temperature for calculating Teq",
645
+ )
646
+
647
+ self.eps_1 = Param(
648
+ default=0.01,
649
+ units=pyunits.K,
650
+ mutable=True,
651
+ doc="Smoothing parameter for Teq",
652
+ )
653
+ self.eps_2 = Param(
654
+ default=0.0005,
655
+ units=pyunits.K,
656
+ mutable=True,
657
+ doc="Smoothing parameter for Teq",
658
+ )
659
+
660
+ # PSE paper Eqn 13
661
+ def rule_t1(b):
662
+ return b._t1 == 0.5 * (
663
+ b.temperature
664
+ + b.temperature_bubble
665
+ + sqrt((b.temperature - b.temperature_bubble) ** 2 + b.eps_1**2)
666
+ )
667
+
668
+ self._t1_constraint = Constraint(rule=rule_t1)
669
+
670
+ # PSE paper Eqn 14
671
+ # TODO : Add option for supercritical extension
672
+ def rule_teq(b):
673
+ return b._teq == 0.5 * (
674
+ b._t1
675
+ + b.temperature_dew
676
+ - sqrt((b._t1 - b.temperature_dew) ** 2 + b.eps_2**2)
677
+ )
678
+
679
+ self._teq_constraint = Constraint(rule=rule_teq)
680
+
681
+ def rule_tr_eq(b, i):
682
+ return b._teq / b._params.temperature_crit[i]
683
+
684
+ self._tr_eq = Expression(
685
+ self._params.component_list,
686
+ rule=rule_tr_eq,
687
+ doc="Component reduced temperatures",
688
+ )
689
+
690
+ def rule_equilibrium(b, i):
691
+ return b.fug_phase_comp["Liq", i] == b.fug_phase_comp["Vap", i]
692
+
693
+ self.equilibrium_constraint = Constraint(
694
+ self._params.component_list, rule=rule_equilibrium
695
+ )
696
+
697
+ # -----------------------------------------------------------------------------
698
+ # Property Methods
699
+ def _dens_mol_phase(self):
700
+ self.dens_mol_phase = Var(
701
+ self._params.phase_list,
702
+ initialize=1.0,
703
+ units=pyunits.mol * pyunits.m**-3,
704
+ doc="Molar density",
705
+ )
706
+
707
+ def rule_dens_mol_phase(b, p):
708
+ if p == "Vap":
709
+ return b._dens_mol_vap()
710
+ else:
711
+ return b._dens_mol_liq()
712
+
713
+ self.eq_dens_mol_phase = Constraint(
714
+ self._params.phase_list, rule=rule_dens_mol_phase
715
+ )
716
+
717
+ def _energy_internal_mol_phase_comp(self):
718
+ self.energy_internal_mol_phase_comp = Var(
719
+ self._params.phase_list,
720
+ self._params.component_list,
721
+ units=pyunits.J / pyunits.mol,
722
+ doc="Phase-component molar specific internal energies",
723
+ )
724
+
725
+ def rule_energy_internal_mol_phase_comp(b, p, j):
726
+ if p == "Vap":
727
+ return b.energy_internal_mol_phase_comp[p, j] == b.enth_mol_phase_comp[
728
+ p, j
729
+ ] - const.gas_constant * (b.temperature - b._params.temperature_ref)
730
+ else:
731
+ return (
732
+ b.energy_internal_mol_phase_comp[p, j]
733
+ == b.enth_mol_phase_comp[p, j]
734
+ )
735
+
736
+ self.eq_energy_internal_mol_phase_comp = Constraint(
737
+ self._params.phase_list,
738
+ self._params.component_list,
739
+ rule=rule_energy_internal_mol_phase_comp,
740
+ )
741
+
742
+ def _energy_internal_mol_phase(self):
743
+ self.energy_internal_mol_phase = Var(
744
+ self._params.phase_list,
745
+ units=pyunits.J / pyunits.mol,
746
+ doc="Phase molar specific internal energies",
747
+ )
748
+
749
+ def rule_energy_internal_mol_phase(b, p):
750
+ return b.energy_internal_mol_phase[p] == sum(
751
+ b.energy_internal_mol_phase_comp[p, i] * b.mole_frac_phase_comp[p, i]
752
+ for i in b._params.component_list
753
+ )
754
+
755
+ self.eq_energy_internal_mol_phase = Constraint(
756
+ self._params.phase_list, rule=rule_energy_internal_mol_phase
757
+ )
758
+
759
+ def _enth_mol_phase_comp(self):
760
+ self.enth_mol_phase_comp = Var(
761
+ self._params.phase_list,
762
+ self._params.component_list,
763
+ initialize=7e5,
764
+ units=pyunits.J / pyunits.mol,
765
+ doc="Phase-component molar specific enthalpies",
766
+ )
767
+
768
+ def rule_enth_mol_phase_comp(b, p, j):
769
+ if p == "Vap":
770
+ return b._enth_mol_comp_vap(j)
771
+ else:
772
+ return b._enth_mol_comp_liq(j)
773
+
774
+ self.eq_enth_mol_phase_comp = Constraint(
775
+ self._params.phase_list,
776
+ self._params.component_list,
777
+ rule=rule_enth_mol_phase_comp,
778
+ )
779
+
780
+ def _enth_mol_phase(self):
781
+ self.enth_mol_phase = Var(
782
+ self._params.phase_list,
783
+ initialize=7e5,
784
+ units=pyunits.J / pyunits.mol,
785
+ doc="Phase molar specific enthalpies",
786
+ )
787
+
788
+ def rule_enth_mol_phase(b, p):
789
+ return b.enth_mol_phase[p] == sum(
790
+ b.enth_mol_phase_comp[p, i] * b.mole_frac_phase_comp[p, i]
791
+ for i in b._params.component_list
792
+ )
793
+
794
+ self.eq_enth_mol_phase = Constraint(
795
+ self._params.phase_list, rule=rule_enth_mol_phase
796
+ )
797
+
798
+ def _entr_mol_phase_comp(self):
799
+ self.entr_mol_phase_comp = Var(
800
+ self._params.phase_list,
801
+ self._params.component_list,
802
+ units=pyunits.J / pyunits.mol / pyunits.K,
803
+ doc="Phase-component molar specific entropies",
804
+ )
805
+
806
+ def rule_entr_mol_phase_comp(b, p, j):
807
+ if p == "Vap":
808
+ return b._entr_mol_comp_vap(j)
809
+ else:
810
+ return b._entr_mol_comp_liq(j)
811
+
812
+ self.eq_entr_mol_phase_comp = Constraint(
813
+ self._params.phase_list,
814
+ self._params.component_list,
815
+ rule=rule_entr_mol_phase_comp,
816
+ )
817
+
818
+ def _entr_mol_phase(self):
819
+ self.entr_mol_phase = Var(
820
+ self._params.phase_list,
821
+ units=pyunits.J / pyunits.mol / pyunits.K,
822
+ doc="Phase molar specific enthropies",
823
+ )
824
+
825
+ def rule_entr_mol_phase(b, p):
826
+ return b.entr_mol_phase[p] == sum(
827
+ b.entr_mol_phase_comp[p, i] * b.mole_frac_phase_comp[p, i]
828
+ for i in b._params.component_list
829
+ )
830
+
831
+ self.eq_entr_mol_phase = Constraint(
832
+ self._params.phase_list, rule=rule_entr_mol_phase
833
+ )
834
+
835
+ # -----------------------------------------------------------------------------
836
+ # General Methods
837
+ def get_material_flow_terms(self, p, j):
838
+ """Create material flow terms for control volume."""
839
+ if not self.is_property_constructed("material_flow_terms"):
840
+ try:
841
+
842
+ def rule_material_flow_terms(blk, p, j):
843
+ return blk.flow_mol_phase_comp[p, j]
844
+
845
+ self.material_flow_terms = Expression(
846
+ self.params.phase_list,
847
+ self.params.component_list,
848
+ rule=rule_material_flow_terms,
849
+ )
850
+ except AttributeError:
851
+ self.del_component(self.material_flow_terms)
852
+
853
+ if j in self.params.component_list:
854
+ return self.material_flow_terms[p, j]
855
+ else:
856
+ return 0
857
+
858
+ def get_enthalpy_flow_terms(self, p):
859
+ """Create enthalpy flow terms."""
860
+ if not self.is_property_constructed("enthalpy_flow_terms"):
861
+ try:
862
+
863
+ def rule_enthalpy_flow_terms(blk, p):
864
+ return blk.flow_mol_phase[p] * blk.enth_mol_phase[p]
865
+
866
+ self.enthalpy_flow_terms = Expression(
867
+ self.params.phase_list, rule=rule_enthalpy_flow_terms
868
+ )
869
+ except AttributeError:
870
+ self.del_component(self.enthalpy_flow_terms)
871
+ return self.enthalpy_flow_terms[p]
872
+
873
+ def get_material_density_terms(self, p, j):
874
+ """Create material density terms."""
875
+ if not self.is_property_constructed("material_density_terms"):
876
+ try:
877
+
878
+ def rule_material_density_terms(b, p, j):
879
+ return self.dens_mol_phase[p] * self.mole_frac_phase_comp[p, j]
880
+
881
+ self.material_density_terms = Expression(
882
+ self.params.phase_list,
883
+ self.params.component_list,
884
+ rule=rule_material_density_terms,
885
+ )
886
+ except AttributeError:
887
+ self.del_component(self.material_density_terms)
888
+
889
+ if j in self.params.component_list:
890
+ return self.material_density_terms[p, j]
891
+ else:
892
+ return 0
893
+
894
+ def get_enthalpy_density_terms(self, p):
895
+ """Create energy density terms."""
896
+ if not self.is_property_constructed("enthalpy_density_terms"):
897
+ try:
898
+
899
+ def rule_energy_density_terms(b, p):
900
+ return self.dens_mol_phase[p] * self.energy_internal_mol_phase[p]
901
+
902
+ self.energy_density_terms = Expression(
903
+ self.params.phase_list, rule=rule_energy_density_terms
904
+ )
905
+ except AttributeError:
906
+ self.del_component(self.energy_density_terms)
907
+ return self.enthalpy_density_terms[p]
908
+
909
+ def default_material_balance_type(self):
910
+ return MaterialBalanceType.componentPhase
911
+
912
+ def default_energy_balance_type(self):
913
+ return EnergyBalanceType.enthalpyTotal
914
+
915
+ def get_material_flow_basis(b):
916
+ return MaterialFlowBasis.molar
917
+
918
+ def define_state_vars(self):
919
+ """Define state vars."""
920
+ return {
921
+ "flow_mol_phase_comp": self.flow_mol_phase_comp,
922
+ "temperature": self.temperature,
923
+ "pressure": self.pressure,
924
+ }
925
+
926
+ # Property package utility functions
927
+ def calculate_bubble_point_temperature(self, clear_components=True):
928
+ """ "To compute the bubble point temperature of the mixture."""
929
+
930
+ if hasattr(self, "eq_temperature_bubble"):
931
+ # Do not delete components if the block already has the components
932
+ clear_components = False
933
+
934
+ calculate_variable_from_constraint(
935
+ self.temperature_bubble, self.eq_temperature_bubble
936
+ )
937
+
938
+ return self.temperature_bubble.value
939
+
940
+ if clear_components is True:
941
+ self.del_component(self.eq_temperature_bubble)
942
+ self.del_component(self._p_sat_bubbleT)
943
+ self.del_component(self.temperature_bubble)
944
+
945
+ def calculate_dew_point_temperature(self, clear_components=True):
946
+ """ "To compute the dew point temperature of the mixture."""
947
+
948
+ if hasattr(self, "eq_temperature_dew"):
949
+ # Do not delete components if the block already has the components
950
+ clear_components = False
951
+
952
+ calculate_variable_from_constraint(
953
+ self.temperature_dew, self.eq_temperature_dew
954
+ )
955
+
956
+ return self.temperature_dew.value
957
+
958
+ # Delete the var/constraint created in this method that are part of the
959
+ # IdealStateBlock if the user desires
960
+ if clear_components is True:
961
+ self.del_component(self.eq_temperature_dew)
962
+ self.del_component(self._p_sat_dewT)
963
+ self.del_component(self.temperature_dew)
964
+
965
+ def calculate_bubble_point_pressure(self, clear_components=True):
966
+ """ "To compute the bubble point pressure of the mixture."""
967
+
968
+ if hasattr(self, "eq_pressure_bubble"):
969
+ # Do not delete components if the block already has the components
970
+ clear_components = False
971
+
972
+ calculate_variable_from_constraint(
973
+ self.pressure_bubble, self.eq_pressure_bubble
974
+ )
975
+
976
+ return self.pressure_bubble.value
977
+
978
+ # Delete the var/constraint created in this method that are part of the
979
+ # IdealStateBlock if the user desires
980
+ if clear_components is True:
981
+ self.del_component(self.eq_pressure_bubble)
982
+ self.del_component(self._p_sat_bubbleP)
983
+ self.del_component(self.pressure_bubble)
984
+
985
+ def calculate_dew_point_pressure(self, clear_components=True):
986
+ """ "To compute the dew point pressure of the mixture."""
987
+
988
+ if hasattr(self, "eq_pressure_dew"):
989
+ # Do not delete components if the block already has the components
990
+ clear_components = False
991
+
992
+ calculate_variable_from_constraint(self.pressure_dew, self.eq_pressure_dew)
993
+
994
+ return self.pressure_dew.value
995
+
996
+ # Delete the var/constraint created in this method that are part of the
997
+ # IdealStateBlock if the user desires
998
+ if clear_components is True:
999
+ self.del_component(self.eq_pressure_dew)
1000
+ self.del_component(self._p_sat_dewP)
1001
+ self.del_component(self.pressure_dew)
1002
+
1003
+ # -----------------------------------------------------------------------------
1004
+ # Bubble and Dew Points
1005
+ # Ideal-Ideal properties allow for the simplifications below
1006
+ # Other methods require more complex equations with shadow compositions
1007
+
1008
+ # For future work, propose the following:
1009
+ # Core class writes a set of constraints Phi_L_i == Phi_V_i
1010
+ # Phi_L_i and Phi_V_i make calls to submethods which add shadow compositions
1011
+ # as needed
1012
+ def _temperature_bubble(self):
1013
+ self.temperature_bubble = Param(
1014
+ initialize=33.0, units=pyunits.K, doc="Bubble point temperature"
1015
+ )
1016
+
1017
+ def _temperature_dew(self):
1018
+
1019
+ self.temperature_dew = Var(
1020
+ initialize=298.15, units=pyunits.K, doc="Dew point temperature"
1021
+ )
1022
+
1023
+ def rule_psat_dew(b, j):
1024
+ return (
1025
+ 1e5
1026
+ * pyunits.Pa
1027
+ * 10
1028
+ ** (
1029
+ b._params.pressure_sat_coeff_A[j]
1030
+ - b._params.pressure_sat_coeff_B[j]
1031
+ / (b.temperature_dew + b._params.pressure_sat_coeff_C[j])
1032
+ )
1033
+ )
1034
+
1035
+ try:
1036
+ # Try to build expression
1037
+ self._p_sat_dewT = Expression(
1038
+ self._params.component_list, rule=rule_psat_dew
1039
+ )
1040
+
1041
+ def rule_temp_dew(b):
1042
+ return (
1043
+ b.pressure
1044
+ * sum(
1045
+ b.mole_frac_comp[i] / b._p_sat_dewT[i]
1046
+ for i in ["toluene", "benzene"]
1047
+ )
1048
+ - 1
1049
+ == 0
1050
+ )
1051
+
1052
+ self.eq_temperature_dew = Constraint(rule=rule_temp_dew)
1053
+ except AttributeError:
1054
+ # If expression fails, clean up so that DAE can try again later
1055
+ # Deleting only var/expression as expression construction will fail
1056
+ # first; if it passes then constraint construction will not fail.
1057
+ self.del_component(self.temperature_dew)
1058
+ self.del_component(self._p_sat_dewT)
1059
+
1060
+ def _pressure_bubble(self):
1061
+ self.pressure_bubble = Param(
1062
+ initialize=1e8, units=pyunits.Pa, doc="Bubble point pressure"
1063
+ )
1064
+
1065
+ def _pressure_dew(self):
1066
+ self.pressure_dew = Var(
1067
+ initialize=298.15, units=pyunits.Pa, doc="Dew point pressure"
1068
+ )
1069
+
1070
+ def rule_psat_dew(b, j):
1071
+ return (
1072
+ 1e5
1073
+ * pyunits.Pa
1074
+ * 10
1075
+ ** (
1076
+ b._params.pressure_sat_coeff_A[j]
1077
+ - b._params.pressure_sat_coeff_B[j]
1078
+ / (b.temperature + b._params.pressure_sat_coeff_C[j])
1079
+ )
1080
+ )
1081
+
1082
+ try:
1083
+ # Try to build expression
1084
+ self._p_sat_dewP = Expression(
1085
+ self._params.component_list, rule=rule_psat_dew
1086
+ )
1087
+
1088
+ def rule_pressure_dew(b):
1089
+ return (
1090
+ b.pressure_dew
1091
+ * sum(
1092
+ b.mole_frac_comp[i] / b._p_sat_dewP[i]
1093
+ for i in ["toluene", "benzene"]
1094
+ )
1095
+ - 1
1096
+ == 0
1097
+ )
1098
+
1099
+ self.eq_pressure_dew = Constraint(rule=rule_pressure_dew)
1100
+ except AttributeError:
1101
+ # If expression fails, clean up so that DAE can try again later
1102
+ # Deleting only var/expression as expression construction will fail
1103
+ # first; if it passes then constraint construction will not fail.
1104
+ self.del_component(self.pressure_dew)
1105
+ self.del_component(self._p_sat_dewP)
1106
+
1107
+ # -----------------------------------------------------------------------------
1108
+ # Liquid phase properties
1109
+ def _dens_mol_liq(b):
1110
+ return b.dens_mol_phase["Liq"] == 1e3 * sum(
1111
+ b.mole_frac_phase_comp["Liq", j]
1112
+ * b._params.dens_liq_param_1[j]
1113
+ / b._params.dens_liq_param_2[j]
1114
+ ** (
1115
+ 1
1116
+ + (1 - b.temperature / b._params.dens_liq_param_3[j])
1117
+ ** b._params.dens_liq_param_4[j]
1118
+ )
1119
+ for j in ["benzene", "toluene"]
1120
+ )
1121
+
1122
+ def _fug_phase_comp(self):
1123
+ def fug_phase_comp_rule(b, p, i):
1124
+ if p == "Liq":
1125
+ if i in ["hydrogen", "methane"]:
1126
+ return b.mole_frac_phase_comp["Liq", i]
1127
+ else:
1128
+ return b.pressure_sat[i] * b.mole_frac_phase_comp["Liq", i]
1129
+ else:
1130
+ if i in ["hydrogen", "methane"]:
1131
+ return 1e-6
1132
+ else:
1133
+ return b.mole_frac_phase_comp["Vap", i] * b.pressure
1134
+
1135
+ self.fug_phase_comp = Expression(
1136
+ self._params.phase_list,
1137
+ self._params.component_list,
1138
+ rule=fug_phase_comp_rule,
1139
+ )
1140
+
1141
+ def _pressure_sat(self):
1142
+ self.pressure_sat = Var(
1143
+ self._params.component_list,
1144
+ initialize=101325,
1145
+ units=pyunits.Pa,
1146
+ doc="Vapor pressure",
1147
+ )
1148
+
1149
+ def rule_P_sat(b, j):
1150
+ return (
1151
+ (
1152
+ log10(b.pressure_sat[j] / pyunits.Pa * 1e-5)
1153
+ - b._params.pressure_sat_coeff_A[j]
1154
+ )
1155
+ * (b._teq + b._params.pressure_sat_coeff_C[j])
1156
+ ) == -b._params.pressure_sat_coeff_B[j]
1157
+
1158
+ self.eq_pressure_sat = Constraint(self._params.component_list, rule=rule_P_sat)
1159
+
1160
+ def _enth_mol_comp_liq(b, j):
1161
+ return b.enth_mol_phase_comp["Liq", j] * 1e3 == (
1162
+ (b._params.cp_ig_5["Liq", j] / 5)
1163
+ * (b.temperature**5 - b._params.temperature_ref**5)
1164
+ + (b._params.cp_ig_4["Liq", j] / 4)
1165
+ * (b.temperature**4 - b._params.temperature_ref**4)
1166
+ + (b._params.cp_ig_3["Liq", j] / 3)
1167
+ * (b.temperature**3 - b._params.temperature_ref**3)
1168
+ + (b._params.cp_ig_2["Liq", j] / 2)
1169
+ * (b.temperature**2 - b._params.temperature_ref**2)
1170
+ + b._params.cp_ig_1["Liq", j] * (b.temperature - b._params.temperature_ref)
1171
+ )
1172
+
1173
+ def _entr_mol_comp_liq(b, j):
1174
+ return b.entr_mol_phase_comp["Liq", j] * 1e3 == (
1175
+ (
1176
+ (b._params.cp_ig_5["Liq", j] / 4)
1177
+ * (b.temperature**4 - b._params.temperature_ref**4)
1178
+ + (b._params.cp_ig_4["Liq", j] / 3)
1179
+ * (b.temperature**3 - b._params.temperature_ref**3)
1180
+ + (b._params.cp_ig_3["Liq", j] / 2)
1181
+ * (b.temperature**2 - b._params.temperature_ref**2)
1182
+ + b._params.cp_ig_2["Liq", j]
1183
+ * (b.temperature - b._params.temperature_ref)
1184
+ + b._params.cp_ig_1["Liq", j]
1185
+ * log(b.temperature / b._params.temperature_ref)
1186
+ )
1187
+ - const.gas_constant
1188
+ * log(
1189
+ b.mole_frac_phase_comp["Liq", j] * b.pressure / b._params.pressure_ref
1190
+ )
1191
+ )
1192
+
1193
+ # -----------------------------------------------------------------------------
1194
+ # Vapour phase properties
1195
+ def _dens_mol_vap(b):
1196
+ return b.pressure == (
1197
+ b.dens_mol_phase["Vap"] * const.gas_constant * b.temperature
1198
+ )
1199
+
1200
+ def _dh_vap(self):
1201
+ # heat of vaporization
1202
+ add_object_reference(self, "dh_vap", self._params.dh_vap)
1203
+
1204
+ def _ds_vap(self):
1205
+ # entropy of vaporization = dh_Vap/T_boil
1206
+ # TODO : something more rigorous would be nice
1207
+ self.ds_vap = Var(
1208
+ self._params.component_list,
1209
+ initialize=86,
1210
+ units=pyunits.J / pyunits.mol / pyunits.K,
1211
+ doc="Entropy of vaporization",
1212
+ )
1213
+
1214
+ def rule_ds_vap(b, j):
1215
+ return b.dh_vap[j] == (b.ds_vap[j] * b._params.temperature_boil[j])
1216
+
1217
+ self.eq_ds_vap = Constraint(self._params.component_list, rule=rule_ds_vap)
1218
+
1219
+ def _enth_mol_comp_vap(b, j):
1220
+ return b.enth_mol_phase_comp["Vap", j] == b.dh_vap[j] + (
1221
+ (b._params.cp_ig_5["Vap", j] / 5)
1222
+ * (b.temperature**5 - b._params.temperature_ref**5)
1223
+ + (b._params.cp_ig_4["Vap", j] / 4)
1224
+ * (b.temperature**4 - b._params.temperature_ref**4)
1225
+ + (b._params.cp_ig_3["Vap", j] / 3)
1226
+ * (b.temperature**3 - b._params.temperature_ref**3)
1227
+ + (b._params.cp_ig_2["Vap", j] / 2)
1228
+ * (b.temperature**2 - b._params.temperature_ref**2)
1229
+ + b._params.cp_ig_1["Vap", j] * (b.temperature - b._params.temperature_ref)
1230
+ )
1231
+
1232
+ def _entr_mol_comp_vap(b, j):
1233
+ return b.entr_mol_phase_comp["Vap", j] == (
1234
+ b.ds_vap[j]
1235
+ + (
1236
+ (b._params.cp_ig_5["Vap", j] / 4)
1237
+ * (b.temperature**4 - b._params.temperature_ref**4)
1238
+ + (b._params.cp_ig_4["Vap", j] / 3)
1239
+ * (b.temperature**3 - b._params.temperature_ref**3)
1240
+ + (b._params.cp_ig_3["Vap", j] / 2)
1241
+ * (b.temperature**2 - b._params.temperature_ref**2)
1242
+ + b._params.cp_ig_2["Vap", j]
1243
+ * (b.temperature - b._params.temperature_ref)
1244
+ + b._params.cp_ig_1["Vap", j]
1245
+ * log(b.temperature / b._params.temperature_ref)
1246
+ )
1247
+ - const.gas_constant
1248
+ * log(
1249
+ b.mole_frac_phase_comp["Vap", j] * b.pressure / b._params.pressure_ref
1250
+ )
1251
+ )
1252
+
1253
+ def calculate_scaling_factors(self):
1254
+ # Get default scale factors
1255
+ super().calculate_scaling_factors()
1256
+
1257
+ is_two_phase = len(self._params.phase_list) == 2
1258
+ sf_flow = iscale.get_scaling_factor(self.flow_mol, default=1, warning=True)
1259
+ sf_T = iscale.get_scaling_factor(self.temperature, default=1, warning=True)
1260
+ sf_P = iscale.get_scaling_factor(self.pressure, default=1, warning=True)
1261
+
1262
+ if self.is_property_constructed("_teq"):
1263
+ iscale.set_scaling_factor(self._teq, sf_T)
1264
+ if self.is_property_constructed("_teq_constraint"):
1265
+ iscale.constraint_scaling_transform(
1266
+ self._teq_constraint, sf_T, overwrite=False
1267
+ )
1268
+
1269
+ if self.is_property_constructed("_t1"):
1270
+ iscale.set_scaling_factor(self._t1, sf_T)
1271
+ if self.is_property_constructed("_t1_constraint"):
1272
+ iscale.constraint_scaling_transform(
1273
+ self._t1_constraint, sf_T, overwrite=False
1274
+ )
1275
+
1276
+ if self.is_property_constructed("_mole_frac_pdew"):
1277
+ iscale.set_scaling_factor(self._mole_frac_pdew, 1e3)
1278
+ iscale.constraint_scaling_transform(
1279
+ self._sum_mole_frac_pdew, 1e3, overwrite=False
1280
+ )
1281
+
1282
+ if self.is_property_constructed("total_flow_balance"):
1283
+ iscale.constraint_scaling_transform(
1284
+ self.total_flow_balance, sf_flow, overwrite=False
1285
+ )
1286
+
1287
+ if self.is_property_constructed("component_flow_balances"):
1288
+ for i, c in self.component_flow_balances.items():
1289
+ if is_two_phase:
1290
+ s = iscale.get_scaling_factor(
1291
+ self.mole_frac_comp[i], default=1, warning=True
1292
+ )
1293
+ s *= sf_flow
1294
+ iscale.constraint_scaling_transform(c, s, overwrite=False)
1295
+ else:
1296
+ s = iscale.get_scaling_factor(
1297
+ self.mole_frac_comp[i], default=1, warning=True
1298
+ )
1299
+ iscale.constraint_scaling_transform(c, s, overwrite=False)
1300
+
1301
+ if self.is_property_constructed("dens_mol_phase"):
1302
+ for c in self.eq_dens_mol_phase.values():
1303
+ iscale.constraint_scaling_transform(c, sf_P, overwrite=False)
1304
+
1305
+ if self.is_property_constructed("dens_mass_phase"):
1306
+ for p, c in self.eq_dens_mass_phase.items():
1307
+ sf = iscale.get_scaling_factor(
1308
+ self.dens_mass_phase[p], default=1, warning=True
1309
+ )
1310
+ iscale.constraint_scaling_transform(c, sf, overwrite=False)
1311
+
1312
+ if self.is_property_constructed("enth_mol_phase"):
1313
+ for p, c in self.eq_enth_mol_phase.items():
1314
+ sf = iscale.get_scaling_factor(
1315
+ self.enth_mol_phase[p], default=1, warning=True
1316
+ )
1317
+ iscale.constraint_scaling_transform(c, sf, overwrite=False)
1318
+
1319
+ if self.is_property_constructed("enth_mol"):
1320
+ sf = iscale.get_scaling_factor(self.enth_mol, default=1, warning=True)
1321
+ sf *= sf_flow
1322
+ iscale.constraint_scaling_transform(self.eq_enth_mol, sf, overwrite=False)
1323
+
1324
+ if self.is_property_constructed("entr_mol_phase"):
1325
+ for p, c in self.eq_entr_mol_phase.items():
1326
+ sf = iscale.get_scaling_factor(
1327
+ self.entr_mol_phase[p], default=1, warning=True
1328
+ )
1329
+ iscale.constraint_scaling_transform(c, sf, overwrite=False)
1330
+
1331
+ if self.is_property_constructed("entr_mol"):
1332
+ sf = iscale.get_scaling_factor(self.entr_mol, default=1, warning=True)
1333
+ sf *= sf_flow
1334
+ iscale.constraint_scaling_transform(self.eq_entr_mol, sf, overwrite=False)
1335
+
1336
+ if self.is_property_constructed("gibbs_mol_phase"):
1337
+ for p, c in self.eq_gibbs_mol_phase.items():
1338
+ sf = iscale.get_scaling_factor(
1339
+ self.gibbs_mol_phase[p], default=1, warning=True
1340
+ )
1341
+ iscale.constraint_scaling_transform(c, sf, overwrite=False)