Skip to content

Commit

Permalink
Merge pull request #1617 from pybamm-team/issue-1505-porosity
Browse files Browse the repository at this point in the history
Issue 1505 porosity
  • Loading branch information
brosaplanella authored Aug 25, 2021
2 parents bf4c1eb + 7a2dc5c commit b12c3ee
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 93 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ example notebook ([#1602](https://github.com/pybamm-team/PyBaMM/pull/1602))

## Bug fixes

- Porosity variation for SEI and plating models is calculated from the film thickness rather than from a separate ODE ([#1617](https://github.com/pybamm-team/PyBaMM/pull/1617))
- Fixed a bug where the order of the indexing for the entries of variables discretised using FEM was incorrect ([#1556](https://github.com/pybamm-team/PyBaMM/pull/1556))
- Fix broken module import for spyder when running a script twice ([#1555](https://github.com/pybamm-team/PyBaMM/pull/1555))
- Fixed ElectrodeSOH model for multi-dimensional simulations ([#1548](https://github.com/pybamm-team/PyBaMM/pull/1548))
Expand Down
1 change: 1 addition & 0 deletions docs/source/models/submodels/porosity/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Porosity
base_porosity
constant_porosity
reaction_driven_porosity
reaction_driven_porosity_ode

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Reaction-driven Model as an ODE
===============================

.. autoclass:: pybamm.porosity.ReactionDrivenODE
:members:



1 change: 1 addition & 0 deletions examples/scripts/calendar_ageing.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

sim.solve(t_eval=t_eval, solver=solver)
sims.append(sim)

pb.dynamic_plot(
sims,
[
Expand Down
6 changes: 5 additions & 1 deletion examples/scripts/cycling_ageing.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,28 @@
"Hold at 4.2 V until C/20",
"Rest for 30 minutes",
"Discharge at C/3 until 2.8 V",
"Rest for 30 minutes",
),
(
"Charge at 1 C until 4.2 V",
"Hold at 4.2 V until C/20",
"Rest for 30 minutes",
"Discharge at 1 C until 2.8 V",
"Rest for 30 minutes",
),
(
"Charge at 1 C until 4.2 V",
"Hold at 4.2 V until C/20",
"Rest for 30 minutes",
"Discharge at 2 C until 2.8 V",
"Rest for 30 minutes",
),
(
"Charge at 1 C until 4.2 V",
"Hold at 4.2 V until C/20",
"Rest for 30 minutes",
"Discharge at 3 C until 2.8 V",
"Discharge at 3 C until 2.8 V (10 second period)",
"Rest for 30 minutes",
),
]
)
Expand Down
2 changes: 1 addition & 1 deletion pybamm/models/full_battery_models/lead_acid/full.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def __init__(self, options=None, name="Full model", build=True):
pybamm.citations.register("Sulzer2019physical")

def set_porosity_submodel(self):
self.submodels["porosity"] = pybamm.porosity.ReactionDriven(
self.submodels["porosity"] = pybamm.porosity.ReactionDrivenODE(
self.param, self.options, False
)

Expand Down
4 changes: 2 additions & 2 deletions pybamm/models/full_battery_models/lead_acid/higher_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def set_full_porosity_submodel(self):
Update porosity submodel, now that we have the spatially heterogeneous
interfacial current densities
"""
self.submodels["full porosity"] = pybamm.porosity.ReactionDriven(
self.submodels["full porosity"] = pybamm.porosity.ReactionDrivenODE(
self.param, self.options, False
)

Expand Down Expand Up @@ -271,7 +271,7 @@ def set_full_porosity_submodel(self):
Update porosity submodel, now that we have the spatially heterogeneous
interfacial current densities
"""
self.submodels["full porosity"] = pybamm.porosity.ReactionDriven(
self.submodels["full porosity"] = pybamm.porosity.ReactionDrivenODE(
self.param, self.options, False
)

Expand Down
2 changes: 1 addition & 1 deletion pybamm/models/full_battery_models/lead_acid/loqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def set_current_collector_submodel(self):

def set_porosity_submodel(self):

self.submodels["leading-order porosity"] = pybamm.porosity.ReactionDriven(
self.submodels["leading-order porosity"] = pybamm.porosity.ReactionDrivenODE(
self.param, self.options, True
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def _get_standard_concentration_variables(self, c_plated_Li):
f"X-averaged {domain} lithium plating concentration": c_plated_Li_av,
f"X-averaged {domain} lithium plating concentration"
" [mol.m-3]": c_plated_Li_av * c_scale,
f"{Domain} lithium plating thickness": L_plated_Li,
f"{Domain} lithium plating thickness [m]": L_plated_Li * L_scale,
f"X-averaged {domain} lithium plating thickness [m]": L_plated_Li_av
* L_scale,
Expand Down
6 changes: 6 additions & 0 deletions pybamm/models/submodels/interface/sei/base_sei.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def _get_standard_thickness_variables(self, L_inner, L_outer):

L_inner_av = pybamm.x_average(L_inner)
L_outer_av = pybamm.x_average(L_outer)
L_tot = L_inner + L_outer
L_tot_av = L_inner_av + L_outer_av

variables = {
"Inner " + domain + " SEI thickness": L_inner,
Expand All @@ -65,6 +67,10 @@ def _get_standard_thickness_variables(self, L_inner, L_outer):
"Outer " + domain + " SEI thickness [m]": L_outer * L_scale,
"X-averaged outer " + domain + " SEI thickness": L_outer_av,
"X-averaged outer " + domain + " SEI thickness [m]": L_outer_av * L_scale,
self.domain + " electrode SEI thickness": L_tot,
self.domain + " electrode SEI thickness [m]": L_tot * L_scale,
"X-averaged " + domain + " SEI thickness": L_tot_av,
"X-averaged " + domain + " SEI thickness [m]": L_tot_av * L_scale,
}

# Get variables related to the total thickness
Expand Down
1 change: 1 addition & 0 deletions pybamm/models/submodels/porosity/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .base_porosity import BaseModel
from .constant_porosity import Constant
from .reaction_driven_porosity import ReactionDriven
from .reaction_driven_porosity_ode import ReactionDrivenODE
97 changes: 19 additions & 78 deletions pybamm/models/submodels/porosity/reaction_driven_porosity.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#
# Class for reaction driven porosity changes
# Class for reaction driven porosity changes as a multiple of SEI/plating thicknesses
#
import pybamm
from .base_porosity import BaseModel


class ReactionDriven(BaseModel):
"""Reaction-driven porosity changes
"""Reaction-driven porosity changes as a multiple of SEI/plating thicknesses
Parameters
----------
Expand All @@ -24,87 +24,28 @@ def __init__(self, param, options, x_average):
super().__init__(param, options)
self.x_average = x_average

def get_fundamental_variables(self):
if self.x_average is True:
eps_n_pc = pybamm.standard_variables.eps_n_pc
eps_s_pc = pybamm.standard_variables.eps_s_pc
eps_p_pc = pybamm.standard_variables.eps_p_pc

eps_n = pybamm.PrimaryBroadcast(eps_n_pc, "negative electrode")
eps_s = pybamm.PrimaryBroadcast(eps_s_pc, "separator")
eps_p = pybamm.PrimaryBroadcast(eps_p_pc, "positive electrode")
else:
eps_n = pybamm.standard_variables.eps_n
eps_s = pybamm.standard_variables.eps_s
eps_p = pybamm.standard_variables.eps_p
variables = self._get_standard_porosity_variables(eps_n, eps_s, eps_p)

return variables

def get_coupled_variables(self, variables):
L_sei_n = variables["Total negative electrode SEI thickness [m]"]
L_sei_0 = self.param.L_inner_0_dim + self.param.L_outer_0_dim
L_pl_n = variables["Negative electrode lithium plating thickness [m]"]

if self.x_average is True:
j_n = variables["X-averaged negative electrode interfacial current density"]
j_p = variables["X-averaged positive electrode interfacial current density"]
j_sei_n = variables[
"X-averaged negative electrode SEI interfacial current density"
]
j_plating = variables[
"X-averaged negative electrode lithium plating "
"interfacial current density"
]
deps_s_dt = pybamm.PrimaryBroadcast(0, "current collector")
else:
j_n = variables["Negative electrode interfacial current density"]
j_p = variables["Positive electrode interfacial current density"]
j_sei_n = variables["Negative electrode SEI interfacial current density"]
j_plating = variables[
"Negative electrode lithium plating interfacial current density"
]
deps_s_dt = pybamm.FullBroadcast(
0, "separator", auxiliary_domains={"secondary": "current collector"}
)
L_tot = (L_sei_n - L_sei_0) + L_pl_n

deps_n_dt = -self.param.beta_surf_n * j_n
if self.options["SEI porosity change"] == "true":
beta_sei_n = self.param.beta_sei_n
deps_n_dt += beta_sei_n * j_sei_n
if self.options["lithium plating porosity change"] == "true":
beta_plating = self.param.beta_plating
deps_n_dt += beta_plating * j_plating
a_n = variables["Negative electrode surface area to volume ratio [m-1]"]

deps_p_dt = -self.param.beta_surf_p * j_p
# This assumes a thin film so curvature effects are neglected. They could be
# included (i.e. for a sphere it is a_n * (L_tot + L_tot ** 2 / R_n + L_tot **
# 3 / (3 * R_n ** 2))) but it is not clear if it is relevant or not.
delta_eps_n = -a_n * L_tot

variables.update(
self._get_standard_porosity_change_variables(
deps_n_dt, deps_s_dt, deps_p_dt
)
eps_n = self.param.epsilon_n_init + delta_eps_n
eps_s = pybamm.FullBroadcast(
self.param.epsilon_s_init, "separator", "current collector"
)
# no SEI or plating in the positive electrode
eps_p = pybamm.FullBroadcast(
self.param.epsilon_p_init, "positive electrode", "current collector"
)
variables = self._get_standard_porosity_variables(eps_n, eps_s, eps_p)

return variables

def set_rhs(self, variables):
if self.x_average is True:
for domain in ["negative electrode", "separator", "positive electrode"]:
eps_av = variables["X-averaged " + domain + " porosity"]
deps_dt_av = variables["X-averaged " + domain + " porosity change"]
self.rhs.update({eps_av: deps_dt_av})
else:
eps = variables["Porosity"]
deps_dt = variables["Porosity change"]
self.rhs = {eps: deps_dt}

def set_initial_conditions(self, variables):
if self.x_average is True:
eps_n_av = variables["X-averaged negative electrode porosity"]
eps_s_av = variables["X-averaged separator porosity"]
eps_p_av = variables["X-averaged positive electrode porosity"]

self.initial_conditions = {
eps_n_av: self.param.epsilon_n_init,
eps_s_av: self.param.epsilon_s_init,
eps_p_av: self.param.epsilon_p_init,
}
else:
eps = variables["Porosity"]
self.initial_conditions = {eps: self.param.epsilon_init}
92 changes: 92 additions & 0 deletions pybamm/models/submodels/porosity/reaction_driven_porosity_ode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#
# Class for reaction driven porosity changes as an ODE
#
import pybamm
from .base_porosity import BaseModel


class ReactionDrivenODE(BaseModel):
"""Reaction-driven porosity changes as an ODE
Parameters
----------
param : parameter class
The parameters to use for this submodel
options : dict
Options dictionary passed from the full model
x_average : bool
Whether to use x-averaged variables (SPM, SPMe, etc) or full variables (DFN)
**Extends:** :class:`pybamm.porosity.BaseModel`
"""

def __init__(self, param, options, x_average):
super().__init__(param, options)
self.x_average = x_average

def get_fundamental_variables(self):
if self.x_average is True:
eps_n_pc = pybamm.standard_variables.eps_n_pc
eps_s_pc = pybamm.standard_variables.eps_s_pc
eps_p_pc = pybamm.standard_variables.eps_p_pc

eps_n = pybamm.PrimaryBroadcast(eps_n_pc, "negative electrode")
eps_s = pybamm.PrimaryBroadcast(eps_s_pc, "separator")
eps_p = pybamm.PrimaryBroadcast(eps_p_pc, "positive electrode")
else:
eps_n = pybamm.standard_variables.eps_n
eps_s = pybamm.standard_variables.eps_s
eps_p = pybamm.standard_variables.eps_p
variables = self._get_standard_porosity_variables(eps_n, eps_s, eps_p)

return variables

def get_coupled_variables(self, variables):

if self.x_average is True:
j_n = variables["X-averaged negative electrode interfacial current density"]
j_p = variables["X-averaged positive electrode interfacial current density"]
deps_s_dt = pybamm.PrimaryBroadcast(0, "current collector")
else:
j_n = variables["Negative electrode interfacial current density"]
j_p = variables["Positive electrode interfacial current density"]
deps_s_dt = pybamm.FullBroadcast(
0, "separator", auxiliary_domains={"secondary": "current collector"}
)

deps_n_dt = -self.param.beta_surf_n * j_n
deps_p_dt = -self.param.beta_surf_p * j_p

variables.update(
self._get_standard_porosity_change_variables(
deps_n_dt, deps_s_dt, deps_p_dt
)
)

return variables

def set_rhs(self, variables):
if self.x_average is True:
for domain in ["negative electrode", "separator", "positive electrode"]:
eps_av = variables["X-averaged " + domain + " porosity"]
deps_dt_av = variables["X-averaged " + domain + " porosity change"]
self.rhs.update({eps_av: deps_dt_av})
else:
eps = variables["Porosity"]
deps_dt = variables["Porosity change"]
self.rhs = {eps: deps_dt}

def set_initial_conditions(self, variables):
if self.x_average is True:
eps_n_av = variables["X-averaged negative electrode porosity"]
eps_s_av = variables["X-averaged separator porosity"]
eps_p_av = variables["X-averaged positive electrode porosity"]

self.initial_conditions = {
eps_n_av: self.param.epsilon_n_init,
eps_s_av: self.param.epsilon_s_init,
eps_p_av: self.param.epsilon_p_init,
}
else:
eps = variables["Porosity"]
self.initial_conditions = {eps: self.param.epsilon_init}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ def test_public_functions(self):
# param = pybamm.LeadAcidParameters()
param = pybamm.LithiumIonParameters()
a_n = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["negative electrode"])
a_p = pybamm.PrimaryBroadcast(pybamm.Scalar(0), ["positive electrode"])

variables = {
"Negative electrode interfacial current density": a_n,
"Negative electrode SEI interfacial current density": a_n,
"Positive electrode interfacial current density": a_p,
"Negative electrode lithium plating interfacial current density": a_n,
"Total negative electrode SEI thickness [m]": a_n,
"Negative electrode lithium plating thickness [m]": a_n,
"Negative electrode surface area to volume ratio [m-1]": a_n,

}
options = {
"SEI": "ec reaction limited",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@ def test_public_functions(self):
param = pybamm.LithiumIonParameters()
a = pybamm.PrimaryBroadcast(pybamm.Scalar(0), "current collector")
variables = {
"X-averaged negative electrode interfacial current density": a,
"X-averaged negative electrode SEI interfacial current density": a,
"X-averaged positive electrode interfacial current density": a,
"X-averaged negative electrode lithium plating "
"interfacial current density": a,
"Total negative electrode SEI thickness [m]": a,
"Negative electrode lithium plating thickness [m]": a,
"Negative electrode surface area to volume ratio [m-1]": a,
}
options = {
"SEI": "ec reaction limited",
Expand Down

0 comments on commit b12c3ee

Please sign in to comment.