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,257 @@
1
+ # Import Pyomo libraries
2
+ from stringprep import in_table_a1
3
+ from pyomo.environ import (
4
+ Var,
5
+ Suffix,
6
+ units as pyunits,
7
+ Set,
8
+ value
9
+ )
10
+ from pyomo.common.config import ConfigBlock, ConfigValue, In
11
+
12
+ # Import IDAES cores
13
+ from idaes.core import (
14
+ declare_process_block_class,
15
+ UnitModelBlockData,
16
+ useDefault,
17
+ )
18
+ from idaes.core.util.config import is_physical_parameter_block
19
+ import idaes.core.util.scaling as iscale
20
+ import idaes.logger as idaeslog
21
+
22
+ from idaes.core.util.tables import create_stream_table_dataframe
23
+ from idaes.core.util.math import smooth_min
24
+ from idaes.core.util.exceptions import ConfigurationError
25
+
26
+ # Set up logger
27
+ _log = idaeslog.getLogger(__name__)
28
+
29
+
30
+ # When using this file the name "MDB" is what is imported
31
+ @declare_process_block_class("MDB")
32
+ class MDBData(UnitModelBlockData):
33
+ """
34
+ Zero order power distirbution board model
35
+ """
36
+
37
+ # CONFIG are options for the unit model, this simple model only has the mandatory config options
38
+ CONFIG = ConfigBlock()
39
+
40
+ CONFIG.declare(
41
+ "dynamic",
42
+ ConfigValue(
43
+ domain=In([False]),
44
+ default=False,
45
+ description="Dynamic model flag - must be False",
46
+ doc="""Indicates whether this model will be dynamic or not,
47
+ **default** = False. The Bus unit does not support dynamic
48
+ behavior, thus this must be False.""",
49
+ ),
50
+ )
51
+ CONFIG.declare(
52
+ "has_holdup",
53
+ ConfigValue(
54
+ default=False,
55
+ domain=In([False]),
56
+ description="Holdup construction flag - must be False",
57
+ doc="""Indicates whether holdup terms should be constructed or not.
58
+ **default** - False. The Bus unit does not have defined volume, thus
59
+ this must be False.""",
60
+ ),
61
+ )
62
+ CONFIG.declare(
63
+ "property_package",
64
+ ConfigValue(
65
+ default=useDefault,
66
+ domain=is_physical_parameter_block,
67
+ description="Property package to use for control volume",
68
+ doc="""Property parameter object used to define property calculations,
69
+ **default** - useDefault.
70
+ **Valid values:** {
71
+ **useDefault** - use default package from parent model or flowsheet,
72
+ **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
73
+ ),
74
+ )
75
+ CONFIG.declare(
76
+ "property_package_args",
77
+ ConfigBlock(
78
+ implicit=True,
79
+ description="Arguments to use for constructing property packages",
80
+ doc="""A ConfigBlock with arguments to be passed to a property block(s)
81
+ and used when constructing these,
82
+ **default** - None.
83
+ **Valid values:** {
84
+ see property package for documentation.}""",
85
+ ),
86
+ )
87
+ CONFIG.declare(
88
+ "num_inlets",
89
+ ConfigValue(
90
+ default=False,
91
+ domain=int,
92
+ description="Number of inlets to add",
93
+ doc="""Number of inlets to add""",
94
+ ),
95
+ )
96
+ CONFIG.declare(
97
+ "num_outlets",
98
+ ConfigValue(
99
+ default=False,
100
+ domain=int,
101
+ description="Number of outlets to add",
102
+ doc="""Number of outlets to add""",
103
+ ),
104
+ )
105
+
106
+ def build(self):
107
+ # build always starts by calling super().build()
108
+ # This triggers a lot of boilerplate in the background for you
109
+ super().build()
110
+
111
+ # This creates blank scaling factors, which are populated later
112
+ self.scaling_factor = Suffix(direction=Suffix.EXPORT)
113
+
114
+
115
+ # Defining parameters of state block class
116
+ tmp_dict = dict(**self.config.property_package_args)
117
+ tmp_dict["parameters"] = self.config.property_package
118
+ tmp_dict["defined_state"] = True # inlet block is an inlet
119
+
120
+ # Add state blocks for inlet, outlet, and waste
121
+ # These include the state variables and any other properties on demand
122
+ num_inlets = self.config.num_inlets
123
+
124
+ self.inlet_list = [ "inlet_" + str(i+1) for i in range(num_inlets) ]
125
+
126
+
127
+ self.inlet_blocks = []
128
+ for name in self.inlet_list:
129
+ # add properties_inlet_1, properties_inlet2 etc
130
+ state_block = self.config.property_package.state_block_class(
131
+ self.flowsheet().config.time, doc="inlet power", **tmp_dict
132
+ )
133
+ self.inlet_blocks.append(state_block)
134
+ # Dynamic equivalent to self.properties_inlet_1 = stateblock
135
+ setattr(self,"properties_" + name, state_block)
136
+ # also add the port
137
+ self.add_port(name=name,block=state_block)
138
+
139
+
140
+ num_outlets = self.config.num_outlets
141
+ self.outlet_list = [ "outlet_" + str(i+1) for i in range(num_outlets) ]
142
+ self.priority = [i for i in range(num_outlets)]
143
+ self.outlet_set = Set(initialize=self.outlet_list)
144
+
145
+
146
+ self.outlet_blocks = []
147
+ for name in self.outlet_list:
148
+ # add properties_outlet_1, properties_outlet2 etc
149
+ state_block = self.config.property_package.state_block_class(
150
+ self.flowsheet().config.time, doc="outlet power", **tmp_dict
151
+ )
152
+ self.outlet_blocks.append(state_block)
153
+ # Dynamic equivalent to self.properties_outlet_1 = stateblock
154
+ setattr(self,"properties_" + name, state_block)
155
+ # also add the port
156
+ self.add_port(name=name,block=state_block)
157
+
158
+ # Add variable for power splitting
159
+ self.priorities= Var(
160
+ self.flowsheet().time,
161
+ self.outlet_set,
162
+ initialize=1.0,
163
+ units = pyunits.W,
164
+ doc="How the power is split between outlets depending on priority",
165
+ )
166
+ #Add array to store power demand at each outlet
167
+ self.demand = []
168
+
169
+ self.available = Var(
170
+ self.flowsheet().time,
171
+ self.outlet_list,
172
+ initialize = 1.0,
173
+ units = pyunits.dimensionless
174
+ )
175
+
176
+ #Expression for total power supply:
177
+ @self.Expression(
178
+ self.flowsheet().time
179
+ )
180
+ def total_power(b, t):
181
+ return (
182
+ sum(
183
+ state_block[t].power for state_block in self.inlet_blocks
184
+ )
185
+ )
186
+
187
+ #Expression for total power demand:
188
+ @self.Expression(
189
+ self.flowsheet().time
190
+ )
191
+ def total_power_demand(b, t):
192
+ return (
193
+ sum(
194
+ state_block[t].power for state_block in self.outlet_blocks
195
+ )
196
+ )
197
+
198
+ #Constraint for available power:
199
+ @self.Constraint(
200
+ self.flowsheet().time,
201
+ self.outlet_list,
202
+ doc="expression for calculating available power"
203
+ )
204
+ def eq_available_power(b,t,o):
205
+ p = self.outlet_list.index(o)
206
+ if o == self.outlet_list[0]:
207
+ return self.available[t,o] == self.total_power[t]
208
+ else:
209
+ outlet_block = getattr(self,"properties_" + self.outlet_list[p-1])
210
+ return self.available[t,o] == self.available[t,self.outlet_list[p-1]] - outlet_block[t].power
211
+
212
+
213
+ @self.Constraint(
214
+ self.flowsheet().time,
215
+ self.outlet_list,
216
+ doc = "power out"
217
+ )
218
+ def eq_power_out(b,t,o):
219
+ outlet_block = getattr(self,"properties_" + o)
220
+ if o == self.outlet_list[-1]:
221
+ return outlet_block[t].power == self.available[t,o]
222
+ else:
223
+ return outlet_block[t].power == smooth_min(self.priorities[t,o], self.available[t,o])
224
+
225
+ @self.Constraint(
226
+ self.flowsheet().time,
227
+ doc = "Power at last outlet"
228
+ )
229
+ def eq_last_outlet(b,t):
230
+ return self.priorities[t, self.outlet_list[-1]] == self.available[t,self.outlet_list[-1]]
231
+
232
+
233
+ def calculate_scaling_factors(self):
234
+ super().calculate_scaling_factors()
235
+
236
+
237
+ def initialize(blk, *args, **kwargs):
238
+ for t in blk.flowsheet().time:
239
+ power_in = 0
240
+ for state_block in blk.inlet_blocks:
241
+ power_in += state_block[t].power.value
242
+
243
+
244
+ def _get_stream_table_contents(self, time_point=0):
245
+ """
246
+ Assume unit has standard configuration of 1 inlet and 1 outlet.
247
+
248
+ Developers should overload this as appropriate.
249
+ """
250
+
251
+ io_dict = {}
252
+ for inlet_name in self.inlet_list:
253
+ io_dict[inlet_name] = getattr(self, inlet_name) # get a reference to the port
254
+
255
+ io_dict = {}
256
+ for outlet_name in self.outlet_list:
257
+ io_dict[outlet_name] = getattr(self, outlet_name)
@@ -0,0 +1,253 @@
1
+ # Import Pyomo tools
2
+ from pyomo.environ import (
3
+ Constraint,
4
+ Var,
5
+ Param,
6
+ Expression,
7
+ Reals,
8
+ NonNegativeReals,
9
+ Suffix,
10
+ )
11
+ from pyomo.environ import units as pyunits
12
+ from pyomo.common.config import ConfigBlock, ConfigValue, Bool
13
+
14
+ # Import IDAES cores
15
+ from idaes.core import (
16
+ declare_process_block_class,
17
+ MaterialFlowBasis,
18
+ PhysicalParameterBlock,
19
+ StateBlockData,
20
+ StateBlock,
21
+ MaterialBalanceType,
22
+ EnergyBalanceType,
23
+ )
24
+ from idaes.core.base.components import Component
25
+ from idaes.core.base.phases import LiquidPhase
26
+ from idaes.core.util.initialization import (
27
+ fix_state_vars,
28
+ revert_state_vars,
29
+ solve_indexed_blocks,
30
+ )
31
+ from idaes.core.base.process_base import ProcessBlockData
32
+ from idaes.core.base import property_meta
33
+ from idaes.core.util.model_statistics import (
34
+ degrees_of_freedom,
35
+ number_unfixed_variables,
36
+ )
37
+ from idaes.core.util.exceptions import PropertyPackageError
38
+ import idaes.core.util.scaling as iscale
39
+ import idaes.logger as idaeslog
40
+ from idaes.core.solvers import get_solver
41
+
42
+ # Set up logger
43
+ _log = idaeslog.getLogger(__name__)
44
+
45
+
46
+ # STEP 2
47
+
48
+ # When using this file the name "PowerParameterBlock" is what is imported
49
+ @declare_process_block_class("PowerParameterBlock")
50
+ class PowerParameterData(PhysicalParameterBlock):
51
+ CONFIG = ProcessBlockData.CONFIG()
52
+ CONFIG.declare(
53
+ "default_arguments",
54
+ ConfigBlock(
55
+ implicit=True, description="Default arguments to use with Property Package"
56
+ ),
57
+ )
58
+
59
+ def build(self):
60
+ """
61
+ Callable method for Block construction.
62
+ """
63
+ super(PowerParameterData, self).build()
64
+
65
+ self._state_block_class = PowerStateBlock
66
+
67
+ # Variables
68
+ self.power = Var(initialize=0, domain=Reals, units=pyunits.W)
69
+
70
+ # Default scaling values should be provided so that our tools can ensure the model is well-scaled
71
+ # Generally scaling factors should be such that if it is multiplied by the variable it will range between 0.01 and 100
72
+ self.set_default_scaling("power", 1e-3)
73
+
74
+ @classmethod
75
+ def define_metadata(cls, obj):
76
+ # see https://github.com/watertap-org/watertap/blob/main/tutorials/creating_a_simple_property_model.ipynb
77
+ obj.add_properties(
78
+ {
79
+ "power": {"method": None},
80
+ }
81
+ )
82
+ obj.add_default_units(
83
+ {
84
+ "time": pyunits.s,
85
+ "length": pyunits.m,
86
+ "mass": pyunits.kg,
87
+ }
88
+ )
89
+
90
+ def build_state_block(self, *args, **kwargs):
91
+ """
92
+ Methods to construct a StateBlock associated with this
93
+ PhysicalParameterBlock. This will automatically set the parameters
94
+ construction argument for the StateBlock.
95
+
96
+ Returns:
97
+ StateBlock
98
+
99
+ """
100
+ # default = kwargs.pop("default", {})
101
+ initialize = kwargs.pop("initialize", {})
102
+
103
+ if initialize == {}:
104
+ kwargs["parameters"] = self
105
+ else:
106
+ for i in initialize.keys():
107
+ initialize[i]["parameters"] = self
108
+
109
+ return self.state_block_class( # pylint: disable=not-callable
110
+ *args, **kwargs, initialize=initialize
111
+ )
112
+
113
+ @property
114
+ def state_block_class(self):
115
+ if self._state_block_class is not None:
116
+ return self._state_block_class
117
+ else:
118
+ raise AttributeError(
119
+ "{} has not assigned a StateBlock class to be associated "
120
+ "with this property package. Please contact the developer of "
121
+ "the property package.".format(self.name)
122
+ )
123
+
124
+ # STEP 3: State Block
125
+ class _PowerStateBlock(StateBlock):
126
+ def initialize(
127
+ self,
128
+ state_args=None,
129
+ state_vars_fixed=False,
130
+ hold_state=False,
131
+ outlvl=idaeslog.NOTSET,
132
+ solver=None,
133
+ optarg=None,
134
+ ):
135
+ """
136
+ Initialization routine for property package.
137
+ Keyword Arguments:
138
+ state_args : Dictionary with initial guesses for the state vars
139
+ chosen. Note that if this method is triggered
140
+ through the control volume, and if initial guesses
141
+ were not provided at the unit model level, the
142
+ control volume passes the inlet values as initial
143
+ guess.The keys for the state_args dictionary are:
144
+
145
+ flow_mass_phase_comp : value at which to initialize
146
+ phase component flows
147
+ pressure : value at which to initialize pressure
148
+ temperature : value at which to initialize temperature
149
+
150
+ state_vars_fixed: Flag to denote if state vars have already been
151
+ fixed.
152
+ - True - states have already been fixed by the
153
+ control volume 1D. Control volume 0D
154
+ does not fix the state vars, so will
155
+ be False if this state block is used
156
+ with 0D blocks.
157
+ - False - states have not been fixed. The state
158
+ block will deal with fixing/unfixing.
159
+ hold_state : flag indicating whether the initialization routine
160
+ should unfix any state variables fixed during
161
+ initialization (default=False).
162
+ - True - states variables are not unfixed, and
163
+ a dict of returned containing flags for
164
+ which states were fixed during
165
+ initialization.
166
+ - False - state variables are unfixed after
167
+ initialization by calling the
168
+ release_state method
169
+ outlvl : sets output level of initialization routine (default=idaeslog.NOTSET)
170
+ solver : Solver object to use during initialization if None is provided
171
+ it will use the default solver for IDAES (default = None)
172
+ optarg : solver options dictionary object (default=None)
173
+ Returns:
174
+ If hold_states is True, returns a dict containing flags for
175
+ which states were fixed during initialization.
176
+ """
177
+
178
+ # Fix state variables
179
+ flags = fix_state_vars(self, state_args)
180
+ # Check that dof = 0 when state variables are fixed
181
+ for k in self.keys():
182
+ dof = degrees_of_freedom(self[k])
183
+ if dof != 0:
184
+ raise PropertyPackageError(
185
+ "\nWhile initializing {sb_name}, the degrees of freedom "
186
+ "are {dof}, when zero is required. \nInitialization assumes "
187
+ "that the state variables should be fixed and that no other "
188
+ "variables are fixed. \nIf other properties have a "
189
+ "predetermined value, use the calculate_state method "
190
+ "before using initialize to determine the values for "
191
+ "the state variables and avoid fixing the property variables."
192
+ "".format(sb_name=self.name, dof=dof)
193
+ )
194
+
195
+ # If input block, return flags, else release state
196
+ if state_vars_fixed is False:
197
+ if hold_state is True:
198
+ return flags
199
+ else:
200
+ self.release_state(flags)
201
+
202
+ def release_state(self, flags, outlvl=idaeslog.NOTSET):
203
+ """
204
+ Method to release state variables fixed during initialisation.
205
+
206
+ Keyword Arguments:
207
+ flags : dict containing information of which state variables
208
+ were fixed during initialization, and should now be
209
+ unfixed. This dict is returned by initialize if
210
+ hold_state=True.
211
+ outlvl : sets output level of of logging
212
+ """
213
+ if flags is None:
214
+ return
215
+ # Unfix state variables
216
+ for attr in flags:
217
+ if flags[attr] is True:
218
+ getattr(self, attr).unfix()
219
+ return
220
+
221
+ # STEP 4:
222
+ @declare_process_block_class("PowerStateBlock", block_class=_PowerStateBlock)
223
+ class PowerStateBlockData(StateBlockData):
224
+ def build(self):
225
+ """Callable method for Block construction."""
226
+ super(PowerStateBlockData, self).build()
227
+
228
+ self.scaling_factor = Suffix(direction=Suffix.EXPORT)
229
+
230
+ self.power = Var(
231
+ initialize=0,
232
+ domain=Reals,
233
+ units=pyunits.W,
234
+ doc="Power flow",
235
+ )
236
+
237
+ # -----------------------------------------------------------------------------
238
+
239
+ def define_state_vars(self):
240
+ """Define state vars."""
241
+ return {
242
+ "power": self.power,
243
+ }
244
+
245
+ # -----------------------------------------------------------------------------
246
+ # Scaling methods
247
+ def calculate_scaling_factors(self):
248
+ super().calculate_scaling_factors()
249
+ # This doesn't do anything, but it's a good example of how to get and set scaling factors in relation to each other.
250
+ sf = iscale.get_scaling_factor(self.power)
251
+ iscale.set_scaling_factor(self.power, sf)
252
+
253
+
@@ -0,0 +1,176 @@
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
+
9
+ # Import IDAES cores
10
+ from idaes.core import (
11
+ declare_process_block_class,
12
+ UnitModelBlockData,
13
+ useDefault,
14
+ )
15
+ from idaes.core.util.config import is_physical_parameter_block
16
+ import idaes.core.util.scaling as iscale
17
+ import idaes.logger as idaeslog
18
+
19
+
20
+ from idaes.core.util.tables import create_stream_table_dataframe
21
+
22
+ from idaes.core.util.exceptions import ConfigurationError
23
+
24
+
25
+
26
+ # Set up logger
27
+ _log = idaeslog.getLogger(__name__)
28
+
29
+
30
+ # When using this file the name "Solar" is what is imported
31
+ @declare_process_block_class("Solar")
32
+ class SolarData(UnitModelBlockData):
33
+ """
34
+ Zero order Link model
35
+ """
36
+
37
+ # CONFIG are options for the unit model, this simple model only has the mandatory config options
38
+ CONFIG = ConfigBlock()
39
+
40
+ CONFIG.declare(
41
+ "dynamic",
42
+ ConfigValue(
43
+ domain=In([False]),
44
+ default=False,
45
+ description="Dynamic model flag - must be False",
46
+ doc="""Indicates whether this model will be dynamic or not,
47
+ **default** = False. The Bus unit does not support dynamic
48
+ behavior, thus this must be False.""",
49
+ ),
50
+ )
51
+ CONFIG.declare(
52
+ "has_holdup",
53
+ ConfigValue(
54
+ default=False,
55
+ domain=In([False]),
56
+ description="Holdup construction flag - must be False",
57
+ doc="""Indicates whether holdup terms should be constructed or not.
58
+ **default** - False. The Bus unit does not have defined volume, thus
59
+ this must be False.""",
60
+ ),
61
+ )
62
+ CONFIG.declare(
63
+ "property_package",
64
+ ConfigValue(
65
+ default=useDefault,
66
+ domain=is_physical_parameter_block,
67
+ description="Property package to use for control volume",
68
+ doc="""Property parameter object used to define property calculations,
69
+ **default** - useDefault.
70
+ **Valid values:** {
71
+ **useDefault** - use default package from parent model or flowsheet,
72
+ **PhysicalParameterObject** - a PhysicalParameterBlock object.}""",
73
+ ),
74
+ )
75
+ CONFIG.declare(
76
+ "property_package_args",
77
+ ConfigBlock(
78
+ implicit=True,
79
+ description="Arguments to use for constructing property packages",
80
+ doc="""A ConfigBlock with arguments to be passed to a property block(s)
81
+ and used when constructing these,
82
+ **default** - None.
83
+ **Valid values:** {
84
+ see property package for documentation.}""",
85
+ ),
86
+ )
87
+
88
+
89
+
90
+ def build(self):
91
+ # build always starts by calling super().build()
92
+ # This triggers a lot of boilerplate in the background for you
93
+ super().build()
94
+
95
+ # This creates blank scaling factors, which are populated later
96
+ self.scaling_factor = Suffix(direction=Suffix.EXPORT)
97
+
98
+
99
+ # Add state blocks for inlet, outlet, and waste
100
+ # These include the state variables and any other properties on demand
101
+ # Add inlet block
102
+ tmp_dict = dict(**self.config.property_package_args)
103
+ tmp_dict["parameters"] = self.config.property_package
104
+ #tmp_dict["defined_state"] = True # inlet block is an inlet
105
+
106
+ # Add outlet
107
+ tmp_dict["defined_state"] = False # outlet is not an inlet
108
+ self.properties_out = self.config.property_package.state_block_class(
109
+ self.flowsheet().config.time,
110
+ doc="Material properties of outlet",
111
+ **tmp_dict
112
+ )
113
+
114
+ # Add ports - oftentimes users interact with these rather than the state blocks
115
+
116
+ self.add_port(name="outlet", block=self.properties_out)
117
+
118
+ # Pyomo variables:
119
+
120
+ # Add variable for efficiency
121
+ self.efficiency = Var(
122
+ initialize=1.0,
123
+ doc="Efficiency of the panel",
124
+ )
125
+
126
+ # Add variable for solar irradiation
127
+ self.irradiation = Var(self.flowsheet().config.time,
128
+ initialize=1.0, units = pyunits.W/pyunits.m**2,
129
+ doc="Amount of sunlight hitting the panel",
130
+ )
131
+
132
+ # Add variable for area
133
+ self.area = Var(
134
+ initialize=1.0, units = pyunits.m**2,
135
+ doc="Size of the panel",
136
+ )
137
+
138
+ # Add variable for no. of solar panels
139
+ self.panel_count = Var(
140
+ initialize=1.0,
141
+ doc="Number of solar panels",
142
+ )
143
+
144
+ # Add constraints
145
+ @self.Constraint(
146
+ self.flowsheet().time,
147
+ doc="Power usage",
148
+ )
149
+ def eq_power_balance(b, t):
150
+ return (
151
+ self.irradiation[t] * self.efficiency * self.area * self.panel_count == b.properties_out[t].power
152
+ )
153
+
154
+ def calculate_scaling_factors(self):
155
+ super().calculate_scaling_factors()
156
+
157
+ def initialize(blk, *args, **kwargs):
158
+ # Just propagate the power from inlet to outlet, good simple method of initialization
159
+ pass
160
+
161
+ def _get_stream_table_contents(self, time_point=0):
162
+ """
163
+ Assume unit has standard configuration of 1 inlet and 1 outlet.
164
+
165
+ Developers should overload this as appropriate.
166
+ """
167
+ try:
168
+ return create_stream_table_dataframe(
169
+ {"Outlet": self.outlet}, time_point=time_point
170
+ )
171
+ except AttributeError:
172
+ raise ConfigurationError(
173
+ f"Unit model {self.name} does not have the standard Port "
174
+ f"names (inlet and outlet). Please contact the unit model "
175
+ f"developer to develop a unit specific stream table."
176
+ )