From a83e14fe15ba15b71fb0c14357e2e7a6e69f9e43 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Tue, 16 Nov 2021 15:32:27 -0500 Subject: [PATCH 1/2] unify sei models and unify plating models --- .../source/models/submodels/interface/sei.rst | 14 +-- .../full_battery_models/base_battery_model.py | 12 +- .../lithium_ion/base_lithium_ion_model.py | 32 +---- .../interface/lithium_plating/__init__.py | 3 +- .../interface/lithium_plating/base_plating.py | 1 + .../{irreversible_plating.py => plating.py} | 25 ++-- .../lithium_plating/reversible_plating.py | 99 ---------------- .../submodels/interface/sei/__init__.py | 6 +- .../submodels/interface/sei/base_sei.py | 2 +- .../sei/electron_migration_limited.py | 108 ----------------- .../sei/interstitial_diffusion_limited.py | 110 ------------------ .../{reaction_limited.py => sei_growth.py} | 94 ++++++++++++--- .../sei/solvent_diffusion_limited.py | 102 ---------------- 13 files changed, 111 insertions(+), 497 deletions(-) rename pybamm/models/submodels/interface/lithium_plating/{irreversible_plating.py => plating.py} (81%) delete mode 100644 pybamm/models/submodels/interface/lithium_plating/reversible_plating.py delete mode 100644 pybamm/models/submodels/interface/sei/electron_migration_limited.py delete mode 100644 pybamm/models/submodels/interface/sei/interstitial_diffusion_limited.py rename pybamm/models/submodels/interface/sei/{reaction_limited.py => sei_growth.py} (58%) delete mode 100644 pybamm/models/submodels/interface/sei/solvent_diffusion_limited.py diff --git a/docs/source/models/submodels/interface/sei.rst b/docs/source/models/submodels/interface/sei.rst index 5d7dc1db25..0d868bf905 100644 --- a/docs/source/models/submodels/interface/sei.rst +++ b/docs/source/models/submodels/interface/sei.rst @@ -7,20 +7,8 @@ SEI models .. autoclass:: pybamm.sei.ConstantSEI :members: -.. autoclass:: pybamm.sei.ElectronMigrationLimited - :members: - -.. autoclass:: pybamm.sei.InterstitialDiffusionLimited +.. autoclass:: pybamm.sei.SEIGrowth :members: .. autoclass:: pybamm.sei.NoSEI :members: - -.. autoclass:: pybamm.sei.ReactionLimited - :members: - -.. autoclass:: pybamm.sei.SolventDiffusionLimited - :members: - -.. autoclass:: pybamm.sei.EcReactionLimited - :members: \ No newline at end of file diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 7e2fba2c56..5cede8cf80 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -87,15 +87,9 @@ class BatteryModelOptions(pybamm.FuzzyDict): - "none": :class:`pybamm.sei.NoSEI` (no SEI growth) - "constant": :class:`pybamm.sei.Constant` (constant SEI thickness) - - "reaction limited": :class:`pybamm.sei.ReactionLimited` - - "solvent-diffusion limited":\ - :class:`pybamm.sei.SolventDiffusionLimited` - - "electron-migration limited": \ - :class:`pybamm.sei.ElectronMigrationLimited` - - "interstitial-diffusion limited": \ - :class:`pybamm.sei.InterstitialDiffusionLimited` - - "ec reaction limited": \ - :class:`pybamm.sei.EcReactionLimited` + - "reaction limited", "solvent-diffusion limited",\ + "electron-migration limited", "interstitial-diffusion limited", \ + or "ec reaction limited": :class:`pybamm.sei.SEIGrowth` * "SEI film resistance" : str Set the submodel for additional term in the overpotential due to SEI. The default value is "none" if the "SEI" option is "none", and diff --git a/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py b/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py index 82d1b4c468..2f1c505c0d 100644 --- a/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py +++ b/pybamm/models/full_battery_models/lithium_ion/base_lithium_ion_model.py @@ -198,24 +198,8 @@ def set_sei_submodel(self): self.submodels["sei"] = pybamm.sei.NoSEI(self.param, self.options) elif self.options["SEI"] == "constant": self.submodels["sei"] = pybamm.sei.ConstantSEI(self.param, self.options) - elif self.options["SEI"] == "reaction limited": - self.submodels["sei"] = pybamm.sei.ReactionLimited( - self.param, reaction_loc, self.options - ) - elif self.options["SEI"] == "solvent-diffusion limited": - self.submodels["sei"] = pybamm.sei.SolventDiffusionLimited( - self.param, reaction_loc, self.options - ) - elif self.options["SEI"] == "electron-migration limited": - self.submodels["sei"] = pybamm.sei.ElectronMigrationLimited( - self.param, reaction_loc, self.options - ) - elif self.options["SEI"] == "interstitial-diffusion limited": - self.submodels["sei"] = pybamm.sei.InterstitialDiffusionLimited( - self.param, reaction_loc, self.options - ) - elif self.options["SEI"] == "ec reaction limited": - self.submodels["sei"] = pybamm.sei.EcReactionLimited( + else: + self.submodels["sei"] = pybamm.sei.SEIGrowth( self.param, reaction_loc, self.options ) @@ -224,14 +208,10 @@ def set_lithium_plating_submodel(self): self.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating( self.param, self.options ) - elif self.options["lithium plating"] == "reversible": - self.submodels[ - "lithium plating" - ] = pybamm.lithium_plating.ReversiblePlating(self.param, self.x_average) - elif self.options["lithium plating"] == "irreversible": - self.submodels[ - "lithium plating" - ] = pybamm.lithium_plating.IrreversiblePlating(self.param, self.x_average) + else: + self.submodels["lithium plating"] = pybamm.lithium_plating.Plating( + self.param, self.x_average, self.options + ) def set_other_reaction_submodels_to_zero(self): self.submodels["negative oxygen interface"] = pybamm.interface.NoReaction( diff --git a/pybamm/models/submodels/interface/lithium_plating/__init__.py b/pybamm/models/submodels/interface/lithium_plating/__init__.py index d69ceca09e..cea178158d 100644 --- a/pybamm/models/submodels/interface/lithium_plating/__init__.py +++ b/pybamm/models/submodels/interface/lithium_plating/__init__.py @@ -1,4 +1,3 @@ from .base_plating import BasePlating from .no_plating import NoPlating -from .reversible_plating import ReversiblePlating -from .irreversible_plating import IrreversiblePlating +from .plating import Plating diff --git a/pybamm/models/submodels/interface/lithium_plating/base_plating.py b/pybamm/models/submodels/interface/lithium_plating/base_plating.py index 2df38bdc3d..d7f474501f 100644 --- a/pybamm/models/submodels/interface/lithium_plating/base_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/base_plating.py @@ -20,6 +20,7 @@ class BasePlating(BaseInterface): Origin of the Differential Voltage Minimum Associated with Li Plating in Lithium-Ion Batteries". Journal of The Electrochemical Society, 167:090540, 2019 + **Extends:** :class:`pybamm.interface.BaseInterface` """ diff --git a/pybamm/models/submodels/interface/lithium_plating/irreversible_plating.py b/pybamm/models/submodels/interface/lithium_plating/plating.py similarity index 81% rename from pybamm/models/submodels/interface/lithium_plating/irreversible_plating.py rename to pybamm/models/submodels/interface/lithium_plating/plating.py index 4f3997d4c4..75ebe01391 100644 --- a/pybamm/models/submodels/interface/lithium_plating/irreversible_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/plating.py @@ -1,18 +1,21 @@ # -# Class for irreversible lithium plating +# Class for lithium plating # import pybamm from .base_plating import BasePlating -class IrreversiblePlating(BasePlating): - """Base class for irreversible lithium plating. +class Plating(BasePlating): + """Class for lithium plating. + Parameters ---------- param : parameter class The parameters to use for this submodel x_average : bool Whether to use x-averaged variables (SPM, SPMe, etc) or full variables (DFN) + options : dict, optional + A dictionary of options to be passed to the model. References ---------- @@ -24,8 +27,8 @@ class IrreversiblePlating(BasePlating): **Extends:** :class:`pybamm.lithium_plating.BasePlating` """ - def __init__(self, param, x_average): - super().__init__(param) + def __init__(self, param, x_average, options): + super().__init__(param, options) self.x_average = x_average pybamm.citations.register("OKane2020") @@ -54,14 +57,22 @@ def get_coupled_variables(self, variables): T = variables["Negative electrode temperature"] eta_sei = variables["SEI film overpotential"] c_plated_Li = variables["Lithium plating concentration"] + j0_stripping = param.j0_stripping(c_e_n, c_plated_Li, T) j0_plating = param.j0_plating(c_e_n, c_plated_Li, T) phi_ref = param.U_n_ref / param.potential_scale eta_stripping = delta_phi + phi_ref + eta_sei eta_plating = -eta_stripping prefactor = 1 / (2 * (1 + self.param.Theta * T)) - # j_stripping is always negative, because there is no stripping, only plating - j_stripping = -j0_plating * pybamm.exp(prefactor * eta_plating) + + if self.options["lithium plating"] == "reversible": + j_stripping = j0_stripping * pybamm.exp( + prefactor * eta_stripping + ) - j0_plating * pybamm.exp(prefactor * eta_plating) + elif self.options["lithium plating"] == "irreversible": + # j_stripping is always negative, because there is no stripping, only + # plating + j_stripping = -j0_plating * pybamm.exp(prefactor * eta_plating) variables.update(self._get_standard_overpotential_variables(eta_stripping)) variables.update(self._get_standard_reaction_variables(j_stripping)) diff --git a/pybamm/models/submodels/interface/lithium_plating/reversible_plating.py b/pybamm/models/submodels/interface/lithium_plating/reversible_plating.py deleted file mode 100644 index dcfb4b59b6..0000000000 --- a/pybamm/models/submodels/interface/lithium_plating/reversible_plating.py +++ /dev/null @@ -1,99 +0,0 @@ -# -# Class for reversible lithium plating -# -import pybamm -from .base_plating import BasePlating - - -class ReversiblePlating(BasePlating): - """Base class for reversible lithium plating. - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - x_average : bool - Whether to use x-averaged variables (SPM, SPMe, etc) or full variables (DFN) - - References - ---------- - .. [1] SEJ O'Kane, ID Campbell, MWJ Marzook, GJ Offer and M Marinescu. "Physical - Origin of the Differential Voltage Minimum Associated with Li Plating in - Lithium-Ion Batteries". Journal of The Electrochemical Society, - 167:090540, 2019 - - **Extends:** :class:`pybamm.lithium_plating.BasePlating` - """ - - def __init__(self, param, x_average): - super().__init__(param) - self.x_average = x_average - pybamm.citations.register("OKane2020") - - def get_fundamental_variables(self): - if self.x_average is True: - c_plated_Li_av = pybamm.Variable( - "X-averaged lithium plating concentration", domain="current collector" - ) - c_plated_Li = pybamm.PrimaryBroadcast(c_plated_Li_av, "negative electrode") - else: - c_plated_Li = pybamm.Variable( - "Lithium plating concentration", - domain="negative electrode", - auxiliary_domains={"secondary": "current collector"}, - ) - - variables = self._get_standard_concentration_variables(c_plated_Li) - - return variables - - def get_coupled_variables(self, variables): - param = self.param - delta_phi = variables["Negative electrode surface potential difference"] - c_e_n = variables["Negative electrolyte concentration"] - T = variables["Negative electrode temperature"] - eta_sei = variables["SEI film overpotential"] - c_plated_Li = variables["Lithium plating concentration"] - j0_stripping = param.j0_stripping(c_e_n, c_plated_Li, T) - j0_plating = param.j0_plating(c_e_n, c_plated_Li, T) - phi_ref = param.U_n_ref / param.potential_scale - eta_stripping = delta_phi + phi_ref + eta_sei - eta_plating = -eta_stripping - prefactor = 1 / (2 * (1 + self.param.Theta * T)) - j_stripping = j0_stripping * pybamm.exp( - prefactor * eta_stripping - ) - j0_plating * pybamm.exp(prefactor * eta_plating) - - variables.update(self._get_standard_overpotential_variables(eta_stripping)) - variables.update(self._get_standard_reaction_variables(j_stripping)) - - # Update whole cell variables, which also updates the "sum of" variables - variables.update(super().get_coupled_variables(variables)) - - return variables - - def set_rhs(self, variables): - if self.x_average is True: - c_plated_Li = variables["X-averaged lithium plating concentration"] - j_stripping = variables[ - "X-averaged lithium plating interfacial current density" - ] - # Note a is dimensionless (has a constant value of 1 if the surface - # area does not change) - a = variables["X-averaged negative electrode surface area to volume ratio"] - else: - c_plated_Li = variables["Lithium plating concentration"] - j_stripping = variables["Lithium plating interfacial current density"] - a = variables["Negative electrode surface area to volume ratio"] - - Gamma_plating = self.param.Gamma_plating - - self.rhs = {c_plated_Li: -Gamma_plating * a * j_stripping} - - def set_initial_conditions(self, variables): - if self.x_average is True: - c_plated_Li = variables["X-averaged lithium plating concentration"] - else: - c_plated_Li = variables["Lithium plating concentration"] - c_plated_Li_0 = self.param.c_plated_Li_0 - - self.initial_conditions = {c_plated_Li: c_plated_Li_0} diff --git a/pybamm/models/submodels/interface/sei/__init__.py b/pybamm/models/submodels/interface/sei/__init__.py index 2c5fb582e1..14c3945622 100644 --- a/pybamm/models/submodels/interface/sei/__init__.py +++ b/pybamm/models/submodels/interface/sei/__init__.py @@ -1,8 +1,4 @@ from .base_sei import BaseModel from .no_sei import NoSEI from .constant_sei import ConstantSEI -from .reaction_limited import ReactionLimited -from .solvent_diffusion_limited import SolventDiffusionLimited -from .electron_migration_limited import ElectronMigrationLimited -from .interstitial_diffusion_limited import InterstitialDiffusionLimited -from .ec_reaction_limited import EcReactionLimited +from .sei_growth import SEIGrowth diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 0e548453b3..d0c1d91126 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -158,7 +158,7 @@ def _get_standard_concentration_variables(self, variables): ) v_bar = param.v_bar # Set scales for the "EC Reaction Limited" model - if isinstance(self, pybamm.sei.EcReactionLimited): + if self.options["SEI"] == "ec reaction limited": L_inner_0 = 0 L_outer_0 = 1 li_mols_per_sei_mols = 2 diff --git a/pybamm/models/submodels/interface/sei/electron_migration_limited.py b/pybamm/models/submodels/interface/sei/electron_migration_limited.py deleted file mode 100644 index b52200fcff..0000000000 --- a/pybamm/models/submodels/interface/sei/electron_migration_limited.py +++ /dev/null @@ -1,108 +0,0 @@ -# -# Class for electron-migration limited SEI growth -# -import pybamm -from .base_sei import BaseModel - - -class ElectronMigrationLimited(BaseModel): - """ - Class for electron-migration limited SEI growth. - - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - reaction_loc : str - Where the reaction happens: "x-average" (SPM, SPMe, etc), - "full electrode" (full DFN), or "interface" (half-cell DFN) - options : dict, optional - A dictionary of options to be passed to the model. - - **Extends:** :class:`pybamm.sei.BaseModel` - """ - - def __init__(self, param, reaction_loc, options=None): - super().__init__(param, options=options) - self.reaction_loc = reaction_loc - - def get_fundamental_variables(self): - if self.reaction_loc == "x-average": - L_inner_av = pybamm.standard_variables.L_inner_av - L_outer_av = pybamm.standard_variables.L_outer_av - L_inner = pybamm.PrimaryBroadcast(L_inner_av, "negative electrode") - L_outer = pybamm.PrimaryBroadcast(L_outer_av, "negative electrode") - elif self.reaction_loc == "full electrode": - L_inner = pybamm.standard_variables.L_inner - L_outer = pybamm.standard_variables.L_outer - elif self.reaction_loc == "interface": - L_inner = pybamm.standard_variables.L_inner_interface - L_outer = pybamm.standard_variables.L_outer_interface - - variables = self._get_standard_thickness_variables(L_inner, L_outer) - variables.update(self._get_standard_concentration_variables(variables)) - - return variables - - def get_coupled_variables(self, variables): - L_sei_inner = variables["Inner SEI thickness"] - if self.reaction_loc == "interface": - phi_s_n = variables["Lithium metal interface electrode potential"] - else: - phi_s_n = variables["Negative electrode potential"] - - U_inner = self.param.U_inner_electron - C_sei = self.param.C_sei_electron - - j_sei = (phi_s_n - U_inner) / (C_sei * L_sei_inner) - - alpha = 0.5 - j_inner = alpha * j_sei - j_outer = (1 - alpha) * j_sei - - variables.update(self._get_standard_reaction_variables(j_inner, j_outer)) - - # Update whole cell variables, which also updates the "sum of" variables - variables.update(super().get_coupled_variables(variables)) - - return variables - - def set_rhs(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - j_inner = variables["X-averaged inner SEI interfacial current density"] - j_outer = variables["X-averaged outer SEI interfacial current density"] - # Note a is dimensionless (has a constant value of 1 if the surface - # area does not change) - a = variables["X-averaged negative electrode surface area to volume ratio"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - j_inner = variables["Inner SEI interfacial current density"] - j_outer = variables["Outer SEI interfacial current density"] - if self.reaction_loc == "interface": - a = 1 - else: - a = variables["Negative electrode surface area to volume ratio"] - - v_bar = self.param.v_bar - Gamma_SEI = self.param.Gamma_SEI - - self.rhs = { - L_inner: -Gamma_SEI * a * j_inner, - L_outer: -v_bar * Gamma_SEI * a * j_outer, - } - - def set_initial_conditions(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - - L_inner_0 = self.param.L_inner_0 - L_outer_0 = self.param.L_outer_0 - - self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0} diff --git a/pybamm/models/submodels/interface/sei/interstitial_diffusion_limited.py b/pybamm/models/submodels/interface/sei/interstitial_diffusion_limited.py deleted file mode 100644 index 0c265fc13a..0000000000 --- a/pybamm/models/submodels/interface/sei/interstitial_diffusion_limited.py +++ /dev/null @@ -1,110 +0,0 @@ -# -# Class for interstitial-diffusion limited SEI growth -# -import pybamm -from .base_sei import BaseModel - - -class InterstitialDiffusionLimited(BaseModel): - """ - Class for interstitial-diffusion limited SEI growth. - - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - reaction_loc : str - Where the reaction happens: "x-average" (SPM, SPMe, etc), - "full electrode" (full DFN), or "interface" (half-cell DFN) - options : dict, optional - A dictionary of options to be passed to the model. - - **Extends:** :class:`pybamm.sei.BaseModel` - """ - - def __init__(self, param, reaction_loc, options=None): - super().__init__(param, options=options) - self.reaction_loc = reaction_loc - - def get_fundamental_variables(self): - if self.reaction_loc == "x-average": - L_inner_av = pybamm.standard_variables.L_inner_av - L_outer_av = pybamm.standard_variables.L_outer_av - L_inner = pybamm.PrimaryBroadcast(L_inner_av, "negative electrode") - L_outer = pybamm.PrimaryBroadcast(L_outer_av, "negative electrode") - elif self.reaction_loc == "full electrode": - L_inner = pybamm.standard_variables.L_inner - L_outer = pybamm.standard_variables.L_outer - elif self.reaction_loc == "interface": - L_inner = pybamm.standard_variables.L_inner_interface - L_outer = pybamm.standard_variables.L_outer_interface - - variables = self._get_standard_thickness_variables(L_inner, L_outer) - variables.update(self._get_standard_concentration_variables(variables)) - - return variables - - def get_coupled_variables(self, variables): - L_sei_inner = variables["Inner SEI thickness"] - # delta_phi = phi_s - phi_e - if self.reaction_loc == "interface": - delta_phi = variables[ - "Lithium metal interface surface potential difference" - ] - else: - delta_phi = variables["Negative electrode surface potential difference"] - - C_sei = self.param.C_sei_inter - - j_sei = -pybamm.exp(-delta_phi) / (C_sei * L_sei_inner) - - alpha = 0.5 - j_inner = alpha * j_sei - j_outer = (1 - alpha) * j_sei - - variables.update(self._get_standard_reaction_variables(j_inner, j_outer)) - - # Update whole cell variables, which also updates the "sum of" variables - variables.update(super().get_coupled_variables(variables)) - - return variables - - def set_rhs(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - j_inner = variables["X-averaged inner SEI interfacial current density"] - j_outer = variables["X-averaged outer SEI interfacial current density"] - # Note a is dimensionless (has a constant value of 1 if the surface - # area does not change) - a = variables["X-averaged negative electrode surface area to volume ratio"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - j_inner = variables["Inner SEI interfacial current density"] - j_outer = variables["Outer SEI interfacial current density"] - if self.reaction_loc == "interface": - a = 1 - else: - a = variables["Negative electrode surface area to volume ratio"] - - v_bar = self.param.v_bar - Gamma_SEI = self.param.Gamma_SEI - - self.rhs = { - L_inner: -Gamma_SEI * a * j_inner, - L_outer: -v_bar * Gamma_SEI * a * j_outer, - } - - def set_initial_conditions(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - - L_inner_0 = self.param.L_inner_0 - L_outer_0 = self.param.L_outer_0 - - self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0} diff --git a/pybamm/models/submodels/interface/sei/reaction_limited.py b/pybamm/models/submodels/interface/sei/sei_growth.py similarity index 58% rename from pybamm/models/submodels/interface/sei/reaction_limited.py rename to pybamm/models/submodels/interface/sei/sei_growth.py index 9de7438408..09f8c3ec94 100644 --- a/pybamm/models/submodels/interface/sei/reaction_limited.py +++ b/pybamm/models/submodels/interface/sei/sei_growth.py @@ -1,13 +1,13 @@ # -# Class for reaction limited SEI growth +# Class for SEI growth # import pybamm from .base_sei import BaseModel -class ReactionLimited(BaseModel): +class SEIGrowth(BaseModel): """ - Class for reaction limited SEI growth. + Class for SEI growth. Parameters ---------- @@ -39,6 +39,9 @@ def get_fundamental_variables(self): L_inner = pybamm.standard_variables.L_inner_interface L_outer = pybamm.standard_variables.L_outer_interface + if self.options["SEI"] == "ec reaction limited": + L_inner = 0 * L_inner # Set L_inner to zero, copying domains + variables = self._get_standard_thickness_variables(L_inner, L_outer) variables.update(self._get_standard_concentration_variables(variables)) @@ -51,8 +54,10 @@ def get_coupled_variables(self, variables): delta_phi = variables[ "Lithium metal interface surface potential difference" ] + phi_s_n = variables["Lithium metal interface electrode potential"] else: delta_phi = variables["Negative electrode surface potential difference"] + phi_s_n = variables["Negative electrode potential"] # Look for current that contributes to the -IR drop # If we can't find the interfacial current density from the main reaction, j, @@ -69,15 +74,68 @@ def get_coupled_variables(self, variables): + self.domain.lower() + " electrode total interfacial current density" ] + + L_sei_inner = variables["Inner SEI thickness"] + L_sei_outer = variables["Outer SEI thickness"] L_sei = variables["Total SEI thickness"] R_sei = self.param.R_sei - alpha = 0.5 - # alpha = param.alpha - C_sei = param.C_sei_reaction - # need to revise for thermal case - j_sei = -(1 / C_sei) * pybamm.exp(-0.5 * (delta_phi - j * L_sei * R_sei)) + if self.options["SEI"] == "reaction limited": + # alpha = param.alpha + C_sei = param.C_sei_reaction + + # need to revise for thermal case + j_sei = -(1 / C_sei) * pybamm.exp(-0.5 * (delta_phi - j * L_sei * R_sei)) + + elif self.options["SEI"] == "electron-migration limited": + U_inner = self.param.U_inner_electron + C_sei = self.param.C_sei_electron + j_sei = (phi_s_n - U_inner) / (C_sei * L_sei_inner) + + elif self.options["SEI"] == "interstitial-diffusion limited": + C_sei = self.param.C_sei_inter + j_sei = -pybamm.exp(-delta_phi) / (C_sei * L_sei_inner) + + elif self.options["SEI"] == "solvent-diffusion limited": + C_sei = self.param.C_sei_solvent + j_sei = -1 / (C_sei * L_sei_outer) + + elif self.options["SEI"] == "ec reaction limited": + C_sei_ec = self.param.C_sei_ec + C_ec = self.param.C_ec + + # we have a linear system for j_sei and c_ec + # c_ec = 1 + j_sei * L_sei * C_ec + # j_sei = - C_sei_ec * c_ec * exp() + # so + # j_sei = - C_sei_ec * exp() - j_sei * L_sei * C_ec * C_sei_ec * exp() + # so + # j_sei = -C_sei_ec * exp() / (1 + L_sei * C_ec * C_sei_ec * exp()) + # c_ec = 1 / (1 + L_sei * C_ec * C_sei_ec * exp()) + # need to revise for thermal case + C_sei_exp = C_sei_ec * pybamm.exp(-0.5 * (delta_phi - j * L_sei * R_sei)) + j_sei = -C_sei_exp / (1 + L_sei * C_ec * C_sei_exp) + c_ec = 1 / (1 + L_sei * C_ec * C_sei_exp) + + # Get variables related to the concentration + c_ec_av = pybamm.x_average(c_ec) + c_ec_scale = self.param.c_ec_0_dim + + variables.update( + { + "EC surface concentration": c_ec, + "EC surface concentration [mol.m-3]": c_ec * c_ec_scale, + "X-averaged EC surface concentration": c_ec_av, + "X-averaged EC surface concentration [mol.m-3]": c_ec_av + * c_ec_scale, + } + ) + + if self.options["SEI"] == "ec reaction limited": + alpha = 0 + else: + alpha = 0.5 j_inner = alpha * j_sei j_outer = (1 - alpha) * j_sei @@ -108,13 +166,16 @@ def set_rhs(self, variables): else: a = variables["Negative electrode surface area to volume ratio"] - v_bar = self.param.v_bar Gamma_SEI = self.param.Gamma_SEI - self.rhs = { - L_inner: -Gamma_SEI * a * j_inner, - L_outer: -v_bar * Gamma_SEI * a * j_outer, - } + if self.options["SEI"] == "ec reaction limited": + self.rhs = {L_outer: -Gamma_SEI * a * j_outer / 2} + else: + v_bar = self.param.v_bar + self.rhs = { + L_inner: -Gamma_SEI * a * j_inner, + L_outer: -v_bar * Gamma_SEI * a * j_outer, + } def set_initial_conditions(self, variables): if self.reaction_loc == "x-average": @@ -123,7 +184,10 @@ def set_initial_conditions(self, variables): else: L_inner = variables["Inner SEI thickness"] L_outer = variables["Outer SEI thickness"] + L_inner_0 = self.param.L_inner_0 L_outer_0 = self.param.L_outer_0 - - self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0} + if self.options["SEI"] == "ec reaction limited": + self.initial_conditions = {L_outer: L_outer_0} + else: + self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0} diff --git a/pybamm/models/submodels/interface/sei/solvent_diffusion_limited.py b/pybamm/models/submodels/interface/sei/solvent_diffusion_limited.py deleted file mode 100644 index 5d4e235e0c..0000000000 --- a/pybamm/models/submodels/interface/sei/solvent_diffusion_limited.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# Class for solvent-diffusion limited SEI growth -# -import pybamm -from .base_sei import BaseModel - - -class SolventDiffusionLimited(BaseModel): - """ - Class for solvent-diffusion limited SEI growth. - - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - reaction_loc : str - Where the reaction happens: "x-average" (SPM, SPMe, etc), - "full electrode" (full DFN), or "interface" (half-cell DFN) - options : dict, optional - A dictionary of options to be passed to the model. - - **Extends:** :class:`pybamm.sei.BaseModel` - """ - - def __init__(self, param, reaction_loc, options=None): - super().__init__(param, options=options) - self.reaction_loc = reaction_loc - - def get_fundamental_variables(self): - if self.reaction_loc == "x-average": - L_inner_av = pybamm.standard_variables.L_inner_av - L_outer_av = pybamm.standard_variables.L_outer_av - L_inner = pybamm.PrimaryBroadcast(L_inner_av, "negative electrode") - L_outer = pybamm.PrimaryBroadcast(L_outer_av, "negative electrode") - elif self.reaction_loc == "full electrode": - L_inner = pybamm.standard_variables.L_inner - L_outer = pybamm.standard_variables.L_outer - elif self.reaction_loc == "interface": - L_inner = pybamm.standard_variables.L_inner_interface - L_outer = pybamm.standard_variables.L_outer_interface - - variables = self._get_standard_thickness_variables(L_inner, L_outer) - variables.update(self._get_standard_concentration_variables(variables)) - - return variables - - def get_coupled_variables(self, variables): - L_sei_outer = variables["Outer SEI thickness"] - - C_sei = self.param.C_sei_solvent - - j_sei = -1 / (C_sei * L_sei_outer) - - alpha = 0.5 - j_inner = alpha * j_sei - j_outer = (1 - alpha) * j_sei - - variables.update(self._get_standard_reaction_variables(j_inner, j_outer)) - - # Update whole cell variables, which also updates the "sum of" variables - variables.update(super().get_coupled_variables(variables)) - - return variables - - def set_rhs(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - j_inner = variables["X-averaged inner SEI interfacial current density"] - j_outer = variables["X-averaged outer SEI interfacial current density"] - # Note a is dimensionless (has a constant value of 1 if the surface - # area does not change) - a = variables["X-averaged negative electrode surface area to volume ratio"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - j_inner = variables["Inner SEI interfacial current density"] - j_outer = variables["Outer SEI interfacial current density"] - if self.reaction_loc == "interface": - a = 1 - else: - a = variables["Negative electrode surface area to volume ratio"] - - v_bar = self.param.v_bar - Gamma_SEI = self.param.Gamma_SEI - - self.rhs = { - L_inner: -Gamma_SEI * a * j_inner, - L_outer: -v_bar * Gamma_SEI * a * j_outer, - } - - def set_initial_conditions(self, variables): - if self.reaction_loc == "x-average": - L_inner = variables["X-averaged inner SEI thickness"] - L_outer = variables["X-averaged outer SEI thickness"] - else: - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] - L_inner_0 = self.param.L_inner_0 - L_outer_0 = self.param.L_outer_0 - - self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0} From d66e13378e80e27e9202981dfa9a8e49ef2b6310 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Tue, 16 Nov 2021 17:48:09 -0500 Subject: [PATCH 2/2] remove EC SEI model --- CHANGELOG.md | 1 + .../interface/sei/ec_reaction_limited.py | 144 ------------------ 2 files changed, 1 insertion(+), 144 deletions(-) delete mode 100644 pybamm/models/submodels/interface/sei/ec_reaction_limited.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 302d810e41..14aa69e538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Features +- Reformatted SEI growth models into a single submodel with conditionals ([#1808](https://github.com/pybamm-team/PyBaMM/pull/1808)) - Stress-induced diffusion is now a separate model option instead of being automatically included when using the particle mechanics submodels ([#1797](https://github.com/pybamm-team/PyBaMM/pull/1797)) - `Experiment`s with drive cycles can be solved ([#1793](https://github.com/pybamm-team/PyBaMM/pull/1793)) - Added surface area to volume ratio as a factor to the SEI equations ([#1790](https://github.com/pybamm-team/PyBaMM/pull/1790)) diff --git a/pybamm/models/submodels/interface/sei/ec_reaction_limited.py b/pybamm/models/submodels/interface/sei/ec_reaction_limited.py deleted file mode 100644 index be9bc1edc6..0000000000 --- a/pybamm/models/submodels/interface/sei/ec_reaction_limited.py +++ /dev/null @@ -1,144 +0,0 @@ -# -# Class for reaction limited SEI growth -# -import pybamm -from .base_sei import BaseModel - - -class EcReactionLimited(BaseModel): - """ - Class for reaction limited SEI growth. This model assumes the "inner" - SEI layer is of zero thickness and only models the "outer" SEI layer. - - Parameters - ---------- - param : parameter class - The parameters to use for this submodel - reaction_loc : str - Where the reaction happens: "x-average" (SPM, SPMe, etc), - "full electrode" (full DFN), or "interface" (half-cell DFN) - options : dict, optional - A dictionary of options to be passed to the model. - - **Extends:** :class:`pybamm.sei.BaseModel` - """ - - def __init__(self, param, reaction_loc, options=None): - super().__init__(param, options=options) - self.reaction_loc = reaction_loc - - def get_fundamental_variables(self): - - if self.reaction_loc == "x-average": - L_inner = pybamm.FullBroadcast(0, "negative electrode", "current collector") - L_outer_av = pybamm.standard_variables.L_outer_av - L_outer = pybamm.PrimaryBroadcast(L_outer_av, "negative electrode") - elif self.reaction_loc == "full electrode": - L_inner = pybamm.FullBroadcast(0, "negative electrode", "current collector") - L_outer = pybamm.standard_variables.L_outer - elif self.reaction_loc == "interface": - L_inner = pybamm.PrimaryBroadcast(0, "current collector") - L_outer = pybamm.standard_variables.L_outer_interface - - variables = self._get_standard_thickness_variables(L_inner, L_outer) - variables.update(self._get_standard_concentration_variables(variables)) - - return variables - - def get_coupled_variables(self, variables): - # delta_phi = phi_s - phi_e - if self.reaction_loc == "interface": - delta_phi = variables[ - "Lithium metal interface surface potential difference" - ] - else: - delta_phi = variables["Negative electrode surface potential difference"] - - L_sei = variables["Outer SEI thickness"] - - # Look for current that contributes to the -IR drop - # If we can't find the interfacial current density from the main reaction, j, - # it's ok to fall back on the total interfacial current density, j_tot - # This should only happen when the interface submodel is "InverseButlerVolmer" - # in which case j = j_tot (uniform) anyway - if "Negative electrode interfacial current density" in variables: - j = variables["Negative electrode interfacial current density"] - elif self.reaction_loc == "interface": - j = variables["Lithium metal total interfacial current density"] - else: - j = variables[ - "X-averaged " - + self.domain.lower() - + " electrode total interfacial current density" - ] - - C_sei_ec = self.param.C_sei_ec - R_sei = self.param.R_sei - C_ec = self.param.C_ec - - # we have a linear system for j_sei and c_ec - # c_ec = 1 + j_sei * L_sei * C_ec - # j_sei = - C_sei_ec * c_ec * exp() - # so - # j_sei = - C_sei_ec * exp() - j_sei * L_sei * C_ec * C_sei_ec * exp() - # so - # j_sei = -C_sei_ec * exp() / (1 + L_sei * C_ec * C_sei_ec * exp()) - # c_ec = 1 / (1 + L_sei * C_ec * C_sei_ec * exp()) - # need to revise for thermal case - C_sei_exp = C_sei_ec * pybamm.exp(-0.5 * (delta_phi - j * L_sei * R_sei)) - j_sei = -C_sei_exp / (1 + L_sei * C_ec * C_sei_exp) - c_ec = 1 / (1 + L_sei * C_ec * C_sei_exp) - - if self.reaction_loc == "interface": - j_inner = pybamm.PrimaryBroadcast(0, "current collector") - else: - j_inner = pybamm.FullBroadcast(0, "negative electrode", "current collector") - j_outer = j_sei - - variables.update(self._get_standard_reaction_variables(j_inner, j_outer)) - - # Get variables related to the concentration - c_ec_av = pybamm.x_average(c_ec) - c_ec_scale = self.param.c_ec_0_dim - - variables.update( - { - "EC surface concentration": c_ec, - "EC surface concentration [mol.m-3]": c_ec * c_ec_scale, - "X-averaged EC surface concentration": c_ec_av, - "X-averaged EC surface concentration [mol.m-3]": c_ec_av * c_ec_scale, - } - ) - - # Update whole cell variables, which also updates the "sum of" variables - variables.update(super().get_coupled_variables(variables)) - - return variables - - def set_rhs(self, variables): - if self.reaction_loc == "x-average": - L_sei = variables["X-averaged outer SEI thickness"] - j_sei = variables["X-averaged outer SEI interfacial current density"] - # Note a is dimensionless (has a constant value of 1 if the surface - # area does not change) - a = variables["X-averaged negative electrode surface area to volume ratio"] - else: - L_sei = variables["Outer SEI thickness"] - j_sei = variables["Outer SEI interfacial current density"] - if self.reaction_loc == "interface": - a = 1 - else: - a = variables["Negative electrode surface area to volume ratio"] - - Gamma_SEI = self.param.Gamma_SEI - - self.rhs = {L_sei: -Gamma_SEI * a * j_sei / 2} - - def set_initial_conditions(self, variables): - if self.reaction_loc == "x-average": - L_sei = variables["X-averaged outer SEI thickness"] - else: - L_sei = variables["Outer SEI thickness"] - L_sei_0 = pybamm.Scalar(1) - - self.initial_conditions = {L_sei: L_sei_0}