Skip to content

Commit

Permalink
Adding SL separator
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewlee94 committed Nov 3, 2023
1 parent b278031 commit 47ae9d8
Show file tree
Hide file tree
Showing 7 changed files with 928 additions and 2 deletions.
2 changes: 1 addition & 1 deletion idaes/models/unit_models/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class FlashData(UnitModelBlockData):
**default** - EnergySplittingType.equal_temperature.
**Valid values:** {
**EnergySplittingType.equal_temperature** - outlet temperatures equal inlet
**EnergySplittingType.equal_molar_enthalpy** - oulet molar enthalpies equal
**EnergySplittingType.equal_molar_enthalpy** - outlet molar enthalpies equal
inlet,
**EnergySplittingType.enthalpy_split** - apply split fractions to enthalpy
flows.}""",
Expand Down
44 changes: 43 additions & 1 deletion idaes/models/unit_models/mscontactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"""
from functools import partial

from pandas import DataFrame

# Import Pyomo libraries
from pyomo.environ import Block, Constraint, RangeSet, Reals, Set, units, Var
from pyomo.common.config import ConfigDict, ConfigValue, Bool, In
Expand Down Expand Up @@ -42,6 +44,7 @@
from idaes.core.solvers import get_solver
from idaes.core.util.model_serializer import to_json, from_json
import idaes.logger as idaeslog
from idaes.core.util.units_of_measurement import report_quantity

__author__ = "Andrew Lee"

Expand Down Expand Up @@ -902,7 +905,46 @@ def initialize(self, **kwargs):
)

def _get_performance_contents(self, time_point=0):
raise NotImplementedError()
assert False
return {"vars": {"Liquid Recovery": self.liquid_recovery[time_point]}}

def _get_stream_table_contents(self, time_point=0):
stream_attributes = {}
stream_attributes["Units"] = {}

sblocks = {}
for stream, pconfig in self.config.streams.keys():
sblock = getattr(self, stream)
flow_dir = pconfig.flow_direction

if pconfig.has_feed:
inlet_state = getattr(self, stream + "_inlet_state")
sblocks[stream + " Inlet"] = inlet_state

if flow_dir == FlowDirection.forward:
outlet = self.elements.last()
elif flow_dir == FlowDirection.backward:
outlet = self.elements.first()
else:
raise BurntToast("If/else overrun when constructing stream table")

sblocks[stream + " Outlet"] = sblock[outlet]

for n, v in sblocks.items():
dvars = v[time_point].define_display_vars()

stream_attributes[n] = {}

for k in dvars:
for i in dvars[k].keys():
stream_key = k if i is None else f"{k} {i}"

quant = report_quantity(dvars[k][i])

stream_attributes[n][stream_key] = quant.m
stream_attributes["Units"][stream_key] = quant.u

return DataFrame.from_dict(stream_attributes, orient="columns")


def _get_state_blocks(blk, t, s, stream):
Expand Down
13 changes: 13 additions & 0 deletions idaes/models/unit_models/solid_liquid/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#################################################################################
# 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.
#################################################################################
from .sl_separator import SLSeparator
301 changes: 301 additions & 0 deletions idaes/models/unit_models/solid_liquid/sl_separator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
#################################################################################
# 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.
#################################################################################
"""
Base model for solid-liquid separations.
This model is intended to form the basis for solid-liquid separations where a mixed
stream enters the unit and is separated into a concentrated solid-liquid stream and
a stream of pure liquid (e.g. filers, thickeners, etc.)
This model assumes:
* separate property packages and ports for the solid and liquid
streams.
* two inlet Ports (solid and liquid)
* three outlet Ports (solids, liquid with solids, separated liquids).
"""
# Import Python libraries
import logging
from pandas import DataFrame

# Import Pyomo libraries
from pyomo.environ import Constraint, Reference
from pyomo.common.config import ConfigBlock, ConfigValue, In, Bool
from pyomo.network import Port

# Import IDAES cores
from idaes.core import (
declare_process_block_class,
MaterialBalanceType,
MomentumBalanceType,
UnitModelBlockData,
useDefault,
)
from idaes.models.unit_models.separator import (
Separator,
SplittingType,
EnergySplittingType,
)
from idaes.core.initialization import BlockTriangularizationInitializer
from idaes.core.util.config import is_physical_parameter_block
from idaes.core.util.units_of_measurement import report_quantity


__author__ = "Andrew Lee"


# Set up logger
logger = logging.getLogger("idaes.unit_model")


