Tutorial: Electrodialysis (ED) unit
====================================
.. raw:: html
ED is a technique that uses ion exchange membranes and electricity to extract ionic compounds from aqueous solutions, with an electrical field serving as the driving force for solute separation. In this work, ED aims to purify NF permeate.
.. image:: https://github.com/user-attachments/assets/386c055d-9935-41fa-b15c-e790fc169f9a
:alt: ed
.. raw:: html
In **desalsim** package, the ED unit is used to model the operation of an Electrodialysis technology. Upon simulation, it calculates the flow rate of the concentrate and dilute streams, their ion concentration, and the electricity requirements of the unit.
The ED function consists of one class: `ElectrodialysisCalc` that consists of six methods: `Ts_cp`, `w_cp`, `Ls_cpv`, `Lw_cp`, `p_osmo`, `dC` (see `Section 2.3 <#section_2_3>`_).
In this tutorial, we will focus on how to use the class and its methods.
**Table 1** gives an overview of the main inputs and outputs for each process unit of Electrodialysis.
.. list-table:: Inputs and Outputs for Electrodialysis
:header-rows: 1
* - Process
- Input
- Output
* - Electrodialysis (ED)
- Feed flow rate [m³/h]
- Flow rate of diluted stream [m³/h] and composition [g/L]
* -
- Ion concentration [g/L]
- Flow rate of concentrate stream [m³/h] and composition [g/L]
* -
- Current density [A/m²]
- Electricity requirements [kWhel]
The mathematical description of Electrodialysis is given in `Mathematical description `_, see Section A.8.
1. Getting started
--------------------
1.1. Import class
^^^^^^^^^^^^^^^^^^
.. code-block:: python
import desalsim
Then import the class:
.. code-block:: python
from desalsim.ed_unit_f import ElectrodialysisCalc
Additionally, function for calculating density (`density_calc.py`) or constants (`comparison.py`) where users can add constant values like MW, prices, etc., need to be imported.
.. code-block:: python
from desalsim.density_calc import density_calc
from desalsim import constants
from desalsim import scaleup
1.2. Define feed characteristics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can initialize the feed solution by setting the flow rate, specifying the focus components and their concentration.
.. code-block:: python
# Input conditions
Sc_i = 43.39 # Salinity at concentrate inlet (g/kg)
Sd_i = Sc_i # Salinity at diluate inlet (g/kg)
# Feed concentration
components = ['Na', 'Cl', 'K', 'Mg', 'Ca', 'SO4', 'HCO3', 'H', 'OH']
Csw = [17.17, 25.47, 0.57, 0.04, 0.03, 0.10]
# Feed flow rate L/h
Qed_in = 1000
# Temperature
T = 20 + 273.15 # K
.. note::
Note that if you want to add more components, you need to update the components list and include the concentration of the new component in the ``Csw``.
You can calculate the density of the feed solution and the water quantity in inflow:
.. code-block:: python
d_in_ed = (density_calc(T - 273, Sc_i) / 1000)
Qed_in = 1000 / d_in_ed
# Calculate the flowrates for dilute and concentrate streams
Qed_in_c = Qed_in / 17 / Ncp
Qed_in_d = Qed_in * 16 / 17 / Ncp
.. note::
In this case, we assumed that the concentrate stream is 1/17 of the total feed flow rate, and the dilute stream is 16/17 of the total feed flow rate.
1.3. Set operating assumptions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You need to set operating assumptions such as the electrical current density.
.. code-block:: python
# Assumptions:
Ij = 400 # Current density (A/m²)
N = 50 # Number of computational cells per cell-pair
Ncp = 1 # Number of identical parallel cell-pairs
A = 1.1 # Active area of cell-pair (m²)
Mem_eff = 0.64 # Membrane efficiency
Vcp = 8 # Applied voltage (V)
Vel = 2.1 # Voltage across the electrodes (V)
# Effective cell-pair area (m²)
Acp = scaleup.scaleup(24, 1000, Qed_in)
Acp_tot = Acp
You need to set the salinity at dilute outlet concentrate outlet in g/kg.
.. code-block:: python
Sc_o = 200 # Salinity at concentrate outlet (g/kg)
Sd_o = 20 # Salinity at dilute outlet (g/kg)
Finally, you need to set assumptions related to pumping like pressure drop (`dp`) and pump efficiency (`npump`).
.. code-block:: python
npump = 0.8 # Pump efficiency (units: -)
dp = 1 # Pressure drop (units: bar)
1.4. Set constants
^^^^^^^^^^^^^^^^^^
You need to set constant parameters:
.. code-block:: python
R = 0.002 # Resistance of rinse stream (ohm)
Rp = 0.015 # Resistance of polarization (ohm)
A = 1.1 # Active area of cell-pair (m²)
F = 96485.3329 # Faraday constant (C/mol)
rho_w = 1000 # Water density kg/m³
D = 1.61e-9 # Diffusion coefficient (m²/s)
tcu = 0.5
veloc = 8.9e-7 # m²/s
h = 0.5 # mm
Sh = 18
1.5. Initializations
^^^^^^^^^^^^^^^^^^^^
First, you need to initialize the parameters:
.. code-block:: python
# Initializations
Sc = np.zeros(N)
Sd = np.zeros(N)
Ns_c = np.zeros(N)
Ns_d = np.zeros(N)
Nw_c = np.zeros(N)
Nw_d = np.zeros(N)
Js = np.zeros(N)
Jw = np.zeros(N)
Mw_in_d_l = np.zeros(N)
Ms_d = np.zeros(N)
Mw_d = np.zeros(N)
M_d = np.zeros(N)
M_c = np.zeros(N)
Q_c = np.zeros(N)
Q_d = np.zeros(N)
Then set the initial values for the concentrate stream:
.. code-block:: python
# Set initial values salt stream
Sc[0] = Sc_i
Ns_c[0] = Qed_in_c * d_in_ed * Sc[0] / MWs # mol/hr
Ms_in_c = Qed_in_c * d_in_ed * Sc_i / 1000 # kg salt/hr
Mw_in_c = Qed_in_c * d_in_ed - Ms_in_c # kg water/hr
Nw_c[0] = Mw_in_c * 1000 / MWw # mol/hr
M_c[0] = Mw_in_c + Ms_in_c
Q_c[0] = Qed_in_c
.. code-block:: python
# Set initial values diluate stream
Sd[0] = Sc_i # g/kg
Ns_d[0] = Qed_in_d * d_in_ed * Sd[0] / MWs # mol/s
Ms_in_d = Qed_in_d * d_in_ed * Sd[0] / 1000 # kg salt/hr
Mw_in_d = Qed_in_d * d_in_ed - Ms_in_d # kg water/hr
Nw_d[0] = Mw_in_d * 1000 / MWw # mol/hr
Mw_in_d_l[0] = Mw_in_d
Ms_d[0] = Ms_in_d
Mw_d[0] = Mw_in_d
M_d[0] = Mw_in_d + Ms_in_d
Q_d[0] = Qed_in_d
Finally, initialize the total cell-pair area.
.. code-block:: python
# Initialize the Acp_tot array
Acp_tot_j = Acp_tot / N
After setting all the required inputs and initialize the values, then you can create the functions' objectives.
2. Use ElectrodialysisCalc class
-------------------------------
`ElectrodialysisCalc` is a class used to represent mass and energy balance for ED Unit. In particular, it calculates the flowrate in each channel, the outlet concentration in each channel, the external Voltage and power needed.
2.1. Overview
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following attributes are available within the `ElectrodialysisCalc` class:
- ``MWs``: Molecular weight of NaCl (g/mol)
- ``MWw``: Molecular weight of water (g/mol)
-``R``: Resistance of rinse stream (ohm)
- ``Rp``: Resistance of polarization (ohm)
- ``A``: Active area of cell-pair (m²)
- ``F``: Faraday constant (C/mol)
- ``T``: Temperature in Kelvin
- ``dp``: Parameter dp
- ``npump``: Pump efficiency
- ``rho_w``: Density of water (kg/m³)
- ``D``: Diffusion coefficient (m²/s)
- ``tcu``: ntcu parameter
- ``h``: Height (mm)
- ``Sh``: Sh parameter
- ``Mem_eff``: Membrane efficiency
- ``Ncp``: Number of cell-pairs
- ``Qed_in``: Inlet flow rate (L/h)
- ``Qed_in_c``: Concentrate inlet flow rate (L/h)
- ``Qed_in_d``: Dilute inlet flow rate (L/h)
The ElectrodialysisCalc class provides the following methods:
.. code-block:: python
# Calculates the transport number for salt in concentrate compartment
Ts_cp(S)
# Calculates the transport number for water in concentrate compartment
w_cp(Sc, Sd)
# Permeability for salt in concentrate compartment
Ls_cp(Sc, Sd)
# Permeability for water in concentrate compartment
Lw_cp(S)
# Calculation for osmotic pressure in concentrate compartment
p_osmo(S, T, MWs)
# Calculate the change in concentration
dC(Ts_cp, tcu, D, Ij, h, Sh)
2.2. Create ElectrodialysisCalc objects
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ElectrodialysisCalc has no inputs.
.. code-block:: python
# Create an instance of the ElectrodialysisCalc class
ed_em = ElectrodialysisCalc()
.. _section_2_3:
2.3. Use Ts_cp, w_cp, Ls_cpv, Lw_cp, p_osmo, dC methods
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ED system is modeled by adapting a model developed by `Nayar et al. `_, keeping both the concentrate and diluate channels fully continuous, with the salinities of both channels varying along the length of the ED stack. The following code simulates the ED unit using the `Ts_cp`, `w_cp`, `Ls_cpv`, `Lw_cp`, `p_osmo`, and `dC` methods.
.. code-block:: python
# Iterate over cells
for j in range(1, N):
# Calculate salinity change
concentration_diff = Sc[j - 1] - Sd[j - 1]
Sc[j] = Sc[j - 1] + (Sc_o - Sc_i) / (N - 1)
Sd[j] = Sd[j - 1] + (Sd_o - Sd_i) / (N - 1)
# Calculate net salt flux
Js[j] = (ElectrodialysisCalc.Ts_cp(Sd[j - 1]) * Ij / F -
(ElectrodialysisCalc.Ls_cp(Sc[j - 1], Sd[j - 1])) * concentration_diff)
# Calculate net water flux
Jw[j] = (ElectrodialysisCalc.Tw_cp(Sc[j - 1], Sd[j - 1]) * Ij / F +
ElectrodialysisCalc.Lw_cp(Sc[j - 1]) * (ElectrodialysisCalc.p_osmo(Sc[j - 1], T, MWs) -
ElectrodialysisCalc.p_osmo(Sd[j - 1], T, MWs)))
# Calculate total concentrate and dilute molar flow rates
Ns_c[j] = Ns_c[j - 1] + Acp_tot_j * Js[j]
Ns_d[j] = Ns_d[j - 1] - Acp_tot_j * Js[j]
Nw_c[j] = Nw_c[j - 1] + Acp_tot_j * Jw[j]
Nw_d[j] = Nw_d[j - 1] - Acp_tot_j * Jw[j]
# Update the flow rates of the concentrate and dilute streams
Q_c[j] = Nw_c[j] * MWw / (rho_w * (1 - Sc[j] / 1000))
Q_d[j] = Nw_d[j] * MWw / (rho_w * (1 - Sd[j] / 1000))
2.3.1. Assign the results to output parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can assign the results to output parameters:
.. code-block:: python
Cc_na_f = Sc[N-1] / MWs * constants.MW_Na
Cc_cl_f = Sc[N-1] / MWs * constants.MW_cl
Sc_out = [Cc_na_f, Cc_cl_f]
2.4. Calculate the concentrate stream flow rate
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# Calculate the concentrate stream flow rate
Mc = (Ns_c[N-1] * MWs / 1000 + Nw_c[N-1] * MWw / 1000) # (kg/hr)
dc_out = density_calc(T-273, Sc[N-1]) / 1000 # (kg/l)
Qc = Mc / dc_out # Concentrate stream volume flow rate (l/hr)
i = 2
for i in range(2, len(Csw)):
Sc_out.append(Csw[i] * Qed_in_c / Qc) # The total effluent concentration concentrate stream
2.5. Calculate the dilute stream flow rate
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# Calculations for diluate stream
Md = (Ns_d[N-1] * MWs / 1000 + Nw_d[N-1] * MWw / 1000) # Mass flow rate (kg/hr)
Sd_f = Sd[N-1]
Cd_na_f = Sd_f / MWs * constants.MW_Na
Cd_cl_f = Sd_f / MWs * constants.MW_cl
dd_out = density_calc(T-273, Sd[N-1]) / 1000 # Density of diluate stream
Qd = Md / dd_out # Diluate stream volume flow rate (l/hr)
Sd_out = [Cd_na_f, Cd_cl_f]
# The total effluent concentration dilute
for i in range(2, len(Csw)):
Sd_out.append(Csw[i] * Qed_in / Qd)
2.6. Calculate energy consumption
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can calculate the total energy requirements for the ED unit using the voltage applied across an ED cell-pair (`Vcp`), the voltage across the electrodes (`Vel`), and the energy for pumping (`Ppump_ed`).
.. code-block:: python
# Energy consumption
Ws = 0
for j in range(N):
Ws += Ij * Acp_tot_j * (Ncp * Vcp + Vel)
print("Power required is " + str(round(Ws / 1000, 2)) + "KW")
# Calculate energy consumption for pumping
Ppump_ed = (Qed_in_d * 1 + Qed_in_c * 1 + Qc * 2 + Qd * 1) / 1000 / 3600 * 1e5 / npump
Eel_t_ed = Ws / 1000 + Ppump_ed / 1000
# Specific energy consumption
sec_ed = Eel_t_ed / (Qed_in / 1000)
2.7. Print results
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can print results from the calculations:
.. code-block:: python
print("Mass flowrate concentrate stream is " + str(round(Mc, 2)) + " kg/hr")
print("Volume flowrate concentrate stream is " + str(round(Qc, 2)) + " l/hr")
print("The total effluent concentration concentrate stream is " + str(round(Sc[N-1], 2)) + "g/kg")
print("-----------------------------------------")
print("Mass flowrate of diluate stream is " + str(round(Md, 2)) + " kg/hr")
print("Volume flowrate diluate stream is " + str(round(Qd, 2)) + " l/hr")
print("The total effluent concentration dilute is " + str(round(Sd[N-1], 2)) + "g/kg")
print("-----------------------------------------")
Mass flowrate concentrate stream is 78.5 kg/hr
Volume flowrate concentrate stream is 67.74 l/hr
The total effluent concentration concentrate stream is 200.0g/kg
Mass flowrate of diluate stream is 921.5 kg/hr
volume flowrate diluate stream is 909.36 l/hr
The total effluent concentration dilute is 20.0g/kg
.. code-block:: python
# Solid mass balance
bal = Qed_in - Md - Mc
bal = (Qed_in * sum(Csw) - Md * (sum(Sd_out)) - Mc * Sc_o) / 1000
print("Mass balance difference is " + str(round(bal, 2)))
error_perc = abs(bal) / (Qed_in * sum(Csw)) * 100
print("Balance error percentage is "+str(round(error_perc,2))+"%")
print("-----------------------------------------")
Mass balance difference is 7.21
Balance error percentage is 0.02%
.. code-block:: python
# Energy consumption
print("Power required is "+str(round(Ws/1000,2))+"KW")
print("Total energy consumption is "+str(round(Eel_t_ed,2))+"KW")
print("Specific energy consumption of Electrodialysis (ED) is "+str(round(sec_ed,2))+"KW/m3 feed")
Power required is 95.19KW
Total energy consumption is 95.26KW
Specific energy consumption of Electrodialysis (ED) is 98.23KW/m3 feed