Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to ED1d for calculating membrane resistance as a function of ion concentration #1522

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
13 changes: 10 additions & 3 deletions docs/technical_reference/unit_models/electrodialysis_1D.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ are parameters that should be provided in order to fully solve the model.
"Cell pair number", ":math:`n`", "cell_pair_num", "None", "dimensionless", 1
"Current utilization coefficient", ":math:`\xi`", "current_utilization", "None", "dimensionless", 1
"Channel height", ":math:`d`", "channel_height", "none", ":math:`m` ", 1
"Membrane areal resistance", ":math:`r`", "membrane_areal_resistance", "['cem', 'aem']", ":math:`\Omega m^2`", 2
"Membrane areal resistance independent on ion concentration", ":math:`r_{const}`", "membrane_areal_resistance_const", "['cem', 'aem']", ":math:`\Omega m^2`", 2
"Membrane areal resistance coefficient to ion concentration", ":math:`r_{coef}`", "membrane_areal_resistance_coef", "['cem', 'aem']", ":math:`\Omega mol m^{-1}`", 2
"Spacer conductivity coefficient", ":math:`r`", "spacer_conductivity_coefficient", "None", "dimensionless", 1
"Cell width", ":math:`b`", "cell_width", "None", ":math:`\text{m}`", 1
"Cell length", ":math:`l`", "cell_length", "None", ":math:`\text{m}`", 1
"Thickness of ion exchange membranes", ":math:`\delta`", "membrane_thickness", "['cem', 'aem']", ":math:`m`", 2
Expand Down Expand Up @@ -186,7 +188,8 @@ Additionally, several other equations are built to describe the electrochemical

"Electrical input condition", ":math:`i(x) = \frac{I}{bl}`, for 'Constant_Current'; :math:`u(x) =U` for 'Constant_Voltage'"
"Ohm's law", ":math:`u(x) = i(x) r_{tot}(x)`"
"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}`"
"mebrane resistance calculation", ":math:`r^{iem}(x)=r^{iem}_{const}+\frac{r^{iem}_{coef}}{c_b^D}`"
"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}`"
"Electrical power consumption", ":math:`P(x)=b\int _0 ^l u(x)i(x) dx`"
"Water-production-specific power consumption", ":math:`P_Q=\frac{P(x=l)}{3.6\times 10^6 nQ_{out}^D}`"
"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]}`"
Expand Down Expand Up @@ -238,7 +241,8 @@ Some other modifications to previously defined equations are made to accommodate
:header: "Original equation description", "Equation replacement", "Condition"

"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`"
"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`"
"mebrane resistance calculation", ":math:`r^{iem}(x)=r^{iem}_{const}+\frac{r^{iem}_{coef}}{c_b^D}`"
"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`"
"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`"
"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`"
"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`"
Expand Down Expand Up @@ -301,9 +305,12 @@ Nomenclature
":math:`p_{osm}`", "Osmotic pressure", ":math:`Pa`"
":math:`r_{tot}`", "Total areal resistance", ":math:`\Omega m^2`"
":math:`r`", "Membrane areal resistance", ":math:`\Omega m^2`"
":math:`r_const`", "Membrane areal resistance independent on ion concentration", ":math:`\Omega m^2`"
":math:`r_coef`", "The dependent cofficient of membrane areal resistance to :math:`1/c_b`", ":math:`\Omega mol m^{-1}`"
":math:`r_{el}`", "Electrode areal resistance", ":math:`\Omega m^2`"
":math:`d`", "Channel height", ":math:`m`"
":math:`\kappa`", "Solution conductivity", ":math:`S m^{-1}\ or\ \Omega^{-1} m^{-1}`"
":math:`\sigma`", "Spacer conductivity coefficient", ":math:`S m^{-1}\ or\ \Omega^{-1} m^{-1}`"
":math:`\eta`", "Current efficiency for desalination", "dimensionless"
":math:`P`", "Power consumption", ":math:`W`"
":math:`P_Q`", "Specific power consumption", ":math:`kW\ h\ m^{-3}`"
Expand Down
16 changes: 11 additions & 5 deletions watertap/flowsheets/electrodialysis/electrodialysis_1stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,10 @@ def set_operating_conditions(m):
m.fs.EDstack.cell_pair_num.fix(100)
m.fs.EDstack.current_utilization.fix(1)
m.fs.EDstack.channel_height.fix(2.7e-4)
m.fs.EDstack.membrane_areal_resistance["cem"].fix(1.89e-4)
m.fs.EDstack.membrane_areal_resistance["aem"].fix(1.77e-4)
m.fs.EDstack.membrane_areal_resistance_const["cem"].fix(1.89e-4)
m.fs.EDstack.membrane_areal_resistance_const["aem"].fix(1.77e-4)
m.fs.EDstack.membrane_areal_resistance_coef["cem"].fix(0)
m.fs.EDstack.membrane_areal_resistance_coef["aem"].fix(0)
m.fs.EDstack.cell_width.fix(0.1)
m.fs.EDstack.cell_length.fix(0.79)
m.fs.EDstack.membrane_thickness["aem"].fix(1.3e-4)
Expand All @@ -220,6 +222,7 @@ def set_operating_conditions(m):
m.fs.EDstack.ion_trans_number_membrane["cem", "Cl_-"].fix(0)
m.fs.EDstack.ion_trans_number_membrane["aem", "Cl_-"].fix(1)
m.fs.EDstack.spacer_porosity.fix(1)
m.fs.EDstack.spacer_conductivity_coefficient.fix(1)

