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,38 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+ from ahuora_builder_types import FlowsheetSchema
4
+ from ahuora_builder_types.flowsheet_schema import SolvedFlowsheetSchema
5
+ from ahuora_builder_types.payloads.solve_request_schema import IdaesSolveRequestPayload
6
+ from .flowsheet_manager import FlowsheetManager
7
+
8
+
9
+ class SolveModelResult(BaseModel):
10
+ input_flowsheet: FlowsheetSchema
11
+ output_flowsheet: SolvedFlowsheetSchema
12
+ solve_index: Optional[int] = None
13
+ scenario_id: Optional[int] = None
14
+ task_id: int
15
+ timing: dict
16
+
17
+ def solve_model(solve_request: IdaesSolveRequestPayload) -> SolveModelResult:
18
+ """Solves the model and returns the results"""
19
+ flowsheet = FlowsheetManager(solve_request.flowsheet)
20
+ #print(solve_request.flowsheet.model_dump_json())
21
+ flowsheet.load()
22
+ flowsheet.initialise()
23
+ flowsheet.report_statistics()
24
+ if solve_request.perform_diagnostics:
25
+ flowsheet.diagnose_problems()
26
+ flowsheet.check_model_valid()
27
+ flowsheet.solve()
28
+ flowsheet.optimize()
29
+ result = flowsheet.serialise()
30
+
31
+ return SolveModelResult(
32
+ input_flowsheet=solve_request.flowsheet,
33
+ output_flowsheet=result,
34
+ solve_index=solve_request.solve_index,
35
+ scenario_id=solve_request.scenario_id,
36
+ task_id=solve_request.task_id,
37
+ timing=flowsheet.timing.close()
38
+ )
@@ -0,0 +1,98 @@
1
+ from pyomo.environ import value
2
+ from pyomo.network import Arc
3
+ from idaes.core.util.tables import _get_state_from_port
4
+ from pyomo.core.base.expression import ScalarExpression
5
+ from ahuora_builder_types.arc_schema import TearGuessSchema
6
+ from .flowsheet_manager_type import FlowsheetManager
7
+ from ahuora_builder_types import PortId
8
+ from .methods.adapter import fix_var
9
+
10
+
11
+ class TearManager:
12
+ """
13
+ Manages the tears in the flowsheet
14
+ """
15
+
16
+ def __init__(self, flowsheet_manager: FlowsheetManager):
17
+ """
18
+ Create a new tear manager
19
+ """
20
+ self._flowsheet_manager = flowsheet_manager
21
+ self._tears: list[Arc] = []
22
+
23
+ def load(self):
24
+ """
25
+ Load all the tears (from the recycle unitops)
26
+ """
27
+ schema = self._flowsheet_manager.schema
28
+
29
+ for arc_schema in schema.arcs:
30
+ if arc_schema.tear_guess:
31
+ self.add_tear(arc_schema)
32
+
33
+ def add_tear(self, arc_schema):
34
+ """
35
+ Add a tear to the flowsheet
36
+ """
37
+ portId = arc_schema.destination
38
+ guess = arc_schema.tear_guess
39
+
40
+ port = self._flowsheet_manager.ports.get_port(portId)
41
+ arc = port.arcs()[0]
42
+ self._tears.append(arc)
43
+
44
+ """
45
+ During model loading, we add in all the constraints and fix the variables
46
+ for this port. Since it is a tear, things need to be deactivated/unfixed
47
+ to ensure 0 degrees of freedom
48
+
49
+ If the state block is defined by constraints, we need to solve it to get the
50
+ correct values for the state variables. Then we deactivate the constraints
51
+ since we will be fixing the state variables instead (where applicable).
52
+ """
53
+
54
+ # hardcoding time indexes to [0] for now
55
+ time_indexes = [0]
56
+
57
+ # need to get the correct value for state variables before running
58
+ # sequential decomposition (since all state variables are fixed
59
+ # during sequential decomposition).
60
+ sb = _get_state_from_port(port, time_indexes[0])
61
+ # deactivate any guesses
62
+ for key, value in guess.items():
63
+ var = getattr(sb, key)
64
+ if value != True:
65
+ if isinstance(var,ScalarExpression):
66
+ pass # we want to solve with constraints
67
+ else:
68
+ # this might give too few dof if we have other constraints. We need to unfix
69
+ # this value
70
+ var.unfix()
71
+
72
+ if len(list(sb.constraints.component_objects())) > 0:
73
+ # state block is defined by some constraints, so we need to solve it.
74
+ # initialize the state block, which should put the correct value in
75
+ # the state variables
76
+ sb.parent_component().initialize()
77
+ # deactivate the state block constraints, since we should use the
78
+ # state variables as guesses (or fix them instead)
79
+ sb.constraints.deactivate()
80
+
81
+ blk = sb.parent_component()
82
+ for key in sb.define_state_vars():
83
+ if guess.get(key, False):
84
+ # deactivate the equality constraint (expanded arcs)
85
+ expanded_arc = getattr(
86
+ self._flowsheet_manager.model.fs, arc._name + "_expanded"
87
+ )
88
+ equality_constraint = getattr(expanded_arc, key + "_equality")
89
+ equality_constraint.deactivate()
90
+
91
+ # fix the variable
92
+ for b in blk.values():
93
+ getattr(b, key).fix()
94
+
95
+ else:
96
+ # unfix this variable
97
+ for b in blk.values():
98
+ getattr(b, key).unfix()
File without changes
@@ -0,0 +1,63 @@
1
+ ### Imports
2
+ from pyomo.environ import ConcreteModel, SolverFactory, SolverStatus, TerminationCondition, Block, TransformationFactory, assert_optimal_termination
3
+ from pyomo.network import SequentialDecomposition, Port, Arc
4
+ from pyomo.core.base.units_container import _PyomoUnit, units as pyomo_units
5
+ from idaes.core import FlowsheetBlock
6
+ from idaes.core.util.model_statistics import report_statistics, degrees_of_freedom
7
+ from idaes.core.util.tables import _get_state_from_port
8
+ import idaes.logger as idaeslog
9
+ from property_packages.build_package import build_package
10
+ from ahuora_builder.custom.custom_compressor import CustomCompressor
11
+
12
+
13
+ ### Utility Methods
14
+ def units(item: str) -> _PyomoUnit:
15
+ ureg = pyomo_units._pint_registry
16
+ pint_unit = getattr(ureg, item)
17
+ return _PyomoUnit(pint_unit, ureg)
18
+
19
+
20
+ ### Build Model
21
+ m = ConcreteModel()
22
+ m.fs = FlowsheetBlock(dynamic=False)
23
+
24
+ # Set up property packages
25
+ m.fs.PP__1 = build_package(
26
+ "helmholtz",
27
+ ["h2o"],
28
+ )
29
+
30
+ # Create unit models
31
+
32
+ # Compressor-1
33
+ m.fs.Compressor_1 = CustomCompressor(
34
+ property_package=m.fs.PP__1,
35
+ power_property_package=m.fs.power_property_package,
36
+ dynamic=False
37
+ )
38
+ m.fs.Compressor_1.deltaP.fix(50000.0 * units("Pa"))
39
+ m.fs.Compressor_1.efficiency_isentropic.fix(0.9 * units("None"))
40
+ sb = _get_state_from_port(m.fs.Compressor_1.inlet, 0)
41
+ sb.constrain_component(sb.pressure, 101325.0 * units("Pa"))
42
+ sb.constrain_component(sb.enth_mol, 4000.0 * units("J/mol"))
43
+ sb.constrain_component(sb.flow_mol, 100.0 * units("mol/s"))
44
+ sb.constrain_component(sb.mole_frac_comp[('h2o',)], 1.0 * units("None"))
45
+
46
+
47
+ ### Check Model Status
48
+ report_statistics(m)
49
+ print("Degrees of freedom:", degrees_of_freedom(m))
50
+
51
+
52
+ ### Initialize Model
53
+ m.fs.Compressor_1.initialize(outlvl=idaeslog.INFO)
54
+
55
+
56
+ ### Solve
57
+ opt = SolverFactory("ipopt")
58
+ res = opt.solve(m, tee=True)
59
+ assert_optimal_termination(res)
60
+
61
+
62
+ ### Report
63
+ m.fs.Compressor_1.report()
@@ -0,0 +1,70 @@
1
+ ### Imports
2
+ from pyomo.environ import ConcreteModel, SolverFactory, SolverStatus, TerminationCondition, Block, TransformationFactory, assert_optimal_termination
3
+ from pyomo.network import SequentialDecomposition, Port, Arc
4
+ from pyomo.core.base.units_container import _PyomoUnit, units as pyomo_units
5
+ from idaes.core import FlowsheetBlock
6
+ from idaes.core.util.model_statistics import report_statistics, degrees_of_freedom
7
+ from idaes.core.util.tables import _get_state_from_port
8
+ import idaes.logger as idaeslog
9
+ from idaes.models.unit_models.heat_exchanger import delta_temperature_underwood_callback
10
+ from property_packages.build_package import build_package
11
+ from ahuora_builder.custom.custom_heat_exchanger import CustomHeatExchanger
12
+
13
+
14
+ ### Utility Methods
15
+ def units(item: str) -> _PyomoUnit:
16
+ ureg = pyomo_units._pint_registry
17
+ pint_unit = getattr(ureg, item)
18
+ return _PyomoUnit(pint_unit, ureg)
19
+
20
+
21
+ ### Build Model
22
+ m = ConcreteModel()
23
+ m.fs = FlowsheetBlock(dynamic=False)
24
+
25
+ # Set up property packages
26
+ m.fs.PP__1 = build_package(
27
+ "helmholtz",
28
+ ["h2o"],
29
+ )
30
+
31
+ # Create unit models
32
+
33
+ # heat_exchanger-1
34
+ m.fs.heat_exchanger_1 = CustomHeatExchanger(
35
+ delta_temperature_callback=delta_temperature_underwood_callback,
36
+ hot_side={"property_package": m.fs.PP__1,"has_pressure_change": True},
37
+ cold_side={"property_package": m.fs.PP__1,"has_pressure_change": True},
38
+ dynamic=False
39
+ )
40
+ m.fs.heat_exchanger_1.overall_heat_transfer_coefficient.fix(100.0 * units("W/(m**2*K)"))
41
+ m.fs.heat_exchanger_1.area.fix(1000.0 * units("m**2"))
42
+ m.fs.heat_exchanger_1.hot_side.deltaP.fix(100.0 * units("Pa"))
43
+ m.fs.heat_exchanger_1.cold_side.deltaP.fix(100.0 * units("Pa"))
44
+ sb = _get_state_from_port(m.fs.heat_exchanger_1.hot_side_inlet, 0)
45
+ sb.constrain_component(sb.pressure, 101325.0 * units("Pa"))
46
+ sb.constrain_component(sb.enth_mol, 40000.0 * units("J/mol"))
47
+ sb.constrain_component(sb.flow_mol, 100.0 * units("mol/s"))
48
+ sb = _get_state_from_port(m.fs.heat_exchanger_1.cold_side_inlet, 0)
49
+ sb.constrain_component(sb.pressure, 101325.0 * units("Pa"))
50
+ sb.constrain_component(sb.enth_mol, 30000.0 * units("J/mol"))
51
+ sb.constrain_component(sb.flow_mol, 100.0 * units("mol/s"))
52
+
53
+
54
+ ### Check Model Status
55
+ report_statistics(m)
56
+ print("Degrees of freedom:", degrees_of_freedom(m))
57
+
58
+
59
+ ### Initialize Model
60
+ m.fs.heat_exchanger_1.initialize(outlvl=idaeslog.INFO)
61
+
62
+
63
+ ### Solve
64
+ opt = SolverFactory("ipopt")
65
+ res = opt.solve(m, tee=True)
66
+ assert_optimal_termination(res)
67
+
68
+
69
+ ### Report
70
+ m.fs.heat_exchanger_1.report()
@@ -0,0 +1,84 @@
1
+ ### Imports
2
+ from pyomo.environ import ConcreteModel, SolverFactory, SolverStatus, TerminationCondition, Block, TransformationFactory, assert_optimal_termination
3
+ from pyomo.network import SequentialDecomposition, Port, Arc
4
+ from pyomo.core.base.units_container import _PyomoUnit, units as pyomo_units
5
+ from idaes.core import FlowsheetBlock
6
+ from idaes.core.util.model_statistics import report_statistics, degrees_of_freedom
7
+ from idaes.core.util.tables import _get_state_from_port
8
+ import idaes.logger as idaeslog
9
+ from property_packages.build_package import build_package
10
+ from ahuora_builder.custom.custom_pump import CustomPump
11
+ from ahuora_builder.custom.custom_heater import DynamicHeater
12
+
13
+
14
+ ### Utility Methods
15
+ def units(item: str) -> _PyomoUnit:
16
+ ureg = pyomo_units._pint_registry
17
+ pint_unit = getattr(ureg, item)
18
+ return _PyomoUnit(pint_unit, ureg)
19
+
20
+ def init_unit(unit: Block) -> None:
21
+ unit.initialize(outlvl=idaeslog.INFO)
22
+
23
+
24
+ ### Build Model
25
+ m = ConcreteModel()
26
+ m.fs = FlowsheetBlock(dynamic=False)
27
+
28
+ # Set up property packages
29
+ m.fs.PP__1 = build_package(
30
+ "helmholtz",
31
+ ["h2o"],
32
+ )
33
+
34
+ # Create unit models
35
+
36
+ # Pump-1
37
+ m.fs.Pump_1 = CustomPump(
38
+ property_package=m.fs.PP__1,
39
+ dynamic=False,
40
+ power_property_package=m.fs.power_property_package
41
+ )
42
+ m.fs.Pump_1.efficiency_pump.fix(0.8 * units("dimensionless"))
43
+ sb = _get_state_from_port(m.fs.Pump_1.inlet, 0)
44
+ sb.constrain_component(sb.pressure, 11000000.0 * units("Pa"))
45
+ sb.constrain_component(sb.enth_mol, 125.0 * units("J/mol"))
46
+ sb.constrain_component(sb.flow_mol, 431.0 * units("mol/s"))
47
+ sb = _get_state_from_port(m.fs.Pump_1.outlet, 0)
48
+ sb.constrain_component(sb.pressure, 22000000.0 * units("Pa"))
49
+
50
+ # Heater-1
51
+ m.fs.Heater_1 = DynamicHeater(
52
+ property_package=m.fs.PP__1,
53
+ has_pressure_change=None,
54
+ dynamic=None,
55
+ has_holdup=None
56
+ )
57
+ m.fs.Heater_1.heat_duty.fix(100.0 * units("J/s"))
58
+
59
+
60
+ ### Connect Unit Models
61
+ m.fs.arc_1 = Arc(source=m.fs.Pump_1.outlet, destination=m.fs.Heater_1.inlet)
62
+
63
+
64
+ ### Check Model Status
65
+ report_statistics(m)
66
+ print("Degrees of freedom:", degrees_of_freedom(m))
67
+
68
+
69
+ ### Initialize Model
70
+ TransformationFactory("network.expand_arcs").apply_to(m)
71
+ seq = SequentialDecomposition()
72
+ seq.set_tear_set([])
73
+ seq.run(m, init_unit)
74
+
75
+
76
+ ### Solve
77
+ opt = SolverFactory("ipopt")
78
+ res = opt.solve(m, tee=True)
79
+ assert_optimal_termination(res)
80
+
81
+
82
+ ### Report
83
+ m.fs.Pump_1.report()
84
+ m.fs.Heater_1.report()
@@ -0,0 +1,73 @@
1
+ ### Imports
2
+ from pyomo.environ import ConcreteModel, SolverFactory, SolverStatus, TerminationCondition, Block, TransformationFactory, assert_optimal_termination
3
+ from pyomo.network import SequentialDecomposition, Port, Arc
4
+ from pyomo.core.base.units_container import _PyomoUnit, units as pyomo_units
5
+ from idaes.core import FlowsheetBlock
6
+ from idaes.core.util.model_statistics import report_statistics, degrees_of_freedom
7
+ from idaes.core.util.tables import _get_state_from_port
8
+ import idaes.logger as idaeslog
9
+ from property_packages.build_package import build_package
10
+ from ahuora_builder.custom.custom_compressor import CustomCompressor
11
+
12
+
13
+ ### Utility Methods
14
+ def units(item: str) -> _PyomoUnit:
15
+ ureg = pyomo_units._pint_registry
16
+ pint_unit = getattr(ureg, item)
17
+ return _PyomoUnit(pint_unit, ureg)
18
+
19
+ def init_unit(unit: Block) -> None:
20
+ unit.initialize(outlvl=idaeslog.INFO)
21
+
22
+
23
+ ### Build Model
24
+ m = ConcreteModel()
25
+ m.fs = FlowsheetBlock(dynamic=False)
26
+
27
+ # Set up property packages
28
+ m.fs.PP__1 = build_package(
29
+ "helmholtz",
30
+ ["h2o"],
31
+ )
32
+
33
+ # Create unit models
34
+
35
+ # Compressor-1
36
+ m.fs.Compressor_1 = CustomCompressor(
37
+ property_package=m.fs.PP__1,
38
+ power_property_package=m.fs.power_property_package,
39
+ dynamic=False
40
+ )
41
+ m.fs.Compressor_1.efficiency_isentropic.fix(1.0 * units("dimensionless"))
42
+ sb = _get_state_from_port(m.fs.Compressor_1.inlet, 0)
43
+ sb.constrain_component(sb.flow_mol, 1.0 * units("mol/s"))
44
+ sb.constrain_component(sb.pressure, 105.0 * units("kPa"))
45
+ sb.constrain_component(sb.enth_mol, 10.541 * units("J/mol"))
46
+ sb = _get_state_from_port(m.fs.Compressor_1.outlet, 0)
47
+ sb.constrain_component(sb.pressure, 110.0 * units("kPa"))
48
+
49
+
50
+ ### Connect Unit Models
51
+ m.fs.arc_1 = Arc(source=m.fs.Compressor_1.outlet, destination=m.fs.Compressor_1.inlet)
52
+
53
+
54
+ ### Check Model Status
55
+ report_statistics(m)
56
+ print("Degrees of freedom:", degrees_of_freedom(m))
57
+
58
+
59
+ ### Initialize Model
60
+ TransformationFactory("network.expand_arcs").apply_to(m)
61
+ seq = SequentialDecomposition()
62
+ seq.set_tear_set([])
63
+ seq.run(m, init_unit)
64
+
65
+
66
+ ### Solve
67
+ opt = SolverFactory("ipopt")
68
+ res = opt.solve(m, tee=True)
69
+ assert_optimal_termination(res)
70
+
71
+
72
+ ### Report
73
+ m.fs.Compressor_1.report()
@@ -0,0 +1,108 @@
1
+ from idaes.models.unit_models import Compressor, Heater
2
+ from idaes.models.unit_models.heat_exchanger import delta_temperature_underwood_callback
3
+
4
+ from ahuora_builder_types import FlowsheetSchema
5
+ from ...generate_python_file import generate_python_code, PythonFileGenerator
6
+
7
+ GENERATE_TESTS = False
8
+
9
+ def test_generate_is_false():
10
+ assert GENERATE_TESTS is False, "test_generate_python_file.py: GENERATE_TESTS should be False to avoid overriding test_solved.json files during tests."
11
+
12
+
13
+ def test_import():
14
+ generator = PythonFileGenerator({})
15
+ heater_name, heater_import = generator.resolve_import(Heater)
16
+ assert heater_name == "Heater"
17
+ assert heater_import == "from idaes.models.unit_models.heater import Heater"
18
+ compressor_name, compressor_import = generator.resolve_import(Compressor)
19
+ assert compressor_name == "Compressor"
20
+ assert compressor_import == "from idaes.models.unit_models.pressure_changer import Compressor"
21
+ delta_t_name, delta_t_import = generator.resolve_import(delta_temperature_underwood_callback)
22
+ assert delta_t_name == "delta_temperature_underwood_callback"
23
+ assert delta_t_import == "from idaes.models.unit_models.heat_exchanger import delta_temperature_underwood_callback"
24
+
25
+
26
+ def test_names():
27
+ # names need to be converted to valid python variable names
28
+ generator = PythonFileGenerator({})
29
+ def check_name(name: str, expected: str):
30
+ # starts with m.fs.
31
+ assert generator.get_name(name) == f"m.fs.{expected}"
32
+ check_name("Pump-1", "Pump_1")
33
+ check_name("Heater-1", "Heater_1")
34
+ check_name("heat_exchanger-1", "heat_exchanger_1")
35
+ check_name("name with spaces", "name_with_spaces")
36
+ check_name("name-with-dashes", "name_with_dashes")
37
+ check_name("name_with_underscores", "name_with_underscores")
38
+ check_name("name_with_123_numbers", "name_with_123_numbers")
39
+ check_name("123_numbers_at_start", "_123_numbers_at_start")
40
+ check_name("name_with_!@#$%^&*()_special_characters", "name_with__special_characters")
41
+ check_name("", "_unnamed_unit")
42
+ check_name(" ", "_unnamed_unit")
43
+ check_name("1", "_1")
44
+ check_name(",", "_unnamed_unit")
45
+
46
+
47
+ def run_generate(input_file: str, expected_file: str) -> None:
48
+ import os
49
+ import json
50
+
51
+ with open(os.path.join(os.path.dirname(__file__), input_file), "r") as f:
52
+ flowsheet = json.load(f)
53
+ flowsheet = FlowsheetSchema.model_validate(flowsheet)
54
+
55
+ result = generate_python_code(flowsheet)
56
+
57
+ # # write result to a file for debugging - or to regenerate expected files
58
+ if GENERATE_TESTS:
59
+ with open(os.path.join(os.path.dirname(__file__), expected_file), "w") as f:
60
+ f.write(result)
61
+
62
+
63
+ # split result line by line to compare each line
64
+ lines = result.split("\n")
65
+
66
+ with open(os.path.join(os.path.dirname(__file__), expected_file), "r") as f:
67
+ expected_lines = f.readlines()
68
+
69
+ # compare each line of the result with the expected file
70
+ i = 0
71
+ while len(lines) > 0 and len(expected_lines) > 0:
72
+ # skip empty lines
73
+ while len(lines) > 0 and lines[0].strip() == "":
74
+ lines.pop(0)
75
+ while len(expected_lines) > 0 and expected_lines[0].strip() == "":
76
+ i += 1
77
+ expected_lines.pop(0)
78
+ if len(lines) == 0:
79
+ assert len(expected_lines) == 0, f"Expected '{expected_lines[0]}'"
80
+ break
81
+ if len(expected_lines) == 0:
82
+ assert len(lines) == 0, f"Got '{lines[0]}'"
83
+ break
84
+ # compare lines
85
+ i += 1
86
+ line = lines.pop(0).strip()
87
+ expected_line = expected_lines.pop(0).strip()
88
+ assert line == expected_line, f"Line {str(i)}: Expected '{expected_line}', got '{line}'"
89
+
90
+
91
+ def test_compressor():
92
+ # basic test
93
+ run_generate("../test_solver/configurations/compressor.json", "configurations/compressor_generated.py")
94
+
95
+
96
+ def test_heat_exchanger():
97
+ # tests a few more features (constants, dictionaries)
98
+ run_generate("../test_solver/configurations/heat_exchanger.json", "configurations/heat_exchanger_generated.py")
99
+
100
+
101
+ def test_pump_and_heater():
102
+ # tests connected components
103
+ run_generate("../test_solver/configurations/pump.json", "configurations/pump_generated.py")
104
+
105
+
106
+ def test_recycle():
107
+ # tests tears
108
+ run_generate("../test_solver/configurations/recycle.json", "configurations/recycle_generated.py")
File without changes
@@ -0,0 +1,59 @@
1
+ {
2
+ "id": 0,
3
+ "name": "BT_PR test",
4
+ "description": "Peng-Robinson test with Benzene and Toluene",
5
+ "dynamic": false,
6
+ "property_packages": [
7
+ {
8
+ "id": 1,
9
+ "compounds":["benzene", "toluene"],
10
+ "type":"peng-robinson",
11
+ "phases":["Vap", "Liq"]
12
+ }
13
+ ],
14
+ "unit_models": [
15
+ {
16
+ "id": 9,
17
+ "type": "heater",
18
+ "name": "Heater-1",
19
+ "args": {
20
+ "property_package": 1
21
+ },
22
+ "properties": {
23
+ "heat_duty": { "data": [ { "id": 1, "value": 200000 } ], "unit": "W" }
24
+ },
25
+ "ports": {
26
+ "inlet": {
27
+ "id": 1,
28
+ "properties": {
29
+ "flow_mol": { "data": [ { "id": 2, "value": 431 } ], "unit": "mol/s" },
30
+ "mole_frac_comp": {
31
+ "data": [
32
+ {
33
+ "discrete_indexes":["benzene"],
34
+ "id": 3,
35
+ "value": 0.8
36
+ },
37
+ {
38
+ "discrete_indexes":["toluene"],
39
+ "id": 4,
40
+ "value": 0.2
41
+ }
42
+
43
+ ],
44
+ "unit": null
45
+ },
46
+ "pressure": { "data": [ { "id": 5, "value": 101325 } ], "unit": "Pa" },
47
+ "temperature": { "data": [ { "id": 6, "value": 303.2 } ], "unit": "K" }
48
+ }
49
+ },
50
+ "outlet": {
51
+ "id": 4,
52
+ "properties": {}
53
+ }
54
+ }
55
+ }
56
+ ],
57
+ "arcs": [
58
+ ]
59
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "id": 0,
3
+ "properties": [
4
+ {
5
+ "id": 1,
6
+ "name": "fs.Heater-1_9.control_volume.heat[0.0]",
7
+ "value": [
8
+ 200000.0
9
+ ],
10
+ "unit": "kg*m**2/s**3",
11
+ "unknown_units": false
12
+ },
13
+ {
14
+ "id": 2,
15
+ "name": "fs.Heater-1_9.control_volume.properties_in[0.0].flow_mol",
16
+ "value": [
17
+ 431.0
18
+ ],
19
+ "unit": "mol/s",
20
+ "unknown_units": false
21
+ },
22
+ {
23
+ "id": 3,
24
+ "name": "fs.Heater-1_9.control_volume.properties_in[0.0].mole_frac_comp[benzene]",
25
+ "value": [
26
+ 0.8
27
+ ],
28
+ "unit": "dimensionless",
29
+ "unknown_units": false
30
+ },
31
+ {
32
+ "id": 4,
33
+ "name": "fs.Heater-1_9.control_volume.properties_in[0.0].mole_frac_comp[toluene]",
34
+ "value": [
35
+ 0.2
36
+ ],
37
+ "unit": "dimensionless",
38
+ "unknown_units": false
39
+ },
40
+ {
41
+ "id": 5,
42
+ "name": "fs.Heater-1_9.control_volume.properties_in[0.0].pressure",
43
+ "value": [
44
+ 101325.0
45
+ ],
46
+ "unit": "kg/m/s**2",
47
+ "unknown_units": false
48
+ },
49
+ {
50
+ "id": 6,
51
+ "name": "fs.Heater-1_9.control_volume.properties_in[0.0].temperature",
52
+ "value": [
53
+ 303.2
54
+ ],
55
+ "unit": "K",
56
+ "unknown_units": false
57
+ }
58
+ ]
59
+ }