Skip to content

Commit

Permalink
Adding StreamScaler unit model, along with the test and rst file. (#1517
Browse files Browse the repository at this point in the history
)

* pull from MEA development branch

* Added test file for StreamScaler unit model and added the unit model to the init file

* pull from MEA development branch

* Added test file for StreamScaler unit model and added the unit model to the init file

* Added rst file for Stream Scaler Unit Model

* Added stream_scaler.rst to the table of contents

* Created the rst file for the stream_scaler unit model

* Changed the default timeout time from 180 seconds -> 360 seconds due to build.py timing out

* Reformatted with Black

* Removed unused code flagged by Pylint

* Removed unused code flagged by Pylint

* Removed unused code flagged by Pylint

* Removed unused code flagged by Pylint

* Added StreamScalerData class to the init file for unit models

* Added Tanner Polley as a co-author

* Adjusted the details of the additional constraints

* 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

* Ran Black on these files

* Fixed degrees of freedom on doc file

* Revised the documentation for Degrees of Freedom for Stream Scaler

* Removed unnecessary comments

---------

Co-authored-by: Doug A <[email protected]>
Co-authored-by: dallan-keylogic <[email protected]>
Co-authored-by: Ludovico Bianchi <[email protected]>
  • Loading branch information
4 people authored Nov 27, 2024
1 parent b032b22 commit e8c9712
Show file tree
Hide file tree
Showing 6 changed files with 658 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Unit Models
skeleton_unit
statejunction
stoichiometric_reactor
stream_scaler
translator
turbine
valve
Expand Down
Original file line number Diff line number Diff line change
@@ -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 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.

Additional Constraints
----------------------

Stream Scaler Blocks write no additional constraints* (besides those naturally occurring in ``StateBlocks``).

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:

1 change: 1 addition & 0 deletions idaes/models/unit_models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
)
from .shell_and_tube_1d import ShellAndTube1D, ShellAndTubeInitializer
from .skeleton_model import SkeletonUnitModel, SkeletonUnitModelData
from .stream_scaler import StreamScaler, StreamScalerData
from .statejunction import StateJunction, StateJunctionInitializer
from .stoichiometric_reactor import StoichiometricReactor
from .translator import Translator
Expand Down
244 changes: 244 additions & 0 deletions idaes/models/unit_models/stream_scaler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
#################################################################################
# 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 functools import partial

from pyomo.environ import (
Block,
PositiveReals,
units as pyunits,
Var,
)
from pyomo.network import Port
from pyomo.common.config import ConfigBlock, ConfigValue, In

from idaes.core import (
declare_process_block_class,
UnitModelBlockData,
useDefault,
)
from idaes.core.util.config import (
is_physical_parameter_block,
)
from idaes.core.base.var_like_expression import VarLikeExpression
from idaes.core.util.tables import create_stream_table_dataframe
import idaes.core.util.scaling as iscale
import idaes.logger as idaeslog

from idaes.models.unit_models.feed import FeedInitializer as StreamScalerInitializer

__author__ = "Douglas Allan, Tanner Polley"


# Set up logger
_log = idaeslog.getLogger(__name__)


@declare_process_block_class("StreamScaler")
class StreamScalerData(UnitModelBlockData):
"""
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",
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.properties = self.config.property_package.build_state_block(
self.flowsheet().time, doc="Material properties at inlet", **tmp_dict
)
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.properties)
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.scaled_expressions.add_component(
var_name, VarLikeExpression(var.index_set(), rule=rule)
)
expr = getattr(self.scaled_expressions, 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.
"""

# Create solver

# Initialize inlet state blocks
flags = blk.properties.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.properties.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
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)
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)
Loading

0 comments on commit e8c9712

Please sign in to comment.