ahuora-builder 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ahuora_builder/__init__.py +0 -0
- ahuora_builder/arc_manager.py +33 -0
- ahuora_builder/build_state.py +57 -0
- ahuora_builder/custom/PIDController.py +494 -0
- ahuora_builder/custom/PySMOModel.py +178 -0
- ahuora_builder/custom/SimpleEffectivenessHX_DH.py +727 -0
- ahuora_builder/custom/__init__.py +0 -0
- ahuora_builder/custom/add_initial_dynamics.py +35 -0
- ahuora_builder/custom/custom_compressor.py +107 -0
- ahuora_builder/custom/custom_cooler.py +33 -0
- ahuora_builder/custom/custom_heat_exchanger.py +183 -0
- ahuora_builder/custom/custom_heat_exchanger_1d.py +258 -0
- ahuora_builder/custom/custom_heater.py +41 -0
- ahuora_builder/custom/custom_pressure_changer.py +34 -0
- ahuora_builder/custom/custom_pump.py +107 -0
- ahuora_builder/custom/custom_separator.py +371 -0
- ahuora_builder/custom/custom_tank.py +133 -0
- ahuora_builder/custom/custom_turbine.py +132 -0
- ahuora_builder/custom/custom_valve.py +300 -0
- ahuora_builder/custom/custom_variable.py +29 -0
- ahuora_builder/custom/direct_steam_injection.py +371 -0
- ahuora_builder/custom/energy/__init__.py +0 -0
- ahuora_builder/custom/energy/acBus.py +280 -0
- ahuora_builder/custom/energy/ac_property_package.py +279 -0
- ahuora_builder/custom/energy/battery.py +170 -0
- ahuora_builder/custom/energy/bus.py +182 -0
- ahuora_builder/custom/energy/energy_mixer.py +195 -0
- ahuora_builder/custom/energy/energy_splitter.py +228 -0
- ahuora_builder/custom/energy/grid.py +173 -0
- ahuora_builder/custom/energy/hydro.py +169 -0
- ahuora_builder/custom/energy/link.py +137 -0
- ahuora_builder/custom/energy/load.py +155 -0
- ahuora_builder/custom/energy/mainDistributionBoard.py +257 -0
- ahuora_builder/custom/energy/power_property_package.py +253 -0
- ahuora_builder/custom/energy/solar.py +176 -0
- ahuora_builder/custom/energy/storage.py +230 -0
- ahuora_builder/custom/energy/storage_wrapper +0 -0
- ahuora_builder/custom/energy/tests/__init__.py +0 -0
- ahuora_builder/custom/energy/tests/test_bus.py +44 -0
- ahuora_builder/custom/energy/tests/test_energy_mixer.py +46 -0
- ahuora_builder/custom/energy/tests/test_mdb.py +49 -0
- ahuora_builder/custom/energy/transformer.py +187 -0
- ahuora_builder/custom/energy/transformer_property_package.py +267 -0
- ahuora_builder/custom/energy/transmissionLine.py +228 -0
- ahuora_builder/custom/energy/wind.py +206 -0
- ahuora_builder/custom/hda_ideal_VLE.py +1341 -0
- ahuora_builder/custom/hda_reaction.py +182 -0
- ahuora_builder/custom/heat_exchanger_1d_wrapper.py +31 -0
- ahuora_builder/custom/integration_block.py +106 -0
- ahuora_builder/custom/inverted.py +81 -0
- ahuora_builder/custom/performance_curves.py +1 -0
- ahuora_builder/custom/reactions/__init__.py +0 -0
- ahuora_builder/custom/reactions/hda_stoich.py +10 -0
- ahuora_builder/custom/simple_separator.py +680 -0
- ahuora_builder/custom/tests/__init__.py +0 -0
- ahuora_builder/custom/tests/test_SimpleEffectivenessHX_DH.py +91 -0
- ahuora_builder/custom/tests/test_custom_tank.py +70 -0
- ahuora_builder/custom/tests/test_direct_steam_injection.py +41 -0
- ahuora_builder/custom/tests/test_simple_separator.py +46 -0
- ahuora_builder/custom/tests/test_waterpipe.py +46 -0
- ahuora_builder/custom/thermal_utility_systems/desuperheater.py +624 -0
- ahuora_builder/custom/thermal_utility_systems/header.py +889 -0
- ahuora_builder/custom/thermal_utility_systems/simple_heat_pump.py +567 -0
- ahuora_builder/custom/thermal_utility_systems/steam_header.py +353 -0
- ahuora_builder/custom/thermal_utility_systems/steam_user.py +944 -0
- ahuora_builder/custom/thermal_utility_systems/temp.py +349 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_desuperheater.py +142 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_header.py +998 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_ntu_hx.py +129 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_simple_heat_pump.py +120 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_steam_header.py +703 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_steam_user.py +277 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_waterpipe.py +36 -0
- ahuora_builder/custom/thermal_utility_systems/tests/test_willans_turbine.py +253 -0
- ahuora_builder/custom/thermal_utility_systems/willans_turbine.py +804 -0
- ahuora_builder/custom/translator.py +129 -0
- ahuora_builder/custom/updated_pressure_changer.py +1404 -0
- ahuora_builder/custom/valve_wrapper.py +38 -0
- ahuora_builder/custom/water_tank_with_units.py +456 -0
- ahuora_builder/diagnostics/__init__.py +0 -0
- ahuora_builder/diagnostics/infeasibilities.py +40 -0
- ahuora_builder/diagnostics/tests/__init__.py +0 -0
- ahuora_builder/diagnostics/tests/test_infeasibilities.py +28 -0
- ahuora_builder/flowsheet_manager.py +542 -0
- ahuora_builder/flowsheet_manager_type.py +20 -0
- ahuora_builder/generate_python_file.py +440 -0
- ahuora_builder/methods/BlockContext.py +84 -0
- ahuora_builder/methods/__init__.py +0 -0
- ahuora_builder/methods/adapter.py +355 -0
- ahuora_builder/methods/adapter_library.py +549 -0
- ahuora_builder/methods/adapter_methods.py +80 -0
- ahuora_builder/methods/expression_parsing.py +105 -0
- ahuora_builder/methods/load_unit_model.py +147 -0
- ahuora_builder/methods/slice_manipulation.py +7 -0
- ahuora_builder/methods/tests/__init__.py +0 -0
- ahuora_builder/methods/tests/test_expression_parsing.py +15 -0
- ahuora_builder/methods/units_handler.py +129 -0
- ahuora_builder/ml_wizard.py +101 -0
- ahuora_builder/port_manager.py +20 -0
- ahuora_builder/properties_manager.py +44 -0
- ahuora_builder/property_package_manager.py +78 -0
- ahuora_builder/solver.py +38 -0
- ahuora_builder/tear_manager.py +98 -0
- ahuora_builder/tests/__init__.py +0 -0
- ahuora_builder/tests/test_generate_python_file/__init__.py +0 -0
- ahuora_builder/tests/test_generate_python_file/configurations/compressor_generated.py +63 -0
- ahuora_builder/tests/test_generate_python_file/configurations/heat_exchanger_generated.py +70 -0
- ahuora_builder/tests/test_generate_python_file/configurations/pump_generated.py +84 -0
- ahuora_builder/tests/test_generate_python_file/configurations/recycle_generated.py +73 -0
- ahuora_builder/tests/test_generate_python_file/test_generate_python_file.py +108 -0
- ahuora_builder/tests/test_solver/__init__.py +0 -0
- ahuora_builder/tests/test_solver/configurations/BT_PR.json +59 -0
- ahuora_builder/tests/test_solver/configurations/BT_PR_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/bus.json +99 -0
- ahuora_builder/tests/test_solver/configurations/bus_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/compound_separator.json +377 -0
- ahuora_builder/tests/test_solver/configurations/compound_separator_solved.json +374 -0
- ahuora_builder/tests/test_solver/configurations/compressor.json +38 -0
- ahuora_builder/tests/test_solver/configurations/compressor_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/constraints.json +44 -0
- ahuora_builder/tests/test_solver/configurations/constraints_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/control.json +39 -0
- ahuora_builder/tests/test_solver/configurations/control_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/dynamic_tank.json +733 -0
- ahuora_builder/tests/test_solver/configurations/dynamic_tank_solved.json +846 -0
- ahuora_builder/tests/test_solver/configurations/elimination.json +39 -0
- ahuora_builder/tests/test_solver/configurations/elimination_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/expressions.json +68 -0
- ahuora_builder/tests/test_solver/configurations/expressions_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/header.json +1192 -0
- ahuora_builder/tests/test_solver/configurations/header_solved.json +761 -0
- ahuora_builder/tests/test_solver/configurations/heat_exchanger.json +63 -0
- ahuora_builder/tests/test_solver/configurations/heat_exchanger_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/heat_pump.json +137 -0
- ahuora_builder/tests/test_solver/configurations/heat_pump_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/machine_learning.json +2156 -0
- ahuora_builder/tests/test_solver/configurations/machine_learning_solved.json +266 -0
- ahuora_builder/tests/test_solver/configurations/mass_flow_tear.json +77 -0
- ahuora_builder/tests/test_solver/configurations/mass_flow_tear_solved.json +68 -0
- ahuora_builder/tests/test_solver/configurations/milk_heater.json +521 -0
- ahuora_builder/tests/test_solver/configurations/milk_heater_solved.json +311 -0
- ahuora_builder/tests/test_solver/configurations/mixer.json +44 -0
- ahuora_builder/tests/test_solver/configurations/mixer_solved.json +86 -0
- ahuora_builder/tests/test_solver/configurations/optimization.json +62 -0
- ahuora_builder/tests/test_solver/configurations/optimization_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/propane_heat_pump.json +167 -0
- ahuora_builder/tests/test_solver/configurations/propane_heat_pump_solved.json +158 -0
- ahuora_builder/tests/test_solver/configurations/propane_recycle.json +141 -0
- ahuora_builder/tests/test_solver/configurations/propane_recycle_solved.json +104 -0
- ahuora_builder/tests/test_solver/configurations/pump.json +64 -0
- ahuora_builder/tests/test_solver/configurations/pump_solved.json +59 -0
- ahuora_builder/tests/test_solver/configurations/pump_unit_conversions.json +63 -0
- ahuora_builder/tests/test_solver/configurations/recycle.json +49 -0
- ahuora_builder/tests/test_solver/configurations/recycle_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/sb_vapor_frac.json +29 -0
- ahuora_builder/tests/test_solver/configurations/sb_vapor_frac_solved.json +29 -0
- ahuora_builder/tests/test_solver/configurations/solar.json +67 -0
- ahuora_builder/tests/test_solver/configurations/solar_solved.json +50 -0
- ahuora_builder/tests/test_solver/configurations/vapor_frac_target.json +67 -0
- ahuora_builder/tests/test_solver/configurations/vapor_frac_target_solved.json +68 -0
- ahuora_builder/tests/test_solver/test_solve_models.py +250 -0
- ahuora_builder/timing.py +65 -0
- ahuora_builder/types/__init__.py +1 -0
- ahuora_builder/unit_model_manager.py +48 -0
- ahuora_builder-0.1.0.dist-info/METADATA +14 -0
- ahuora_builder-0.1.0.dist-info/RECORD +167 -0
- ahuora_builder-0.1.0.dist-info/WHEEL +4 -0
ahuora_builder/solver.py
ADDED
|
@@ -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
|
|
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
|
+
}
|