#
# Class for electrolyte diffusion employing stefan-maxwell
#
import numpy as np
import pybamm
from .base_electrolyte_diffusion import BaseElectrolyteDiffusion
[docs]
class Full(BaseElectrolyteDiffusion):
"""Class for conservation of mass in the electrolyte employing the
Stefan-Maxwell constitutive equations. (Full refers to unreduced by
asymptotic methods)
Parameters
----------
param : parameter class
The parameters to use for this submodel
options : dict, optional
A dictionary of options to be passed to the model.
x_average_state : bool, optional
Whether to make the x-averaged electrolyte concentration an algebraic state
when a surface form is used. This keeps the Jacobian sparse for the
composite conductivity submodels, which read the x-average in every
equation, but only adds an extra state for the full conductivity submodel,
which never reads it. Default is False.
"""
def __init__(self, param, options=None, x_average_state=False):
super().__init__(param, options)
self.x_average_state = x_average_state
@property
def _c_e_av_is_state(self) -> bool:
return self.x_average_state and self.options["surface form"] != "false"
def _get_standard_whole_cell_concentration_variables(self, c_e):
if not self._c_e_av_is_state:
return super()._get_standard_whole_cell_concentration_variables(c_e)
c_e.print_name = "c_e"
c_e_av = pybamm.Variable(
"X-averaged electrolyte concentration [mol.m-3]",
domain="current collector",
bounds=(0, np.inf),
scale=self.param.c_e_init_av,
)
variables = {
"Electrolyte concentration [mol.m-3]": c_e,
"X-averaged electrolyte concentration [mol.m-3]": c_e_av,
}
nondim = {
name.replace("[mol.m-3]", "[Molar]"): var / 1000
for name, var in variables.items()
}
variables.update(nondim)
return variables
[docs]
def get_fundamental_variables(self):
eps_c_e_dict = {}
for domain in self.options.whole_cell_domains:
Domain = domain.capitalize()
eps_c_e_k = pybamm.Variable(
f"{Domain} porosity times concentration [mol.m-3]",
domain=domain,
auxiliary_domains={"secondary": "current collector"},
bounds=(0, np.inf),
scale=self.param.c_e_init_av,
)
eps_c_e_k.print_name = f"eps_c_e_{domain[0]}"
eps_c_e_dict[domain] = eps_c_e_k
variables = self._get_standard_porosity_times_concentration_variables(
eps_c_e_dict
)
return variables
[docs]
def get_coupled_variables(self, variables):
c_e_dict = {}
for domain in self.options.whole_cell_domains:
Domain = domain.capitalize()
eps_k = variables[f"{Domain} porosity"]
eps_c_e_k = variables[f"{Domain} porosity times concentration [mol.m-3]"]
c_e_k = eps_c_e_k / eps_k
c_e_dict[domain] = c_e_k
variables["Electrolyte concentration concatenation [mol.m-3]"] = (
pybamm.concatenation(*c_e_dict.values())
)
variables.update(self._get_standard_domain_concentration_variables(c_e_dict))
c_e = (
variables["Porosity times concentration [mol.m-3]"] / variables["Porosity"]
)
variables.update(self._get_standard_whole_cell_concentration_variables(c_e))
# Whole domain
tor = variables["Electrolyte transport efficiency"]
i_e = variables["Electrolyte current density [A.m-2]"]
v_box = variables["Volume-averaged velocity [m.s-1]"]
T = variables["Cell temperature [K]"]
N_e_diffusion = -tor * self.param.D_e(c_e, T) * pybamm.grad(c_e)
N_e_migration = self.param.t_plus(c_e, T) * i_e / self.param.F
N_e_convection = c_e * v_box
N_e = N_e_diffusion + N_e_migration + N_e_convection
variables.update(self._get_standard_flux_variables(N_e))
variables.update(
{
"Electrolyte diffusion flux [mol.m-2.s-1]": N_e_diffusion,
"Electrolyte migration flux [mol.m-2.s-1]": N_e_migration,
"Electrolyte convection flux [mol.m-2.s-1]": N_e_convection,
}
)
return variables
[docs]
def set_rhs(self, variables):
eps_c_e = variables["Porosity times concentration [mol.m-3]"]
c_e = variables["Electrolyte concentration [mol.m-3]"]
N_e = variables["Electrolyte flux [mol.m-2.s-1]"]
div_Vbox = variables["Transverse volume-averaged acceleration [m.s-2]"]
sum_s_a_j = variables["Sum of electrolyte reaction source terms [A.m-3]"]
sum_s_a_j.print_name = "aj"
source_terms = sum_s_a_j / self.param.F
self.rhs = {eps_c_e: -pybamm.div(N_e) + source_terms - c_e * div_Vbox}
[docs]
def set_algebraic(self, variables):
if not self._c_e_av_is_state:
return
c_e_av = variables["X-averaged electrolyte concentration [mol.m-3]"]
c_e = variables["Electrolyte concentration [mol.m-3]"]
c_e_av_expr = pybamm.x_average(c_e)
self.algebraic = {c_e_av: (c_e_av - c_e_av_expr) / self.param.c_e_init_av}
[docs]
def set_initial_conditions(self, variables):
eps_c_e = variables["Porosity times concentration [mol.m-3]"]
self.initial_conditions = {
eps_c_e: self.param.epsilon_init * self.param.c_e_init
}
if self._c_e_av_is_state:
c_e_av = variables["X-averaged electrolyte concentration [mol.m-3]"]
self.initial_conditions[c_e_av] = self.param.c_e_init
[docs]
def set_boundary_conditions(self, variables):
c_e = variables["Electrolyte concentration [mol.m-3]"]
c_e_conc = variables["Electrolyte concentration concatenation [mol.m-3]"]
T = variables["Cell temperature [K]"]
tor = variables["Electrolyte transport efficiency"]
i_boundary_cc = variables["Current collector current density [A.m-2]"]
def flux_bc(side):
# returns the flux at a separator/electrode interface
# assuming v_box = 0 for now
return (
pybamm.boundary_value(
-(1 - self.param.t_plus(c_e, T))
/ (tor * self.param.D_e(c_e, T) * self.param.F),
side,
)
* i_boundary_cc
)
if self.options.whole_cell_domains[0] == "negative electrode":
# left bc at anode/current collector interface
lbc = pybamm.Scalar(0)
elif self.options.whole_cell_domains[0] == "separator":
# left bc at anode/separator interface
lbc = flux_bc("left")
if self.options.whole_cell_domains[-1] == "positive electrode":
# right bc at cathode/current collector interface
rbc = pybamm.Scalar(0)
# elif self.options.whole_cell_domains[-1] == "separator":
# # right bc at separator/cathode interface
# rbc = flux_bc("right")
# add boundary conditions to both forms of the concentration
self.boundary_conditions = {
c_e: {"left": (lbc, "Neumann"), "right": (rbc, "Neumann")},
c_e_conc: {"left": (lbc, "Neumann"), "right": (rbc, "Neumann")},
}