# check zero degrees of freedom
check_dof(m)
Expand Down Expand Up @@ -266,7 +269,7 @@ def optimize_system(m, solver=None, checkpoint=None, fail_flag=True):
# Choose and unfix variables to be optimized
m.fs.EDstack.voltage_applied[0].unfix()
m.fs.EDstack.cell_pair_num.unfix()
m.fs.EDstack.cell_pair_num.set_value(10)
m.fs.EDstack.cell_pair_num.set_value(30)
# Give narrower bounds to optimizing variables if available
m.fs.EDstack.voltage_applied[0].setlb(0.5)
m.fs.EDstack.voltage_applied[0].setub(20)
Expand All @@ -280,8 +283,9 @@ def optimize_system(m, solver=None, checkpoint=None, fail_flag=True):
print("---report model statistics---\n ", report_statistics(m.fs))
if solver is None:
solver = get_solver()
results = solver.solve(m, tee=True)
check_solve(results, checkpoint=checkpoint, logger=_log, fail_flag=fail_flag)
solve(m, solver=solver, tee=True)
m.fs.EDstack.cell_pair_num.fix(round(value(m.fs.EDstack.cell_pair_num)))
solve(m, solver=solver, tee=True)


def display_model_metrics(m):
Expand Down Expand Up @@ -319,6 +323,7 @@ def display_model_metrics(m):
data=[
value(m.fs.EDstack.recovery_mass_H2O[0]),
value(m.fs.mem_area),
value(m.fs.EDstack.cell_pair_num),
value(m.fs.EDstack.voltage_applied[0]),
value(m.fs.costing.specific_energy_consumption),
value(m.fs.costing.LCOW),
Expand All @@ -327,6 +332,7 @@ def display_model_metrics(m):
index=[
"Water recovery by mass",
"Total membrane area (aem or cem), m2",
"Cell pair number",
"Operation Voltage, V",
"Specific energy consumption, kWh/m3",
"Levelized cost of water, $/m3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,10 @@ def _condition_base(m):
m.fs.EDstack.water_trans_number_membrane["aem"].fix(4.3)
m.fs.EDstack.water_permeability_membrane["cem"].fix(2.16e-14)
m.fs.EDstack.water_permeability_membrane["aem"].fix(1.75e-14)
m.fs.EDstack.membrane_areal_resistance["cem"].fix(1.89e-4)
m.fs.EDstack.membrane_areal_resistance["aem"].fix(1.77e-4)
m.fs.EDstack.membrane_areal_resistance_const["cem"].fix(1.89e-4)
m.fs.EDstack.membrane_areal_resistance_const["aem"].fix(1.77e-4)
m.fs.EDstack.membrane_areal_resistance_coef["cem"].fix(0)
m.fs.EDstack.membrane_areal_resistance_coef["aem"].fix(0)
m.fs.EDstack.solute_diffusivity_membrane["cem", "Na_+"].fix(3.28e-11)
m.fs.EDstack.solute_diffusivity_membrane["aem", "Na_+"].fix(3.28e-11)
m.fs.EDstack.solute_diffusivity_membrane["cem", "Cl_-"].fix(3.28e-11)
Expand All @@ -299,6 +301,7 @@ def _condition_base(m):
# Spacer properties
m.fs.EDstack.spacer_porosity.fix(0.83)
m.fs.EDstack.spacer_specific_area.fix(10400)
m.fs.EDstack.spacer_conductivity_coefficient.fix(1)

# Electrochemical properties
m.fs.EDstack.electrodes_resistance.fix(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,23 +240,23 @@ def export_variables(flowsheet=None, exports=None, build_options=None, **kwargs)
)

exports.add(
obj=fs.EDstack.membrane_areal_resistance["cem"],
obj=fs.EDstack.membrane_areal_resistance_const["cem"],
name="Areal resistnace of CEM",
ui_units=pyunits.ohm * pyunits.meter**2,
display_units="ohm m^2",
rounding=2,
description="Areal resistnace of the cation exchange membrane",
description="Constant areal resistance of the cation exchange membrane measured in concentrated electrolyte.",
is_input=True,
input_category="Membrane properties",
is_output=False,
)
exports.add(
obj=fs.EDstack.membrane_areal_resistance["aem"],
obj=fs.EDstack.membrane_areal_resistance_const["aem"],
name="Areal resistnace of AEM",
ui_units=pyunits.ohm * pyunits.meter**2,
display_units="ohm m^2",
rounding=2,
description="Areal resistnace of the anion exchange membrane",
description="Constant areal resistance of the anion exchange membrane measured in concentrated electrolyte.",
is_input=True,
input_category="Membrane properties",
is_output=False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ def test_optimization(self, electrodialysis_1D1stack):
edfs.optimize_system(m)
isinstance(m.fs.objective, Objective)
assert m.fs.objective.expr == m.fs.costing.LCOW
assert degrees_of_freedom(m) == 1

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

assert value(m.fs.EDstack.recovery_mass_H2O[0]) == pytest.approx(
0.4846, rel=1e-3
0.48455, rel=1e-3
)
assert value(m.fs.mem_area) == pytest.approx(1.1060, rel=1e-3)
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(
7.03676, rel=1e-3
)
assert value(m.fs.mem_area) == pytest.approx(1.0980, rel=1e-3)
assert value(m.fs.EDstack.voltage_applied[0]) == pytest.approx(7.0325, rel=1e-3)
assert value(m.fs.costing.specific_energy_consumption) == pytest.approx(
2.3062, rel=1e-3
2.2922, rel=1e-3
)
assert value(m.fs.costing.LCOW) == pytest.approx(0.42546, rel=1e-3)
assert value(m.fs.costing.LCOW) == pytest.approx(0.42547, rel=1e-3)

@pytest.mark.unit
def test_main_fun(self, electrodialysis_1D1stack):
Expand Down
Loading
Loading