Skip to content

Commit

Permalink
Add documentation and change the name of ion_dspmde (#858)
Browse files Browse the repository at this point in the history
* init

* docs/technical_reference/property_models/DSPMDE.rst

* save work

* save work

* rough draft completed, changed pp name

* change doc name

* add index

* save work

* changed all dspmde names

* debug

* sphinx debug

* sphinx debug

* format improvement

* format improvement

* format improvement

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* sphinx debug

* format

* format

* format

* format

* format

* test black format for doc

* missed name change

* missed name change\
  • Loading branch information
lbibl authored Nov 30, 2022
1 parent 80e27cd commit 0331dcf
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 149 deletions.
2 changes: 1 addition & 1 deletion docs/technical_reference/property_models/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Property Models
coagulation
ASM1
ASM2D

mc_aq_sol
133 changes: 133 additions & 0 deletions docs/technical_reference/property_models/mc_aq_sol.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
Multi-Component Aqueous Solution (MCAS) Property Package
=========================================================

This property package implements property relationships for an aqueous solution that may contain multiple neutral and/or ionic solutes.

This MCAS property package
* sets H2O as the solvent;
* supports multiple solute components including ions and neutral molecules;
* supports only liquid phase;
* uses molar flow rate (in mol/s), temperature and pressure as the initial state variables;
* does not support dynamics.

Classes
-------
.. currentmodule:: watertap.property_models.multicomp_aq_sol_prop_pack

.. autoclass:: MCASParameterBlock
:members:
:noindex:

.. autoclass:: MCASParameterData
:members:
:noindex:

.. autoclass:: _MCASStateBlock
:members:
:noindex:

.. autoclass:: MCASStateBlockData
:members:
:noindex:

Sets
----
.. csv-table::
:header: "Description", "Symbol", "Members"

"AqueousPhase", ":math:`p`", "{'Liq'}"
"component_list", ":math:`j`", "{'H2O', solute_list :sup:`1`}"
"solute_set", ":math:`j`", "{neutral species in solute_list} :sup:`2`"
"ion_set", ":math:`j`", "{ionic species in solute_list}"
"cation_set", ":math:`j`", "{cationic species in solute_list}"
"anion_set", ":math:`j`", "{anionic species in solute_list}"

**Notes**
:sup:`1` solute_list is provided by a necessary configuration to use this property package.
:sup:`2` In the implementing codes, the "solute_set" was declared to include only neutral species for current programming convenience; in other places throughout this document, "solute" by itself includes both ionic and neutral species solvated by water.

State variables
---------------
.. csv-table::
:header: "Description", "Symbol", "Coded Var Name", "Index", "Unit"

"Component molar flow rate", ":math:`N`", "flow_mol_phase_comp", "[p, j]", ":math:`\text{mol s}^{-1}`"
"Temperature", ":math:`T`", "temperature", "None", ":math:`\text{K}`"
"Pressure", ":math:`P`", "pressure", "None", ":math:`\text{Pa}`"

Calculated Properties
---------------------
.. csv-table::
:header: "Description", "Symbol", "Coded Var Name", "Index", "Unit", "Calculation Methods"

"Component mass flow rate", ":math:`M`", "flow_mass_phase_comp", "[p, j]", ":math:`\text{kg s}^{-1}`", ":math:`M=Nm_N`"
"Component charge-equivalent molar flow rate", ":math:`\tilde{N}`", "flow_equiv_phase_comp", "[p, j]", ":math:`\text{mol s}^{-1}`", ":math:`\tilde{N}=N\left|z\right|`"
"Component charge-equivalent molar concentration", ":math:`\tilde{n}`", "conc_equiv_phase_comp", "[p, j]", ":math:`\text{mol m}^{-3}`", ":math:`\tilde{n}=n\left|z\right|`"
"Component mass fraction", ":math:`x`", "mass_frac_phase_comp", "[p, j]", ":math:`\text{dimensionless}`", ":math:`x_j=\frac{M_j}{\sum_j{M_j}}`"
"Mass density of aqueous phase", ":math:`\rho`", "dens_mass_phase", "[p]", ":math:`\text{kg m}^{-3}`", ":math:`\rho=1000 \text{kg m}^{-3}` or :math:`\rho=\rho_w + \textbf{f} \left(\sum_{j\in solute}{x_j}, T\right)` :sup:`1`"
"Mass density of solvent water", ":math:`\rho_w`", "dens_mass_w_phase", "[p]", ":math:`\text{kg m}^{-3}`",":math:`\rho_w=\textbf{f}\left(T\right)` :sup:`1`"
"Phase volumetric flowrate", ":math:`Q`", "flow_vol_phase", "[p]", ":math:`\text{m}^3\text{ } \text{s}^{-1}`", ":math:`Q=\frac{\sum_j{N_j m_{Nj}}}{\rho}`"
"Total volumetric flowrate", ":math:`Q_{tot}`", "flow_vol", "None", ":math:`\text{m}^3\text{ } \text{s}^{-1}`",":math:`Q_{tot}=\sum_p{Q_p}`"
"Component molar concentration", ":math:`n`", "conc_mol_phase_comp", "[p, j]", ":math:`\text{mol m}^{-3}`",":math:`nm_N=m`"
"Component mass concentration", ":math:`m`", "conc_mass_phase_comp", "[p, j]", ":math:`\text{kg m}^{-3}`",":math:`m=\rho x`"
"Component molar fraction", ":math:`y`", "mole_frac_phase_comp", "[p, j]", ":math:`\text{dimensionless}`", ":math:`y_j=\frac{N_j}{\sum_j{N_j}}`"
"Component molality", ":math:`b`", "molality_phase_comp", "[p, j]", ":math:`\text{mol kg}^{-1}`",":math:`b=\frac{N}{N_{H_2O} m_{N\text{H_2O}}}`"
"Component diffusivity", ":math:`D`", "diffus_phase_comp", "[p, j]", ":math:`\text{m}^2 \text{ } \text{s}^{-1}`", ":math:`D=` input from users"
"Dynamic viscosity", ":math:`\mu`", "visc_d_phase", "[p]", ":math:`\text{Pa s}`", ":math:`\mu=` input from users"
"Kinematic viscosity", ":math:`\nu`", "visc_k_phase", "[p]", ":math:`\text{m}^2 \text{ s}^{-1}`",":math:`\nu=\mu\rho^{-1}`"
"Phase osmotic pressure", ":math:`\Pi`", "pressure_osm_phase", "[p]", ":math:`\text{Pa}`",":math:`\Pi=RT\sum_{j\in solute}{n_j}`"
"Component stokes radius", ":math:`r_h`", "radius_stokes_comp", "[j]", ":math:`\text{m}`", ":math:`r_h=` input from users"
"Component molecular weight", ":math:`m_N`", "mw_comp", "[j]", ":math:`\text{kg mol}^{-1}`",":math:`m_N=` input from users"
"Ion component electrical mobility", ":math:`\mu_e`", "elec_mobility_phase_comp", "[p,j]", ":math:`\text{m}^2\text{ }\text{V}^{-1}\text{ }\text{s}^{-1}`", ":math:`\mu_e=` input from users or :math:`\mu_e=\frac{D\left|z\right|F}{RT}`"
"Ion component transport number", ":math:`t`", "trans_num_phase_comp", "[p, j]", ":math:`\text{dimensionless}`", ":math:`t=` input from users or :math:`t_j=\frac{\left|z_j\right|\mu_{ej} n_j}{\sum_{j\in ion}{\left|z_j\right|\mu_{ej} n_j}}`"
"Phase equivalent conductivity", ":math:`\Lambda`", "equiv_conductivity_phase", "[p]", ":math:`\text{m}^2 \text{ } \Omega^{-1} \text{ mol}^{-1}`", ":math:`\Lambda=` input from users or :math:`\Lambda=\frac{\sum_{j\in ion}{F\left|z_j\right|\mu_{ej} n_j}}{\sum_{j\in cation}{\left|z_j\right|n_j}}`"
"Phase electrical conductivity", ":math:`\lambda`", "elec_cond_phase", "[p]", ":math:`\Omega^{-1} \text{ m}^{-1}`", ":math:`\lambda=\Lambda\sum_{j\in cation}{\left|z_j\right|n_j}`"
"Ion component charge", ":math:`z`", "charge_comp", "[j]", ":math:`\text{dimensionless}`", ":math:`z=` input from users"
"Component activity coefficient", ":math:`\gamma`", "act_coeff_phase_comp", "[j]", ":math:`\text{dimensionless}`", ":math:`\gamma=` input from users"
"Dielectric constant", ":math:`\epsilon`", "dielectric_constant", "none", ":math:`\text{dimensionless}`", ":math:`\epsilon=` input from users"
"Debye-Huckel constant", ":math:`A`", "deby_huckel_constant", "none", ":math:`\text{dimensionless}`", ":math:`A=\frac{\left(2 \pi N_A\right)^{0.5}}{log(10)} \left(\frac{\textbf{e}^2}{4 \pi \epsilon \epsilon_0 kT}\right)^{\frac{3}{2}}`"
"Ionic Strength", ":math:`I`", "ionic_strength_molal", "none", ":math:`\text{mol kg}^{-1}`", ":math:`I=0.5\sum_{j\in ion}{z_j^2b_j}`"

**Notes**
:sup:`1` :math:`\textbf{f}(\cdot)` refers to empirical correlations of phase or solvent mass density to seawater salinity and temperature following the study of Sharqawy et al. (2010).

Physical/chemical constants
---------------------------
.. csv-table::
:header: "Description", "Symbol", "Value", "Unit"

"Idea gas constant", ":math:`R`", "8.3145", ":math:`\text{J mol}^{-1} \text{K}^{-1}`"
"Faraday constant", ":math:`F`", "96485.33", ":math:`\text{C mol}^{-1}`"
"Avogadro constant", ":math:`N_A`", "6.022e23", ":math:`\text{dimensionless}`"
"Boltzmann constant", ":math:`k`", "1.381e-23", ":math:`\text{J K}^{-1}`"
"Vacuum permittivity", ":math:`\epsilon_0`", "8.854e-12", ":math:`\text{F m}^{-1}`"
"Elementary charge", ":math:`\textbf{e}`", "1.602e-19", ":math:`\text{C}`"

Scaling
-------
A comprehensive scaling factor calculation method is coded in this property package. Among the state variables (:math:`N, T, \text{and } p`), default scaling factors for :math:`T` and :math:`p` were set and do not need users' input, while, for :math:`N`, usually require a user input via an interface. The coding interface to set defalut scaling factor for :math:`N` and call the scaling calculation for other variables is the following.

.. code-block::
m.fs.properties.set_default_scaling('flow_mol_phase_comp', 1e2, index=('Liq','{component name}'))
# m is the model name, and fs is the instanced flowsheet block of m.
calculate_scaling_factors(m)
Users also have the authority to set a scaling factor for non-state variables via the following codes:

.. code-block::
import idaes.core.util.scaling as iscale #import the needed utility package
...
iscale.set_scaling_factor(m.fs.properties.{property_name}, 100)
Proper scaling of variables is, in many cases, crucial to solver's performance in finding an optimal solution of a problem. While designing scaling can have a mathematical sophistication, a general rule is to scale all variables as close to 1 as possible, e.g., in the range of 1e-2 to 1e2.


Reference
---------

M.H. Sharqawy, J.H.L. V, S.M. Zubair, Thermophysical properties of seawater: a review of existing correlations and data, Desalination and Water Treatment. 16 (2010) 354–380. https://doi.org/10.5004/dwt.2010.1079. (2017 corrections provided at http://web.mit.edu/seawater )

Bard, A. J., Faulkner, L. R., & White, H. S. (2022). Electrochemical methods: fundamentals and applications. John Wiley & Sons.

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from watertap.costing.watertap_costing_package import (
WaterTAPCosting,
)
from watertap.property_models.ion_DSPMDE_prop_pack import DSPMDEParameterBlock
from watertap.property_models.multicomp_aq_sol_prop_pack import MCASParameterBlock

__author__ = "Xiangyu Bi"

Expand Down Expand Up @@ -70,7 +70,7 @@ def build():
"elec_mobility_data": {("Liq", "Na_+"): 5.19e-8, ("Liq", "Cl_-"): 7.92e-8},
"charge": {"Na_+": 1, "Cl_-": -1},
}
m.fs.properties = DSPMDEParameterBlock(**ion_dict)
m.fs.properties = MCASParameterBlock(**ion_dict)
m.fs.costing = WaterTAPCosting()
m.fs.feed = Feed(property_package=m.fs.properties)
m.fs.separator = Separator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
WaterTAPCosting,
make_capital_cost_var,
)
from watertap.property_models.ion_DSPMDE_prop_pack import DSPMDEParameterBlock
from watertap.property_models.multicomp_aq_sol_prop_pack import MCASParameterBlock
import watertap.examples.flowsheets.electrodialysis.electrodialysis_1stack as edfs

