Skip to content

Commit 3d2c4a0

Browse files
authored
Add method to ED1d for calculating membrane resistance as a function of ion concentration (watertap-org#1522)
* add new methods for resistance calc * update doc to reflect the changes * format * changes on the ui.py and format of rst * format * debug macos err * format * debug err in macos x86 * debug err in macos x86 * debug err in macos x86 * debug err in macos x86 * typos, single-pass ed flowsheet refined for stability across os * notations on doc, citation updates * format * rename a var in rst * rename var; typo corr * blk
1 parent 9eb240e commit 3d2c4a0

File tree

7 files changed

+196
-72
lines changed

7 files changed

+196
-72
lines changed

docs/technical_reference/unit_models/electrodialysis_1D.rst

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ the opposite ion selectivity of cation- and anion-exchange membranes (cem and ae
1616
one cell to its adjacent cell in a cell-pair treatment unit (Figure 1). The ion-departing cell is called a **diluate
1717
channel** and the ion-entering cell a **concentrate channel**. Recovered (desalinated) water is
1818
collected from diluate channels of all cell pairs while the concentrate product can be disposed of as brine
19-
or retreated. More overview of the electrodialysis technology can be found in the *References*.
19+
or retreated. More overview of the electrodialysis technology can be found in [1]_, [2]_, and [3]_.
2020

2121
.. figure:: ../../_static/unit_models/EDdiagram.png
2222
:width: 400
@@ -120,7 +120,9 @@ are parameters that should be provided in order to fully solve the model.
120120
"Cell pair number", ":math:`n`", "cell_pair_num", "None", "dimensionless", 1
121121
"Current utilization coefficient", ":math:`\xi`", "current_utilization", "None", "dimensionless", 1
122122
"Channel height", ":math:`d`", "channel_height", "none", ":math:`m` ", 1
123-
"Membrane areal resistance", ":math:`r`", "membrane_areal_resistance", "['cem', 'aem']", ":math:`\Omega m^2`", 2
123+
"Membrane areal resistance at infinitive ion concentration", ":math:`r_{const}`", "membrane_areal_resistance", "['cem', 'aem']", ":math:`\Omega m^2`", 2
124+
"Membrane areal resistance coefficient to the reciprocal of the ion concentration", ":math:`r_{coef}`", "membrane_areal_resistance_coef", "['cem', 'aem']", ":math:`\Omega mol m^{-1}`", 2
125+
"Spacer conductivity coefficient", ":math:`\sigma`", "spacer_conductivity_coefficient", "None", "dimensionless", 1
124126
"Cell width", ":math:`b`", "cell_width", "None", ":math:`\text{m}`", 1
125127
"Cell length", ":math:`l`", "cell_length", "None", ":math:`\text{m}`", 1
126128
"Thickness of ion exchange membranes", ":math:`\delta`", "membrane_thickness", "['cem', 'aem']", ":math:`m`", 2
@@ -168,7 +170,7 @@ discretization manner using the "finite difference" or "collocation" method impl
168170

169171
Mass balance equations are summarized in **Table 3**. Mass transfer mechanisms account for solute electrical migration, diffusion,
170172
water osmosis, and electroosmosis. Theoretical principles, e.g., continuity equation, Fick's law, and Ohm's law,
171-
to simulate these processes are well developed and some good summaries for the electrodialysis scenario can be found in the *References*.
173+
to simulate these processes are well developed and some good summaries for the electrodialysis scenario can be found in [1]_.
172174

173175
.. csv-table:: **Table 3** Mass Balance Equations
174176
:header: "Description", "Equation", "Index set"
@@ -186,11 +188,18 @@ Additionally, several other equations are built to describe the electrochemical
186188

187189
"Electrical input condition", ":math:`i(x) = \frac{I}{bl}`, for 'Constant_Current'; :math:`u(x) =U` for 'Constant_Voltage'"
188190
"Ohm's law", ":math:`u(x) = i(x) r_{tot}(x)`"
189-
"Resistance calculation", ":math:`r_{tot}(x)=n\left(r^{cem}+r^{aem}+\frac{d}{\kappa^C(x)}+\frac{d}{\kappa^D(x)}\right)+r_{el}`"
191+
"mebrane resistance calculation \ :sup:`1`", ":math:`r^{iem}(x)=r^{iem}_{const}+\frac{r^{iem}_{coef}}{c_b^D}` [7]_"
192+
"Total resistance calculation", ":math:`r_{tot}(x)=n\left(r^{cem}(x)+r^{aem}(x)+\frac{d}{\sigma \kappa^C(x)}+\frac{d}{\sigma \kappa^D(x)}\right)+r_{el}` \ :sup:`2`"
190193
"Electrical power consumption", ":math:`P(x)=b\int _0 ^l u(x)i(x) dx`"
191194
"Water-production-specific power consumption", ":math:`P_Q=\frac{P(x=l)}{3.6\times 10^6 nQ_{out}^D}`"
192195
"Current efficiency for desalination", ":math:`bi(x)\eta(x)=-\sum_{j \in[cation]}{\left[\left(\frac{\partial N_j ^D(x)}{\partial x}\right) z_j F\right]}`"
193196

197+
**Note**
198+
199+
:sup:`1` We now consider the experimentally observed dependence of membrane resistance on electrolyte concentration using an empirical relationship reported by [7]_.
200+
:sup:`2` We used a coefficient multiplied by the solution conductance, denoted by :math:`\sigma`, to account for the spacer's conductance shadowing effect.
201+
202+
194203
All equations are coded as "constraints" (Pyomo). Isothermal and isobaric conditions apply.
195204

196205
Extended simulation
@@ -201,7 +210,7 @@ This model supports extensive simulations of (1) the nonohmic potential across i
201210
Users can customize these extenions via two configurations: `has_nonohmic_potential_membrane` that triggers the calculation of nonohmic
202211
potentials across ion exchange membranes and `has_Nernst_diffusion_layer` that triggers the simulation of a concentration-polarized Nernst
203212
diffusion layer including its ohmic and nonohmic potential changes. Based on a electrochemical cell setup in Figure 2 and established theoretical
204-
descriptions (*References*), our model accounts for the cross-membrane diffusion and Donnan potentials (nonohmic), ion concentration polarization
213+
descriptions ([4]_, [5]_), our model accounts for the cross-membrane diffusion and Donnan potentials (nonohmic), ion concentration polarization
205214
in assumed Nernst diffusion layers (NDL), and the ohmic and nonohmic (i.e., diffusion) potentials across NDLs. These extensions make the model
206215
closer to the non-ideal physical conditions that can be encountered in real desalination practices.
207216

@@ -238,7 +247,8 @@ Some other modifications to previously defined equations are made to accommodate
238247
:header: "Original equation description", "Equation replacement", "Condition"
239248

240249
"Ohm's law", ":math:`u(x) = i(x) r_{tot}(x) + \phi_m(x) + \phi_d^{ohm}(x) + \phi_d^{nonohm}(x)` \ :sup:`1`", "`has_nonohmic_potential_membrane == True` and/or \ `has_Nernst_diffusion_layer==True`"
241-
"Resistance calculation", ":math:`r_{tot}(x)=n\left(r^{cem}+r^{aem}+\frac{d- \Delta_{cem}^L(x) - \Delta_{aem}^R(x)}{\kappa^C(x)}+\frac{d- \Delta_{cem}^R(x) - \Delta_{aem}^L(x)}{\kappa^D(x)}\right)+r_{el}`", "`has_Nernst_diffusion_layer==True`"
250+
"mebrane resistance calculation", ":math:`r^{iem}(x)=r^{iem}_{const}+\frac{r^{iem}_{coef}}{c_b^D}`"
251+
"total resistance calculation", ":math:`r_{tot}(x)=n\left(r^{cem}(x)+r^{aem}(x)+\frac{d- \Delta_{cem}^L(x) - \Delta_{aem}^R(x)}{\sigma \kappa^C(x)}+\frac{d- \Delta_{cem}^R(x) - \Delta_{aem}^L(x)}{\sigma \kappa^D(x)}\right)+r_{el}`", "`has_Nernst_diffusion_layer==True`"
242252
"mass transfer flux, concentrate, solute", ":math:`J_j^{C} = \left(t_j^{cem}-t_j^{aem} \right)\frac{\xi i(x)}{ z_j F}-\left(\frac{D_j^{cem}}{\delta ^{cem}}\left(c_{s,j}^{L,cem}(x)-c_{s,j}^{R,cem}(x) \right) +\frac{D_j^{aem}}{\delta ^{aem}} \left(c_{s,j}^{R,aem}(x)-c_{s,j}^{L,aem}(x) \right)\right)`", "`has_nonohmic_potential_membrane == True` and/or \ `has_Nernst_diffusion_layer==True`"
243253
"mass transfer flux, diluate, solute", ":math:`J_j^{D} = -\left(t_j^{cem}-t_j^{aem} \right)\frac{\xi i(x)}{ z_j F}+\left(\frac{D_j^{cem}}{\delta ^{cem}}\left(c_{s,j}^{L,cem}(x)-c_{s,j}^{R,cem}(x) \right) +\frac{D_j^{aem}}{\delta ^{aem}} \left(c_{s,j}^{R,aem}(x)-c_{s,j}^{L,aem}(x) \right)\right)`", "`has_nonohmic_potential_membrane == True` and/or \ `has_Nernst_diffusion_layer==True`"
244254
"mass transfer flux, concentrate, H\ :sub:`2`\ O", ":math:`J_j^{C} = \left(t_w^{cem}+t_w^{aem} \right)\frac{i(x)}{F}+\left(L^{cem} \left(p_{s, osm}^{cem, L}(x)-p_{s, osm}^{cem, R}(x) \right)+L^{aem} \left(p_{s, osm}^{aem, R}(x)-p_{s, osm}^{aem, L}(x) \right)\right)\frac{\rho_w}{M_w}`", "`has_Nernst_diffusion_layer==True`"
@@ -250,7 +260,7 @@ Some other modifications to previously defined equations are made to accommodate
250260

251261
Frictional pressure drop
252262
^^^^^^^^^^^^^^^^^^^^^^^^
253-
This model can optionally calculate pressured drops along the flow path in the diluate and concentrate channels through config ``has_pressure_change`` and ``pressure_drop_method``. Under the assumption of identical diluate and concentrate channels and starting flow rates, the flow velocities in the two channels are approximated equal and invariant over the channel length when calculating the frictional pressure drops. This approximation is based on the evaluation that the actual velocity variation over the channel length caused by water mass transfer across the consecutive channels leads to negligible errors as compared to the uncertainties carried by the frictional pressure method itself. **Table 7** gives essential equations to simulate the pressure drop. Among extensive literatures using these equations, a good reference paper is by Wright et. al., 2018 (*References*).
263+
This model can optionally calculate pressured drops along the flow path in the diluate and concentrate channels through config ``has_pressure_change`` and ``pressure_drop_method``. Under the assumption of identical diluate and concentrate channels and starting flow rates, the flow velocities in the two channels are approximated equal and invariant over the channel length when calculating the frictional pressure drops. This approximation is based on the evaluation that the actual velocity variation over the channel length caused by water mass transfer across the consecutive channels leads to negligible errors as compared to the uncertainties carried by the frictional pressure method itself. **Table 7** gives essential equations to simulate the pressure drop. Among extensive literatures using these equations, a good reference paper is by Wright et. al., 2018 ([6]_).
254264

255265
.. csv-table:: **Table 7** Essential equations supporting the pressure drop calculation
256266
:header: "Description", "Equation", "Condition"
@@ -301,9 +311,12 @@ Nomenclature
301311
":math:`p_{osm}`", "Osmotic pressure", ":math:`Pa`"
302312
":math:`r_{tot}`", "Total areal resistance", ":math:`\Omega m^2`"
303313
":math:`r`", "Membrane areal resistance", ":math:`\Omega m^2`"
314+
":math:`r_const`", "Membrane areal resistance independent on ion concentration", ":math:`\Omega m^2`"
315+
":math:`r_coef`", "The dependent cofficient of membrane areal resistance to :math:`1/c_b`", ":math:`\Omega mol m^{-1}`"
304316
":math:`r_{el}`", "Electrode areal resistance", ":math:`\Omega m^2`"
305317
":math:`d`", "Channel height", ":math:`m`"
306318
":math:`\kappa`", "Solution conductivity", ":math:`S m^{-1}\ or\ \Omega^{-1} m^{-1}`"
319+
":math:`\sigma`", "Spacer conductivity coefficient", ":math:`S m^{-1}\ or\ \Omega^{-1} m^{-1}`"
307320
":math:`\eta`", "Current efficiency for desalination", "dimensionless"
308321
":math:`P`", "Power consumption", ":math:`W`"
309322
":math:`P_Q`", "Specific power consumption", ":math:`kW\ h\ m^{-3}`"
@@ -351,18 +364,16 @@ Nomenclature
351364

352365
References
353366
----------
354-
Strathmann, H. (2010). Electrodialysis, a mature technology with a multitude of new applications.
355-
Desalination, 264(3), 268-288.
367+
.. [1] Strathmann, H. (2010). Electrodialysis, a mature technology with a multitude of new applications. Desalination, 264(3), 268-288.
368+
369+
.. [2] Strathmann, H. (2004). Ion-exchange membrane separation processes. Elsevier. Ch. 4.
356370
357-
Strathmann, H. (2004). Ion-exchange membrane separation processes. Elsevier. Ch. 4.
371+
.. [3] Campione, A., Cipollina, A., Bogle, I. D. L., Gurreri, L., Tamburini, A., Tedesco, M., & Micale, G. (2019). A hierarchical model for novel schemes of electrodialysis desalination. Desalination, 465, 79-93.
358372
359-
Campione, A., Cipollina, A., Bogle, I. D. L., Gurreri, L., Tamburini, A., Tedesco, M., & Micale, G. (2019).
360-
A hierarchical model for novel schemes of electrodialysis desalination. Desalination, 465, 79-93.
373+
.. [4] Campione, A., Gurreri, L., Ciofalo, M., Micale, G., Tamburini, A., & Cipollina, A. (2018). Electrodialysis for water desalination: A critical assessment of recent developments on process fundamentals, models and applications. Desalination, 434, 121-160.
361374
362-
Campione, A., Gurreri, L., Ciofalo, M., Micale, G., Tamburini, A., & Cipollina, A. (2018).
363-
Electrodialysis for water desalination: A critical assessment of recent developments on process
364-
fundamentals, models and applications. Desalination, 434, 121-160.
375+
.. [5] Spiegler, K. S. (1971). Polarization at ion exchange membrane-solution interfaces. Desalination, 9(4), 367-385.
365376
366-
Spiegler, K. S. (1971). Polarization at ion exchange membrane-solution interfaces. Desalination, 9(4), 367-385.
377+
.. [6] Wright, N. C., Shah, S. R., & Amrose, S. E. (2018). A robust model of brackish water electrodialysis desalination with experimental comparison at different size scales. Desalination, 443, 27-43.
367378
368-
Wright, N. C., Shah, S. R., & Amrose, S. E. (2018). A robust model of brackish water electrodialysis desalination with experimental comparison at different size scales. Desalination, 443, 27-43.
379+
.. [7] Galama, A. H., Vermaas, D. A., Veerman, J., Saakes, M., Rijnaarts, H. H. M., Post, J. W., & Nijmeijer, K. (2014). Membrane resistance: The effect of salinity gradients over a cation exchange membrane. Journal of membrane science, 467, 279-291.

watertap/flowsheets/electrodialysis/electrodialysis_1stack.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ def set_operating_conditions(m):
207207
m.fs.EDstack.channel_height.fix(2.7e-4)
208208
m.fs.EDstack.membrane_areal_resistance["cem"].fix(1.89e-4)
209209
m.fs.EDstack.membrane_areal_resistance["aem"].fix(1.77e-4)
210+
m.fs.EDstack.membrane_areal_resistance_coef["cem"].fix(0)
211+
m.fs.EDstack.membrane_areal_resistance_coef["aem"].fix(0)
210212
m.fs.EDstack.cell_width.fix(0.1)
211213
m.fs.EDstack.cell_length.fix(0.79)
212214
m.fs.EDstack.membrane_thickness["aem"].fix(1.3e-4)
@@ -220,6 +222,7 @@ def set_operating_conditions(m):
220222
m.fs.EDstack.ion_trans_number_membrane["cem", "Cl_-"].fix(0)
221223
m.fs.EDstack.ion_trans_number_membrane["aem", "Cl_-"].fix(1)
222224
m.fs.EDstack.spacer_porosity.fix(1)
225+
m.fs.EDstack.spacer_conductivity_coefficient.fix(1)
223226

224227
# check zero degrees of freedom
225228
check_dof(m)
@@ -266,7 +269,7 @@ def optimize_system(m, solver=None, checkpoint=None, fail_flag=True):
266269
# Choose and unfix variables to be optimized
267270
m.fs.EDstack.voltage_applied[0].unfix()
268271
m.fs.EDstack.cell_pair_num.unfix()
269-
m.fs.EDstack.cell_pair_num.set_value(10)
272+
m.fs.EDstack.cell_pair_num.set_value(30)
270273
# Give narrower bounds to optimizing variables if available
271274
m.fs.EDstack.voltage_applied[0].setlb(0.5)
272275
m.fs.EDstack.voltage_applied[0].setub(20)
@@ -280,8 +283,9 @@ def optimize_system(m, solver=None, checkpoint=None, fail_flag=True):
280283
print("---report model statistics---\n ", report_statistics(m.fs))
281284
if solver is None:
282285
solver = get_solver()
283-
results = solver.solve(m, tee=True)
284-
check_solve(results, checkpoint=checkpoint, logger=_log, fail_flag=fail_flag)
286+
solve(m, solver=solver, tee=True)
287+
m.fs.EDstack.cell_pair_num.fix(round(value(m.fs.EDstack.cell_pair_num)))
288+
solve(m, solver=solver, tee=True)
285289

286290

287291
def display_model_metrics(m):
@@ -319,6 +323,7 @@ def display_model_metrics(m):
319323
data=[
320324
value(m.fs.EDstack.recovery_mass_H2O[0]),
321325
value(m.fs.mem_area),
326+
value(m.fs.EDstack.cell_pair_num),
322327
value(m.fs.EDstack.voltage_applied[0]),
323328
value(m.fs.costing.specific_energy_consumption),
324329
value(m.fs.costing.LCOW),
@@ -327,6 +332,7 @@ def display_model_metrics(m):
327332
index=[
328333
"Water recovery by mass",
329334
"Total membrane area (aem or cem), m2",
335+
"Cell pair number",
330336
"Operation Voltage, V",
331337
"Specific energy consumption, kWh/m3",
332338
"Levelized cost of water, $/m3",

watertap/flowsheets/electrodialysis/electrodialysis_1stack_conc_recirc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ def _condition_base(m):
279279
m.fs.EDstack.water_permeability_membrane["aem"].fix(1.75e-14)
280280
m.fs.EDstack.membrane_areal_resistance["cem"].fix(1.89e-4)
281281
m.fs.EDstack.membrane_areal_resistance["aem"].fix(1.77e-4)
282+
m.fs.EDstack.membrane_areal_resistance_coef["cem"].fix(0)
283+
m.fs.EDstack.membrane_areal_resistance_coef["aem"].fix(0)
282284
m.fs.EDstack.solute_diffusivity_membrane["cem", "Na_+"].fix(3.28e-11)
283285
m.fs.EDstack.solute_diffusivity_membrane["aem", "Na_+"].fix(3.28e-11)
284286
m.fs.EDstack.solute_diffusivity_membrane["cem", "Cl_-"].fix(3.28e-11)
@@ -299,6 +301,7 @@ def _condition_base(m):
299301
# Spacer properties
300302
m.fs.EDstack.spacer_porosity.fix(0.83)
301303
m.fs.EDstack.spacer_specific_area.fix(10400)
304+
m.fs.EDstack.spacer_conductivity_coefficient.fix(1)
302305

303306
# Electrochemical properties
304307
m.fs.EDstack.electrodes_resistance.fix(0)

watertap/flowsheets/electrodialysis/electrodialysis_1stack_conc_recirc_ui.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ def export_variables(flowsheet=None, exports=None, build_options=None, **kwargs)
245245
ui_units=pyunits.ohm * pyunits.meter**2,
246246
display_units="ohm m^2",
247247
rounding=2,
248-
description="Areal resistnace of the cation exchange membrane",
248+
description="Constant areal resistance of the cation exchange membrane measured in concentrated electrolyte.",
249249
is_input=True,
250250
input_category="Membrane properties",
251251
is_output=False,
@@ -256,7 +256,7 @@ def export_variables(flowsheet=None, exports=None, build_options=None, **kwargs)
256256
ui_units=pyunits.ohm * pyunits.meter**2,
257257
display_units="ohm m^2",
258258
rounding=2,
259-
description="Areal resistnace of the anion exchange membrane",
259+
description="Constant areal resistance of the anion exchange membrane measured in concentrated electrolyte.",
260260
is_input=True,
261261
input_category="Membrane properties",
262262
is_output=False,

watertap/flowsheets/electrodialysis/tests/test_electrodialysis_1stack.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,6 @@ def test_optimization(self, electrodialysis_1D1stack):
173173
edfs.optimize_system(m)
174174
isinstance(m.fs.objective, Objective)
175175
assert m.fs.objective.expr == m.fs.costing.LCOW
176-
assert degrees_of_freedom(m) == 1
177176

178177
assert value(m.fs.feed.properties[0].flow_vol_phase["Liq"]) == pytest.approx(
179178
8.7e-5, abs=1e-6
@@ -188,14 +187,16 @@ def test_optimization(self, electrodialysis_1D1stack):
188187
assert value(m.fs.disposal_salinity) == pytest.approx(18.1124, rel=1e-3)
189188

190189
assert value(m.fs.EDstack.recovery_mass_H2O[0]) == pytest.approx(
191-
0.4846, rel=1e-3
190+
0.48455, rel=1e-3
191+
)
192+
assert value(m.fs.mem_area) == pytest.approx(1.1060, rel=1e-3)
193+
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(
194+
7.03676, rel=1e-3
192195
)
193-
assert value(m.fs.mem_area) == pytest.approx(1.0980, rel=1e-3)
194-
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(7.0325, rel=1e-3)
195196
assert value(m.fs.costing.specific_energy_consumption) == pytest.approx(
196-
2.3062, rel=1e-3
197+
2.2922, rel=1e-3
197198
)
198-
assert value(m.fs.costing.LCOW) == pytest.approx(0.42546, rel=1e-3)
199+
assert value(m.fs.costing.LCOW) == pytest.approx(0.42547, rel=1e-3)
199200

200201
@pytest.mark.unit
201202
def test_main_fun(self, electrodialysis_1D1stack):

0 commit comments

Comments
 (0)