prommis.nanofiltration.multi_component_diafiltration#
Multi-Component Diafiltration Unit Model#
Author: Molly Dougher
This membrane unit model is for the multi-component diafiltration of a multi-salt system with a common anion. Currently, the model and property packages support one, two, and three salt systems; however, the model can be extended to \(n\) salts by supplying the appropriate properties and arguments (see below). The membrane is designed for use in a diafiltration cascade, i.e., the model represents one spiral-wound membrane module piece within a cascade of several membranes.
Configuration Arguments#
The Multi-Component Diafiltration unit model requires a property package that provides the valency (\(z_i\)), infinite dilution diffusion coefficient (\(D_i\)) in \(\mathrm{mm}^2 \, \mathrm{h}^{-1}\), thermodynamic reflection coefficient (\(\sigma_i\)), partition coefficients (\(H_{i,r}\) and \(H_{i,p}\)) at the retentate-membrane and membrane-permeate interfaces, and number of dissolved species (\(n_i\)) for each ion \(i\) in solution. When used in a flowsheet, the user can provide separate property packages for the feed and product streams.
There are four required arguments:
cation_list(list of cations present in the system)default=["Li", "Co"]anion_list(list of anions present in the system)default=["Cl"]NFE_module_length(the desired number of finite elements across the width of the membrane (i.e., the module length))NFE_membrane_thickness(the desired number of finite elements across the thickness of the membrane)
Degrees of Freedom#
The Multi-Component Diafiltration unit model has \(5+2n\) degrees of freedom, where \(n\) is the number of cations in the system:
the length of the membrane module (
total_module_length)the length of the membrane (
total_membrane_length)the pressure applied to the membrane system (
applied_pressure)the volumetric flow rate of the feed (
feed_flow_volume)the cation concentration in the feed (
feed_conc_mol_comp[t,k])the volumetric flow rate of the diafiltrate (
diafiltrate_flow_volume)the cation concentration in the diafiltrate (
diafiltrate_conc_mol_comp[t,k])
Model Structure#
There are three phases in the Multi-Component Diafiltration model: the retentate, the membrane, and the permeate. The retentate and the permeate are only discretized with respect to \(x\) (parallel to the membrane surface), while the membrane is discretized with respect to both \(x\) and \(z\) (perpendicular to the membrane surface). The resulting system of partial differential algebraic equations is solved by discretizing with the backward finite difference method.
Assumptions#
The membrane module dimensions, maximum applied pressure, and inlet flow rates assume that one tube (one instance of this model) consists of 4 NF270-440 membranes in series.
The partitioning relationships, which describe how the solutes transition (partition) across the solution-membrane interfaces, are derived assuming Donnan equilibrium. The partitioning coefficients incorporate both steric and Donnan effects.
The default value for the membrane’s surface charge (\(-44 \, \mathrm{mM}\)), was calculated using zeta potential measurements for NF270 membranes. (See this reference). Currently, the default property package only supports negatively charged membranes.
The membrane is assumed to be \(100 \, \mathrm{nm}\) thick.
The default value for the membrane permeability (\(0.01 \, \mathrm{m} \, \mathrm{h}^{-1} \, \mathrm{bar}^{-1}\)) is based off of parameter estimation results from this reference for NF270 membranes.
The formation of a boundary layer at the membrane surface due to concentration polarization is neglected for mathematical simplicity.
The dominating transport mechanism within the bulk/retentate solution is convection in the \(x\)-direction (parallel to the membrane surface). The dominating transport mechanism within the permeate solution is convection in the \(z\)-direction (perpendicular to the membrane surface).
The transport mechanisms modeled within the membrane are convection, diffusion, and electromigration. Diffusion within the membrane that is normal to the pore walls is ignored, meaning the concentration gradient of ion \(i\) within the membrane only has a \(z\)-component (perpendicular to the membrane surface).
Sets#
The Multi-Component Diafiltration model defines the following discrete sets for solutes and cations in the system, respectively:
where \(n\) is the desired number of cations.
There are 2 continuous sets for each length dimension: dimensionless_module_length (in the \(x\)-direction parallel to the membrane surface) and dimensionless_membrane_thickness (in the \(z\)-direction perpendicular to the membrane surface). \(x\) and \(z\) are non-dimensionalized (denoted as \(\bar{x}\) and \(\bar{z}\), respectively) using the module length (\(w\)) and membrane thickness (\(l\)), respectively, to improve numerical stability.
Some variables have a time domain to be compatible with the property package, even though this is not a dynamic model. Thus, the following set is defined for time.
Default Model Parameters#
The Multi-Component Diafiltration model has the following parameters.
Parameter |
Description |
Name |
Default Value |
Units |
|---|---|---|---|---|
\(\epsilon\) |
numerical tolerance for zero values |
|
1e-10 |
|
\(l\) |
thickness of the membrane |
|
1e-07 |
\(\mathrm{m}\) |
\(L_p\) |
hydraulic permeability of the membrane |
|
0.01 |
\(\mathrm{m} \, \mathrm{h}^{-1} \, \mathrm{bar}^{-1}\) |
\(T\) |
temperature of the system |
|
298 |
\(\mathrm{K}\) |
\(\chi\) |
concentration of surface charge on the membrane |
|
-140 |
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
Variables#
The Multi-Component Diafiltration model adds the following variables.
Variable |
Description |
Name |
Units |
Indexed over |
|---|---|---|---|---|
\(c_{i,d}\) |
ion concentration in the diafiltrate |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\) and \(i \in \mathcal{I}\) |
\(c_{i,f}\) |
ion concentration in the feed |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\) and \(i \in \mathcal{I}\) |
\(c_{i,m}\) |
ion concentration in the membrane |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), amd \(i \in \mathcal{I}\) |
\(c_{i,p}\) |
ion concentration in the permeate |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), amd \(i \in \mathcal{I}\) |
\(c_{i,r}\) |
ion concentration in the retentate |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), amd \(i \in \mathcal{I}\) |
\(\tilde{D}\) |
diffusion & convection coefficient denominator in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(\bar{z}\) |
\(D_{kj}^{bilinear}\) |
bilinear cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^4 \, \mathrm{h}^{-2} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(\alpha_k^{bilinear}\) |
bilinear convection coefficient in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), and \(k \in \mathcal{K}\) |
\(D_{kj}\) |
cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(\alpha_k\) |
convection coefficient in the membrane |
|
\(\mathrm{dimensionless}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), and \(k \in \mathcal{K}\) |
\(j_i\) |
molar flux of ions across the membrane |
|
\(\mathrm{mol} \, \mathrm{m}^{-2} \, \mathrm{h}^{-1}\) |
\(t\), \(\bar{x}\), amd \(i \in \mathcal{I}\) |
\(J_w\) |
water flux across the membrane |
|
\(\mathrm{m}^3 \, \mathrm{m}^{-2} \, \mathrm{h}^{-1}\) |
\(t\) and \(\bar{x}\) |
\(L\) |
length of the membrane |
|
\(\mathrm{m}\) |
|
\(\Delta \pi\) |
osmotic pressure of feed-side fluid |
|
\(\mathrm{bar}\) |
\(t\) and \(\bar{x}\) |
\(\Delta P\) |
applied pressure to the membrane |
|
\(\mathrm{bar}\) |
\(t\) |
\(q_d\) |
volumetric flow rate of the diafiltrate |
|
\(\mathrm{m}^3 \, \mathrm{h}^{-1}\) |
\(t\) |
\(q_f\) |
volumetric flow rate of the feed |
|
\(\mathrm{m}^3 \, \mathrm{h}^{-1}\) |
\(t\) |
\(q_p\) |
volumetric flow rate of the permeate |
|
\(\mathrm{m}^3 \, \mathrm{h}^{-1}\) |
\(t\) and \(\bar{x}\) |
\(q_r\) |
volumetric flow rate of the retentate |
|
\(\mathrm{m}^3 \, \mathrm{h}^{-1}\) |
\(t\) and \(\bar{x}\) |
\(w\) |
length of the membrane module |
|
\(\mathrm{m}\) |
Derivative Variables#
The Multi-Component Diafiltration model adds the following derivative variables.
Variable |
Description |
Name |
Units |
Indexed over |
|---|---|---|---|---|
\(\frac{\mathrm{d}c_{k,r}}{\mathrm{d}\bar{x}}\) |
ion concentration gradient in the retentate |
|
\(\mathrm{kg} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(k \in \mathcal{K}\) |
\(\frac{\mathrm{d}q_r}{\mathrm{d}\bar{x}}\) |
retentate flow rate gradient |
|
\(\mathrm{m}^3 \, \mathrm{h}^{-1}\) |
\(t\) and \(\bar{x}\) |
\(\frac{\partial c_{k,m}}{\partial \bar{z}}\) |
ion concentration gradient in the membrane |
|
\(\mathrm{kg} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}\), and \(k \in \mathcal{K}\) |
Constraints#
Differential mole balances:
Bulk flux balances:
Overall water flux through the membrane:
Cation flux through the membrane:
Derived from the extended Nernst-Planck equation
where
where the subscript \(a\) represents the anion in solution.
The diffusion and convection coefficients are reformulated to bilinear constraints:
Note that the single solute diffusion coefficients are provided in \(\mathrm{mm}^2\ \, \mathrm{h}^{-1}\) to improve numerical stability, but the diffusion coefficients in the Nernst-Planck equations must be converted to \(\mathrm{m}^2\ \, \mathrm{h}^{-1}\).
No applied potential on the system:
Electroneutrality:
Partitioning:
At the the retentate-membrane interface:
At the membrane-permeate interface:
Boundary conditions:
The following constraints (which are expected to be zero) are enforced to improve numerical stability (with the appropriate constraints deactivated as described above):
- class prommis.nanofiltration.multi_component_diafiltration.MultiComponentDiafiltration(*args, **kwds)#
- Parameters:
rule (function) – A rule function or None. Default rule calls build().
concrete (bool) – If True, make this a toplevel model. Default - False.
ctype (class) –
Pyomo ctype of the block. Default - pyomo.environ.Block
Config args
- dynamic
Indicates whether this model will be dynamic or not, default = useDefault. Valid values: { useDefault - get flag from parent (default = False), True - set as a dynamic model, False - set as a steady-state model.}
- has_holdup
Indicates whether holdup terms should be constructed or not. Must be True if dynamic = True, default - False. Valid values: { useDefault - get flag from parent (default = False), True - construct holdup terms, False - do not construct holdup terms}
- property_package
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.}
- property_package_args
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}
- cation_list
List of cations present in the system
- anion_list
List of anions present in the system
- NFE_module_length
Number of discretization points in the x-direction (across module length)
- NFE_membrane_thickness
Number of discretization points in the z-direction (across membrane thickness)
initialize (dict) – ProcessBlockData config for individual elements. Keys are BlockData indexes and values are dictionaries with config arguments as keys.
idx_map (function) – Function to take the index of a BlockData element and return the index in the initialize dict from which to read arguments. This can be provided to override the default behavior of matching the BlockData index exactly to the index in initialize.
- Returns:
(MultiComponentDiafiltration) New instance
- class prommis.nanofiltration.multi_component_diafiltration.MultiComponentDiafiltrationData(component)[source]#
Multi-Component Diafiltration Unit Model Class.
- add_mutable_parameters()[source]#
Adds default parameters for the multi-component diafiltration unit model.
Values can be changed by the user during implementation.
Assumes membrane thickness of 100 nm.
Membrane permeability and fixed charged are estimated from: Liu, Xinhong, et al. (2025) https://doi.org/10.1021/acs.iecr.4c04763
- add_scaling_factors()[source]#
Assigns scaling factors to certain variables and constraints to improve solver performance.
- add_variables()[source]#
Adds variables for the multi-component diafiltration unit model.
Membrane module dimensions and maximum flowrate (17 m3/h) are estimated from NF270-440 modules.
Assumes 4 modules in series.
- deactivate_unnecessary_objects()[source]#
Deactivates variables and constraints not needed in the multi-component diafiltration unit model.
- default_initializer#
- class prommis.nanofiltration.multi_component_diafiltration.MultiComponentDiafiltrationInitializer(**kwargs)[source]#
Multi-Component Diafiltration Initializer Class.
- constraint_tolerance
Tolerance for checking constraint convergence
- output_level
Set output level for logging messages
- block_solver
Solver to use for NxN blocks
- block_solver_options
Dict of options to use to set solver.options.
- block_solver_options
- tol
Convergence tolerance for block solver
- max_iter
Iteration limit for block solver
- block_solver_writer_config
Dict of writer_config arguments to pass to block solver
- block_solver_writer_config
- linear_presolve
Whether to use linear presolver with block solver
- scale_model
Whether to apply model scaling with block solver
- block_solver_call_options
Dict of arguments to be passed as part of the solver.solve call, such as tee=True.
- calculate_variable_options
Dict of options to pass to calc_var_kwds argument in solve_strongly_connected_components method.
- skip_final_solve
Skip solving the block after block triangularization finishes.This solve may be necessary to decrease the scaled constraint residuals below the specified tolerance because, until Pyomo issue #3785 is addressed, solve_strongly_connected_components does not take scaling into account.