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