From 8c18a88dbc42cb10c7db49bacdf9fb73ca44594b Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 8 Sep 2022 16:12:53 -0400 Subject: [PATCH 1/9] add composite SEI model --- examples/scripts/compare_lithium_ion.py | 8 +- .../scripts/compare_lithium_ion_two_phase.py | 1 + .../seis/example_composite/README.md | 6 + .../seis/example_composite/parameters.csv | 28 ++ .../full_battery_models/base_battery_model.py | 7 +- .../lithium_ion/base_lithium_ion_model.py | 50 ++-- .../full_battery_models/lithium_ion/dfn.py | 14 +- .../full_battery_models/lithium_ion/spm.py | 14 +- pybamm/models/standard_variables.py | 29 -- .../active_material/total_active_material.py | 52 +--- .../submodels/interface/base_interface.py | 29 +- .../interface/kinetics/base_kinetics.py | 12 +- .../inverse_kinetics/inverse_butler_volmer.py | 2 +- .../interface/kinetics/total_main_kinetics.py | 42 +-- .../submodels/interface/sei/__init__.py | 1 + .../submodels/interface/sei/base_sei.py | 192 +++++++------ .../submodels/interface/sei/constant_sei.py | 12 +- .../models/submodels/interface/sei/no_sei.py | 10 +- .../submodels/interface/sei/sei_growth.py | 167 ++++++----- .../submodels/interface/sei/total_sei.py | 53 ++++ .../porosity/reaction_driven_porosity.py | 2 +- pybamm/parameters/lithium_ion_parameters.py | 260 +++++++++--------- pybamm/parameters/parameter_sets.py | 2 +- .../base_lithium_ion_tests.py | 14 + .../base_lithium_ion_tests.py | 4 + 25 files changed, 544 insertions(+), 467 deletions(-) create mode 100644 pybamm/input/parameters/lithium_ion/seis/example_composite/README.md create mode 100644 pybamm/input/parameters/lithium_ion/seis/example_composite/parameters.csv create mode 100644 pybamm/models/submodels/interface/sei/total_sei.py diff --git a/examples/scripts/compare_lithium_ion.py b/examples/scripts/compare_lithium_ion.py index 6108036b9b..2aeeedb2c0 100644 --- a/examples/scripts/compare_lithium_ion.py +++ b/examples/scripts/compare_lithium_ion.py @@ -7,10 +7,10 @@ # load models models = [ - pybamm.lithium_ion.SPM(), - pybamm.lithium_ion.SPMe(), - pybamm.lithium_ion.DFN(), - pybamm.lithium_ion.NewmanTobias(), + pybamm.lithium_ion.SPM({"SEI": "solvent-diffusion limited"}), + # pybamm.lithium_ion.SPMe(), + # pybamm.lithium_ion.DFN(), + # pybamm.lithium_ion.NewmanTobias(), ] # create and run simulations diff --git a/examples/scripts/compare_lithium_ion_two_phase.py b/examples/scripts/compare_lithium_ion_two_phase.py index 46ef789287..a75b38e304 100644 --- a/examples/scripts/compare_lithium_ion_two_phase.py +++ b/examples/scripts/compare_lithium_ion_two_phase.py @@ -8,6 +8,7 @@ options = { "particle phases": ("2", "1"), "open circuit potential": (("single", "current sigmoid"), "single"), + "SEI": "ec reaction limited", } models = [ pybamm.lithium_ion.SPM(options), diff --git a/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md b/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md new file mode 100644 index 0000000000..22f7ab47b3 --- /dev/null +++ b/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md @@ -0,0 +1,6 @@ +# SEI parameters + +Example parameters for composite SEI on silicon/graphite. Both phases use the same values, from the paper + +> Yang, X., Leng, Y., Zhang, G., Ge, S., Wang, C. (2017). Modeling of lithium plating induced aging of lithium-ion batteries: Transition from linear to nonlinear aging. Journal of Power Sources, 360, 28-40. + diff --git a/pybamm/input/parameters/lithium_ion/seis/example_composite/parameters.csv b/pybamm/input/parameters/lithium_ion/seis/example_composite/parameters.csv new file mode 100644 index 0000000000..dfe02277dc --- /dev/null +++ b/pybamm/input/parameters/lithium_ion/seis/example_composite/parameters.csv @@ -0,0 +1,28 @@ +Name [units],Value,Reference,Notes +# Empty rows and rows starting with ‘#’ will be ignored,,, +,,, +# SEI properties,,, +Primary: Ratio of lithium moles to SEI moles,2,,Assume SEI made of LEDC and/or Li2CO3 +Primary: Inner SEI partial molar volume [m3.mol-1],9.585e-5, Safari paper, +Primary: Outer SEI partial molar volume [m3.mol-1],9.585e-5, Safari paper, +Primary: SEI resistivity [Ohm.m],2e5, Safari paper, +Primary: Initial inner SEI thickness [m], 2.5E-9, 2.5E-9 1/2 of initial thickness in Safari paper, +Primary: Initial outer SEI thickness [m], 2.5E-9, 1/2 of initial thickness in Safari paper, +Primary: EC initial concentration in electrolyte [mol.m-3], 4.541E3, Safari paper, +Primary: EC diffusivity [m2.s-1], 2E-18, adjusted parameter in Yang paper, +Primary: SEI kinetic rate constant [m.s-1], 1e-12, adjusted parameter in Yang paper, +Primary: SEI open-circuit potential [V], 0.4, Safari paper, +Primary: SEI growth activation energy [J.mol-1], 0,, +,,, +Secondary: Ratio of lithium moles to SEI moles,2,,Assume SEI made of LEDC and/or Li2CO3 +Secondary: Inner SEI partial molar volume [m3.mol-1],9.585e-5, Safari paper, +Secondary: Outer SEI partial molar volume [m3.mol-1],9.585e-5, Safari paper, +Secondary: SEI resistivity [Ohm.m],2e5, Safari paper, +Secondary: Initial inner SEI thickness [m], 2.5E-9, 2.5E-9 1/2 of initial thickness in Safari paper, +Secondary: Initial outer SEI thickness [m], 2.5E-9, 1/2 of initial thickness in Safari paper, +Secondary: EC initial concentration in electrolyte [mol.m-3], 4.541E3, Safari paper, +Secondary: EC diffusivity [m2.s-1], 2E-18, adjusted parameter in Yang paper, +Secondary: SEI kinetic rate constant [m.s-1], 1e-12, adjusted parameter in Yang paper, +Secondary: SEI open-circuit potential [V], 0.4, Safari paper, +Secondary: SEI growth activation energy [J.mol-1], 0,, +,,, \ 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 67bfc486d6..e03ae0487c 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -507,7 +507,6 @@ def __init__(self, extra_options): options["surface form"] != "false" and options["particle size"] == "single" and options["particle"] == "Fickian diffusion" - and options["SEI"] == "none" and options["particle mechanics"] == "none" and options["loss of active material"] == "none" and options["lithium plating"] == "none" @@ -1253,8 +1252,10 @@ def set_voltage_variables(self): eta_sei_av = self.variables["SEI film overpotential"] eta_sei_av_dim = self.variables["SEI film overpotential [V]"] else: - eta_sei_av = self.variables["X-averaged SEI film overpotential"] - eta_sei_av_dim = self.variables["X-averaged SEI film overpotential [V]"] + eta_sei_av = self.variables[f"X-averaged {phase_n}SEI film overpotential"] + eta_sei_av_dim = self.variables[ + f"X-averaged {phase_n}SEI film overpotential [V]" + ] # TODO: add current collector losses to the voltage in 3D 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 f6eeb6b200..70545e7944 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 @@ -269,28 +269,38 @@ def set_sei_submodel(self): else: reaction_loc = "full electrode" - if self.options["SEI"] == "none": - 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) - else: - self.submodels["sei"] = pybamm.sei.SEIGrowth( - self.param, reaction_loc, self.options, cracks=False - ) - # Do not set "sei on cracks" submodel for half-cells - # For full cells, "sei on cracks" submodel must be set, even if it is zero - if reaction_loc != "interface": - if ( - self.options["SEI"] in ["none", "constant"] - or self.options["SEI on cracks"] == "false" - ): - self.submodels["sei on cracks"] = pybamm.sei.NoSEI( - self.param, self.options, cracks=True - ) + phases = self.options.phases["negative"] + for phase in phases: + if self.options["SEI"] == "none": + submodel = pybamm.sei.NoSEI(self.param, self.options, phase) + elif self.options["SEI"] == "constant": + submodel = pybamm.sei.ConstantSEI(self.param, self.options, phase) else: - self.submodels["sei on cracks"] = pybamm.sei.SEIGrowth( - self.param, reaction_loc, self.options, cracks=True + submodel = pybamm.sei.SEIGrowth( + self.param, reaction_loc, self.options, phase, cracks=False ) + self.submodels[f"{phase} sei"] = submodel + # Do not set "sei on cracks" submodel for half-cells + # For full cells, "sei on cracks" submodel must be set, even if it is zero + if reaction_loc != "interface": + if ( + self.options["SEI"] in ["none", "constant"] + or self.options["SEI on cracks"] == "false" + ): + submodel = pybamm.sei.NoSEI( + self.param, self.options, phase, cracks=True + ) + else: + submodel = pybamm.sei.SEIGrowth( + self.param, reaction_loc, self.options, phase, cracks=True + ) + self.submodels[f"{phase} sei on cracks"] = submodel + + if len(phases) > 1: + self.submodels["total sei"] = pybamm.sei.TotalSEI(self.param, self.options) + self.submodels["total sei on cracks"] = pybamm.sei.TotalSEI( + self.param, self.options, cracks=True + ) def set_lithium_plating_submodel(self): if self.options["lithium plating"] == "none": diff --git a/pybamm/models/full_battery_models/lithium_ion/dfn.py b/pybamm/models/full_battery_models/lithium_ion/dfn.py index 1218fd1856..140825975f 100644 --- a/pybamm/models/full_battery_models/lithium_ion/dfn.py +++ b/pybamm/models/full_battery_models/lithium_ion/dfn.py @@ -59,17 +59,19 @@ def set_convection_submodel(self): def set_intercalation_kinetics_submodel(self): for domain in ["negative", "positive"]: intercalation_kinetics = self.get_intercalation_kinetics(domain) - for phase in self.options.phases[domain]: + phases = self.options.phases[domain] + for phase in phases: submod = intercalation_kinetics( self.param, domain, "lithium-ion main", self.options, phase ) self.submodels[f"{domain.lower()} {phase} interface"] = submod - self.submodels[ - f"total {domain} interface" - ] = pybamm.kinetics.TotalMainKinetics( - self.param, domain, "lithium-ion main", self.options - ) + if len(phases) > 1: + self.submodels[ + f"total {domain} interface" + ] = pybamm.kinetics.TotalMainKinetics( + self.param, domain, "lithium-ion main", self.options + ) def set_particle_submodel(self): for domain in ["negative", "positive"]: diff --git a/pybamm/models/full_battery_models/lithium_ion/spm.py b/pybamm/models/full_battery_models/lithium_ion/spm.py index 1425962942..fb79b53e97 100644 --- a/pybamm/models/full_battery_models/lithium_ion/spm.py +++ b/pybamm/models/full_battery_models/lithium_ion/spm.py @@ -95,16 +95,18 @@ def set_intercalation_kinetics_submodel(self): ) else: intercalation_kinetics = self.get_intercalation_kinetics(domain) - for phase in self.options.phases[domain]: + phases = self.options.phases[domain] + for phase in phases: submod = intercalation_kinetics( self.param, domain, "lithium-ion main", self.options, phase ) self.submodels[f"{domain} {phase} interface"] = submod - self.submodels[ - f"total {domain} interface" - ] = pybamm.kinetics.TotalMainKinetics( - self.param, domain, "lithium-ion main", self.options - ) + if len(phases) > 1: + self.submodels[ + f"total {domain} interface" + ] = pybamm.kinetics.TotalMainKinetics( + self.param, domain, "lithium-ion main", self.options + ) def set_particle_submodel(self): for domain in ["negative", "positive"]: diff --git a/pybamm/models/standard_variables.py b/pybamm/models/standard_variables.py index 389002710b..1d815a6281 100644 --- a/pybamm/models/standard_variables.py +++ b/pybamm/models/standard_variables.py @@ -199,35 +199,6 @@ def __init__(self): ) self.T_vol_av = pybamm.Variable("Volume-averaged cell temperature") - # SEI variables - self.L_inner_av = pybamm.Variable( - "X-averaged inner SEI thickness", - domain="current collector", - ) - self.L_inner = pybamm.Variable( - "Inner SEI thickness", - domain=["negative electrode"], - auxiliary_domains={"secondary": "current collector"}, - ) - self.L_outer_av = pybamm.Variable( - "X-averaged outer SEI thickness", - domain="current collector", - ) - self.L_outer = pybamm.Variable( - "Outer SEI thickness", - domain=["negative electrode"], - auxiliary_domains={"secondary": "current collector"}, - ) - # For SEI reaction at the li metal/separator interface in a li metal model - self.L_inner_interface = pybamm.Variable( - "Inner SEI thickness", - domain=["current collector"], - ) - self.L_outer_interface = pybamm.Variable( - "Outer SEI thickness", - domain=["current collector"], - ) - # Interface utilisation self.u_n = pybamm.Variable( "Negative electrode interface utilisation", diff --git a/pybamm/models/submodels/active_material/total_active_material.py b/pybamm/models/submodels/active_material/total_active_material.py index cf14d1704d..4017f9930f 100644 --- a/pybamm/models/submodels/active_material/total_active_material.py +++ b/pybamm/models/submodels/active_material/total_active_material.py @@ -29,51 +29,25 @@ def get_coupled_variables(self, variables): domain = Domain.lower() phases = self.options.phases[domain] - eps_solid = sum( - variables[f"{Domain} electrode {phase} active material volume fraction"] - for phase in phases - ) - eps_solid_av = sum( - variables[ - f"X-averaged {domain} electrode {phase} active material volume fraction" - ] - for phase in phases - ) - variables.update( - { - f"{Domain} electrode active material volume fraction": eps_solid, - f"X-averaged {domain} electrode active material volume fraction" - "": eps_solid_av, - } - ) + # For each of the variables, the variable name without the phase name + # is constructed by summing all of the variable names with the phases + for variable_template in [ + f"{Domain} electrode {{}}active material volume fraction", + f"X-averaged {domain} electrode {{}}active material volume fraction", + f"{Domain} electrode {{}}active material volume fraction change", + f"X-averaged {domain} electrode {{}}active material volume fraction change", + ]: + sumvar = sum( + variables[variable_template.format(phase + " ")] for phase in phases + ) + variables[variable_template.format("")] = sumvar if self.options["particle shape"] != "no particles": + # capacity doesn't fit the template so needs to be done separately C = sum( variables[f"{Domain} electrode {phase} phase capacity [A.h]"] for phase in phases ) variables.update({f"{Domain} electrode capacity [A.h]": C}) - deps_solid_dt = sum( - variables[ - f"{Domain} electrode {phase} active material volume fraction change" - ] - for phase in phases - ) - deps_solid_dt_av = sum( - variables[ - f"X-averaged {domain} electrode {phase} active material " - "volume fraction change" - ] - for phase in phases - ) - variables.update( - { - f"{Domain} electrode active material volume fraction change" - "": deps_solid_dt, - f"X-averaged {domain} electrode active material volume fraction change" - "": deps_solid_dt_av, - } - ) - return variables diff --git a/pybamm/models/submodels/interface/base_interface.py b/pybamm/models/submodels/interface/base_interface.py index b3da991f57..23aea2a22a 100644 --- a/pybamm/models/submodels/interface/base_interface.py +++ b/pybamm/models/submodels/interface/base_interface.py @@ -29,8 +29,7 @@ class BaseInterface(pybamm.BaseSubModel): def __init__(self, param, domain, reaction, options, phase="primary"): super().__init__(param, domain, options=options, phase=phase) if reaction in ["lithium-ion main", "lithium metal plating"]: - # can be "" or "primary " or "secondary " - self.reaction_name = self.phase_name + self.reaction_name = "" elif reaction == "lead-acid main": self.reaction_name = "" # empty reaction name for the main reaction elif reaction == "lead-acid oxygen": @@ -38,6 +37,15 @@ def __init__(self, param, domain, reaction, options, phase="primary"): elif reaction in ["SEI", "SEI on cracks", "lithium plating"]: self.reaction_name = reaction + " " + if reaction in [ + "lithium-ion main", + "lithium metal plating", + "SEI", + "SEI on cracks", + ]: + # phase_name can be "" or "primary " or "secondary " + self.reaction_name = self.phase_name + self.reaction_name + self.reaction = reaction def _get_exchange_current_density(self, variables): @@ -296,6 +304,7 @@ def _get_standard_volumetric_current_density_variables(self, variables): a = variables[ f"{Domain} electrode {phase_name}surface area to volume ratio" ] + j = variables[f"{Domain} electrode {reaction_name}interfacial current density"] a_j = a * j a_j_av = pybamm.x_average(a_j) @@ -350,14 +359,15 @@ def _get_standard_overpotential_variables(self, eta_r): return variables def _get_standard_sei_film_overpotential_variables(self, eta_sei): - pot_scale = self.param.potential_scale + phase_name = self.phase_name + Phase_name = phase_name.capitalize() if self.half_cell: # half-cell domain variables = { - "SEI film overpotential": eta_sei, - "SEI film overpotential [V]": eta_sei * pot_scale, + f"{Phase_name}SEI film overpotential": eta_sei, + f"{Phase_name}SEI film overpotential [V]": eta_sei * pot_scale, } return variables @@ -371,10 +381,11 @@ def _get_standard_sei_film_overpotential_variables(self, eta_sei): eta_sei = pybamm.PrimaryBroadcast(eta_sei, self.domain_for_broadcast) variables = { - "SEI film overpotential": eta_sei, - "X-averaged SEI film overpotential": eta_sei_av, - "SEI film overpotential [V]": eta_sei * pot_scale, - "X-averaged SEI film overpotential [V]": eta_sei_av * pot_scale, + f"{Phase_name}SEI film overpotential": eta_sei, + f"X-averaged {phase_name}SEI film overpotential": eta_sei_av, + f"{Phase_name}SEI film overpotential [V]": eta_sei * pot_scale, + f"X-averaged {phase_name}SEI film overpotential [V]": eta_sei_av + * pot_scale, } return variables diff --git a/pybamm/models/submodels/interface/kinetics/base_kinetics.py b/pybamm/models/submodels/interface/kinetics/base_kinetics.py index cdb565e63e..fa6253abf3 100644 --- a/pybamm/models/submodels/interface/kinetics/base_kinetics.py +++ b/pybamm/models/submodels/interface/kinetics/base_kinetics.py @@ -100,16 +100,16 @@ def get_coupled_variables(self, variables): # Add SEI resistance in the negative electrode if self.domain == "Negative": if self.half_cell: - R_sei = self.param.R_sei - L_sei = variables["Total SEI thickness"] # on interface + R_sei = self.phase_param.R_sei + L_sei = variables[f"Total {phase_name}SEI thickness"] # on interface eta_sei = -j_tot_av * L_sei * R_sei elif self.options["SEI film resistance"] == "average": - R_sei = self.param.R_sei - L_sei_av = variables["X-averaged total SEI thickness"] + R_sei = self.phase_param.R_sei + L_sei_av = variables[f"X-averaged total {phase_name}SEI thickness"] eta_sei = -j_tot_av * L_sei_av * R_sei elif self.options["SEI film resistance"] == "distributed": - R_sei = self.param.R_sei - L_sei = variables["Total SEI thickness"] + R_sei = self.phase_param.R_sei + L_sei = variables[f"Total {phase_name}SEI thickness"] j_tot = variables[ f"Total negative electrode {phase_name}" "interfacial current density variable" diff --git a/pybamm/models/submodels/interface/kinetics/inverse_kinetics/inverse_butler_volmer.py b/pybamm/models/submodels/interface/kinetics/inverse_kinetics/inverse_butler_volmer.py index 6c8fd4132b..f0445e2c83 100644 --- a/pybamm/models/submodels/interface/kinetics/inverse_kinetics/inverse_butler_volmer.py +++ b/pybamm/models/submodels/interface/kinetics/inverse_kinetics/inverse_butler_volmer.py @@ -76,7 +76,7 @@ def get_coupled_variables(self, variables): # With SEI resistance (distributed and averaged have the same effect here) if self.domain == "Negative": if self.options["SEI film resistance"] != "none": - R_sei = self.param.R_sei + R_sei = self.phase_param.R_sei if self.half_cell: L_sei = variables["Total SEI thickness"] else: diff --git a/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py b/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py index 334304656c..55c6b3ef3d 100644 --- a/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py +++ b/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py @@ -10,8 +10,6 @@ class TotalMainKinetics(pybamm.BaseSubModel): Class summing up contributions to the main (e.g. intercalation) reaction for cases with primary, secondary, ... reactions e.g. silicon-graphite - Parameters - ---------- Parameters ---------- param : @@ -38,35 +36,17 @@ def get_coupled_variables(self, variables): return variables phases = self.options.phases[domain] - if len(phases) > 1: - a_j = sum( - variables[ - f"{Domain} electrode {phase} volumetric interfacial current density" - ] - for phase in phases - ) - a_j_dim = sum( - variables[ - f"{Domain} electrode {phase} volumetric " - "interfacial current density [A.m-3]" - ] - for phase in phases - ) - a_j_av = sum( - variables[ - f"X-averaged {domain} electrode {phase} volumetric " - "interfacial current density" - ] - for phase in phases - ) - variables.update( - { - f"{Domain} electrode volumetric interfacial current density": a_j, - f"{Domain} electrode volumetric " - "interfacial current density [A.m-2]": a_j_dim, - f"X-averaged {domain} electrode volumetric " - "interfacial current density": a_j_av, - } + # For each of the variables, the variable name without the phase name + # is constructed by summing all of the variable names with the phases + for variable_template in [ + f"{Domain} electrode {{}}volumetric interfacial current density", + f"{Domain} electrode {{}}volumetric " "interfacial current density [A.m-3]", + f"X-averaged {domain} electrode {{}}volumetric " + "interfacial current density", + ]: + sumvar = sum( + variables[variable_template.format(phase + " ")] for phase in phases ) + variables[variable_template.format("")] = sumvar return variables diff --git a/pybamm/models/submodels/interface/sei/__init__.py b/pybamm/models/submodels/interface/sei/__init__.py index 14c3945622..5f151daf0e 100644 --- a/pybamm/models/submodels/interface/sei/__init__.py +++ b/pybamm/models/submodels/interface/sei/__init__.py @@ -1,4 +1,5 @@ from .base_sei import BaseModel +from .total_sei import TotalSEI from .no_sei import NoSEI from .constant_sei import ConstantSEI 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 480211c692..2c0d26b0d1 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -12,19 +12,23 @@ class BaseModel(BaseInterface): ---------- param : parameter class The parameters to use for this submodel - options : dict, optional + options : dict A dictionary of options to be passed to the model. + phase : str, optional + Phase of the particle (default is "primary") + cracks : bool, optional + Whether this is a submodel for standard SEI or SEI on cracks **Extends:** :class:`pybamm.interface.BaseInterface` """ - def __init__(self, param, options=None, cracks=False): + def __init__(self, param, options, phase="primary", cracks=False): if cracks is True: reaction = "SEI on cracks" else: reaction = "SEI" domain = "Negative" - super().__init__(param, domain, reaction, options=options) + super().__init__(param, domain, reaction, options=options, phase=phase) def get_coupled_variables(self, variables): # Update some common variables @@ -34,17 +38,17 @@ def get_coupled_variables(self, variables): if self.reaction_loc != "interface": variables.update( { - f"X-averaged negative electrode {self.reaction} interfacial " + f"X-averaged negative electrode {self.reaction_name}interfacial " "current density": variables[ - f"X-averaged {self.reaction} interfacial current density" + f"X-averaged {self.reaction_name}interfacial current density" ], - f"Negative electrode {self.reaction} interfacial current " + f"Negative electrode {self.reaction_name}interfacial current " "density": variables[ - f"{self.reaction} interfacial current density" + f"{self.reaction_name}interfacial current density" ], - f"Negative electrode {self.reaction} interfacial current " + f"Negative electrode {self.reaction_name}interfacial current " "density [A.m-2]": variables[ - f"{self.reaction} interfacial current density [A.m-2]" + f"{self.reaction_name}interfacial current density [A.m-2]" ], } ) @@ -54,15 +58,16 @@ def get_coupled_variables(self, variables): variables.update( { - f"X-averaged positive electrode {self.reaction} interfacial current " - "density": zero_av, - f"Positive electrode {self.reaction} interfacial current density": zero, - f"Positive electrode {self.reaction} interfacial current density " - "[A.m-2]": zero, - f"X-averaged positive electrode {self.reaction} volumetric interfacial " - "current density": zero_av, - f"Positive electrode {self.reaction} volumetric interfacial current " - "density": zero, + f"X-averaged positive electrode {self.reaction} " + "interfacial current density": zero_av, + f"Positive electrode {self.reaction} " + "interfacial current density": zero, + f"Positive electrode {self.reaction} " + "interfacial current density [A.m-2]": zero, + f"X-averaged positive electrode {self.reaction} " + "volumetric interfacial current density": zero_av, + f"Positive electrode {self.reaction} " + "volumetric interfacial current density": zero, } ) @@ -92,13 +97,13 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): if isinstance(self, pybamm.sei.NoSEI): L_scale = 1 else: - L_scale = param.L_sei_0_dim + L_scale = self.phase_param.L_sei_0_dim variables = { - f"Inner {self.reaction} thickness": L_inner, - f"Inner {self.reaction} thickness [m]": L_inner * L_scale, - f"Outer {self.reaction} thickness": L_outer, - f"Outer {self.reaction} thickness [m]": L_outer * L_scale, + f"Inner {self.reaction_name}thickness": L_inner, + f"Inner {self.reaction_name}thickness [m]": L_inner * L_scale, + f"Outer {self.reaction_name}thickness": L_outer, + f"Outer {self.reaction_name}thickness [m]": L_outer * L_scale, } if self.reaction_loc != "interface": @@ -106,11 +111,11 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): L_outer_av = pybamm.x_average(L_outer) variables.update( { - f"X-averaged inner {self.reaction} thickness": L_inner_av, - f"X-averaged inner {self.reaction} thickness [m]": L_inner_av + f"X-averaged inner {self.reaction_name}thickness": L_inner_av, + f"X-averaged inner {self.reaction_name}thickness [m]": L_inner_av * L_scale, - f"X-averaged outer {self.reaction} thickness": L_outer_av, - f"X-averaged outer {self.reaction} thickness [m]": L_outer_av + f"X-averaged outer {self.reaction_name}thickness": L_outer_av, + f"X-averaged outer {self.reaction_name}thickness [m]": L_outer_av * L_scale, } ) @@ -126,23 +131,23 @@ def _get_standard_total_thickness_variables(self, L_sei): L_scale = 1 R_sei_dim = 1 else: - L_scale = self.param.L_sei_0_dim - R_sei_dim = self.param.R_sei_dimensional + L_scale = self.phase_param.L_sei_0_dim + R_sei_dim = self.phase_param.R_sei_dimensional variables = { - f"{self.reaction} thickness": L_sei, - f"{self.reaction} [m]": L_sei * L_scale, - f"Total {self.reaction} thickness": L_sei, - f"Total {self.reaction} thickness [m]": L_sei * L_scale, + f"{self.reaction_name}thickness": L_sei, + f"{self.reaction_name}[m]": L_sei * L_scale, + f"Total {self.reaction_name}thickness": L_sei, + f"Total {self.reaction_name}thickness [m]": L_sei * L_scale, } if self.reaction_loc != "interface": L_sei_av = pybamm.x_average(L_sei) variables.update( { - f"X-averaged {self.reaction} thickness": L_sei_av, - f"X-averaged {self.reaction} thickness [m]": L_sei_av * L_scale, - f"X-averaged total {self.reaction} thickness": L_sei_av, - f"X-averaged total {self.reaction} thickness [m]": L_sei_av + f"X-averaged {self.reaction_name}thickness": L_sei_av, + f"X-averaged {self.reaction_name}thickness [m]": L_sei_av * L_scale, + f"X-averaged total {self.reaction_name}thickness": L_sei_av, + f"X-averaged total {self.reaction_name}thickness [m]": L_sei_av * L_scale, } ) @@ -157,7 +162,8 @@ def _get_standard_total_thickness_variables(self, L_sei): def _get_standard_concentration_variables(self, variables): """Update variables related to the SEI concentration.""" - param = self.param + phase_param = self.phase_param + reaction_name = self.reaction_name # Set scales to one for the "no SEI" model so that they are not required # by parameter values in general @@ -171,33 +177,35 @@ def _get_standard_concentration_variables(self, variables): else: if self.reaction_loc == "interface": # scales in mol/m2 (n is an interfacial quantity) - n_scale = param.L_sei_0_dim / param.V_bar_inner_dimensional - n_outer_scale = param.L_sei_0_dim / param.V_bar_outer_dimensional + n_scale = phase_param.L_sei_0_dim / phase_param.V_bar_inner_dimensional + n_outer_scale = ( + phase_param.L_sei_0_dim / phase_param.V_bar_outer_dimensional + ) else: # scales in mol/m3 (n is a bulk quantity) n_scale = ( - param.L_sei_0_dim - * param.n.prim.a_typ - / param.V_bar_inner_dimensional + phase_param.L_sei_0_dim + * phase_param.a_typ + / phase_param.V_bar_inner_dimensional ) n_outer_scale = ( - param.L_sei_0_dim - * param.n.prim.a_typ - / param.V_bar_outer_dimensional + phase_param.L_sei_0_dim + * phase_param.a_typ + / phase_param.V_bar_outer_dimensional ) - v_bar = param.v_bar - z_sei = param.z_sei + v_bar = phase_param.v_bar + z_sei = phase_param.z_sei # Set scales for the "EC Reaction Limited" model if self.options["SEI"] == "ec reaction limited": L_inner_0 = 0 L_outer_0 = 1 else: - L_inner_0 = param.L_inner_0 - L_outer_0 = param.L_outer_0 + L_inner_0 = phase_param.L_inner_0 + L_outer_0 = phase_param.L_outer_0 if self.reaction == "SEI": - L_inner = variables["Inner SEI thickness"] - L_outer = variables["Outer SEI thickness"] + L_inner = variables[f"Inner {reaction_name}thickness"] + L_outer = variables[f"Outer {reaction_name}thickness"] n_inner = L_inner # inner SEI concentration n_outer = L_outer # outer SEI concentration @@ -223,22 +231,26 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - "Inner SEI concentration [mol.m-3]": n_inner * n_scale, - "X-averaged inner SEI concentration [mol.m-3]": n_inner_av - * n_scale, - "Outer SEI concentration [mol.m-3]": n_outer * n_outer_scale, - "X-averaged outer SEI concentration [mol.m-3]": n_outer_av - * n_outer_scale, - "SEI concentration [mol.m-3]": n_SEI * n_scale, - "X-averaged SEI concentration [mol.m-3]": n_SEI_av * n_scale, - "Loss of lithium to SEI [mol]": Q_sei, - "Loss of capacity to SEI [A.h]": Q_sei * self.param.F / 3600, + f"Inner {reaction_name}concentration [mol.m-3]": n_inner * n_scale, + f"X-averaged inner {reaction_name}" + "concentration [mol.m-3]": n_inner_av * n_scale, + f"Outer {reaction_name}" + "concentration [mol.m-3]": n_outer * n_outer_scale, + f"X-averaged outer {reaction_name}" + "concentration [mol.m-3]": n_outer_av * n_outer_scale, + f"{reaction_name}concentration [mol.m-3]": n_SEI * n_scale, + f"X-averaged {reaction_name}" + "concentration [mol.m-3]": n_SEI_av * n_scale, + f"Loss of lithium to {reaction_name}[mol]": Q_sei, + f"Loss of capacity to {reaction_name}[A.h]": Q_sei + * self.param.F + / 3600, } ) # Concentration variables are handled slightly differently for SEI on cracks elif self.reaction == "SEI on cracks": - L_inner_cr = variables["Inner SEI on cracks thickness"] - L_outer_cr = variables["Outer SEI on cracks thickness"] + L_inner_cr = variables[f"Inner {reaction_name}thickness"] + L_outer_cr = variables[f"Outer {reaction_name}thickness"] roughness = variables[self.domain + " electrode roughness ratio"] n_inner_cr = L_inner_cr * (roughness - 1) # inner SEI cracks concentration @@ -251,7 +263,7 @@ def _get_standard_concentration_variables(self, variables): n_SEI_cr_av = pybamm.yz_average(pybamm.x_average(n_SEI_cr)) # Calculate change in SEI cracks concentration with respect to initial state - rho_cr = param.n.rho_cr + rho_cr = self.param.n.rho_cr n_SEI_cr_init = 2 * rho_cr * (L_inner_0 + L_outer_0 / v_bar) / 10000 delta_n_SEI_cr = n_SEI_cr_av - n_SEI_cr_init @@ -267,18 +279,19 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - "Inner SEI on cracks concentration [mol.m-3]": n_inner_cr * n_scale, - "X-averaged inner SEI on cracks " + f"Inner {reaction_name}" + "concentration [mol.m-3]": n_inner_cr * n_scale, + f"X-averaged inner {reaction_name}" "concentration [mol.m-3]": n_inner_cr_av * n_scale, - "Outer SEI on cracks concentration [mol.m-3]": n_outer_cr - * n_outer_scale, - "X-averaged outer SEI on cracks " + f"Outer {reaction_name}" + "concentration [mol.m-3]": n_outer_cr * n_outer_scale, + f"X-averaged outer {reaction_name}" "concentration [mol.m-3]": n_outer_cr_av * n_outer_scale, - "SEI on cracks concentration [mol.m-3]": n_SEI_cr * n_scale, - "X-averaged SEI on cracks concentration [mol.m-3]": n_SEI_cr_av - * n_scale, - "Loss of lithium to SEI on cracks [mol]": Q_sei_cr, - "Loss of capacity to SEI on cracks [A.h]": Q_sei_cr + f"{reaction_name}" "concentration [mol.m-3]": n_SEI_cr * n_scale, + f"X-averaged {reaction_name}" + "concentration [mol.m-3]": n_SEI_cr_av * n_scale, + f"Loss of lithium to {reaction_name}[mol]": Q_sei_cr, + f"Loss of capacity to {reaction_name}[A.h]": Q_sei_cr * self.param.F / 3600, } @@ -303,22 +316,22 @@ def _get_standard_reaction_variables(self, j_inner, j_outer): variables : dict The variables which can be derived from the SEI currents. """ - j_scale = self.param.n.prim.j_scale + j_scale = self.phase_param.j_scale j_i_av = pybamm.x_average(j_inner) j_o_av = pybamm.x_average(j_outer) variables = { - f"Inner {self.reaction} interfacial current density": j_inner, - f"Inner {self.reaction} interfacial current density [A.m-2]": j_inner + f"Inner {self.reaction_name}interfacial current density": j_inner, + f"Inner {self.reaction_name}interfacial current density [A.m-2]": j_inner * j_scale, - f"X-averaged inner {self.reaction} interfacial current density": j_i_av, - f"X-averaged inner {self.reaction} " + f"X-averaged inner {self.reaction_name}interfacial current density": j_i_av, + f"X-averaged inner {self.reaction_name}" "interfacial current density [A.m-2]": j_i_av * j_scale, - f"Outer {self.reaction} interfacial current density": j_outer, - f"Outer {self.reaction} interfacial current density [A.m-2]": j_outer + f"Outer {self.reaction_name}interfacial current density": j_outer, + f"Outer {self.reaction_name}interfacial current density [A.m-2]": j_outer * j_scale, - f"X-averaged outer {self.reaction} interfacial current density": j_o_av, - f"X-averaged outer {self.reaction} " + f"X-averaged outer {self.reaction_name}interfacial current density": j_o_av, + f"X-averaged outer {self.reaction_name}" "interfacial current density [A.m-2]": j_o_av * j_scale, } @@ -329,19 +342,20 @@ def _get_standard_reaction_variables(self, j_inner, j_outer): def _get_standard_total_reaction_variables(self, j_sei): """Update variables related to total SEI interfacial current density.""" - j_scale = self.param.n.prim.j_scale + j_scale = self.phase_param.j_scale variables = { - f"{self.reaction} interfacial current density": j_sei, - f"{self.reaction} interfacial current density [A.m-2]": j_sei * j_scale, + f"{self.reaction_name}interfacial current density": j_sei, + f"{self.reaction_name}interfacial current density [A.m-2]": j_sei * j_scale, } if self.reaction_loc != "interface": j_sei_av = pybamm.x_average(j_sei) variables.update( { - f"X-averaged {self.reaction} interfacial current density": j_sei_av, - f"X-averaged {self.reaction} " + f"X-averaged {self.reaction_name}" + "interfacial current density": j_sei_av, + f"X-averaged {self.reaction_name}" "interfacial current density [A.m-2]": j_sei_av * j_scale, } ) diff --git a/pybamm/models/submodels/interface/sei/constant_sei.py b/pybamm/models/submodels/interface/sei/constant_sei.py index b397da0abb..ae1f78beb5 100644 --- a/pybamm/models/submodels/interface/sei/constant_sei.py +++ b/pybamm/models/submodels/interface/sei/constant_sei.py @@ -17,14 +17,16 @@ class ConstantSEI(BaseModel): ---------- param : parameter class The parameters to use for this submodel - options : dict, optional + options : dict A dictionary of options to be passed to the model. + phase : str, optional + Phase of the particle (default is "primary") **Extends:** :class:`pybamm.sei.BaseModel` """ - def __init__(self, param, options=None): - super().__init__(param, options=options) + def __init__(self, param, options, phase="primary"): + super().__init__(param, options=options, phase=phase) if self.half_cell: self.reaction_loc = "interface" else: @@ -32,8 +34,8 @@ def __init__(self, param, options=None): def get_fundamental_variables(self): # Constant thicknesses - L_inner = self.param.L_inner_0 - L_outer = self.param.L_outer_0 + L_inner = self.phase_param.L_inner_0 + L_outer = self.phase_param.L_outer_0 variables = self._get_standard_thickness_variables(L_inner, L_outer) # Concentrations (derived from thicknesses) diff --git a/pybamm/models/submodels/interface/sei/no_sei.py b/pybamm/models/submodels/interface/sei/no_sei.py index 4092704f97..fbadb472e3 100644 --- a/pybamm/models/submodels/interface/sei/no_sei.py +++ b/pybamm/models/submodels/interface/sei/no_sei.py @@ -13,14 +13,18 @@ class NoSEI(BaseModel): ---------- param : parameter class The parameters to use for this submodel - options : dict, optional + options : dict A dictionary of options to be passed to the model. + phase : str, optional + Phase of the particle (default is "primary") + cracks : bool, optional + Whether this is a submodel for standard SEI or SEI on cracks **Extends:** :class:`pybamm.sei.BaseModel` """ - def __init__(self, param, options=None, cracks=False): - super().__init__(param, options=options, cracks=cracks) + def __init__(self, param, options, phase="primary", cracks=False): + super().__init__(param, options=options, phase=phase, cracks=cracks) if self.half_cell: self.reaction_loc = "interface" else: diff --git a/pybamm/models/submodels/interface/sei/sei_growth.py b/pybamm/models/submodels/interface/sei/sei_growth.py index 09a4014877..36dcacaac5 100644 --- a/pybamm/models/submodels/interface/sei/sei_growth.py +++ b/pybamm/models/submodels/interface/sei/sei_growth.py @@ -16,52 +16,45 @@ class SEIGrowth(BaseModel): reaction_loc : str Where the reaction happens: "x-average" (SPM, SPMe, etc), "full electrode" (full DFN), or "interface" (half-cell model) - options : dict, optional + options : dict A dictionary of options to be passed to the model. + phase : str, optional + Phase of the particle (default is "primary") + cracks : bool, optional + Whether this is a submodel for standard SEI or SEI on cracks **Extends:** :class:`pybamm.sei.BaseModel` """ - def __init__(self, param, reaction_loc, options=None, cracks=False): - super().__init__(param, options=options, cracks=cracks) + def __init__(self, param, reaction_loc, options, phase="primary", cracks=False): + super().__init__(param, options=options, phase=phase, cracks=cracks) self.reaction_loc = reaction_loc def get_fundamental_variables(self): - if self.reaction == "SEI on cracks": + Ls = [] + for pos in ["inner", "outer"]: + Pos = pos.capitalize() if self.reaction_loc == "x-average": - L_inner_av = pybamm.Variable( - "X-averaged inner SEI on cracks thickness", - domain="current collector", - ) - L_inner = pybamm.PrimaryBroadcast(L_inner_av, "negative electrode") - L_outer_av = pybamm.Variable( - "X-averaged outer SEI on cracks thickness", + L_av = pybamm.Variable( + f"X-averaged {pos} {self.reaction_name}thickness", domain="current collector", ) - L_outer = pybamm.PrimaryBroadcast(L_outer_av, "negative electrode") + L_av.print_name = f"L_{pos}_av" + L = pybamm.PrimaryBroadcast(L_av, "negative electrode") elif self.reaction_loc == "full electrode": - L_inner = pybamm.Variable( - "Inner SEI on cracks thickness", + L = pybamm.Variable( + f"{Pos} {self.reaction_name}thickness", domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, ) - L_outer = pybamm.Variable( - "Outer SEI on cracks thickness", - domain="negative electrode", - auxiliary_domains={"secondary": "current collector"}, - ) - elif self.reaction == "SEI": - 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 + L = pybamm.Variable( + f"{Pos} {self.reaction_name}thickness", domain="current collector" + ) + L.print_name = f"L_{pos}" + Ls.append(L) + + L_inner, L_outer = Ls if self.options["SEI"] == "ec reaction limited": L_inner = 0 * L_inner # Set L_inner to zero, copying domains @@ -71,7 +64,7 @@ def get_fundamental_variables(self): return variables def get_coupled_variables(self, variables): - param = self.param + phase_param = self.phase_param # delta_phi = phi_s - phi_e if self.reaction_loc == "interface": delta_phi = variables[ @@ -98,36 +91,36 @@ def get_coupled_variables(self, variables): + " electrode total interfacial current density" ] - L_sei_inner = variables[f"Inner {self.reaction} thickness"] - L_sei_outer = variables[f"Outer {self.reaction} thickness"] - L_sei = variables[f"Total {self.reaction} thickness"] + L_sei_inner = variables[f"Inner {self.reaction_name}thickness"] + L_sei_outer = variables[f"Outer {self.reaction_name}thickness"] + L_sei = variables[f"Total {self.reaction_name}thickness"] T = variables["Negative electrode temperature"] - R_sei = param.R_sei + R_sei = phase_param.R_sei eta_SEI = delta_phi - j * L_sei * R_sei # Thermal prefactor for reaction, interstitial and EC models - prefactor = 1 / (1 + param.Theta * T) + prefactor = 1 / (1 + self.param.Theta * T) if self.options["SEI"] == "reaction limited": - C_sei = param.C_sei_reaction + C_sei = phase_param.C_sei_reaction j_sei = -(1 / C_sei) * pybamm.exp(-0.5 * prefactor * eta_SEI) elif self.options["SEI"] == "electron-migration limited": - U_inner = param.U_inner_electron - C_sei = param.C_sei_electron + U_inner = phase_param.U_inner_electron + C_sei = phase_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 = param.C_sei_inter + C_sei = phase_param.C_sei_inter j_sei = -pybamm.exp(-prefactor * delta_phi) / (C_sei * L_sei_inner) elif self.options["SEI"] == "solvent-diffusion limited": - C_sei = param.C_sei_solvent + C_sei = phase_param.C_sei_solvent j_sei = -1 / (C_sei * L_sei_outer) elif self.options["SEI"] == "ec reaction limited": - C_sei_ec = param.C_sei_ec - C_ec = param.C_ec + C_sei_ec = phase_param.C_sei_ec + C_ec = phase_param.C_ec # we have a linear system for j_sei and c_ec # c_ec = 1 + j_sei * L_sei * C_ec @@ -143,36 +136,28 @@ def get_coupled_variables(self, variables): # Get variables related to the concentration c_ec_av = pybamm.x_average(c_ec) - c_ec_scale = param.c_ec_0_dim + c_ec_scale = phase_param.c_ec_0_dim if self.reaction == "SEI on cracks": - variables.update( - { - "EC concentration on cracks": c_ec, - "EC concentration on cracks [mol.m-3]": c_ec * c_ec_scale, - "X-averaged EC concentration on cracks": c_ec_av, - "X-averaged EC concentration on cracks [mol.m-3]": c_ec_av - * c_ec_scale, - } - ) + name = "EC concentration on cracks" else: - 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, - } - ) + name = "EC surface concentration" + variables.update( + { + name: c_ec, + f"{name} [mol.m-3]": c_ec * c_ec_scale, + f"X-averaged {name}": c_ec_av, + f"X-averaged {name} [mol.m-3]": c_ec_av * c_ec_scale, + } + ) if self.options["SEI"] == "ec reaction limited": inner_sei_proportion = 0 else: - inner_sei_proportion = param.inner_sei_proportion + inner_sei_proportion = phase_param.inner_sei_proportion # All SEI growth mechanisms assumed to have Arrhenius dependence - Arrhenius = pybamm.exp(param.E_over_RT_sei * (1 - prefactor)) + Arrhenius = pybamm.exp(phase_param.E_over_RT_sei * (1 - prefactor)) j_inner = inner_sei_proportion * Arrhenius * j_sei j_outer = (1 - inner_sei_proportion) * Arrhenius * j_sei @@ -186,27 +171,37 @@ def get_coupled_variables(self, variables): return variables def set_rhs(self, variables): + phase_name = self.phase_name + if self.reaction_loc == "x-average": - L_inner = variables[f"X-averaged inner {self.reaction} thickness"] - L_outer = variables[f"X-averaged outer {self.reaction} thickness"] + L_inner = variables[f"X-averaged inner {self.reaction_name}thickness"] + L_outer = variables[f"X-averaged outer {self.reaction_name}thickness"] j_inner = variables[ - f"X-averaged inner {self.reaction} interfacial current density" + f"X-averaged inner {self.reaction_name}interfacial current density" ] j_outer = variables[ - f"X-averaged outer {self.reaction} interfacial current density" + f"X-averaged outer {self.reaction_name}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"] + a = variables[ + f"X-averaged negative electrode {phase_name}surface area to volume ratio" + ] else: - L_inner = variables[f"Inner {self.reaction} thickness"] - L_outer = variables[f"Outer {self.reaction} thickness"] - j_inner = variables[f"Inner {self.reaction} interfacial current density"] - j_outer = variables[f"Outer {self.reaction} interfacial current density"] + L_inner = variables[f"Inner {self.reaction_name}thickness"] + L_outer = variables[f"Outer {self.reaction_name}thickness"] + j_inner = variables[ + f"Inner {self.reaction_name}interfacial current density" + ] + j_outer = variables[ + f"Outer {self.reaction_name}interfacial current density" + ] if self.reaction_loc == "interface": a = 1 else: - a = variables["Negative electrode surface area to volume ratio"] + a = variables[ + f"Negative electrode {phase_name}surface area to volume ratio" + ] # The spreading term acts to spread out SEI along the cracks as they grow. # For SEI on initial surface (as opposed to cracks), it is zero. @@ -217,18 +212,22 @@ def set_rhs(self, variables): else: l_cr = variables["Negative particle crack length"] dl_cr = variables["Negative particle cracking rate"] - spreading_outer = dl_cr / l_cr * (self.param.L_outer_0 / 10000 - L_outer) - spreading_inner = dl_cr / l_cr * (self.param.L_inner_0 / 10000 - L_inner) + spreading_outer = ( + dl_cr / l_cr * (self.phase_param.L_outer_0 / 10000 - L_outer) + ) + spreading_inner = ( + dl_cr / l_cr * (self.phase_param.L_inner_0 / 10000 - L_inner) + ) else: spreading_outer = 0 spreading_inner = 0 - Gamma_SEI = self.param.Gamma_SEI + Gamma_SEI = self.phase_param.Gamma_SEI if self.options["SEI"] == "ec reaction limited": self.rhs = {L_outer: -Gamma_SEI * a * j_outer + spreading_outer} else: - v_bar = self.param.v_bar + v_bar = self.phase_param.v_bar self.rhs = { L_inner: -Gamma_SEI * a * j_inner + spreading_inner, L_outer: -v_bar * Gamma_SEI * a * j_outer + spreading_outer, @@ -236,14 +235,14 @@ def set_rhs(self, variables): def set_initial_conditions(self, variables): if self.reaction_loc == "x-average": - L_inner = variables[f"X-averaged inner {self.reaction} thickness"] - L_outer = variables[f"X-averaged outer {self.reaction} thickness"] + L_inner = variables[f"X-averaged inner {self.reaction_name}thickness"] + L_outer = variables[f"X-averaged outer {self.reaction_name}thickness"] else: - L_inner = variables[f"Inner {self.reaction} thickness"] - L_outer = variables[f"Outer {self.reaction} thickness"] + L_inner = variables[f"Inner {self.reaction_name}thickness"] + L_outer = variables[f"Outer {self.reaction_name}thickness"] - L_inner_0 = self.param.L_inner_0 - L_outer_0 = self.param.L_outer_0 + L_inner_0 = self.phase_param.L_inner_0 + L_outer_0 = self.phase_param.L_outer_0 if self.reaction == "SEI on cracks": # Dividing by 10000 makes initial condition effectively zero diff --git a/pybamm/models/submodels/interface/sei/total_sei.py b/pybamm/models/submodels/interface/sei/total_sei.py new file mode 100644 index 0000000000..21f0c0fb03 --- /dev/null +++ b/pybamm/models/submodels/interface/sei/total_sei.py @@ -0,0 +1,53 @@ +# +# Class summing up contributions to the SEI reaction +# for cases with primary, secondary, ... reactions e.g. silicon-graphite +# +import pybamm + + +class TotalSEI(pybamm.BaseSubModel): + """ + Class summing up contributions to the SEI reaction + for cases with primary, secondary, ... reactions e.g. silicon-graphite + + Parameters + ---------- + param : + model parameters + options: dict + A dictionary of options to be passed to the model. + See :class:`pybamm.BaseBatteryModel` + + **Extends:** :class:`pybamm.interface.BaseInterface` + """ + + def __init__(self, param, options, cracks=False): + if cracks is True: + self.reaction = "SEI on cracks" + else: + self.reaction = "SEI" + super().__init__(param, options=options) + + def get_coupled_variables(self, variables): + if self.half_cell is True: + return variables + + phases = self.options.phases["negative"] + # For each of the variables, the variable name without the phase name + # is constructed by summing all of the variable names with the phases + for variable_template in [ + f"Negative electrode {{}}{self.reaction} volumetric " + "interfacial current density", + f"Negative electrode {{}}{self.reaction} volumetric " + "interfacial current density [A.m-3]", + f"X-averaged negative electrode {{}}{self.reaction} volumetric " + "interfacial current density", + f"Loss of lithium to {{}}{self.reaction} [mol]", + f"Loss of capacity to {{}}{self.reaction} [A.h]", + ]: + sumvar = sum( + variables[variable_template.format(phase + " ")] for phase in phases + ) + variables[variable_template.format("")] = sumvar + + return variables diff --git a/pybamm/models/submodels/porosity/reaction_driven_porosity.py b/pybamm/models/submodels/porosity/reaction_driven_porosity.py index b4d1413703..8bb5223224 100644 --- a/pybamm/models/submodels/porosity/reaction_driven_porosity.py +++ b/pybamm/models/submodels/porosity/reaction_driven_porosity.py @@ -25,7 +25,7 @@ def __init__(self, param, options, x_average): def get_coupled_variables(self, variables): L_sei_n = variables["Total SEI thickness [m]"] - L_sei_0 = self.param.L_inner_0_dim + self.param.L_outer_0_dim + L_sei_0 = self.param.n.prim.L_inner_0_dim + self.param.n.prim.L_outer_0_dim L_pl_n = variables["Lithium plating thickness [m]"] L_dead_n = variables["Dead lithium thickness [m]"] L_sei_cr_n = variables["Total SEI on cracks thickness [m]"] diff --git a/pybamm/parameters/lithium_ion_parameters.py b/pybamm/parameters/lithium_ion_parameters.py index 87d668c8b6..9ae0c238ad 100644 --- a/pybamm/parameters/lithium_ion_parameters.py +++ b/pybamm/parameters/lithium_ion_parameters.py @@ -108,55 +108,6 @@ def _set_dimensional_parameters(self): self.n.epsilon_init, self.s.epsilon_init, self.p.epsilon_init ) - # SEI parameters - self.V_bar_inner_dimensional = pybamm.Parameter( - "Inner SEI partial molar volume [m3.mol-1]" - ) - self.V_bar_outer_dimensional = pybamm.Parameter( - "Outer SEI partial molar volume [m3.mol-1]" - ) - - self.m_sei_dimensional = pybamm.Parameter( - "SEI reaction exchange current density [A.m-2]" - ) - - self.R_sei_dimensional = pybamm.Parameter("SEI resistivity [Ohm.m]") - self.D_sol_dimensional = pybamm.Parameter( - "Outer SEI solvent diffusivity [m2.s-1]" - ) - self.c_sol_dimensional = pybamm.Parameter( - "Bulk solvent concentration [mol.m-3]" - ) - self.U_inner_dimensional = pybamm.Parameter( - "Inner SEI open-circuit potential [V]" - ) - self.U_outer_dimensional = pybamm.Parameter( - "Outer SEI open-circuit potential [V]" - ) - self.kappa_inner_dimensional = pybamm.Parameter( - "Inner SEI electron conductivity [S.m-1]" - ) - self.D_li_dimensional = pybamm.Parameter( - "Inner SEI lithium interstitial diffusivity [m2.s-1]" - ) - self.c_li_0_dimensional = pybamm.Parameter( - "Lithium interstitial reference concentration [mol.m-3]" - ) - self.L_inner_0_dim = pybamm.Parameter("Initial inner SEI thickness [m]") - self.L_outer_0_dim = pybamm.Parameter("Initial outer SEI thickness [m]") - self.L_sei_0_dim = self.L_inner_0_dim + self.L_outer_0_dim - self.E_sei_dimensional = pybamm.Parameter( - "SEI growth activation energy [J.mol-1]" - ) - - # EC reaction - self.c_ec_0_dim = pybamm.Parameter( - "EC initial concentration in electrolyte [mol.m-3]" - ) - self.D_ec_dim = pybamm.Parameter("EC diffusivity [m2.s-1]") - self.k_sei_dim = pybamm.Parameter("SEI kinetic rate constant [m.s-1]") - self.U_sei_dim = pybamm.Parameter("SEI open-circuit potential [V]") - # Lithium plating parameters self.V_bar_plated_Li = pybamm.Parameter( "Lithium metal partial molar volume [m3.mol-1]" @@ -325,86 +276,6 @@ def _set_dimensionless_parameters(self): / (self.therm.rho_eff_dim_ref * self.F * self.Delta_T * self.L_x) ) - # SEI parameters - self.inner_sei_proportion = pybamm.Parameter("Inner SEI reaction proportion") - - self.z_sei = pybamm.Parameter("Ratio of lithium moles to SEI moles") - - self.E_over_RT_sei = self.E_sei_dimensional / self.R / self.T_ref - - self.C_sei_reaction = ( - self.n.prim.j_scale / self.m_sei_dimensional - ) * pybamm.exp(-(self.F * self.n.U_ref / (2 * self.R * self.T_ref))) - - self.C_sei_solvent = ( - self.n.prim.j_scale - * self.L_sei_0_dim - / (self.c_sol_dimensional * self.F * self.D_sol_dimensional) - ) - - self.C_sei_electron = ( - self.n.prim.j_scale - * self.F - * self.L_sei_0_dim - / (self.kappa_inner_dimensional * self.R * self.T_ref) - ) - - self.C_sei_inter = ( - self.n.prim.j_scale - * self.L_sei_0_dim - / (self.D_li_dimensional * self.c_li_0_dimensional * self.F) - ) - - self.U_inner_electron = self.F * self.U_inner_dimensional / self.R / self.T_ref - - self.R_sei = ( - self.F - * self.n.prim.j_scale - * self.R_sei_dimensional - * self.L_sei_0_dim - / self.R - / self.T_ref - ) - - self.v_bar = self.V_bar_outer_dimensional / self.V_bar_inner_dimensional - self.c_sei_scale = ( - self.L_sei_0_dim * self.n.prim.a_typ / self.V_bar_inner_dimensional - ) - self.c_sei_outer_scale = ( - self.L_sei_0_dim * self.n.prim.a_typ / self.V_bar_outer_dimensional - ) - - self.L_inner_0 = self.L_inner_0_dim / self.L_sei_0_dim - self.L_outer_0 = self.L_outer_0_dim / self.L_sei_0_dim - - # ratio of SEI reaction scale to intercalation reaction - self.Gamma_SEI = ( - self.V_bar_inner_dimensional * self.n.prim.j_scale * self.timescale - ) / (self.F * self.z_sei * self.L_sei_0_dim) - - # EC reaction - self.C_ec = ( - self.L_sei_0_dim - * self.n.prim.j_scale - / (self.F * self.c_ec_0_dim * self.D_ec_dim) - ) - self.C_sei_ec = ( - self.F - * self.k_sei_dim - * self.c_ec_0_dim - / self.n.prim.j_scale - * ( - pybamm.exp( - -( - self.F - * (self.n.U_ref - self.U_sei_dim) - / (2 * self.R * self.T_ref) - ) - ) - ) - ) - self.c_sei_init = self.c_ec_0_dim / self.c_sei_outer_scale - # lithium plating parameters self.c_plated_Li_0 = self.c_plated_Li_0_dim / self.c_Li_typ @@ -493,7 +364,7 @@ def j0_plating(self, c_e, c_Li, T): def dead_lithium_decay_rate(self, L_sei): """Dimensionless exchange-current density for stripping""" - L_sei_dim = L_sei * self.L_sei_0_dim + L_sei_dim = L_sei * self.n.prim.L_sei_0_dim return self.dead_lithium_decay_rate_dimensional(L_sei_dim) * self.timescale @@ -861,6 +732,55 @@ def _set_dimensional_parameters(self): self.U_init_dim = self.U_dimensional(self.c_init_av, main.T_init_dim) + # SEI parameters + self.V_bar_inner_dimensional = pybamm.Parameter( + f"{pref}Inner SEI partial molar volume [m3.mol-1]" + ) + self.V_bar_outer_dimensional = pybamm.Parameter( + f"{pref}Outer SEI partial molar volume [m3.mol-1]" + ) + + self.m_sei_dimensional = pybamm.Parameter( + f"{pref}SEI reaction exchange current density [A.m-2]" + ) + + self.R_sei_dimensional = pybamm.Parameter(f"{pref}SEI resistivity [Ohm.m]") + self.D_sol_dimensional = pybamm.Parameter( + f"{pref}Outer SEI solvent diffusivity [m2.s-1]" + ) + self.c_sol_dimensional = pybamm.Parameter( + f"{pref}Bulk solvent concentration [mol.m-3]" + ) + self.U_inner_dimensional = pybamm.Parameter( + f"{pref}Inner SEI open-circuit potential [V]" + ) + self.U_outer_dimensional = pybamm.Parameter( + f"{pref}Outer SEI open-circuit potential [V]" + ) + self.kappa_inner_dimensional = pybamm.Parameter( + f"{pref}Inner SEI electron conductivity [S.m-1]" + ) + self.D_li_dimensional = pybamm.Parameter( + f"{pref}Inner SEI lithium interstitial diffusivity [m2.s-1]" + ) + self.c_li_0_dimensional = pybamm.Parameter( + f"{pref}Lithium interstitial reference concentration [mol.m-3]" + ) + self.L_inner_0_dim = pybamm.Parameter(f"{pref}Initial inner SEI thickness [m]") + self.L_outer_0_dim = pybamm.Parameter(f"{pref}Initial outer SEI thickness [m]") + self.L_sei_0_dim = self.L_inner_0_dim + self.L_outer_0_dim + self.E_sei_dimensional = pybamm.Parameter( + f"{pref}SEI growth activation energy [J.mol-1]" + ) + + # EC reaction + self.c_ec_0_dim = pybamm.Parameter( + f"{pref}EC initial concentration in electrolyte [mol.m-3]" + ) + self.D_ec_dim = pybamm.Parameter(f"{pref}EC diffusivity [m2.s-1]") + self.k_sei_dim = pybamm.Parameter(f"{pref}SEI kinetic rate constant [m.s-1]") + self.U_sei_dim = pybamm.Parameter(f"{pref}SEI open-circuit potential [V]") + def D_dimensional(self, sto, T): """Dimensional diffusivity in particle. Note this is defined as a function of stochiometry""" @@ -964,6 +884,8 @@ def _set_scales(self): def _set_dimensionless_parameters(self): main = self.main_param + domain_param = self.domain_param + pref = self.phase_prefactor # Timescale ratios self.C_diff = self.tau_diffusion / main.timescale @@ -997,6 +919,84 @@ def _set_dimensionless_parameters(self): self.U_init_dim - self.domain_param.U_ref ) / main.potential_scale + # SEI parameters + self.inner_sei_proportion = pybamm.Parameter( + f"{pref}Inner SEI reaction proportion" + ) + + self.z_sei = pybamm.Parameter(f"{pref}Ratio of lithium moles to SEI moles") + + self.E_over_RT_sei = self.E_sei_dimensional / main.R / main.T_ref + + self.C_sei_reaction = (self.j_scale / self.m_sei_dimensional) * pybamm.exp( + -(main.F * domain_param.U_ref / (2 * main.R * main.T_ref)) + ) + + self.C_sei_solvent = ( + self.j_scale + * self.L_sei_0_dim + / (self.c_sol_dimensional * main.F * self.D_sol_dimensional) + ) + + self.C_sei_electron = ( + self.j_scale + * main.F + * self.L_sei_0_dim + / (self.kappa_inner_dimensional * main.R * main.T_ref) + ) + + self.C_sei_inter = ( + self.j_scale + * self.L_sei_0_dim + / (self.D_li_dimensional * self.c_li_0_dimensional * main.F) + ) + + self.U_inner_electron = main.F * self.U_inner_dimensional / main.R / main.T_ref + + self.R_sei = ( + main.F + * self.j_scale + * self.R_sei_dimensional + * self.L_sei_0_dim + / main.R + / main.T_ref + ) + + self.v_bar = self.V_bar_outer_dimensional / self.V_bar_inner_dimensional + self.c_sei_scale = self.L_sei_0_dim * self.a_typ / self.V_bar_inner_dimensional + self.c_sei_outer_scale = ( + self.L_sei_0_dim * self.a_typ / self.V_bar_outer_dimensional + ) + + self.L_inner_0 = self.L_inner_0_dim / self.L_sei_0_dim + self.L_outer_0 = self.L_outer_0_dim / self.L_sei_0_dim + + # ratio of SEI reaction scale to intercalation reaction + self.Gamma_SEI = ( + self.V_bar_inner_dimensional * self.j_scale * main.timescale + ) / (main.F * self.z_sei * self.L_sei_0_dim) + + # EC reaction + self.C_ec = ( + self.L_sei_0_dim * self.j_scale / (main.F * self.c_ec_0_dim * self.D_ec_dim) + ) + self.C_sei_ec = ( + main.F + * self.k_sei_dim + * self.c_ec_0_dim + / self.j_scale + * ( + pybamm.exp( + -( + main.F + * (domain_param.U_ref - self.U_sei_dim) + / (2 * main.R * main.T_ref) + ) + ) + ) + ) + self.c_sei_init = self.c_ec_0_dim / self.c_sei_outer_scale + def D(self, c_s, T): """Dimensionless particle diffusivity""" sto = c_s diff --git a/pybamm/parameters/parameter_sets.py b/pybamm/parameters/parameter_sets.py index 6f1021d30c..11c9339ec2 100644 --- a/pybamm/parameters/parameter_sets.py +++ b/pybamm/parameters/parameter_sets.py @@ -180,7 +180,7 @@ "positive electrode": "nmc_Chen2020", "electrolyte": "lipf6_Nyman2008", "experiment": "1C_discharge_from_full_Chen2020", - "sei": "example", + "sei": "example_composite", "citation": ["Chen2020", "Ai2022"], } diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py index 025ab0ca4e..51b1eb395b 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py @@ -253,3 +253,17 @@ def test_composite_graphite_silicon(self): {f"Primary: {name}": (1 - x) * 0.75, f"Secondary: {name}": x * 0.75} ) self.run_basic_processing_test(options, parameter_values=parameter_values) + + def test_composite_graphite_silicon_sei(self): + options = { + "particle phases": ("2", "1"), + "open circuit potential": (("single", "current sigmoid"), "single"), + "SEI": "ec reaction limited", + } + parameter_values = pybamm.ParameterValues("Chen2020_composite") + name = "Negative electrode active material volume fraction" + x = 0.1 + parameter_values.update( + {f"Primary: {name}": (1 - x) * 0.75, f"Secondary: {name}": x * 0.75} + ) + self.run_basic_processing_test(options, parameter_values=parameter_values) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py index 2eaefc434a..c3188c2891 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py @@ -324,6 +324,10 @@ def test_well_posed_particle_phases(self): options = {"particle phases": ("1", "2")} self.check_well_posedness(options) + def test_well_posed_particle_phases_sei(self): + options = {"particle phases": "2", "SEI": "ec reaction limited"} + self.check_well_posedness(options) + def test_well_posed_current_sigmoid_ocp(self): options = {"open circuit potential": "current sigmoid"} self.check_well_posedness(options) From 11cc3c902095855d3b49d53b9d22514ccf64fa35 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 8 Sep 2022 16:14:44 -0400 Subject: [PATCH 2/9] fix flake8 and revert examples --- examples/scripts/compare_lithium_ion.py | 8 ++++---- examples/scripts/compare_lithium_ion_two_phase.py | 1 - pybamm/models/full_battery_models/base_battery_model.py | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/scripts/compare_lithium_ion.py b/examples/scripts/compare_lithium_ion.py index 2aeeedb2c0..6108036b9b 100644 --- a/examples/scripts/compare_lithium_ion.py +++ b/examples/scripts/compare_lithium_ion.py @@ -7,10 +7,10 @@ # load models models = [ - pybamm.lithium_ion.SPM({"SEI": "solvent-diffusion limited"}), - # pybamm.lithium_ion.SPMe(), - # pybamm.lithium_ion.DFN(), - # pybamm.lithium_ion.NewmanTobias(), + pybamm.lithium_ion.SPM(), + pybamm.lithium_ion.SPMe(), + pybamm.lithium_ion.DFN(), + pybamm.lithium_ion.NewmanTobias(), ] # create and run simulations diff --git a/examples/scripts/compare_lithium_ion_two_phase.py b/examples/scripts/compare_lithium_ion_two_phase.py index a75b38e304..46ef789287 100644 --- a/examples/scripts/compare_lithium_ion_two_phase.py +++ b/examples/scripts/compare_lithium_ion_two_phase.py @@ -8,7 +8,6 @@ options = { "particle phases": ("2", "1"), "open circuit potential": (("single", "current sigmoid"), "single"), - "SEI": "ec reaction limited", } models = [ pybamm.lithium_ion.SPM(options), diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index e03ae0487c..f9b7d06d24 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -4,7 +4,6 @@ import pybamm import numbers -import functools class BatteryModelOptions(pybamm.FuzzyDict): From 4689cd67c0038836b88013bc235273d268d0954a Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 8 Sep 2022 16:16:57 -0400 Subject: [PATCH 3/9] fix flake8 for real this time --- pybamm/models/submodels/interface/sei/base_sei.py | 2 -- pybamm/models/submodels/interface/sei/sei_growth.py | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 2c0d26b0d1..2edc6192a4 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -90,8 +90,6 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): variables : dict The variables which can be derived from the SEI thicknesses. """ - param = self.param - # Set length scale to one for the "no SEI" model so that it is not # required by parameter values in general if isinstance(self, pybamm.sei.NoSEI): diff --git a/pybamm/models/submodels/interface/sei/sei_growth.py b/pybamm/models/submodels/interface/sei/sei_growth.py index 36dcacaac5..b46a31834b 100644 --- a/pybamm/models/submodels/interface/sei/sei_growth.py +++ b/pybamm/models/submodels/interface/sei/sei_growth.py @@ -185,7 +185,8 @@ def set_rhs(self, variables): # Note a is dimensionless (has a constant value of 1 if the surface # area does not change) a = variables[ - f"X-averaged negative electrode {phase_name}surface area to volume ratio" + f"X-averaged negative electrode {phase_name}" + "surface area to volume ratio" ] else: L_inner = variables[f"Inner {self.reaction_name}thickness"] From 844e12c7fa2b57a553a4501b289999216a4bb147 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 8 Sep 2022 17:23:28 -0400 Subject: [PATCH 4/9] fix lead-acid models --- .../full_battery_models/lead_acid/base_lead_acid_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py b/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py index d4cf410895..87433137ff 100644 --- a/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py +++ b/pybamm/models/full_battery_models/lead_acid/base_lead_acid_model.py @@ -104,7 +104,7 @@ def set_active_material_submodel(self): def set_sei_submodel(self): - self.submodels["sei"] = pybamm.sei.NoSEI(self.param) + self.submodels["sei"] = pybamm.sei.NoSEI(self.param, self.options) def set_lithium_plating_submodel(self): From 00dcf48c33755366a05221bb121370ec7bd00eff Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 8 Sep 2022 19:38:22 -0400 Subject: [PATCH 5/9] skip Newman Tobias tests --- pybamm/models/full_battery_models/base_battery_model.py | 2 +- .../test_lithium_ion/test_newman_tobias.py | 3 +++ .../test_lithium_ion/test_newman_tobias.py | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index f9b7d06d24..a915d7e5dd 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -514,7 +514,7 @@ def __init__(self, extra_options): "If there are multiple particle phases: 'surface form' cannot be " "'false', 'particle size' must be 'false', 'particle' must be " "'Fickian diffusion'. Also the following must " - "be 'none': 'SEI', 'particle mechanics', " + "be 'none': 'particle mechanics', " "'loss of active material', 'lithium plating'" ) diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py index 10a3209739..d3f7e1f9bf 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py @@ -23,6 +23,9 @@ def test_charge(self): def test_composite_graphite_silicon(self): pass # skip this test + def test_composite_graphite_silicon_sei(self): + pass # skip this test + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py index 17480d538c..5ef48d5d60 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_newman_tobias.py @@ -18,6 +18,9 @@ def test_electrolyte_options(self): def test_well_posed_particle_phases(self): pass # skip this test + def test_well_posed_particle_phases_sei(self): + pass # skip this test + if __name__ == "__main__": print("Add -v for more debug output") From 463424e477b981a278a158ed24e6818941216184 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Fri, 9 Sep 2022 09:29:50 -0400 Subject: [PATCH 6/9] fix examples --- examples/notebooks/models/using-submodels.ipynb | 6 +++--- examples/scripts/custom_model.py | 6 ++++-- .../submodels/interface/kinetics/total_main_kinetics.py | 3 --- pybamm/models/submodels/interface/sei/total_sei.py | 3 --- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/notebooks/models/using-submodels.ipynb b/examples/notebooks/models/using-submodels.ipynb index e7eb75704d..423245c74f 100644 --- a/examples/notebooks/models/using-submodels.ipynb +++ b/examples/notebooks/models/using-submodels.ipynb @@ -490,8 +490,8 @@ "model.submodels[\n", " \"Positive particle mechanics\"\n", "] = pybamm.particle_mechanics.NoMechanics(model.param, \"Positive\", model.options)\n", - "model.submodels[\"sei\"] = pybamm.sei.NoSEI(model.param)\n", - "model.submodels[\"sei on cracks\"] = pybamm.sei.NoSEI(model.param, cracks=True)\n", + "model.submodels[\"sei\"] = pybamm.sei.NoSEI(model.param, model.options)\n", + "model.submodels[\"sei on cracks\"] = pybamm.sei.NoSEI(model.param, model.options, cracks=True)\n", "model.submodels[\"lithium plating\"] = pybamm.lithium_plating.NoPlating(model.param)" ] }, @@ -632,7 +632,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.12" + "version": "3.9.13" }, "toc": { "base_numbering": 1, diff --git a/examples/scripts/custom_model.py b/examples/scripts/custom_model.py index 56c4fa098b..e93f9609cd 100644 --- a/examples/scripts/custom_model.py +++ b/examples/scripts/custom_model.py @@ -98,8 +98,10 @@ model.submodels["Positive particle mechanics"] = pybamm.particle_mechanics.NoMechanics( model.param, "Positive", model.options ) -model.submodels["sei"] = pybamm.sei.NoSEI(model.param) -model.submodels["sei on cracks"] = pybamm.sei.NoSEI(model.param, cracks=True) +model.submodels["sei"] = pybamm.sei.NoSEI(model.param, model.options) +model.submodels["sei on cracks"] = pybamm.sei.NoSEI( + model.param, model.options, cracks=True +) model.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating(model.param) # build model diff --git a/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py b/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py index 55c6b3ef3d..2571566379 100644 --- a/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py +++ b/pybamm/models/submodels/interface/kinetics/total_main_kinetics.py @@ -32,9 +32,6 @@ def get_coupled_variables(self, variables): Domain = self.domain domain = Domain.lower() - if Domain == "Negative" and self.half_cell is True: - return variables - phases = self.options.phases[domain] # For each of the variables, the variable name without the phase name # is constructed by summing all of the variable names with the phases diff --git a/pybamm/models/submodels/interface/sei/total_sei.py b/pybamm/models/submodels/interface/sei/total_sei.py index 21f0c0fb03..bb1c5ed162 100644 --- a/pybamm/models/submodels/interface/sei/total_sei.py +++ b/pybamm/models/submodels/interface/sei/total_sei.py @@ -29,9 +29,6 @@ def __init__(self, param, options, cracks=False): super().__init__(param, options=options) def get_coupled_variables(self, variables): - if self.half_cell is True: - return variables - phases = self.options.phases["negative"] # For each of the variables, the variable name without the phase name # is constructed by summing all of the variable names with the phases From 08c6a8de816c3ae675a6834777f4384faa251709 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Mon, 12 Sep 2022 11:47:02 -0400 Subject: [PATCH 7/9] codacy --- pybamm/input/discharge_data/Ecker2015/README.md | 2 +- .../parameters/lithium_ion/cells/kokam_Ecker2015/README.md | 2 +- .../experiments/1C_discharge_from_full_Ecker2015/README.md | 2 +- .../negative_electrodes/graphite_Ecker2015/README.md | 2 +- .../positive_electrodes/LiNiCoO2_Ecker2015/README.md | 2 +- .../parameters/lithium_ion/seis/example_composite/README.md | 1 - .../lithium_ion/separators/separator_Ecker2015/README.md | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pybamm/input/discharge_data/Ecker2015/README.md b/pybamm/input/discharge_data/Ecker2015/README.md index 307010fa89..a547e2a8f9 100644 --- a/pybamm/input/discharge_data/Ecker2015/README.md +++ b/pybamm/input/discharge_data/Ecker2015/README.md @@ -2,7 +2,7 @@ Experimental data (Time [s], Terminal voltage [V]) for a 1C and 5C constant current discharge of a Kokam SLPB 75106100 cell at a temperature of 25°C from ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. The data were collected from Figure 8 of diff --git a/pybamm/input/parameters/lithium_ion/cells/kokam_Ecker2015/README.md b/pybamm/input/parameters/lithium_ion/cells/kokam_Ecker2015/README.md index 84a5337e67..0285001fca 100644 --- a/pybamm/input/parameters/lithium_ion/cells/kokam_Ecker2015/README.md +++ b/pybamm/input/parameters/lithium_ion/cells/kokam_Ecker2015/README.md @@ -4,7 +4,7 @@ Parameters for a Kokam SLPB 75106100 cell, from the papers > Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery I. determination of parameters." Journal of the Electrochemical Society 162.9 (2015): A1836-A1848. ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. The tab placement parameters are taken from measurements in diff --git a/pybamm/input/parameters/lithium_ion/experiments/1C_discharge_from_full_Ecker2015/README.md b/pybamm/input/parameters/lithium_ion/experiments/1C_discharge_from_full_Ecker2015/README.md index ae69fbf332..a05c59ddc1 100644 --- a/pybamm/input/parameters/lithium_ion/experiments/1C_discharge_from_full_Ecker2015/README.md +++ b/pybamm/input/parameters/lithium_ion/experiments/1C_discharge_from_full_Ecker2015/README.md @@ -2,5 +2,5 @@ Discharge lithium-ion battery from full charge at 1C, using the initial conditions from the paper ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857.. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857.. diff --git a/pybamm/input/parameters/lithium_ion/negative_electrodes/graphite_Ecker2015/README.md b/pybamm/input/parameters/lithium_ion/negative_electrodes/graphite_Ecker2015/README.md index 579bccf170..63a3af5f14 100644 --- a/pybamm/input/parameters/lithium_ion/negative_electrodes/graphite_Ecker2015/README.md +++ b/pybamm/input/parameters/lithium_ion/negative_electrodes/graphite_Ecker2015/README.md @@ -4,7 +4,7 @@ Parameters for a graphite negative electrode, from the papers: > Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery I. determination of parameters." Journal of the Electrochemical Society 162.9 (2015): A1836-A1848. ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. The fits to data for the electrode and electrolyte properties are those provided by Dr. Simon O’Kane in the paper: diff --git a/pybamm/input/parameters/lithium_ion/positive_electrodes/LiNiCoO2_Ecker2015/README.md b/pybamm/input/parameters/lithium_ion/positive_electrodes/LiNiCoO2_Ecker2015/README.md index c4077715c4..3920ccc286 100644 --- a/pybamm/input/parameters/lithium_ion/positive_electrodes/LiNiCoO2_Ecker2015/README.md +++ b/pybamm/input/parameters/lithium_ion/positive_electrodes/LiNiCoO2_Ecker2015/README.md @@ -4,7 +4,7 @@ Parameters for a Lithium Nickel Cobalt Oxide positive electrode, from the papers > Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery i. determination of parameters." Journal of the Electrochemical Society 162.9 (2015): A1836-A1848. ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. The fits to data for the electrode and electrolyte properties are those provided by Dr. Simon O’Kane in the paper: diff --git a/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md b/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md index 22f7ab47b3..f98642e325 100644 --- a/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md +++ b/pybamm/input/parameters/lithium_ion/seis/example_composite/README.md @@ -3,4 +3,3 @@ Example parameters for composite SEI on silicon/graphite. Both phases use the same values, from the paper > Yang, X., Leng, Y., Zhang, G., Ge, S., Wang, C. (2017). Modeling of lithium plating induced aging of lithium-ion batteries: Transition from linear to nonlinear aging. Journal of Power Sources, 360, 28-40. - diff --git a/pybamm/input/parameters/lithium_ion/separators/separator_Ecker2015/README.md b/pybamm/input/parameters/lithium_ion/separators/separator_Ecker2015/README.md index d734b7b1fa..861f192486 100644 --- a/pybamm/input/parameters/lithium_ion/separators/separator_Ecker2015/README.md +++ b/pybamm/input/parameters/lithium_ion/separators/separator_Ecker2015/README.md @@ -4,7 +4,7 @@ Parameters for the separator from the papers > Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery i. determination of parameters." Journal of the Electrochemical Society 162.9 (2015): A1836-A1848. ->Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. +> Ecker, Madeleine, et al. "Parameterization of a physico-chemical model of a lithium-ion battery II. Model validation." Journal of The Electrochemical Society 162.9 (2015): A1849-A1857. The thermal material properties are for a 5 Ah power pouch cell by Kokam. The data are extracted from From cf767e332ece5a50a0eee9c5c3cc3f1f58fc88e3 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Wed, 14 Sep 2022 16:17:55 -0400 Subject: [PATCH 8/9] fix test --- pybamm/models/submodels/interface/sei/base_sei.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index e93b23c4f2..5f8090e7b7 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -171,6 +171,8 @@ def _get_standard_concentration_variables(self, variables): v_bar = 1 L_inner_0 = 0 L_outer_0 = 0 + L_inner_crack_0 = 0 + L_outer_crack_0 = 0 z_sei = 1 else: if self.reaction_loc == "interface": From d59b9b13202e4d66e7560799d103d6c71ed8fdc9 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Wed, 14 Sep 2022 20:30:54 -0400 Subject: [PATCH 9/9] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c48a8cd63..b3473634b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Features - Added function `pybamm.get_git_commit_info()`, which returns information about the last git commit, useful for reproducibility ([#2293](https://github.com/pybamm-team/PyBaMM/pull/2293)) +- Added SEI model for composite electrodes ([#2290](https://github.com/pybamm-team/PyBaMM/pull/2290)) - For experiments, the simulation now automatically checks and skips steps that cannot be performed (e.g. "Charge at 1C until 4.2V" from 100% SOC) ([#2212](https://github.com/pybamm-team/PyBaMM/pull/2212)) ## Bug fixes