Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solid-Liquid Separator Base Model #1283

Merged
merged 11 commits into from
Nov 7, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ Unit Models
translator
turbine
valve

solid_liquid/index
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Solid-Liquid Unit Models
========================

This library contains models specific for solid-liquid processing operations (although some of them may also be applicable in other circumstances). Models in this library assume streams consist of separate solid and liquid phases (with separate property packages) and thus all streams are represented by separate solid and liquid ports. Separation of solids and liquids is generally assumed to result in a concentrated solid-liquid stream and a clarified liquid stream.

.. toctree::
:maxdepth: 1

sl_separator
thickener0d
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
Generic Solid-Liquid Separator
==============================

The ``SLSeparator`` unit model is a general purpose model for representing the separation of mixed streams containing solids and liquids, such as filtration and thickening. The model assumes that the mixed stream is separated into a concentrated solid-liquid stream and a stream of clarified liquid.

Degrees of Freedom
------------------

The ``SLSeparator`` model has 1 degree of freedom, which is generally the liquid recovery fraction. Note that due to the assumption that all solids report to the concentrated outlet, the solid inlet and outlet Ports link to the same variables (and thus cannot be fixed independently).

Model Structure
---------------

The model consists of two parts. The solid phase is represented by a single StateBlock as all solids in the inlet are assumed to report to the concentrated outlet. The liquid phase is represented by a standard :ref:`Separator <reference_guides/model_libraries/generic/unit_models/separator:Separator>` block which splits the incoming liquid between the concentrated and clarified outlets.

The ``SLSeparator`` unit model has a total of 5 ports:

* ``solid_inlet`` representing the solids in the incoming stream,
* ``liquid_inlet`` representing the liquid in the incoming stream,
* ``solid_outlet`` representing the solids in the concentrated stream,
* ``retained_liquid_outlet`` representing the liquid in the concentrated stream, and,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* ``retained_liquid_outlet`` representing the liquid in the concentrated stream, and,
* ``retained_liquid_outlet`` representing the liquid in the concentrated stream

Don't think the "and" is necessary in this list. People might assume that some text is supposed to follow but is just missing

* ``recovered_liquid_outlet`` representing the liquid in the clarified stream.

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

The ``SLSeparator`` model adds no additional constraints beyond those written by the :ref:`Separator <reference_guides/model_libraries/generic/unit_models/separator:Separator>` and StateBlocks.

Variables
---------

The ``SLSeparator`` adds one Reference to a variable in the :ref:`Separator <reference_guides/model_libraries/generic/unit_models/separator:Separator>`.

============ ================ ================================================================================================
Variable Name Notes
============ ================ ================================================================================================
:math:`R_t` liquid_recovery Fraction of liquid which reports to the clarified stream, reference to ``split.split_fraction``
============ ================ ================================================================================================

.. module:: idaes.models.unit_models.solid_liquid.sl_separator

SLSeparator Class
-----------------

.. autoclass:: SLSeparator
:members:

SLSeparatorData Class
---------------------

.. autoclass:: SLSeparatorData
:members:
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
Thickener (0D)
==============

The ``Thickener0D`` unit model is an extension of the :ref:`SLSeparator <reference_guides/model_libraries/generic/unit_models/solid_liquid/sl_separator:Generic Solid-Liquid Separator>` model which adds constraints to estimate the area and height of a vessel required to achieve the desired separation of solid and liquid based on experimental measurements of the settling velocity. This model is based on correlations described in:

[1] Coulson & Richardson's Chemical Engineering, Volume 2 Particle Technology & Separation Processes (4th Ed.), Butterworth-Heinemann (2001)

Sizing Thickeners and Pinch Point
---------------------------------

The approach for sizing the thickener vessel used in this model relies on identifying a pinch point in the thickener which is the limiting condition for the settling velocity of the suspension as described in [1]. The pinch point is described by the condition:

.. math:: max \left( \frac{(Y - Y_{under})}{u(Y)} \right)

where :math:`Y` is the mass based liquid-to-solid ratio, :math:`u(Y)` is the settling velocity of the suspension as a function of :math:`Y` and :math:`Y_{under}` is the liquid-solid ratio at the thickener underflow. Correlations exist which can predict :math:`u(Y)` however in many cases it is necessary to measure this experimentally. Additionally, whilst there are techniques for embedding the maximization operation within an equation-oriented model, these approaches tend to be highly non-linear and require the user to provide good values for scaling parameters and thus have not been implemented yet.

