From 43982e5702c43caf08d1f180de920b0289d2d51c Mon Sep 17 00:00:00 2001 From: Doug A Date: Mon, 23 Sep 2024 16:36:53 -0400 Subject: [PATCH 01/21] pull from MEA development branch --- idaes/models/unit_models/stream_scaler.py | 255 ++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 idaes/models/unit_models/stream_scaler.py diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py new file mode 100644 index 0000000000..f300b1574a --- /dev/null +++ b/idaes/models/unit_models/stream_scaler.py @@ -0,0 +1,255 @@ +################################################################################# +# The Institute for the Design of Advanced Energy Systems Integrated Platform +# Framework (IDAES IP) was produced under the DOE Institute for the +# Design of Advanced Energy Systems (IDAES). +# +# Copyright (c) 2018-2023 by the software owners: The Regents of the +# University of California, through Lawrence Berkeley National Laboratory, +# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon +# University, West Virginia University Research Corporation, et al. +# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md +# for full copyright and license information. +################################################################################# +""" +Unit model to adjust size of streams to represent, for example, a stream being split across several identical units, +which are then all modeled as a single IDAES unit +""" +from enum import Enum +from functools import partial + +from pyomo.environ import ( + Block, + check_optimal_termination, + Param, + PositiveReals, + Reals, + RangeSet, + units as pyunits, + Var, +) +from pyomo.network import Port +from pyomo.common.config import ConfigBlock, ConfigValue, In, ListOf, Bool + +from idaes.core import ( + declare_process_block_class, + UnitModelBlockData, + useDefault, + MaterialBalanceType, + MaterialFlowBasis, +) +from idaes.core.util.config import ( + is_physical_parameter_block, + is_state_block, +) +from idaes.core.util.exceptions import ( + BurntToast, + ConfigurationError, + PropertyNotSupportedError, + InitializationError, +) +from idaes.core.base.var_like_expression import VarLikeExpression +from idaes.core.util.math import smooth_min +from idaes.core.util.tables import create_stream_table_dataframe +import idaes.core.util.scaling as iscale +from idaes.core.solvers import get_solver +import idaes.logger as idaeslog + +__author__ = "Douglas Allan" + + +# Set up logger +_log = idaeslog.getLogger(__name__) + + +@declare_process_block_class("StreamScaler") +class StreamScalerData(UnitModelBlockData): + """ + TODO + """ + + CONFIG = ConfigBlock() + CONFIG.declare( + "dynamic", + ConfigValue( + domain=In([False]), + default=False, + description="Dynamic model flag - must be False", + doc="""Indicates whether this model will be dynamic or not, + **default** = False. Scaler blocks are always steady-state.""", + ), + ) + CONFIG.declare( + "has_holdup", + ConfigValue( + default=False, + domain=In([False]), + description="Holdup construction flag - must be False", + doc="Scaler blocks do not contain holdup, thus this must be False.", + ), + ) + CONFIG.declare( + "property_package", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for StreamScaler", + doc="""Property parameter object used to define property +calculations, **default** - useDefault. +**Valid values:** { +**useDefault** - use default package from parent model or flowsheet, +**PropertyParameterObject** - a PropertyParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing property packages", + doc="""A ConfigBlock with arguments to be passed to a property +block(s) and used when constructing these, +**default** - None. +**Valid values:** { +see property package for documentation.}""", + ), + ) + + def build(self): + """ + General build method for StreamScalerData. This method calls a number + of sub-methods which automate the construction of expected attributes + of unit models. + + Inheriting models should call `super().build`. + + Args: + None + + Returns: + None + """ + # Call super.build() + super(StreamScalerData, self).build() + + tmp_dict = dict(**self.config.property_package_args) + tmp_dict["has_phase_equilibrium"] = False + tmp_dict["defined_state"] = True + + # Call setup methods from ControlVolumeBlockData + self._get_property_package() + self._get_indexing_sets() + + self.inlet_block = self.config.property_package.build_state_block( + self.flowsheet().time, doc="Material properties at inlet", **tmp_dict + ) + self.outlet_block = Block() + self.multiplier = Var( + initialize=1, + domain=PositiveReals, + units=pyunits.dimensionless, + doc="Factor by which to scale dimensionless streams" + ) + self.add_inlet_port(name="inlet", block=self.inlet_block) + self.outlet = Port(doc="Outlet port") + + def rule_scale_var(b, *args, var=None): + return self.multiplier * var[args] + + def rule_no_scale_var(b, *args, var=None): + return var[args] + + for var_name in self.inlet.vars.keys(): + var = getattr(self.inlet, var_name) + if "flow" in var_name: + rule=partial(rule_scale_var, var=var) + else: + rule=partial(rule_no_scale_var, var=var) + self.outlet_block.add_component( + var_name, + VarLikeExpression( + var.index_set(), + rule=rule + ) + ) + expr = getattr(self.outlet_block, var_name) + self.outlet.add(expr, var_name) + + def initialize_build( + blk, outlvl=idaeslog.NOTSET, optarg=None, solver=None, hold_state=False + ): + """ + Initialization routine for StreamScaler. + + Keyword Arguments: + outlvl : sets output level of initialization routine + optarg : solver options dictionary object (default=None, use + default solver options) + solver : str indicating which solver to use during + initialization (default = None, use default solver) + hold_state : flag indicating whether the initialization routine + should unfix any state variables fixed during + initialization, **default** - False. **Valid values:** + **True** - states variables are not unfixed, and a dict of + returned containing flags for which states were fixed + during initialization, **False** - state variables are + unfixed after initialization by calling the release_state + method. + + Returns: + If hold_states is True, returns a dict containing flags for which + states were fixed during initialization. + """ + init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") + solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") + + # Create solver + + # Initialize inlet state blocks + flags = blk.inlet_block.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + hold_state=True, + ) + + if hold_state is True: + return flags + else: + blk.release_state(flags, outlvl=outlvl) + + def release_state(blk, flags, outlvl=idaeslog.NOTSET): + """ + Method to release state variables fixed during initialization. + + Keyword Arguments: + flags : dict containing information of which state variables + were fixed during initialization, and should now be + unfixed. This dict is returned by initialize if + hold_state = True. + outlvl : sets output level of logging + + Returns: + None + """ + blk.inlet_block.release_state(flags, outlvl=outlvl) + + def _get_stream_table_contents(self, time_point=0): + io_dict = { + "Inlet": self.inlet, + "Outlet": self.outlet, + } + return create_stream_table_dataframe(io_dict, time_point=time_point) + + def calculate_scaling_factors(self): + # Scaling factors for the property block are calculated automatically + super().calculate_scaling_factors() + + # Need to pass on scaling factors from the property block to the outlet + # VarLikeExpressions so arcs get scaled right + scale = 1/self.multiplier.value + for var_name in self.inlet.vars.keys(): + var = getattr(self.inlet, var_name) + outlet_expr = getattr(self.outlet, var_name) + for key, subvar in var.items(): + sf = iscale.get_scaling_factor(subvar, default=1, warning=True) + iscale.set_scaling_factor(outlet_expr[key],scale*sf) + \ No newline at end of file From 00576ace568e56f0cbe15ba5c8dc5c4cc2ace88d Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 3 Oct 2024 14:08:11 -0600 Subject: [PATCH 02/21] Added test file for StreamScaler unit model and added the unit model to the init file --- idaes/models/unit_models/__init__.py | 1 + idaes/models/unit_models/stream_scaler.py | 9 +- .../unit_models/tests/test_stream_scaler.py | 375 ++++++++++++++++++ 3 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 idaes/models/unit_models/tests/test_stream_scaler.py diff --git a/idaes/models/unit_models/__init__.py b/idaes/models/unit_models/__init__.py index 7b5397d290..73a42446d3 100644 --- a/idaes/models/unit_models/__init__.py +++ b/idaes/models/unit_models/__init__.py @@ -40,6 +40,7 @@ ) from .shell_and_tube_1d import ShellAndTube1D, ShellAndTubeInitializer from .skeleton_model import SkeletonUnitModel, SkeletonUnitModelData +from .stream_scaler import StreamScaler from .statejunction import StateJunction, StateJunctionInitializer from .stoichiometric_reactor import StoichiometricReactor from .translator import Translator diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index f300b1574a..21ea9939c1 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -54,6 +54,8 @@ from idaes.core.solvers import get_solver import idaes.logger as idaeslog +from idaes.models.unit_models.feed import FeedInitializer as StreamScalerInitializer + __author__ = "Douglas Allan" @@ -64,9 +66,12 @@ @declare_process_block_class("StreamScaler") class StreamScalerData(UnitModelBlockData): """ - TODO + Unit model to adjust size of streams to represent, for example, a stream being split across several identical units, + which are then all modeled as a single IDAES unit """ + default_initializer = StreamScalerInitializer + CONFIG = ConfigBlock() CONFIG.declare( "dynamic", @@ -235,7 +240,7 @@ def release_state(blk, flags, outlvl=idaeslog.NOTSET): def _get_stream_table_contents(self, time_point=0): io_dict = { "Inlet": self.inlet, - "Outlet": self.outlet, + # "Outlet": self.outlet, } return create_stream_table_dataframe(io_dict, time_point=time_point) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py new file mode 100644 index 0000000000..c540f82626 --- /dev/null +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -0,0 +1,375 @@ +################################################################################# +# The Institute for the Design of Advanced Energy Systems Integrated Platform +# Framework (IDAES IP) was produced under the DOE Institute for the +# Design of Advanced Energy Systems (IDAES). +# +# Copyright (c) 2018-2024 by the software owners: The Regents of the +# University of California, through Lawrence Berkeley National Laboratory, +# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon +# University, West Virginia University Research Corporation, et al. +# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md +# for full copyright and license information. +################################################################################# +""" +Tests for Stream Scaler unit model. + +Author: Andrew Lee +""" + +import pytest +import pandas +from numpy import number + +from pyomo.environ import ( + check_optimal_termination, + ConcreteModel, + value, + units as pyunits, +) + +from idaes.core import FlowsheetBlock +from idaes.models.unit_models.stream_scaler import StreamScaler, StreamScalerInitializer + +from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import ( + BTXParameterBlock, +) + +from idaes.models.properties import iapws95 +from idaes.models.properties.examples.saponification_thermo import ( + SaponificationParameterBlock, +) + +from idaes.core.util.model_statistics import ( + number_variables, + number_total_constraints, + number_unused_variables, + variables_set, +) +from idaes.core.util.testing import PhysicalParameterTestBlock, initialization_tester +from idaes.core.solvers import get_solver +from idaes.core.initialization import ( + BlockTriangularizationInitializer, + InitializationStatus, +) +from idaes.core.util import DiagnosticsToolbox + +# ----------------------------------------------------------------------------- +# Get default solver for testing +solver = get_solver("ipopt_v2") + + +# ----------------------------------------------------------------------------- +@pytest.mark.unit +def test_config(): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + + m.fs.properties = PhysicalParameterTestBlock() + + m.fs.unit = StreamScaler(property_package=m.fs.properties) + + # Check unit config arguments + assert len(m.fs.unit.config) == 4 + + assert not m.fs.unit.config.dynamic + assert not m.fs.unit.config.has_holdup + assert m.fs.unit.config.property_package is m.fs.properties + + assert m.fs.unit.default_initializer is StreamScalerInitializer + + +class TestSaponification(object): + @pytest.fixture(scope="class") + def sapon(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.properties = SaponificationParameterBlock() + m.fs.unit = StreamScaler(property_package=m.fs.properties) + m.fs.unit.multiplier.fix(1) + + m.fs.unit.inlet.flow_vol.fix(1.0e-03) + m.fs.unit.inlet.conc_mol_comp[0, "H2O"].fix(55388.0) + m.fs.unit.inlet.conc_mol_comp[0, "NaOH"].fix(100.0) + m.fs.unit.inlet.conc_mol_comp[0, "EthylAcetate"].fix(100.0) + m.fs.unit.inlet.conc_mol_comp[0, "SodiumAcetate"].fix(1e-8) + m.fs.unit.inlet.conc_mol_comp[0, "Ethanol"].fix(1e-8) + + m.fs.unit.inlet.temperature.fix(303.15) + m.fs.unit.inlet.pressure.fix(101325.0) + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, sapon): + + assert hasattr(sapon.fs.unit, "inlet") + assert len(sapon.fs.unit.inlet.vars) == 4 + assert hasattr(sapon.fs.unit.inlet, "flow_vol") + assert hasattr(sapon.fs.unit.inlet, "conc_mol_comp") + assert hasattr(sapon.fs.unit.inlet, "temperature") + assert hasattr(sapon.fs.unit.inlet, "pressure") + + assert number_variables(sapon) == 9 + assert number_total_constraints(sapon) == 0 + assert number_unused_variables(sapon) == 9 + + @pytest.mark.component + def test_structural_issues(self, sapon): + dt = DiagnosticsToolbox(sapon) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, sapon): + perf_dict = sapon.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, sapon): + stable = sapon.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "Volumetric Flowrate": getattr( + pyunits.pint_registry, "m**3/second" + ), + "Molar Concentration H2O": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration NaOH": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration EthylAcetate": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration SodiumAcetate": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration Ethanol": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Temperature": getattr(pyunits.pint_registry, "K"), + "Pressure": getattr(pyunits.pint_registry, "Pa"), + }, + "Inlet": { + "Volumetric Flowrate": 1e-3, + "Molar Concentration H2O": 55388, + "Molar Concentration NaOH": 100.00, + "Molar Concentration EthylAcetate": 100.00, + "Molar Concentration SodiumAcetate": 0, + "Molar Concentration Ethanol": 0, + "Temperature": 303.15, + "Pressure": 1.0132e05, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, sapon): + initialization_tester(sapon) + + # No solve or numerical tests, as StreamScaler block has nothing to solve + +class TestBTX(object): + @pytest.fixture(scope="class") + def btx(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.properties = BTXParameterBlock(valid_phase="Liq") + m.fs.unit = StreamScaler(property_package=m.fs.properties) + m.fs.unit.multiplier.fix(1) + m.fs.unit.inlet.flow_mol[0].fix(5) # mol/s + m.fs.unit.inlet.temperature[0].fix(365) # K + m.fs.unit.inlet.pressure[0].fix(101325) # Pa + m.fs.unit.inlet.mole_frac_comp[0, "benzene"].fix(0.5) + m.fs.unit.inlet.mole_frac_comp[0, "toluene"].fix(0.5) + # x = m.fs.unit + # print(variables_set(x)) + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, btx): + + assert hasattr(btx.fs.unit, "inlet") + assert len(btx.fs.unit.inlet.vars) == 4 + assert hasattr(btx.fs.unit.inlet, "flow_mol") + assert hasattr(btx.fs.unit.inlet, "mole_frac_comp") + assert hasattr(btx.fs.unit.inlet, "temperature") + assert hasattr(btx.fs.unit.inlet, "pressure") + + # x1 = number_variables(btx) + # x2 = number_total_constraints(btx) + # x3 = number_unused_variables(btx) + + assert number_variables(btx) == 9 + assert number_total_constraints(btx) == 3 + assert number_unused_variables(btx) == 3 + + @pytest.mark.component + def test_structural_issues(self, btx): + dt = DiagnosticsToolbox(btx) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, btx): + perf_dict = btx.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, btx): + stable = btx.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "flow_mol": getattr(pyunits.pint_registry, "mole/second"), + "mole_frac_comp benzene": getattr( + pyunits.pint_registry, "dimensionless" + ), + "mole_frac_comp toluene": getattr( + pyunits.pint_registry, "dimensionless" + ), + "temperature": getattr(pyunits.pint_registry, "kelvin"), + "pressure": getattr(pyunits.pint_registry, "Pa"), + }, + "Inlet": { + "flow_mol": 5.0, + "mole_frac_comp benzene": 0.5, + "mole_frac_comp toluene": 0.5, + "temperature": 365, + "pressure": 101325.0, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, btx): + initialization_tester(btx) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_solve(self, btx): + results = solver.solve(btx) + + # Check for optimal solution + assert check_optimal_termination(results) + + # @pytest.mark.solver + # @pytest.mark.skipif(solver is None, reason="Solver not available") + # @pytest.mark.component + # def test_solution(self, btx): + # assert pytest.approx(5, abs=1e-3) == value( + # btx.fs.unit.properties[0].flow_mol_phase["Liq"] + # ) + # assert pytest.approx(0.5, abs=1e-3) == value( + # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "benzene"] + # ) + # assert pytest.approx(0.5, abs=1e-3) == value( + # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "toluene"] + # ) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_numerical_issues(self, btx): + dt = DiagnosticsToolbox(btx) + dt.assert_no_numerical_warnings() + + +# ----------------------------------------------------------------------------- +@pytest.mark.iapws +@pytest.mark.skipif(not iapws95.iapws95_available(), reason="IAPWS not available") +class TestIAPWS(object): + @pytest.fixture(scope="class") + def iapws(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + + m.fs.properties = iapws95.Iapws95ParameterBlock() + + m.fs.unit = StreamScaler(property_package=m.fs.properties) + + # x = m.fs.unit + # print(variables_set(x)) + m.fs.unit.multiplier.fix(1) + m.fs.unit.inlet.flow_mol[0].fix(100) + m.fs.unit.inlet.enth_mol[0].fix(5000) + m.fs.unit.inlet.pressure[0].fix(101325) + + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, iapws): + assert len(iapws.fs.unit.inlet.vars) == 3 + assert hasattr(iapws.fs.unit.inlet, "flow_mol") + assert hasattr(iapws.fs.unit.inlet, "enth_mol") + assert hasattr(iapws.fs.unit.inlet, "pressure") + + assert number_variables(iapws) == 4 + assert number_total_constraints(iapws) == 0 + assert number_unused_variables(iapws) == 4 + + @pytest.mark.component + def test_structural_issues(self, iapws): + dt = DiagnosticsToolbox(iapws) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, iapws): + perf_dict = iapws.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, iapws): + stable = iapws.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "Molar Flow": getattr(pyunits.pint_registry, "mole/second"), + "Mass Flow": getattr(pyunits.pint_registry, "kg/second"), + "T": getattr(pyunits.pint_registry, "K"), + "P": getattr(pyunits.pint_registry, "Pa"), + "Vapor Fraction": getattr(pyunits.pint_registry, "dimensionless"), + "Molar Enthalpy": getattr(pyunits.pint_registry, "J/mole"), + }, + "Inlet": { + "Molar Flow": 100, + "Mass Flow": 1.8015, + "T": 339.43, + "P": 101325, + "Vapor Fraction": 0, + "Molar Enthalpy": 5000, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, iapws): + initialization_tester(iapws) + + # No solve or numerical tests as there is nothing to solve for \ No newline at end of file From 1adb5b2988a53fdc847f46920a1e5615b33dbc56 Mon Sep 17 00:00:00 2001 From: Doug A Date: Mon, 23 Sep 2024 16:36:53 -0400 Subject: [PATCH 03/21] pull from MEA development branch --- idaes/models/unit_models/stream_scaler.py | 255 ++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 idaes/models/unit_models/stream_scaler.py diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py new file mode 100644 index 0000000000..f300b1574a --- /dev/null +++ b/idaes/models/unit_models/stream_scaler.py @@ -0,0 +1,255 @@ +################################################################################# +# The Institute for the Design of Advanced Energy Systems Integrated Platform +# Framework (IDAES IP) was produced under the DOE Institute for the +# Design of Advanced Energy Systems (IDAES). +# +# Copyright (c) 2018-2023 by the software owners: The Regents of the +# University of California, through Lawrence Berkeley National Laboratory, +# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon +# University, West Virginia University Research Corporation, et al. +# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md +# for full copyright and license information. +################################################################################# +""" +Unit model to adjust size of streams to represent, for example, a stream being split across several identical units, +which are then all modeled as a single IDAES unit +""" +from enum import Enum +from functools import partial + +from pyomo.environ import ( + Block, + check_optimal_termination, + Param, + PositiveReals, + Reals, + RangeSet, + units as pyunits, + Var, +) +from pyomo.network import Port +from pyomo.common.config import ConfigBlock, ConfigValue, In, ListOf, Bool + +from idaes.core import ( + declare_process_block_class, + UnitModelBlockData, + useDefault, + MaterialBalanceType, + MaterialFlowBasis, +) +from idaes.core.util.config import ( + is_physical_parameter_block, + is_state_block, +) +from idaes.core.util.exceptions import ( + BurntToast, + ConfigurationError, + PropertyNotSupportedError, + InitializationError, +) +from idaes.core.base.var_like_expression import VarLikeExpression +from idaes.core.util.math import smooth_min +from idaes.core.util.tables import create_stream_table_dataframe +import idaes.core.util.scaling as iscale +from idaes.core.solvers import get_solver +import idaes.logger as idaeslog + +__author__ = "Douglas Allan" + + +# Set up logger +_log = idaeslog.getLogger(__name__) + + +@declare_process_block_class("StreamScaler") +class StreamScalerData(UnitModelBlockData): + """ + TODO + """ + + CONFIG = ConfigBlock() + CONFIG.declare( + "dynamic", + ConfigValue( + domain=In([False]), + default=False, + description="Dynamic model flag - must be False", + doc="""Indicates whether this model will be dynamic or not, + **default** = False. Scaler blocks are always steady-state.""", + ), + ) + CONFIG.declare( + "has_holdup", + ConfigValue( + default=False, + domain=In([False]), + description="Holdup construction flag - must be False", + doc="Scaler blocks do not contain holdup, thus this must be False.", + ), + ) + CONFIG.declare( + "property_package", + ConfigValue( + default=useDefault, + domain=is_physical_parameter_block, + description="Property package to use for StreamScaler", + doc="""Property parameter object used to define property +calculations, **default** - useDefault. +**Valid values:** { +**useDefault** - use default package from parent model or flowsheet, +**PropertyParameterObject** - a PropertyParameterBlock object.}""", + ), + ) + CONFIG.declare( + "property_package_args", + ConfigBlock( + implicit=True, + description="Arguments to use for constructing property packages", + doc="""A ConfigBlock with arguments to be passed to a property +block(s) and used when constructing these, +**default** - None. +**Valid values:** { +see property package for documentation.}""", + ), + ) + + def build(self): + """ + General build method for StreamScalerData. This method calls a number + of sub-methods which automate the construction of expected attributes + of unit models. + + Inheriting models should call `super().build`. + + Args: + None + + Returns: + None + """ + # Call super.build() + super(StreamScalerData, self).build() + + tmp_dict = dict(**self.config.property_package_args) + tmp_dict["has_phase_equilibrium"] = False + tmp_dict["defined_state"] = True + + # Call setup methods from ControlVolumeBlockData + self._get_property_package() + self._get_indexing_sets() + + self.inlet_block = self.config.property_package.build_state_block( + self.flowsheet().time, doc="Material properties at inlet", **tmp_dict + ) + self.outlet_block = Block() + self.multiplier = Var( + initialize=1, + domain=PositiveReals, + units=pyunits.dimensionless, + doc="Factor by which to scale dimensionless streams" + ) + self.add_inlet_port(name="inlet", block=self.inlet_block) + self.outlet = Port(doc="Outlet port") + + def rule_scale_var(b, *args, var=None): + return self.multiplier * var[args] + + def rule_no_scale_var(b, *args, var=None): + return var[args] + + for var_name in self.inlet.vars.keys(): + var = getattr(self.inlet, var_name) + if "flow" in var_name: + rule=partial(rule_scale_var, var=var) + else: + rule=partial(rule_no_scale_var, var=var) + self.outlet_block.add_component( + var_name, + VarLikeExpression( + var.index_set(), + rule=rule + ) + ) + expr = getattr(self.outlet_block, var_name) + self.outlet.add(expr, var_name) + + def initialize_build( + blk, outlvl=idaeslog.NOTSET, optarg=None, solver=None, hold_state=False + ): + """ + Initialization routine for StreamScaler. + + Keyword Arguments: + outlvl : sets output level of initialization routine + optarg : solver options dictionary object (default=None, use + default solver options) + solver : str indicating which solver to use during + initialization (default = None, use default solver) + hold_state : flag indicating whether the initialization routine + should unfix any state variables fixed during + initialization, **default** - False. **Valid values:** + **True** - states variables are not unfixed, and a dict of + returned containing flags for which states were fixed + during initialization, **False** - state variables are + unfixed after initialization by calling the release_state + method. + + Returns: + If hold_states is True, returns a dict containing flags for which + states were fixed during initialization. + """ + init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") + solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") + + # Create solver + + # Initialize inlet state blocks + flags = blk.inlet_block.initialize( + outlvl=outlvl, + optarg=optarg, + solver=solver, + hold_state=True, + ) + + if hold_state is True: + return flags + else: + blk.release_state(flags, outlvl=outlvl) + + def release_state(blk, flags, outlvl=idaeslog.NOTSET): + """ + Method to release state variables fixed during initialization. + + Keyword Arguments: + flags : dict containing information of which state variables + were fixed during initialization, and should now be + unfixed. This dict is returned by initialize if + hold_state = True. + outlvl : sets output level of logging + + Returns: + None + """ + blk.inlet_block.release_state(flags, outlvl=outlvl) + + def _get_stream_table_contents(self, time_point=0): + io_dict = { + "Inlet": self.inlet, + "Outlet": self.outlet, + } + return create_stream_table_dataframe(io_dict, time_point=time_point) + + def calculate_scaling_factors(self): + # Scaling factors for the property block are calculated automatically + super().calculate_scaling_factors() + + # Need to pass on scaling factors from the property block to the outlet + # VarLikeExpressions so arcs get scaled right + scale = 1/self.multiplier.value + for var_name in self.inlet.vars.keys(): + var = getattr(self.inlet, var_name) + outlet_expr = getattr(self.outlet, var_name) + for key, subvar in var.items(): + sf = iscale.get_scaling_factor(subvar, default=1, warning=True) + iscale.set_scaling_factor(outlet_expr[key],scale*sf) + \ No newline at end of file From 0af89d183e795ff022fff7c8c9a8b934dd318b99 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 3 Oct 2024 14:08:11 -0600 Subject: [PATCH 04/21] Added test file for StreamScaler unit model and added the unit model to the init file --- idaes/models/unit_models/__init__.py | 1 + idaes/models/unit_models/stream_scaler.py | 9 +- .../unit_models/tests/test_stream_scaler.py | 375 ++++++++++++++++++ 3 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 idaes/models/unit_models/tests/test_stream_scaler.py diff --git a/idaes/models/unit_models/__init__.py b/idaes/models/unit_models/__init__.py index 7b5397d290..73a42446d3 100644 --- a/idaes/models/unit_models/__init__.py +++ b/idaes/models/unit_models/__init__.py @@ -40,6 +40,7 @@ ) from .shell_and_tube_1d import ShellAndTube1D, ShellAndTubeInitializer from .skeleton_model import SkeletonUnitModel, SkeletonUnitModelData +from .stream_scaler import StreamScaler from .statejunction import StateJunction, StateJunctionInitializer from .stoichiometric_reactor import StoichiometricReactor from .translator import Translator diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index f300b1574a..21ea9939c1 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -54,6 +54,8 @@ from idaes.core.solvers import get_solver import idaes.logger as idaeslog +from idaes.models.unit_models.feed import FeedInitializer as StreamScalerInitializer + __author__ = "Douglas Allan" @@ -64,9 +66,12 @@ @declare_process_block_class("StreamScaler") class StreamScalerData(UnitModelBlockData): """ - TODO + Unit model to adjust size of streams to represent, for example, a stream being split across several identical units, + which are then all modeled as a single IDAES unit """ + default_initializer = StreamScalerInitializer + CONFIG = ConfigBlock() CONFIG.declare( "dynamic", @@ -235,7 +240,7 @@ def release_state(blk, flags, outlvl=idaeslog.NOTSET): def _get_stream_table_contents(self, time_point=0): io_dict = { "Inlet": self.inlet, - "Outlet": self.outlet, + # "Outlet": self.outlet, } return create_stream_table_dataframe(io_dict, time_point=time_point) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py new file mode 100644 index 0000000000..c540f82626 --- /dev/null +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -0,0 +1,375 @@ +################################################################################# +# The Institute for the Design of Advanced Energy Systems Integrated Platform +# Framework (IDAES IP) was produced under the DOE Institute for the +# Design of Advanced Energy Systems (IDAES). +# +# Copyright (c) 2018-2024 by the software owners: The Regents of the +# University of California, through Lawrence Berkeley National Laboratory, +# National Technology & Engineering Solutions of Sandia, LLC, Carnegie Mellon +# University, West Virginia University Research Corporation, et al. +# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md +# for full copyright and license information. +################################################################################# +""" +Tests for Stream Scaler unit model. + +Author: Andrew Lee +""" + +import pytest +import pandas +from numpy import number + +from pyomo.environ import ( + check_optimal_termination, + ConcreteModel, + value, + units as pyunits, +) + +from idaes.core import FlowsheetBlock +from idaes.models.unit_models.stream_scaler import StreamScaler, StreamScalerInitializer + +from idaes.models.properties.activity_coeff_models.BTX_activity_coeff_VLE import ( + BTXParameterBlock, +) + +from idaes.models.properties import iapws95 +from idaes.models.properties.examples.saponification_thermo import ( + SaponificationParameterBlock, +) + +from idaes.core.util.model_statistics import ( + number_variables, + number_total_constraints, + number_unused_variables, + variables_set, +) +from idaes.core.util.testing import PhysicalParameterTestBlock, initialization_tester +from idaes.core.solvers import get_solver +from idaes.core.initialization import ( + BlockTriangularizationInitializer, + InitializationStatus, +) +from idaes.core.util import DiagnosticsToolbox + +# ----------------------------------------------------------------------------- +# Get default solver for testing +solver = get_solver("ipopt_v2") + + +# ----------------------------------------------------------------------------- +@pytest.mark.unit +def test_config(): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + + m.fs.properties = PhysicalParameterTestBlock() + + m.fs.unit = StreamScaler(property_package=m.fs.properties) + + # Check unit config arguments + assert len(m.fs.unit.config) == 4 + + assert not m.fs.unit.config.dynamic + assert not m.fs.unit.config.has_holdup + assert m.fs.unit.config.property_package is m.fs.properties + + assert m.fs.unit.default_initializer is StreamScalerInitializer + + +class TestSaponification(object): + @pytest.fixture(scope="class") + def sapon(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.properties = SaponificationParameterBlock() + m.fs.unit = StreamScaler(property_package=m.fs.properties) + m.fs.unit.multiplier.fix(1) + + m.fs.unit.inlet.flow_vol.fix(1.0e-03) + m.fs.unit.inlet.conc_mol_comp[0, "H2O"].fix(55388.0) + m.fs.unit.inlet.conc_mol_comp[0, "NaOH"].fix(100.0) + m.fs.unit.inlet.conc_mol_comp[0, "EthylAcetate"].fix(100.0) + m.fs.unit.inlet.conc_mol_comp[0, "SodiumAcetate"].fix(1e-8) + m.fs.unit.inlet.conc_mol_comp[0, "Ethanol"].fix(1e-8) + + m.fs.unit.inlet.temperature.fix(303.15) + m.fs.unit.inlet.pressure.fix(101325.0) + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, sapon): + + assert hasattr(sapon.fs.unit, "inlet") + assert len(sapon.fs.unit.inlet.vars) == 4 + assert hasattr(sapon.fs.unit.inlet, "flow_vol") + assert hasattr(sapon.fs.unit.inlet, "conc_mol_comp") + assert hasattr(sapon.fs.unit.inlet, "temperature") + assert hasattr(sapon.fs.unit.inlet, "pressure") + + assert number_variables(sapon) == 9 + assert number_total_constraints(sapon) == 0 + assert number_unused_variables(sapon) == 9 + + @pytest.mark.component + def test_structural_issues(self, sapon): + dt = DiagnosticsToolbox(sapon) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, sapon): + perf_dict = sapon.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, sapon): + stable = sapon.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "Volumetric Flowrate": getattr( + pyunits.pint_registry, "m**3/second" + ), + "Molar Concentration H2O": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration NaOH": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration EthylAcetate": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration SodiumAcetate": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Molar Concentration Ethanol": getattr( + pyunits.pint_registry, "mole/m**3" + ), + "Temperature": getattr(pyunits.pint_registry, "K"), + "Pressure": getattr(pyunits.pint_registry, "Pa"), + }, + "Inlet": { + "Volumetric Flowrate": 1e-3, + "Molar Concentration H2O": 55388, + "Molar Concentration NaOH": 100.00, + "Molar Concentration EthylAcetate": 100.00, + "Molar Concentration SodiumAcetate": 0, + "Molar Concentration Ethanol": 0, + "Temperature": 303.15, + "Pressure": 1.0132e05, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, sapon): + initialization_tester(sapon) + + # No solve or numerical tests, as StreamScaler block has nothing to solve + +class TestBTX(object): + @pytest.fixture(scope="class") + def btx(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + m.fs.properties = BTXParameterBlock(valid_phase="Liq") + m.fs.unit = StreamScaler(property_package=m.fs.properties) + m.fs.unit.multiplier.fix(1) + m.fs.unit.inlet.flow_mol[0].fix(5) # mol/s + m.fs.unit.inlet.temperature[0].fix(365) # K + m.fs.unit.inlet.pressure[0].fix(101325) # Pa + m.fs.unit.inlet.mole_frac_comp[0, "benzene"].fix(0.5) + m.fs.unit.inlet.mole_frac_comp[0, "toluene"].fix(0.5) + # x = m.fs.unit + # print(variables_set(x)) + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, btx): + + assert hasattr(btx.fs.unit, "inlet") + assert len(btx.fs.unit.inlet.vars) == 4 + assert hasattr(btx.fs.unit.inlet, "flow_mol") + assert hasattr(btx.fs.unit.inlet, "mole_frac_comp") + assert hasattr(btx.fs.unit.inlet, "temperature") + assert hasattr(btx.fs.unit.inlet, "pressure") + + # x1 = number_variables(btx) + # x2 = number_total_constraints(btx) + # x3 = number_unused_variables(btx) + + assert number_variables(btx) == 9 + assert number_total_constraints(btx) == 3 + assert number_unused_variables(btx) == 3 + + @pytest.mark.component + def test_structural_issues(self, btx): + dt = DiagnosticsToolbox(btx) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, btx): + perf_dict = btx.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, btx): + stable = btx.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "flow_mol": getattr(pyunits.pint_registry, "mole/second"), + "mole_frac_comp benzene": getattr( + pyunits.pint_registry, "dimensionless" + ), + "mole_frac_comp toluene": getattr( + pyunits.pint_registry, "dimensionless" + ), + "temperature": getattr(pyunits.pint_registry, "kelvin"), + "pressure": getattr(pyunits.pint_registry, "Pa"), + }, + "Inlet": { + "flow_mol": 5.0, + "mole_frac_comp benzene": 0.5, + "mole_frac_comp toluene": 0.5, + "temperature": 365, + "pressure": 101325.0, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, btx): + initialization_tester(btx) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_solve(self, btx): + results = solver.solve(btx) + + # Check for optimal solution + assert check_optimal_termination(results) + + # @pytest.mark.solver + # @pytest.mark.skipif(solver is None, reason="Solver not available") + # @pytest.mark.component + # def test_solution(self, btx): + # assert pytest.approx(5, abs=1e-3) == value( + # btx.fs.unit.properties[0].flow_mol_phase["Liq"] + # ) + # assert pytest.approx(0.5, abs=1e-3) == value( + # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "benzene"] + # ) + # assert pytest.approx(0.5, abs=1e-3) == value( + # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "toluene"] + # ) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_numerical_issues(self, btx): + dt = DiagnosticsToolbox(btx) + dt.assert_no_numerical_warnings() + + +# ----------------------------------------------------------------------------- +@pytest.mark.iapws +@pytest.mark.skipif(not iapws95.iapws95_available(), reason="IAPWS not available") +class TestIAPWS(object): + @pytest.fixture(scope="class") + def iapws(self): + m = ConcreteModel() + m.fs = FlowsheetBlock(dynamic=False) + + m.fs.properties = iapws95.Iapws95ParameterBlock() + + m.fs.unit = StreamScaler(property_package=m.fs.properties) + + # x = m.fs.unit + # print(variables_set(x)) + m.fs.unit.multiplier.fix(1) + m.fs.unit.inlet.flow_mol[0].fix(100) + m.fs.unit.inlet.enth_mol[0].fix(5000) + m.fs.unit.inlet.pressure[0].fix(101325) + + return m + + @pytest.mark.build + @pytest.mark.unit + def test_build(self, iapws): + assert len(iapws.fs.unit.inlet.vars) == 3 + assert hasattr(iapws.fs.unit.inlet, "flow_mol") + assert hasattr(iapws.fs.unit.inlet, "enth_mol") + assert hasattr(iapws.fs.unit.inlet, "pressure") + + assert number_variables(iapws) == 4 + assert number_total_constraints(iapws) == 0 + assert number_unused_variables(iapws) == 4 + + @pytest.mark.component + def test_structural_issues(self, iapws): + dt = DiagnosticsToolbox(iapws) + dt.assert_no_structural_warnings() + + @pytest.mark.ui + @pytest.mark.unit + def test_get_performance_contents(self, iapws): + perf_dict = iapws.fs.unit._get_performance_contents() + + assert perf_dict is None + + @pytest.mark.ui + @pytest.mark.unit + def test_get_stream_table_contents(self, iapws): + stable = iapws.fs.unit._get_stream_table_contents() + + expected = pandas.DataFrame.from_dict( + { + "Units": { + "Molar Flow": getattr(pyunits.pint_registry, "mole/second"), + "Mass Flow": getattr(pyunits.pint_registry, "kg/second"), + "T": getattr(pyunits.pint_registry, "K"), + "P": getattr(pyunits.pint_registry, "Pa"), + "Vapor Fraction": getattr(pyunits.pint_registry, "dimensionless"), + "Molar Enthalpy": getattr(pyunits.pint_registry, "J/mole"), + }, + "Inlet": { + "Molar Flow": 100, + "Mass Flow": 1.8015, + "T": 339.43, + "P": 101325, + "Vapor Fraction": 0, + "Molar Enthalpy": 5000, + }, + } + ) + + pandas.testing.assert_frame_equal(stable, expected, rtol=1e-4, atol=1e-4) + + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_initialize(self, iapws): + initialization_tester(iapws) + + # No solve or numerical tests as there is nothing to solve for \ No newline at end of file From f689ae17cbeb9a0ddae15822e92d1d9c73a350d6 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Tue, 29 Oct 2024 14:25:21 -0600 Subject: [PATCH 05/21] Added rst file for Stream Scaler Unit Model --- .../generic/unit_models/stream_scaler.rst | 46 +++++++++++++++++++ .../unit_models/tests/test_stream_scaler.py | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst diff --git a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst new file mode 100644 index 0000000000..5d77e6c81f --- /dev/null +++ b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst @@ -0,0 +1,46 @@ +Stream Scaler Block +============= + +Stream Scaler Blocks are used to adjust size of streams to represent, for example, a stream being split across several identical units, which are then all modeled as a single IDAES unit + +Degrees of Freedom +------------------ + +Stream Scaler blocks generally have zero degrees of freedom. + +Model Structure +--------------- + +Stream Scaler Blocks consists of a single StateBlock (named properties), each with an inlet and outlet port. + +Additional Constraints +---------------------- + +Stream Scaler Blocks write no additional constraints to the model. + +Variables +--------- + +Stream Scaler blocks add no additional Variables. + +.. module:: idaes.models.unit_models.stream_scaler + + +Initialization +-------------- + +.. autoclass:: StreamScalerInitializer + :members: initialization_routine + +StreamScaler Class +------------- + +.. autoclass:: StreamScaler + :members: + +StreamScalerData Class +----------------- + +.. autoclass:: StreamScalerData + :members: + diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py index c540f82626..9930eb2bba 100644 --- a/idaes/models/unit_models/tests/test_stream_scaler.py +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -13,7 +13,7 @@ """ Tests for Stream Scaler unit model. -Author: Andrew Lee +Author: Tanner Polley """ import pytest From 288a39ef71a1d7d2f954e3a8a466fea3316cdc9d Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 11:19:26 -0600 Subject: [PATCH 06/21] Added stream_scaler.rst to the table of contents --- .../model_libraries/generic/unit_models/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/index.rst b/docs/reference_guides/model_libraries/generic/unit_models/index.rst index 40f6746e7b..c988b29c9c 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/index.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/index.rst @@ -27,6 +27,7 @@ Unit Models skeleton_unit statejunction stoichiometric_reactor + stream_scaler translator turbine valve From 9c512e86c0e762704136da1dd744411c5c6b26c1 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 11:20:04 -0600 Subject: [PATCH 07/21] Created the rst file for the stream_scaler unit model --- .../model_libraries/generic/unit_models/stream_scaler.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst index 5d77e6c81f..ddbb613cbc 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst @@ -1,5 +1,5 @@ Stream Scaler Block -============= +=================== Stream Scaler Blocks are used to adjust size of streams to represent, for example, a stream being split across several identical units, which are then all modeled as a single IDAES unit @@ -33,13 +33,13 @@ Initialization :members: initialization_routine StreamScaler Class -------------- +------------------ .. autoclass:: StreamScaler :members: StreamScalerData Class ------------------ +---------------------- .. autoclass:: StreamScalerData :members: From 0d4d24451e48db4b9ed04574094515a1847479b6 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 11:20:40 -0600 Subject: [PATCH 08/21] Changed the default timeout time from 180 seconds -> 360 seconds due to build.py timing out --- docs/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build.py b/docs/build.py index 52ad82e441..004576a381 100644 --- a/docs/build.py +++ b/docs/build.py @@ -213,8 +213,8 @@ def main() -> int: "-t", "--timeout", dest="timeout", - help="Timeout (in seconds) for sphinx-build (default=180)", - default=180, + help="Timeout (in seconds) for sphinx-build (default=360)", + default=360, type=int, ) prs.add_argument( From 603c4b59e295e66018492b74e47047e9a0298162 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 11:51:33 -0600 Subject: [PATCH 09/21] Reformatted with Black --- idaes/models/unit_models/stream_scaler.py | 19 +++++++------------ .../unit_models/tests/test_stream_scaler.py | 3 ++- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index 21ea9939c1..fd7cd96471 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -144,14 +144,14 @@ def build(self): self._get_indexing_sets() self.inlet_block = self.config.property_package.build_state_block( - self.flowsheet().time, doc="Material properties at inlet", **tmp_dict + self.flowsheet().time, doc="Material properties at inlet", **tmp_dict ) self.outlet_block = Block() self.multiplier = Var( initialize=1, domain=PositiveReals, units=pyunits.dimensionless, - doc="Factor by which to scale dimensionless streams" + doc="Factor by which to scale dimensionless streams", ) self.add_inlet_port(name="inlet", block=self.inlet_block) self.outlet = Port(doc="Outlet port") @@ -165,15 +165,11 @@ def rule_no_scale_var(b, *args, var=None): for var_name in self.inlet.vars.keys(): var = getattr(self.inlet, var_name) if "flow" in var_name: - rule=partial(rule_scale_var, var=var) + rule = partial(rule_scale_var, var=var) else: - rule=partial(rule_no_scale_var, var=var) + rule = partial(rule_no_scale_var, var=var) self.outlet_block.add_component( - var_name, - VarLikeExpression( - var.index_set(), - rule=rule - ) + var_name, VarLikeExpression(var.index_set(), rule=rule) ) expr = getattr(self.outlet_block, var_name) self.outlet.add(expr, var_name) @@ -250,11 +246,10 @@ def calculate_scaling_factors(self): # Need to pass on scaling factors from the property block to the outlet # VarLikeExpressions so arcs get scaled right - scale = 1/self.multiplier.value + scale = 1 / self.multiplier.value for var_name in self.inlet.vars.keys(): var = getattr(self.inlet, var_name) outlet_expr = getattr(self.outlet, var_name) for key, subvar in var.items(): sf = iscale.get_scaling_factor(subvar, default=1, warning=True) - iscale.set_scaling_factor(outlet_expr[key],scale*sf) - \ No newline at end of file + iscale.set_scaling_factor(outlet_expr[key], scale * sf) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py index 9930eb2bba..c2a8d3978a 100644 --- a/idaes/models/unit_models/tests/test_stream_scaler.py +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -177,6 +177,7 @@ def test_initialize(self, sapon): # No solve or numerical tests, as StreamScaler block has nothing to solve + class TestBTX(object): @pytest.fixture(scope="class") def btx(self): @@ -372,4 +373,4 @@ def test_get_stream_table_contents(self, iapws): def test_initialize(self, iapws): initialization_tester(iapws) - # No solve or numerical tests as there is nothing to solve for \ No newline at end of file + # No solve or numerical tests as there is nothing to solve for From 086e80fd113d31e031dc7d9d37cb6e08f357b392 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 12:21:44 -0600 Subject: [PATCH 10/21] Removed unused code flagged by Pylint --- idaes/models/unit_models/stream_scaler.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index fd7cd96471..2f29b13849 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -19,11 +19,8 @@ from pyomo.environ import ( Block, - check_optimal_termination, - Param, PositiveReals, Reals, - RangeSet, units as pyunits, Var, ) @@ -34,24 +31,13 @@ declare_process_block_class, UnitModelBlockData, useDefault, - MaterialBalanceType, - MaterialFlowBasis, ) from idaes.core.util.config import ( is_physical_parameter_block, - is_state_block, -) -from idaes.core.util.exceptions import ( - BurntToast, - ConfigurationError, - PropertyNotSupportedError, - InitializationError, ) from idaes.core.base.var_like_expression import VarLikeExpression -from idaes.core.util.math import smooth_min from idaes.core.util.tables import create_stream_table_dataframe import idaes.core.util.scaling as iscale -from idaes.core.solvers import get_solver import idaes.logger as idaeslog from idaes.models.unit_models.feed import FeedInitializer as StreamScalerInitializer From 91a0f64d238b36ad4eee7db7a7c57ea21d2c68c6 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 31 Oct 2024 12:22:05 -0600 Subject: [PATCH 11/21] Removed unused code flagged by Pylint --- idaes/models/unit_models/stream_scaler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index 2f29b13849..28752ba773 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -25,7 +25,7 @@ Var, ) from pyomo.network import Port -from pyomo.common.config import ConfigBlock, ConfigValue, In, ListOf, Bool +from pyomo.common.config import ConfigBlock, ConfigValue, In from idaes.core import ( declare_process_block_class, From 4bc4aea541cd2d0b9dbf1148c6f4382166a6bb3e Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 1 Nov 2024 12:12:44 -0600 Subject: [PATCH 12/21] Removed unused code flagged by Pylint --- idaes/models/unit_models/stream_scaler.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index 28752ba773..df7478f813 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -14,13 +14,11 @@ Unit model to adjust size of streams to represent, for example, a stream being split across several identical units, which are then all modeled as a single IDAES unit """ -from enum import Enum from functools import partial from pyomo.environ import ( Block, PositiveReals, - Reals, units as pyunits, Var, ) From 40ac61a3230f4721d7991c3dc2e8a4d5ba93f14f Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 1 Nov 2024 12:29:41 -0600 Subject: [PATCH 13/21] Removed unused code flagged by Pylint --- idaes/models/unit_models/stream_scaler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index df7478f813..3aa88c0aec 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -183,8 +183,8 @@ def initialize_build( If hold_states is True, returns a dict containing flags for which states were fixed during initialization. """ - init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") - solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") + # init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") + # solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver From 85b1679db5b4ae5e4bc84456082e8118a939e914 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 15 Nov 2024 14:12:53 -0700 Subject: [PATCH 14/21] Added StreamScalerData class to the init file for unit models --- idaes/models/unit_models/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idaes/models/unit_models/__init__.py b/idaes/models/unit_models/__init__.py index 73a42446d3..6cf96a7cf0 100644 --- a/idaes/models/unit_models/__init__.py +++ b/idaes/models/unit_models/__init__.py @@ -40,7 +40,7 @@ ) from .shell_and_tube_1d import ShellAndTube1D, ShellAndTubeInitializer from .skeleton_model import SkeletonUnitModel, SkeletonUnitModelData -from .stream_scaler import StreamScaler +from .stream_scaler import StreamScaler, StreamScalerData from .statejunction import StateJunction, StateJunctionInitializer from .stoichiometric_reactor import StoichiometricReactor from .translator import Translator From 30565ecf087fd3b31ee8363338ba658e78217fd5 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 15 Nov 2024 14:13:26 -0700 Subject: [PATCH 15/21] Added Tanner Polley as a co-author --- idaes/models/unit_models/stream_scaler.py | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index 3aa88c0aec..51c00a88a2 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -40,7 +40,7 @@ from idaes.models.unit_models.feed import FeedInitializer as StreamScalerInitializer -__author__ = "Douglas Allan" +__author__ = "Douglas Allan, Tanner Polley" # Set up logger @@ -127,17 +127,17 @@ def build(self): self._get_property_package() self._get_indexing_sets() - self.inlet_block = self.config.property_package.build_state_block( + self.properties = self.config.property_package.build_state_block( self.flowsheet().time, doc="Material properties at inlet", **tmp_dict ) - self.outlet_block = Block() + self.scaled_expressions = Block() self.multiplier = Var( initialize=1, domain=PositiveReals, units=pyunits.dimensionless, doc="Factor by which to scale dimensionless streams", ) - self.add_inlet_port(name="inlet", block=self.inlet_block) + self.add_inlet_port(name="inlet", block=self.properties) self.outlet = Port(doc="Outlet port") def rule_scale_var(b, *args, var=None): @@ -152,10 +152,10 @@ def rule_no_scale_var(b, *args, var=None): rule = partial(rule_scale_var, var=var) else: rule = partial(rule_no_scale_var, var=var) - self.outlet_block.add_component( + self.scaled_expressions.add_component( var_name, VarLikeExpression(var.index_set(), rule=rule) ) - expr = getattr(self.outlet_block, var_name) + expr = getattr(self.scaled_expressions, var_name) self.outlet.add(expr, var_name) def initialize_build( @@ -183,13 +183,11 @@ def initialize_build( If hold_states is True, returns a dict containing flags for which states were fixed during initialization. """ - # init_log = idaeslog.getInitLogger(blk.name, outlvl, tag="unit") - # solve_log = idaeslog.getSolveLogger(blk.name, outlvl, tag="unit") # Create solver # Initialize inlet state blocks - flags = blk.inlet_block.initialize( + flags = blk.properties.initialize( outlvl=outlvl, optarg=optarg, solver=solver, @@ -215,7 +213,7 @@ def release_state(blk, flags, outlvl=idaeslog.NOTSET): Returns: None """ - blk.inlet_block.release_state(flags, outlvl=outlvl) + blk.properties.release_state(flags, outlvl=outlvl) def _get_stream_table_contents(self, time_point=0): io_dict = { @@ -230,7 +228,12 @@ def calculate_scaling_factors(self): # Need to pass on scaling factors from the property block to the outlet # VarLikeExpressions so arcs get scaled right - scale = 1 / self.multiplier.value + if self.multiplier.value == 0: + default = 1 + else: + default = 1 / self.multiplier.value + + scale = iscale.get_scaling_factor(self.multiplier, default=default, warning=False) for var_name in self.inlet.vars.keys(): var = getattr(self.inlet, var_name) outlet_expr = getattr(self.outlet, var_name) From 7fa9c2a80dd869ad2fae87c22461fb851d937b43 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 15 Nov 2024 14:13:55 -0700 Subject: [PATCH 16/21] Adjusted the details of the additional constraints --- .../model_libraries/generic/unit_models/stream_scaler.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst index ddbb613cbc..b6de2bbccc 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst @@ -16,7 +16,7 @@ Stream Scaler Blocks consists of a single StateBlock (named properties), each wi Additional Constraints ---------------------- -Stream Scaler Blocks write no additional constraints to the model. +Stream Scaler Blocks write no additional constraints* (besides those naturally occurring in StateBlocks). Variables --------- From 15a042652e00c6b90e3563f6cf2bbd593334c1ee Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 15 Nov 2024 14:15:19 -0700 Subject: [PATCH 17/21] Resolved the test_solution method for btx and removed the comment for no solve or numerical tests to be done for the iapws since the unit model has no unit model level contraints. Tried to run the test_solve but got no feasible solution --- .../unit_models/tests/test_stream_scaler.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py index c2a8d3978a..9251df3c98 100644 --- a/idaes/models/unit_models/tests/test_stream_scaler.py +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -271,19 +271,19 @@ def test_solve(self, btx): # Check for optimal solution assert check_optimal_termination(results) - # @pytest.mark.solver - # @pytest.mark.skipif(solver is None, reason="Solver not available") - # @pytest.mark.component - # def test_solution(self, btx): - # assert pytest.approx(5, abs=1e-3) == value( - # btx.fs.unit.properties[0].flow_mol_phase["Liq"] - # ) - # assert pytest.approx(0.5, abs=1e-3) == value( - # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "benzene"] - # ) - # assert pytest.approx(0.5, abs=1e-3) == value( - # btx.fs.unit.properties[0].mole_frac_phase_comp["Liq", "toluene"] - # ) + @pytest.mark.solver + @pytest.mark.skipif(solver is None, reason="Solver not available") + @pytest.mark.component + def test_solution(self, btx): + assert pytest.approx(5, abs=1e-3) == value( + btx.fs.unit.inlet.flow_mol[0] + ) + assert pytest.approx(0.5, abs=1e-3) == value( + btx.fs.unit.inlet.mole_frac_comp[0, "benzene"] + ) + assert pytest.approx(0.5, abs=1e-3) == value( + btx.fs.unit.inlet.mole_frac_comp[0, "toluene"] + ) @pytest.mark.solver @pytest.mark.skipif(solver is None, reason="Solver not available") @@ -372,5 +372,3 @@ def test_get_stream_table_contents(self, iapws): @pytest.mark.component def test_initialize(self, iapws): initialization_tester(iapws) - - # No solve or numerical tests as there is nothing to solve for From 194f47690cb210742fe0e6ae0739707712ca7539 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Fri, 15 Nov 2024 14:20:35 -0700 Subject: [PATCH 18/21] Ran Black on these files --- idaes/models/unit_models/stream_scaler.py | 4 +++- idaes/models/unit_models/tests/test_stream_scaler.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/idaes/models/unit_models/stream_scaler.py b/idaes/models/unit_models/stream_scaler.py index 51c00a88a2..1b6455eb0d 100644 --- a/idaes/models/unit_models/stream_scaler.py +++ b/idaes/models/unit_models/stream_scaler.py @@ -233,7 +233,9 @@ def calculate_scaling_factors(self): else: default = 1 / self.multiplier.value - scale = iscale.get_scaling_factor(self.multiplier, default=default, warning=False) + scale = iscale.get_scaling_factor( + self.multiplier, default=default, warning=False + ) for var_name in self.inlet.vars.keys(): var = getattr(self.inlet, var_name) outlet_expr = getattr(self.outlet, var_name) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py index 9251df3c98..8d58ad868c 100644 --- a/idaes/models/unit_models/tests/test_stream_scaler.py +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -275,9 +275,7 @@ def test_solve(self, btx): @pytest.mark.skipif(solver is None, reason="Solver not available") @pytest.mark.component def test_solution(self, btx): - assert pytest.approx(5, abs=1e-3) == value( - btx.fs.unit.inlet.flow_mol[0] - ) + assert pytest.approx(5, abs=1e-3) == value(btx.fs.unit.inlet.flow_mol[0]) assert pytest.approx(0.5, abs=1e-3) == value( btx.fs.unit.inlet.mole_frac_comp[0, "benzene"] ) From 2df7345aea53046b413d5f3cfdea2f7cc0eaa6db Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Mon, 18 Nov 2024 12:32:53 -0700 Subject: [PATCH 19/21] Fixed degrees of freedom on doc file --- .../model_libraries/generic/unit_models/stream_scaler.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst index b6de2bbccc..fd91ec134e 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst @@ -6,7 +6,7 @@ Stream Scaler Blocks are used to adjust size of streams to represent, for exampl Degrees of Freedom ------------------ -Stream Scaler blocks generally have zero degrees of freedom. +Stream Scaler blocks have one degree of freedom (and an additional variable). Model Structure --------------- From faced0b9a9e884fa52c955bd3566a86d4ce8815c Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Mon, 18 Nov 2024 15:46:54 -0700 Subject: [PATCH 20/21] Revised the documentation for Degrees of Freedom for Stream Scaler --- .../model_libraries/generic/unit_models/stream_scaler.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst index fd91ec134e..13a71068fe 100644 --- a/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst +++ b/docs/reference_guides/model_libraries/generic/unit_models/stream_scaler.rst @@ -6,17 +6,17 @@ Stream Scaler Blocks are used to adjust size of streams to represent, for exampl Degrees of Freedom ------------------ -Stream Scaler blocks have one degree of freedom (and an additional variable). +Stream Scaler blocks have one degree of freedom (beyond the state variables in the ``StateBlock`` properties), a ``Var`` called ``multiplier``. It is the factor by which extensive state variables (defined as those having "flow" in their name) are scaled, with ``output_var = multiplier * input_var``. Model Structure --------------- -Stream Scaler Blocks consists of a single StateBlock (named properties), each with an inlet and outlet port. +Stream Scaler Blocks consists of a single ``StateBlock`` (named properties), each with an inlet and outlet port. Additional Constraints ---------------------- -Stream Scaler Blocks write no additional constraints* (besides those naturally occurring in StateBlocks). +Stream Scaler Blocks write no additional constraints* (besides those naturally occurring in ``StateBlocks``). Variables --------- From 779296e48bd8218852643625d4fdb99ecc63a945 Mon Sep 17 00:00:00 2001 From: tannerpolley Date: Thu, 21 Nov 2024 15:48:39 -0700 Subject: [PATCH 21/21] Removed unnecessary comments --- idaes/models/unit_models/tests/test_stream_scaler.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/idaes/models/unit_models/tests/test_stream_scaler.py b/idaes/models/unit_models/tests/test_stream_scaler.py index 8d58ad868c..608c91be4b 100644 --- a/idaes/models/unit_models/tests/test_stream_scaler.py +++ b/idaes/models/unit_models/tests/test_stream_scaler.py @@ -191,8 +191,6 @@ def btx(self): m.fs.unit.inlet.pressure[0].fix(101325) # Pa m.fs.unit.inlet.mole_frac_comp[0, "benzene"].fix(0.5) m.fs.unit.inlet.mole_frac_comp[0, "toluene"].fix(0.5) - # x = m.fs.unit - # print(variables_set(x)) return m @pytest.mark.build @@ -206,10 +204,6 @@ def test_build(self, btx): assert hasattr(btx.fs.unit.inlet, "temperature") assert hasattr(btx.fs.unit.inlet, "pressure") - # x1 = number_variables(btx) - # x2 = number_total_constraints(btx) - # x3 = number_unused_variables(btx) - assert number_variables(btx) == 9 assert number_total_constraints(btx) == 3 assert number_unused_variables(btx) == 3 @@ -304,8 +298,6 @@ def iapws(self): m.fs.unit = StreamScaler(property_package=m.fs.properties) - # x = m.fs.unit - # print(variables_set(x)) m.fs.unit.multiplier.fix(1) m.fs.unit.inlet.flow_mol[0].fix(100) m.fs.unit.inlet.enth_mol[0].fix(5000)