__author__ = "Xiangyu Bi"
Expand All @@ -69,7 +69,7 @@ def test_build_model(self, electrodialysis_1D1stack):
# Test basic build
assert isinstance(m, ConcreteModel)
assert isinstance(m.fs, FlowsheetBlock)
assert isinstance(m.fs.properties, DSPMDEParameterBlock)
assert isinstance(m.fs.properties, MCASParameterBlock)
assert isinstance(m.fs.costing, Block)
assert isinstance(m.fs.feed, Feed)
assert isinstance(m.fs.separator, Separator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
#
###############################################################################
"""
Initial property package for multi-ionic system for use in the
Donnan Steric Pore Model with Dielectric Exclusion (DSPM-DE)
This property package computes a multi-component aqueous solution that can
contain ionic and/or neutral solute species. It supports basic calculation
of component quanitities and some physical, chemical and electrical properties.
This property package was formerly named as "ion_DSPMDE_prop_pack" for its use of
Donnan Steric Pore Model with Dielectric Exclusion (DSPMDE).
"""

# TODO:
Expand Down Expand Up @@ -101,8 +105,8 @@ class TransportNumberCalculation(Enum):
ElectricalMobility = auto()


@declare_process_block_class("DSPMDEParameterBlock")
class DSPMDEParameterData(PhysicalParameterBlock):
@declare_process_block_class("MCASParameterBlock")
class MCASParameterData(PhysicalParameterBlock):
CONFIG = PhysicalParameterBlock.CONFIG()

CONFIG.declare(
Expand Down Expand Up @@ -262,7 +266,7 @@ def build(self):
"""
super().build()

self._state_block_class = DSPMDEStateBlock
self._state_block_class = MCASStateBlock

# phases
self.Liq = AqueousPhase()
Expand Down Expand Up @@ -495,10 +499,10 @@ def define_metadata(cls, obj):
)


class _DSPMDEStateBlock(StateBlock):
class _MCASStateBlock(StateBlock):
"""
This Class contains methods which should be applied to Property Blocks as a
whole, rather than individual elements of indexed Property Blocks.
This Class contains methods which should be applied to Property Blocks as a whole, rather
than individual elements of indexed Property Blocks.
"""

def initialize(
Expand All @@ -512,44 +516,38 @@ def initialize(
):
"""
Initialization routine for property package.
Keyword Arguments:
state_args : Dictionary with initial guesses for the state vars
chosen. Note that if this method is triggered
through the control volume, and if initial guesses
were not provided at the unit model level, the
control volume passes the inlet values as initial
guess.The keys for the state_args dictionary are:
flow_mol_phase_comp : value at which to initialize
phase component flows
pressure : value at which to initialize pressure
temperature : value at which to initialize temperature
chosen. Note that if this method is triggered through the control
volume, and if initial guesses were not provided at the unit model
level, the control volume passes the inlet values as initial guess.
The keys for the state_args dictionary are:
flow_mol_phase_comp : value to initialize phase component flows;
pressure : value at which to initialize pressure;
temperature : value at which to initialize temperature.
outlvl : sets output level of initialization routine (default=idaeslog.NOTSET)
optarg : solver options dictionary object (default=None)
state_vars_fixed: Flag to denote if state vars have already been
fixed.
- True - states have already been fixed by the
control volume 1D. Control volume 0D
does not fix the state vars, so will
be False if this state block is used
with 0D blocks.
- False - states have not been fixed. The state
block will deal with fixing/unfixing.
solver : Solver object to use during initialization if None is provided
it will use the default solver for IDAES (default = None)
state_vars_fixed : Flag to denote if state vars have already
been fixed.
- True - states have already been fixed by the control volume
1D. Control volume 0D does not fix the state vars, so will be
False if this state block is used with 0D blocks.
- False - states have not been fixed. The state block will deal
with fixing/unfixing.
solver : Solver object to use during initialization. If None
is provided, it will use the default solver for IDAES (default = None)
hold_state : flag indicating whether the initialization routine
should unfix any state variables fixed during
initialization (default=False).
- 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
- True - state 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.
If hold_states is True, returns a dict containing flags for which states were fixed
during initialization.
"""
# Get loggers
init_log = idaeslog.getInitLogger(self.name, outlvl, tag="properties")
Expand Down Expand Up @@ -761,20 +759,23 @@ def calculate_state(
optarg=None,
):
"""
Solves state blocks given a set of variables and their values. These variables can
be state variables or properties. This method is typically used before
initialization to solve for state variables because non-state variables (i.e. properties)
cannot be fixed in initialization routines.
Solves state blocks given a set of variables and their values. These variables can be
state variables or properties. This method is typically used before initialization to
solve for state variables because non-state variables (i.e. properties) cannot be fixed
in initialization routines.
Keyword Arguments:
var_args : dictionary with variables and their values, they can be state variables or properties
var_args : dictionary with variables and their values, they
can be state variables or properties
{(VAR_NAME, INDEX): VALUE}
hold_state : flag indicating whether all of the state variables should be fixed after calculate state.
hold_state : flag indicating whether all of the state
variables should be fixed after calculate state.
True - State variables will be fixed.
False - State variables will remain unfixed, unless already fixed.
outlvl : idaes logger object that sets output level of solve call (default=idaeslog.NOTSET)
solver : solver name string if None is provided the default solver
for IDAES will be used (default = None)
outlvl : idaes logger object that sets output level of solve
call (default=idaeslog.NOTSET)
solver : solver name string if None is provided the default
solver for IDAES will be used (default = None)
optarg : solver options dictionary object (default={})
Returns:
Expand Down Expand Up @@ -862,8 +863,8 @@ def calculate_state(
return results


@declare_process_block_class("DSPMDEStateBlock", block_class=_DSPMDEStateBlock)
class DSPMDEStateBlockData(StateBlockData):
@declare_process_block_class("MCASStateBlock", block_class=_MCASStateBlock)
class MCASStateBlockData(StateBlockData):
def build(self):
"""Callable method for Block construction."""
super().build()
Expand Down Expand Up @@ -1303,7 +1304,7 @@ def _ionic_strength_molal(self):
def rule_ionic_strength_molal(b):
return b.ionic_strength_molal == 0.5 * sum(
b.charge_comp[j] ** 2 * b.molality_phase_comp["Liq", j]
for j in self.params.ion_set | self.params.solute_set
for j in self.params.ion_set
)

self.eq_ionic_strength_molal = Constraint(rule=rule_ionic_strength_molal)
Expand Down
Loading

0 comments on commit 0331dcf

Please sign in to comment.