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,34 @@
1
+ from idaes.models.unit_models.pressure_changer import PressureChangerData
2
+ from .inverted import add_inverted, initialise_inverted
3
+ from idaes.core import declare_process_block_class
4
+
5
+ @declare_process_block_class("CustomPressureChanger")
6
+ class CustomPressureChangerData(PressureChangerData):
7
+ """
8
+ Custom Pressure Changer model that includes inverted deltaP property.
9
+ """
10
+
11
+ def build(self, *args, **kwargs):
12
+ """
13
+ Build method for the CustomPressureChangerData class.
14
+ This method initializes the control volume and sets up the model.
15
+ """
16
+ super().build(*args, **kwargs)
17
+
18
+ # add deltaP_inverted as a property
19
+ add_inverted(self, "deltaP")
20
+
21
+ def initialize_build(
22
+ self,*args,**kwargs,
23
+ ):
24
+ """
25
+ Initialization method for the CustomPressureChangerData class.
26
+
27
+ Args:
28
+ state_args (dict): Arguments to be passed to the state block
29
+ solver (str): Solver to use for initialization
30
+ optarg (dict): Solver arguments dictionary
31
+ """
32
+ initialise_inverted(self, "deltaP")
33
+
34
+ super().initialize_build(*args, **kwargs)
@@ -0,0 +1,107 @@
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
+ # Import IDAES cores
11
+ from idaes.core import (
12
+ declare_process_block_class,
13
+ UnitModelBlockData,
14
+ useDefault,
15
+ )
16
+ from idaes.core.util.config import is_physical_parameter_block
17
+ import idaes.core.util.scaling as iscale
18
+ import idaes.logger as idaeslog
19
+
20
+ from ..custom.updated_pressure_changer import (
21
+
22
+ PumpData,
23
+ )
24
+
25
+
26
+
27
+
28
+ # When using this file the name "CustomCompressor" is what is imported
29
+ @declare_process_block_class("CustomPump")
30
+ class CustomPumpData(PumpData):
31
+ """
32
+ Zero order Load model
33
+ """
34
+
35
+ # CONFIG are options for the unit model, this simple model only has the mandatory config options
36
+ CONFIG = PumpData.CONFIG()
37
+
38
+ CONFIG.declare(
39
+ "power_property_package",
40
+ ConfigValue(
41
+ default=useDefault,
42
+ domain=is_physical_parameter_block,
43
+ description="Property package to use for power",
44
+ doc="""Power Property parameter object used to define power calculations,
45
+ **default** - useDefault.
46
+ **Valid values:** {
47
+ **useDefault** - use default package from parent model or flowsheet,
48
+ **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
49
+ ),
50
+ )
51
+ CONFIG.declare(
52
+ "power_property_package_args",
53
+ ConfigBlock(
54
+ implicit=True,
55
+ description="Arguments to use for constructing power property packages",
56
+ doc="""A ConfigBlock with arguments to be passed to a property block(s)
57
+ and used when constructing these,
58
+ **default** - None.
59
+ **Valid values:** {
60
+ see property package for documentation.}""",
61
+ ),
62
+ )
63
+
64
+ def build(self):
65
+ # build always starts by calling super().build()
66
+ # This triggers a lot of boilerplate in the background for you
67
+ super().build()
68
+
69
+ # This creates blank scaling factors, which are populated later
70
+ self.scaling_factor = Suffix(direction=Suffix.EXPORT)
71
+
72
+
73
+ # Add state blocks for inlet, outlet, and waste
74
+ # These include the state variables and any other properties on demand
75
+
76
+ tmp_dict = dict(**self.config.property_package_args)
77
+ tmp_dict["parameters"] = self.config.property_package
78
+ tmp_dict["defined_state"] = True # inlet block is an inlet
79
+ # Add inlet block
80
+ # self.properties_in = self.config.property_package.state_block_class(
81
+ # self.flowsheet().config.time, doc="Material properties of inlet", **tmp_dict
82
+ # )
83
+
84
+
85
+ # Add outlet and waste block
86
+ tmp_dict["defined_state"] = False # outlet and waste block is not an inlet
87
+ self.power_properties_out = self.config.power_property_package.state_block_class(
88
+ self.flowsheet().config.time,
89
+ doc="Material properties of outlet",
90
+ **tmp_dict
91
+ )
92
+
93
+ # Add ports - oftentimes users interact with these rather than the state blocks
94
+ self.add_port(name="power_outlet", block=self.power_properties_out)
95
+
96
+ # Add constraints
97
+ # Usually unit models use a control volume to do the mass, energy, and momentum
98
+ # balances, however, they will be explicitly written out in this example
99
+ @self.Constraint(
100
+ self.flowsheet().time,
101
+ doc="Power out",
102
+ )
103
+ def eq_power_out(b, t):
104
+ return (
105
+ self.power_properties_out[t].power == self.work_mechanical[t] * -1
106
+ )
107
+
@@ -0,0 +1,371 @@
1
+ from idaes.models.unit_models.separator import SeparatorData, SplittingType
2
+
3
+ from functools import partial
4
+ from pandas import DataFrame
5
+
6
+ from pyomo.environ import (
7
+ Block,
8
+ check_optimal_termination,
9
+ Constraint,
10
+ Param,
11
+ Reals,
12
+ Reference,
13
+ Set,
14
+ Var,
15
+ value,
16
+ )
17
+ from pyomo.network import Port
18
+ from pyomo.common.config import ConfigBlock, ConfigValue, In, ListOf, Bool
19
+
20
+ from idaes.core import (
21
+ declare_process_block_class,
22
+ UnitModelBlockData,
23
+ useDefault,
24
+ MaterialBalanceType,
25
+ MomentumBalanceType,
26
+ MaterialFlowBasis,
27
+ VarLikeExpression,
28
+ )
29
+ from idaes.core.util.config import (
30
+ is_physical_parameter_block,
31
+ is_state_block,
32
+ )
33
+ from idaes.core.util.exceptions import (
34
+ BurntToast,
35
+ ConfigurationError,
36
+ PropertyNotSupportedError,
37
+ InitializationError,
38
+ )
39
+ from idaes.core.solvers import get_solver
40
+ from idaes.core.util.tables import create_stream_table_dataframe
41
+ from idaes.core.util.model_statistics import degrees_of_freedom
42
+ import idaes.logger as idaeslog
43
+ import idaes.core.util.scaling as iscale
44
+ from idaes.core.util.units_of_measurement import report_quantity
45
+ from idaes.core.initialization import ModularInitializerBase
46
+
47
+
48
+ # This only changes a couple of lines in the original SeparatorData class, to not fix state variables by default.
49
+ # The state block initialisation already does this if needed, so we can just set their value.
50
+ # This is because if the state block has extra constraints, such as for flow_mass, then fixing flow_mol will over-define the system.
51
+ # It might be worth making this a pr to idaes.
52
+
53
+ @declare_process_block_class("CustomSeparator")
54
+ class CustomSeparatorData(SeparatorData):
55
+
56
+ def initialize_build(
57
+ blk, outlvl=idaeslog.NOTSET, optarg=None, solver=None, hold_state=False
58
+ ):
59
+ """
60
+ Initialization routine for separator
61
+
62
+ Keyword Arguments:
63
+ outlvl : sets output level of initialization routine
64
+ optarg : solver options dictionary object (default=None, use
65
+ default solver options)
66
+ solver : str indicating which solver to use during
67
+ initialization (default = None, use default solver)
68
+ hold_state : flag indicating whether the initialization routine
69
+ should unfix any state variables fixed during
70
+ initialization, **default** - False. **Valid values:**
71
+ **True** - states variables are not unfixed, and a dict of
72
+ returned containing flags for which states were fixed
73
+ during initialization, **False** - state variables are
74
+ unfixed after initialization by calling the release_state
75
+ method.
76
+
77
+ Returns:
78
+ If hold_states is True, returns a dict containing flags for which
79
+ states were fixed during initialization.
80
+ """
81
+ init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit")
82
+ solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit")
83
+
84
+ # Create solver
85
+ opt = get_solver(solver, optarg)
86
+
87
+ # Initialize mixed state block
88
+ if blk.config.mixed_state_block is not None:
89
+ mblock = blk.config.mixed_state_block
90
+ else:
91
+ mblock = blk.mixed_state
92
+ flags = mblock.initialize(
93
+ outlvl=outlvl,
94
+ optarg=optarg,
95
+ solver=solver,
96
+ hold_state=True,
97
+ )
98
+
99
+ # Solve for split fractions only
100
+ component_status = {}
101
+ for c in blk.component_objects((Block, Constraint)):
102
+ for i in c:
103
+ if not c[i].local_name == "sum_split_frac":
104
+ # Record current status of components to restore later
105
+ component_status[c[i]] = c[i].active
106
+ c[i].deactivate()
107
+
108
+ if degrees_of_freedom(blk) != 0:
109
+ with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
110
+ res = opt.solve(blk, tee=slc.tee)
111
+ init_log.info(
112
+ "Initialization Step 1 Complete: {}".format(idaeslog.condition(res))
113
+ )
114
+
115
+ for c, s in component_status.items():
116
+ if s:
117
+ c.activate()
118
+
119
+ if blk.config.ideal_separation:
120
+ # If using ideal splitting, initialization should be complete
121
+ return flags
122
+
123
+ # Initialize outlet StateBlocks
124
+ outlet_list = blk.create_outlet_list()
125
+
126
+ # Premises for initializing outlet states:
127
+ # 1. Intensive states remain unchanged - this is either a valid premise
128
+ # or the actual state is impossible to calculate without solving the
129
+ # full separator model.
130
+ # 2. Extensive states are use split fractions if index matches, or
131
+ # average of split fractions for outlet otherwise
132
+ for o in outlet_list:
133
+ # Get corresponding outlet StateBlock
134
+ o_block = getattr(blk, o + "_state")
135
+
136
+ # Create dict to store fixed status of state variables
137
+ o_flags = {}
138
+ for t in blk.flowsheet().time:
139
+
140
+ # Calculate values for state variables
141
+ s_vars = o_block[t].define_state_vars()
142
+ for v in s_vars:
143
+ for k in s_vars[v]:
144
+ # Record whether variable was fixed or not
145
+ o_flags[t, v, k] = s_vars[v][k].fixed
146
+
147
+ # If fixed, use current value
148
+ # otherwise calculate guess from mixed state and fix
149
+ if not s_vars[v][k].fixed:
150
+ m_var = getattr(mblock[t], s_vars[v].local_name)
151
+ if "flow" in v:
152
+ # If a "flow" variable, is extensive
153
+ # Apply split fraction
154
+ if blk.config.split_basis == SplittingType.totalFlow:
155
+ # All flows split by outlet
156
+ s_vars[v][k].set_value(
157
+ value(m_var[k] * blk.split_fraction[(t, o)])
158
+ )
159
+ elif "_phase_comp" in v:
160
+ # Need to match indices, but use split frac
161
+ if (
162
+ blk.config.split_basis
163
+ == SplittingType.phaseComponentFlow
164
+ ):
165
+ s_vars[v][k].set_value(
166
+ value(
167
+ m_var[k]
168
+ * blk.split_fraction[(t, o) + (k,)]
169
+ )
170
+ )
171
+ elif (
172
+ blk.config.split_basis
173
+ == SplittingType.phaseFlow
174
+ ):
175
+ s_vars[v][k].set_value(
176
+ value(
177
+ m_var[k]
178
+ * blk.split_fraction[(t, o) + (k[0],)]
179
+ )
180
+ )
181
+ elif (
182
+ blk.config.split_basis
183
+ == SplittingType.componentFlow
184
+ ):
185
+ s_vars[v][k].set_value(
186
+ value(
187
+ m_var[k]
188
+ * blk.split_fraction[(t, o) + (k[1],)]
189
+ )
190
+ )
191
+ else:
192
+ raise BurntToast(
193
+ "{} encountered unrecognised "
194
+ "SplittingType. This should not "
195
+ "occur - please send this bug to "
196
+ "the IDAES developers.".format(blk.name)
197
+ )
198
+ elif "_phase" in v:
199
+ if (
200
+ blk.config.split_basis
201
+ == SplittingType.phaseComponentFlow
202
+ ):
203
+ # Need average split fraction
204
+ avg_split = value(
205
+ sum(
206
+ blk.split_fraction[t, o, k, j]
207
+ for j in mblock.component_list
208
+ )
209
+ / len(mblock.component_list)
210
+ )
211
+ s_vars[v][k].set_value(value(m_var[k] * avg_split))
212
+ elif (
213
+ blk.config.split_basis
214
+ == SplittingType.phaseFlow
215
+ ):
216
+ s_vars[v][k].set_value(
217
+ value(
218
+ m_var[k]
219
+ * blk.split_fraction[(t, o) + (k,)]
220
+ )
221
+ )
222
+ elif (
223
+ blk.config.split_basis
224
+ == SplittingType.componentFlow
225
+ ):
226
+ # Need average split fraction
227
+ avg_split = value(
228
+ sum(
229
+ blk.split_fraction[t, o, j]
230
+ for j in mblock.component_list
231
+ )
232
+ / len(mblock.component_list)
233
+ )
234
+ s_vars[v][k].set_value(value(m_var[k] * avg_split))
235
+ else:
236
+ raise BurntToast(
237
+ "{} encountered unrecognised "
238
+ "SplittingType. This should not "
239
+ "occur - please send this bug to "
240
+ "the IDAES developers.".format(blk.name)
241
+ )
242
+ elif "_comp" in v:
243
+ if (
244
+ blk.config.split_basis
245
+ == SplittingType.phaseComponentFlow
246
+ ):
247
+ # Need average split fraction
248
+ avg_split = value(
249
+ sum(
250
+ blk.split_fraction[t, o, p, k]
251
+ for p in mblock.phase_list
252
+ )
253
+ / len(mblock.phase_list)
254
+ )
255
+ s_vars[v][k].set_value(value(m_var[k] * avg_split))
256
+ elif (
257
+ blk.config.split_basis
258
+ == SplittingType.phaseFlow
259
+ ):
260
+ # Need average split fraction
261
+ avg_split = value(
262
+ sum(
263
+ blk.split_fraction[t, o, p]
264
+ for p in mblock.phase_list
265
+ )
266
+ / len(mblock.phase_list)
267
+ )
268
+ s_vars[v][k].set_value(value(m_var[k] * avg_split))
269
+ elif (
270
+ blk.config.split_basis
271
+ == SplittingType.componentFlow
272
+ ):
273
+ s_vars[v][k].set_value(
274
+ value(
275
+ m_var[k]
276
+ * blk.split_fraction[(t, o) + (k,)]
277
+ )
278
+ )
279
+ else:
280
+ raise BurntToast(
281
+ "{} encountered unrecognised "
282
+ "SplittingType. This should not "
283
+ "occur - please send this bug to "
284
+ "the IDAES developers.".format(blk.name)
285
+ )
286
+ else:
287
+ # Assume unindexed extensive state
288
+ # Need average split
289
+ if (
290
+ blk.config.split_basis
291
+ == SplittingType.phaseComponentFlow
292
+ ):
293
+ # Need average split fraction
294
+ avg_split = value(
295
+ sum(
296
+ blk.split_fraction[t, o, p, j]
297
+ for (p, j) in mblock.phase_component_set
298
+ )
299
+ / len(mblock.phase_component_set)
300
+ )
301
+ elif (
302
+ blk.config.split_basis
303
+ == SplittingType.phaseFlow
304
+ ):
305
+ # Need average split fraction
306
+ avg_split = value(
307
+ sum(
308
+ blk.split_fraction[t, o, p]
309
+ for p in mblock.phase_list
310
+ )
311
+ / len(mblock.phase_list)
312
+ )
313
+ elif (
314
+ blk.config.split_basis
315
+ == SplittingType.componentFlow
316
+ ):
317
+ # Need average split fraction
318
+ avg_split = value(
319
+ sum(
320
+ blk.split_fraction[t, o, j]
321
+ for j in mblock.component_list
322
+ )
323
+ / len(mblock.component_list)
324
+ )
325
+ else:
326
+ raise BurntToast(
327
+ "{} encountered unrecognised "
328
+ "SplittingType. This should not "
329
+ "occur - please send this bug to "
330
+ "the IDAES developers.".format(blk.name)
331
+ )
332
+ s_vars[v][k].set_value(value(m_var[k] * avg_split))
333
+ else:
334
+ # Otherwise intensive, equate to mixed stream
335
+ s_vars[v][k].set_value(m_var[k].value)
336
+
337
+ # Call initialization routine for outlet StateBlock
338
+ o_block.initialize(
339
+ outlvl=outlvl,
340
+ optarg=optarg,
341
+ solver=solver,
342
+ hold_state=False,
343
+ )
344
+
345
+ # Revert fixed status of variables to what they were before
346
+ for t in blk.flowsheet().time:
347
+ s_vars = o_block[t].define_state_vars()
348
+ for v in s_vars:
349
+ for k in s_vars[v]:
350
+ s_vars[v][k].fixed = o_flags[t, v, k]
351
+
352
+ if blk.config.mixed_state_block is None:
353
+ with idaeslog.solver_log(solve_log, idaeslog.DEBUG) as slc:
354
+ res = opt.solve(blk, tee=slc.tee)
355
+
356
+ if not check_optimal_termination(res):
357
+ raise InitializationError(
358
+ f"{blk.name} failed to initialize successfully. Please "
359
+ f"check the output logs for more information."
360
+ )
361
+
362
+ init_log.info(
363
+ "Initialization Step 2 Complete: {}".format(idaeslog.condition(res))
364
+ )
365
+ else:
366
+ init_log.info("Initialization Complete.")
367
+
368
+ if hold_state is True:
369
+ return flags
370
+ else:
371
+ blk.release_state(flags, outlvl=outlvl)
@@ -0,0 +1,133 @@
1
+ from idaes.core import declare_process_block_class, MaterialBalanceType
2
+ from .water_tank_with_units import WaterTankData
3
+ from .add_initial_dynamics import add_initial_dynamics
4
+ from pyomo.environ import Reference, Var
5
+ from pyomo.dae import DerivativeVar
6
+ from pyomo.environ import units as pyunit
7
+ from .inverted import add_inverted, initialise_inverted
8
+
9
+
10
+ def CustomTank(*args, **kwargs):
11
+ # In dynamics mode, we need to use the default material balance type of componentPhase.
12
+ # This does balances for liquid and vapor phases separately. This is needed becasue
13
+ # accumulation terms are phase specific.
14
+ # However, in steady-state, this is not necessary, because we don't have accumulation terms.
15
+ # So this would make the system over-defined, as the state block defines the phase equilibrium already.
16
+ is_dynamic = kwargs.get("dynamic")
17
+ if is_dynamic:
18
+ kwargs["material_balance_type"] = MaterialBalanceType.componentPhase
19
+ else:
20
+ kwargs["material_balance_type"] = MaterialBalanceType.componentTotal
21
+
22
+ return DynamicTank(*args, **kwargs)
23
+
24
+
25
+ @declare_process_block_class("DynamicTank")
26
+ class DynamicTankData(WaterTankData):
27
+ """
28
+ Water tank model with dynamic capabilities.
29
+ Some extra properties are added to IDAES's tank model to allow for easier specification of initial conditions.
30
+ """
31
+
32
+ def build(self, *args, **kwargs):
33
+ """
34
+ Build method for the DynamicHeaterData class.
35
+ This method initializes the control volume and sets up the model.
36
+ """
37
+
38
+ super().build(*args, **kwargs)
39
+
40
+ self.flow_rate_out = Reference(self.outlet.flow_mol)
41
+
42
+ # add deltaP_inverted as a property
43
+ add_inverted(self,"heat_duty")
44
+
45
+ # Because it's hard to specify the initial conditions directly,
46
+ # Create a state block for the initial conditions.
47
+
48
+ if not self.config.dynamic:
49
+ return # There is no need to add these extra properties.
50
+
51
+ self.initial_block = self.config.property_package.build_state_block(
52
+ [0],
53
+ defined_state=True,
54
+ )
55
+
56
+ if len(self.config.property_package.component_list) > 1:
57
+
58
+ # We are assuming that the composition does not change at the initial time step. In theory it could, but
59
+ # we can worry about that later.
60
+ @self.Constraint(
61
+ self.config.property_package.component_list,
62
+ doc="Initial composition constraint",
63
+ )
64
+ def initial_composition_constraint(b, j):
65
+ return (
66
+ b.initial_block[0].mole_frac_comp[j]
67
+ == b.control_volume.properties_in[0].mole_frac_comp[j]
68
+ )
69
+
70
+ # The initial temperature, pressure, and flow amount is set by the user.
71
+ self.initial_pressure = Var(initialize=101325, units=pyunit.Pa)
72
+ @self.Constraint(doc="Initial pressure constraint")
73
+ def initial_pressure_constraint(b):
74
+ return b.initial_block[0].pressure == b.initial_pressure
75
+
76
+ self.initial_holdup = Var(initialize=300, units=pyunit.mol)
77
+ @self.Constraint(doc="Initial flow constraint")
78
+ def initial_holdup_mol_constraint(b):
79
+ return (
80
+ b.initial_block[0].flow_mol * pyunit.s == b.initial_holdup
81
+ ) # cancel out the seconds as we are using it for holdup not accumulation.
82
+
83
+
84
+ self.initial_level = Var(initialize=300, units=pyunit.m)
85
+ @self.Constraint(doc="Initial level constraint")
86
+ def initial_level_constraint(b):
87
+ return b.initial_holdup == b.tank_cross_sect_area * b.initial_level * b.initial_block[0].dens_mol
88
+
89
+ self.initial_temperature = Var(initialize=300, units=pyunit.K)
90
+ @self.Constraint(doc="Initial temperature constraint")
91
+ def initial_temperature_constraint(b):
92
+ return b.initial_block[0].temperature == b.initial_temperature
93
+
94
+ # The temperature, pressure and flow are used to calculate the other properties.
95
+ @self.Constraint(
96
+ self.config.property_package.phase_list,
97
+ self.config.property_package.component_list,
98
+ doc="Defining accumulation",
99
+ )
100
+ def initial_material_conditions_constraint(b, p, j):
101
+ return (
102
+ b.initial_block[0].flow_mol
103
+ * b.initial_block[0].mole_frac_phase_comp[p, j]
104
+ + b.control_volume.material_accumulation[0, p, j]
105
+ == b.control_volume.material_holdup[0, p, j]
106
+ )
107
+
108
+ @self.Constraint(
109
+ self.config.property_package.phase_list, doc="Defining accumulation"
110
+ )
111
+ def initial_energy_conditions_constraint(b, p):
112
+ return (
113
+ b.initial_block[0].flow_mol
114
+ * b.initial_block[0].phase_frac[p]
115
+ * b.initial_block[0].enth_mol_phase[p]
116
+ + b.control_volume.energy_accumulation[0, p]
117
+ == b.control_volume.energy_holdup[0, p]
118
+ )
119
+
120
+
121
+
122
+ def initialize(self, *args, **kwargs):
123
+ """
124
+ Initialization method for the DynamicTankData class.
125
+ This method initializes the control volume and sets up the model.
126
+ """
127
+ # Copy initial conditions from inverted properties
128
+ initialise_inverted(self,"heat_duty")
129
+
130
+ if self.config.dynamic:
131
+ self.initial_block.initialize()
132
+
133
+ super().initialize(*args, **kwargs)