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. The model can be built with or without the assumption of a boundary layer. 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\)), diffusion coefficients (\(D_{i,bl}\) and \(D_{i,m}\)) within the boundary layer and membrane, respectively, 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 six configuration arguments to create an instance of the Multi-Component Diafiltration Unit Model:
cation_list: list of cations present in the systemdefault=["Li", "Co"]anion_list: list of anions present in the systemdefault=["Cl"]include_boundary_layer: Boolean to specify if the model is to be built with a boundary layerdefault=TrueNFE_module_length: the desired number of finite elements across the width of the membrane (i.e., the module length)default=10NFE_boundary_layer_thickness: the desired number of finite elements across the thickness of the boundary layerdefault=5NFE_membrane_thickness: the desired number of finite elements across the thickness of the membranedefault=5
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 (up to) four regions in the Multi-Component Diafiltration model: the retentate, the boundary layer, the membrane, and the permeate. The retentate and the permeate are only discretized with respect to module length (\(x\)-direction), while the boundary layer and membrane are discretized with respect to both module length (\(x\)-direction) and thickness (\(z_{bl}\)-direction and \(z_{m}\)-direction, respectively). The resulting system of partial differential algebraic equations is solved by discretizing with the backward finite difference method.
A schematic of the Multi-Component Diafiltration model’s geometry can be found here.
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 boundary layer thickness is assumed to be \(20 \, \mathrm{\mu m}\) and the membrane thickness is assumed to be \(100 \, \mathrm{nm}\).
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 dominating transport mechanism within the bulk/retentate and permeate solutions is convection.
The transport mechanisms modeled within the both the boundary layer and the membrane are convection, diffusion, and electromigration.
The system is uniform with respect to the wound-dimension of the membrane.
Diffusion that occurs normal to the direction of flux is assumed to be negligible, meaning the concentration gradient of ion \(i\) only has a \(z_{bl}\)- or \(z_m\)-component (perpendicular to the membrane surface).
Sets#
The Multi-Component Diafiltration model defines the following discrete sets for solutes (\(\mathcal{I}\)) and cations (\(\mathcal{K}\)) in the system:
where \(n\) is the desired number of cations.
There are 3 continuous sets for each length dimension: dimensionless_module_length (in the \(x\)-direction parallel to the membrane surface), dimensionless_boundary_layer_thickness (in the \(z_{bl}\)-direction perpendicular to the membrane surface), and dimensionless_membrane_thickness (in the \(z_m\)-direction perpendicular to the membrane surface). The length dimensions, \(x\), \(z_{bl}\), and \(z_m\), are non-dimensionalized as \(\bar{x}\), \(\bar{z}_{bl}\), and \(\bar{z}_m\), respectively, using the module length (\(w\)), boundary layer thickness (\(\delta\)), and membrane thickness (\(l\)), respectively, to improve numerical stability.
Note: \(z_{bl}\) and \(z_m\) point in the same direction (perpendicular to the membrane surface), but are defined as separate length scales to simplify the implementation of the model. The appropriate boundary conditions between \(z_{bl}\) and \(z_m\) are enforced within the model.
Though this is not implemented as a dynamic model, a 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\) |
|
\(\delta\) |
boundary layer thickness |
|
\(2e-05\) |
\(\mathrm{m}\) |
\(l\) |
membrane thickness |
|
\(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 |
|
\(-44\) |
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
Variables#
The Multi-Component Diafiltration model adds the following variables.
Variable |
Description |
Name |
Units |
Indexed over |
|---|---|---|---|---|
\(c_{i,bl}\) |
ion concentration in the boundary layer |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_{bl}\), and \(i \in \mathcal{I}\) |
\(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}_m\), and \(i \in \mathcal{I}\) |
\(c_{i,p}\) |
ion concentration in the permeate |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(i \in \mathcal{I}\) |
\(c_{i,r}\) |
ion concentration in the retentate |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(i \in \mathcal{I}\) |
\(\tilde{D}_{bl}\) |
cross-diffusion coefficient denominator in the boundary layer |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(\bar{z}_{bl}\) |
\(D_{kj,bl}^{bilinear}\) |
bilinear cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^4 \, \mathrm{h}^{-2} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_{bl}\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(D_{kj,bl}\) |
cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1}\) |
\(t\), \(\bar{x}\), \(\bar{z}_{bl}\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(\tilde{D}_m\) |
cross-diffusion & convection coefficient denominator in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), and \(\bar{z}_m\) |
\(D_{kj,m}^{bilinear}\) |
bilinear cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^4 \, \mathrm{h}^{-2} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_m\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(\alpha_{k,m}^{bilinear}\) |
bilinear convection coefficient in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1} \, \mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_m\), and \(k \in \mathcal{K}\) |
\(D_{kj,m}\) |
cross-diffusion coefficient in the membrane |
|
\(\mathrm{mm}^2 \, \mathrm{h}^{-1}\) |
\(t\), \(\bar{x}\), \(\bar{z}_m\), \(k \in \mathcal{K}\), and \(j \in \mathcal{K}\) |
\(\alpha_{k,m}\) |
convection coefficient in the membrane |
|
\(\mathrm{dimensionless}\) |
\(t\), \(\bar{x}\), \(\bar{z}_m\), and \(k \in \mathcal{K}\) |
\(j_i\) |
molar flux of ions through the membrane |
|
\(\mathrm{mol} \, \mathrm{m}^{-2} \, \mathrm{h}^{-1}\) |
\(t\), \(\bar{x}\), and \(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{mol} \, \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,bl}}{\partial \bar{z}_{bl}}\) |
ion concentration gradient in the boundary layer |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_{bl}\), and \(k \in \mathcal{K}\) |
\(\frac{\partial c_{k,m}}{\partial \bar{z}_m}\) |
ion concentration gradient in the membrane |
|
\(\mathrm{mol} \, \mathrm{m}^{-3}\) |
\(t\), \(\bar{x}\), \(\bar{z}_m\), and \(k \in \mathcal{K}\) |
Constraints#
Differential mole balances:
Bulk flux balances:
Overall water flux through the membrane:
without a boundary layer:
with a boundary layer:
Cation flux through the boundary layer and membrane:
Derived from the extended Nernst-Planck equation
where
and 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. When used in the Nernst-Planck equations, the diffusion coefficients are converted to \(\mathrm{m}^2\ \, \mathrm{h}^{-1}\).
No applied potential on the system:
Electroneutrality:
Partitioning:
At the feed-side solution-membrane interface:
without a boundary layer:
with a boundary layer:
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
- include_boundary_layer
Boolean to specify if the model is to be built with a boundary layer
- NFE_module_length
Number of finite elements across module length (in the x-direction)
- NFE_boundary_layer_thickness
Number of finite elements across the boundary layer (in the z-direction)
- NFE_membrane_thickness
Number of finite elements across the membrane thickness (in the z-direction)
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.