In the current implementation of the model, :math:`Y_{pinch}` and :math:`u_{pinch}` are created as user-defined input variables which should generally be fixed at appropriate values. Users may choose to add a correlation for :math:`u(Y)` if they choose.

Degrees of Freedom
------------------

The ``Thickener0D`` model has 5 degrees of freedom, which is are generally chosen from:
andrewlee94 marked this conversation as resolved.
Show resolved Hide resolved

* the liquid recovery fraction or underflow liquid-to-solid ratio,
* the liquid-to-solid ratio and settling velocity at the pinch (critical) point,
* the cross-sectional area of the thickener,
* the depth of the clarification zone,
* the depth of the sedimentation zone or the required settling time (experimental).

Model Structure
---------------

The ``Thickener0D`` model has the same structure as the :ref:`SLSeparator <reference_guides/model_libraries/generic/unit_models/solid_liquid/sl_separator:Model Structure>`.

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

The ``Thickener0D`` model adds the following additional constraints beyond those written by the :ref:`SLSeparator <reference_guides/model_libraries/generic/unit_models/solid_liquid/sl_separator:Additional Constraints>`.

The cross-sectional area of the thickener is calculated using:

.. math:: A = \frac{S_t}{\rho_{liq, t}} \times \frac{(Y_{pinch, t}-Y_{under, t})}{u_{pinch, t}}

where :math:`A` is the cross-section area of the thickener, :math:`S_t` is the mass flowrate of solids entering the thickener, :math:`\rho_{liq}` is the mass density of the liquid phase, :math:`Y_{pinch}` and :math:`Y_{under}` are the mass-based liquid-to-solid ratios at the pinch point and underflow respectively and :math:`u_{pinch}` is the sedimentation velocity of the suspension at the pinch point (Eqn 5.54, pg. 198 in [1]).

The liquid-solid ratio at the underflow can be calculated using:

.. math:: Y_{under, t} = \frac{L_{under, t}}{S_t}

where :math:`L_{under}` is the liquid mass flowrate at the underflow.

The total height of the thickener is calculated using:

.. math:: H = \frac{S_t \tau_{t}}{A\rho_{sol, t}} \times \left( 1+\frac{\rho_{sol}}{\rho_{liq}} \times Y_{avg} \right) + H_{clarified}

where :math:`H` is the total height of the thickener, :math:`H_{clarified}` is the height of the clarification zone, :math:`\tau` is the empirically measured settling time required to achieve the desired underflow conditions and :math:`Y_avg` is the average liquid-solid ratio in the thickener (assumed to be linear) (Eqn 5.55, pg. 198 in [1]).

Variables
---------

The ``Thickener0D`` adds the following variables in addition to those in the :ref:`SLSeparator <reference_guides/model_libraries/generic/unit_models/solid_liquid/sl_separator:Variables>`.

===================== ======================== ====== =====================================================================
Variable Name Index Notes
===================== ======================== ====== =====================================================================
:math:`A` area None Cross-sectional area of thickener
:math:`H` height None Total height of thickener
:math:`H_{clarified}` height_clarified None Height of clarification zone in thickener (height above feed point)
:math:`u_{pinch}` settling_velocity_pinch time Settling velocity of suspension at pinch point
:math:`Y_{pinch}` liquid_solid_pinch time Liquid-solid ratio at pinch point
:math:`Y_{under}` liquid_solid_underflow time Liquid-solid ratio at underflow
:math:`\tau` settling_time time Settling time in thickener
===================== ======================== ====== =====================================================================

.. module:: idaes.models.unit_models.solid_liquid.thickener

SLSeparator Class
-----------------

.. autoclass:: Thickener0D
:members:

SLSeparatorData Class
---------------------

.. autoclass:: Thickener0DData
:members:
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
45 changes: 44 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,47 @@ def initialize(self, **kwargs):
)

def _get_performance_contents(self, time_point=0):
raise NotImplementedError()
# Due to the flexibility of the MSContactor and the number of possible terms
# that could be included here, we will leave this up to the user to define.
return {}

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

sblocks = {}
for stream, pconfig in self.config.streams.items():
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[time_point]

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[time_point, outlet]

for n, v in sblocks.items():
dvars = v.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
14 changes: 14 additions & 0 deletions idaes/models/unit_models/solid_liquid/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#################################################################################
# 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
from .thickener import Thickener0D
Loading
Loading