@declare_process_block_class("SLSeparator")
class SLSeparatorData(UnitModelBlockData):
"""
Standard Solid-Liquid Separator Unit Model Class
"""

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. Flash units do not support dynamic behavior.""",
),
)
CONFIG.declare(
"has_holdup",
ConfigValue(
default=False,
domain=In([False]),
description="Holdup construction flag - must be False",
doc="""Indicates whether holdup terms should be constructed or not.
**default** - False. Flash 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(
"momentum_balance_type",
ConfigValue(
default=MomentumBalanceType.pressureTotal,
domain=In(MomentumBalanceType),
description="Momentum balance construction flag",
doc="""Indicates what type of momentum balance should be constructed,
**default** - MomentumBalanceType.pressureTotal.
**Valid values:** {
**MomentumBalanceType.none** - exclude momentum balances,
**MomentumBalanceType.pressureTotal** - single pressure balance for material,
**MomentumBalanceType.pressurePhase** - pressure balances for each phase,
**MomentumBalanceType.momentumTotal** - single momentum balance for material,
**MomentumBalanceType.momentumPhase** - momentum balances for each phase.}""",
),
)
CONFIG.declare(
"energy_split_basis",
ConfigValue(
default=EnergySplittingType.equal_temperature,
domain=EnergySplittingType,
description="Type of constraint to write for energy splitting",
doc="""Argument indicating basis to use for splitting energy this is
not used for when ideal_separation == True.
**default** - EnergySplittingType.equal_temperature.
**Valid values:** {
**EnergySplittingType.equal_temperature** - outlet temperatures equal inlet
**EnergySplittingType.equal_molar_enthalpy** - outlet molar enthalpies equal
inlet,
**EnergySplittingType.enthalpy_split** - apply split fractions to enthalpy
flows.}""",
),
)
CONFIG.declare(
"solid_property_package",
ConfigValue(
default=useDefault,
domain=is_physical_parameter_block,
description="Property package to use for solid phase",
doc="""Property parameter object used to define solid phase property
calculations,
**default** - useDefault.
**Valid values:** {
**useDefault** - use default package from parent model or flowsheet,
**PropertyParameterObject** - a PropertyParameterBlock object.}""",
),
)
CONFIG.declare(
"solid_property_package_args",
ConfigBlock(
implicit=True,
description="Arguments to use for constructing solid phase property packages",
doc="""A ConfigBlock with arguments to be passed to a solid phase property
block(s) and used when constructing these,
**default** - None.
**Valid values:** {
see property package for documentation.}""",
),
)
CONFIG.declare(
"liquid_property_package",
ConfigValue(
default=useDefault,
domain=is_physical_parameter_block,
description="Property package to use for liquid phase",
doc="""Property parameter object used to define liquid phase property
calculations,
**default** - useDefault.
**Valid values:** {
**useDefault** - use default package from parent model or flowsheet,
**PropertyParameterObject** - a PropertyParameterBlock object.}""",
),
)
CONFIG.declare(
"liquid_property_package_args",
ConfigBlock(
implicit=True,
description="Arguments to use for constructing liquid phase property packages",
doc="""A ConfigBlock with arguments to be passed to a liquid phase property
block(s) and used when constructing these,
**default** - None.
**Valid values:** {
see property package for documentation.}""",
),
)

default_initializer = BlockTriangularizationInitializer

def build(self):
"""
Begin building model (pre-DAE transformation).
Args:
None
Returns:
None
"""
# Call UnitModel.build to setup dynamics
super().build()

# Build Solid Phase - inlet=outlet so only need a StateBlock
# Setup StateBlock argument dict
tmp_dict = dict(**self.config.solid_property_package_args)
tmp_dict["has_phase_equilibrium"] = False
tmp_dict["defined_state"] = True

self.solid_state = self.config.solid_property_package.build_state_block(
self.flowsheet().time, doc="Solid properties in separator", **tmp_dict
)

# Add Solids Ports
self.add_port(
name="solid_inlet", block=self.solid_state, doc="Solid inlet to separator"
)
self.add_port(
name="solid_outlet",
block=self.solid_state,
doc="Solid outlet from separator",
)

# Add Liquid Inlet
# Setup StateBlock argument dict
tmp_dict = dict(**self.config.liquid_property_package_args)
tmp_dict["has_phase_equilibrium"] = False
tmp_dict["defined_state"] = True

self.liquid_inlet_state = self.config.liquid_property_package.build_state_block(
self.flowsheet().time,
doc="Liquid properties at inlet to separator",
**tmp_dict,
)

self.split = Separator(
property_package=self.config.liquid_property_package,
property_package_args=self.config.liquid_property_package_args,
outlet_list=["recovered", "retained"],
split_basis=SplittingType.totalFlow,
ideal_separation=False,
mixed_state_block=self.liquid_inlet_state,
has_phase_equilibrium=False,
material_balance_type=self.config.material_balance_type,
momentum_balance_type=self.config.momentum_balance_type,
energy_split_basis=self.config.energy_split_basis,
)

# Add liquid ports
self.add_port(
name="liquid_inlet",
block=self.liquid_inlet_state,
doc="Liquid inlet to separator",
)
self.recovered_liquid_outlet = Port(extends=self.split.recovered)
self.retained_liquid_outlet = Port(extends=self.split.retained)

# Add liquid recovery
self.liquid_recovery = Reference(self.split.split_fraction[:, "recovered"])

def initialize(self, **kwargs):
raise NotImplementedError(
"The SLSeparator unit model does not support the old initialization API. "
"Please use the new API (InitializerObjects) instead."
)

def _get_performance_contents(self, time_point=0):

return {"vars": {"Liquid Recovery": self.liquid_recovery[time_point]}}

def _get_stream_table_contents(self, time_point=0):
stream_attributes = {}
stream_attributes["Units"] = {}

sblocks = {
"Solid Inlet": self.solid_state,
"Liquid Inlet": self.liquid_inlet_state,
"Solid Outlet": self.solid_state,
"Liquid in Solids Outlet": self.split.retained_state,
"Recovered Liquid Outlet": self.split.recovered_state,
}

for n, v in sblocks.items():
dvars = v[time_point].define_display_vars()

stream_attributes[n] = {}

for k in dvars:
for i in dvars[k].keys():
stream_key = k if i is None else f"{k} {i}"

quant = report_quantity(dvars[k][i])

stream_attributes[n][stream_key] = quant.m
stream_attributes["Units"][stream_key] = quant.u

return DataFrame.from_dict(stream_attributes, orient="columns")
Empty file.
Loading

0 comments on commit 47ae9d8

Please sign in to comment.