Source code for idaes.models.unit_models.feed_flash

#################################################################################
# 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-2026 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.
#################################################################################
"""
Standard IDAES Feed block with phase equilibrium.
"""

from enum import Enum

# Import Pyomo libraries
from pyomo.environ import Reference
from pyomo.common.config import ConfigBlock, ConfigValue, In
from pyomo.common.collections import ComponentMap

# Import IDAES cores
from idaes.core import (
    ControlVolume0DBlock,
    declare_process_block_class,
    MaterialBalanceType,
    MomentumBalanceType,
    UnitModelBlockData,
    useDefault,
)
from idaes.core.util.config import is_physical_parameter_block
from idaes.core.util.tables import create_stream_table_dataframe
from idaes.core.util.initialization import fix_state_vars
from idaes.core.scaling import CustomScalerBase

__author__ = "Andrew Lee, Daison Caballero"


# Enumerate options for material balances
class FlashType(Enum):
    """
    Enum of supported flash types.
    """

    isothermal = 1
    isenthalpic = 2


class FeedFlashScaler(CustomScalerBase):
    """
    Default modular scaler for FeedFlash unit model.

    This Scaler relies on the modular scaler for the ControlVolume0D.
    There are no unit model level variables to scale---those that do exist
    are just References for the variables on the ControlVolume0D.
    """

    def variable_scaling_routine(
        self, model, overwrite: bool = False, submodel_scalers: ComponentMap = None
    ):
        self.call_submodel_scaler_method(
            model.control_volume,
            method="variable_scaling_routine",
            submodel_scalers=submodel_scalers,
            overwrite=overwrite,
        )

    def constraint_scaling_routine(
        self, model, overwrite: bool = False, submodel_scalers: ComponentMap = None
    ):
        """
        Routine to apply scaling factors to constraints in model.

        Args:
            model: model to be scaled
            overwrite: whether to overwrite existing scaling factors
            submodel_scalers: dict of Scalers to use for sub-models, keyed by submodel local name

        Returns:
            None
        """
        self.call_submodel_scaler_method(
            model.control_volume,
            method="constraint_scaling_routine",
            submodel_scalers=submodel_scalers,
            overwrite=overwrite,
        )
        if hasattr(model, "isothermal"):
            for t in model.flowsheet().time:
                self.scale_constraint_by_nominal_value(
                    model.isothermal[t], overwrite=overwrite
                )
        if hasattr(model, "isenthalpic"):
            for t in model.flowsheet().time:
                self.scale_constraint_by_nominal_value(
                    model.isenthalpic[t], overwrite=overwrite
                )


[docs] @declare_process_block_class("FeedFlash") class FeedFlashData(UnitModelBlockData): """ Standard Feed block with phase equilibrium """ default_scaler = FeedFlashScaler CONFIG = ConfigBlock() CONFIG.declare( "dynamic", ConfigValue( domain=In([False]), default=False, description="Dynamic model flag - must be False", doc="""Feed units do not support dynamic behavior.""", ), ) CONFIG.declare( "has_holdup", ConfigValue( default=False, domain=In([False]), description="Holdup construction flag - must be False", doc="""Feed units do not have defined volume, thus this must be False.""", ), ) CONFIG.declare( "material_balance_type", ConfigValue( default=MaterialBalanceType.useDefault, domain=In(MaterialBalanceType), description="Material balance construction flag", doc="""Indicates what type of mass balance should be constructed, **default** - MaterialBalanceType.useDefault. **Valid values:** { **MaterialBalanceType.useDefault - refer to property package for default balance type **MaterialBalanceType.none** - exclude material balances, **MaterialBalanceType.componentPhase** - use phase component balances, **MaterialBalanceType.componentTotal** - use total component balances, **MaterialBalanceType.elementTotal** - use total element balances, **MaterialBalanceType.total** - use total material balance.}""", ), ) CONFIG.declare( "flash_type", ConfigValue( default=FlashType.isothermal, domain=In(FlashType), description="Type of flash to perform", doc="""Indicates what type of flash operation should be used. **default** - FlashType.isothermal. **Valid values:** { **FlashType.isothermal** - specify temperature, **FlashType.isenthalpic** - specify enthalpy.}""", ), ) CONFIG.declare( "property_package", ConfigValue( default=useDefault, domain=is_physical_parameter_block, description="Property package to use for control volume", doc="""Property parameter object used to define property calculations, **default** - useDefault. **Valid values:** { **useDefault** - use default package from parent model or flowsheet, **PhysicalParameterObject** - a PhysicalParameterBlock 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.}""", ), )
[docs] def build(self): """ Begin building model. Args: None Returns: None """ # Call UnitModel.build to setup dynamics super(FeedFlashData, self).build() # Build Control Volume self.control_volume = ControlVolume0DBlock( dynamic=self.config.dynamic, has_holdup=self.config.has_holdup, property_package=self.config.property_package, property_package_args=self.config.property_package_args, ) # No need for control volume geometry self.control_volume.add_state_blocks(has_phase_equilibrium=True) self.control_volume.add_material_balances( balance_type=self.config.material_balance_type, has_phase_equilibrium=True ) # Add isothermal constraint if self.config.flash_type == FlashType.isothermal: @self.Constraint(self.flowsheet().time, doc="Isothermal constraint") def isothermal(b, t): return ( b.control_volume.properties_in[t].temperature == b.control_volume.properties_out[t].temperature ) elif self.config.flash_type == FlashType.isenthalpic: @self.Constraint(self.flowsheet().time, doc="Isothermal constraint") def isenthalpic(b, t): cv = b.control_volume return sum( cv.properties_in[t].get_enthalpy_flow_terms(p) for p in cv.properties_in[t].phase_list ) == sum( cv.properties_out[t].get_enthalpy_flow_terms(p) for p in cv.properties_in[t].phase_list ) self.control_volume.add_momentum_balances( balance_type=MomentumBalanceType.pressureTotal ) # Add references to all feed state vars s_vars = self.control_volume.properties_in[ self.flowsheet().time.first() ].define_state_vars() for s in s_vars: l_name = s_vars[s].local_name if s_vars[s].is_indexed(): slicer = self.control_volume.properties_in[:].component(l_name)[...] else: slicer = self.control_volume.properties_in[:].component(l_name) r = Reference(slicer) setattr(self, s, r) # Add Ports self.add_outlet_port()
def _get_stream_table_contents(self, time_point=0): return create_stream_table_dataframe( {"Outlet": self.outlet}, time_point=time_point )
[docs] def fix_initialization_states(self): """ Fix feed state variables Returns: None """ fix_state_vars(self.control_volume.properties_in)