From cf53ff1d9e74eda7e68bc65b5dea5c18f7fcf872 Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Mon, 5 Jun 2023 12:17:24 +0100 Subject: [PATCH 1/7] fixed tests --- .../scripts/compare_lithium_ion_half_cell.py | 6 +- pybamm/expression_tree/averages.py | 2 +- .../full_battery_models/base_battery_model.py | 24 +++----- .../lithium_ion/basic_dfn_half_cell.py | 5 -- .../lithium_ion/electrode_soh_half_cell.py | 12 ++-- .../full_battery_models/lithium_ion/mpm.py | 2 +- .../full_battery_models/lithium_metal/dfn.py | 2 +- .../submodels/interface/sei/base_sei.py | 39 ++++++------ pybamm/parameters/lithium_ion_parameters.py | 8 +-- .../size_distribution_parameters.py | 60 +++++++++---------- pybamm/simulation.py | 2 +- .../base_lithium_ion_half_cell_tests.py | 2 +- .../test_basic_half_cell_models.py | 4 +- .../test_simulation_with_experiment.py | 2 +- .../test_base_battery_model.py | 22 ++----- .../base_lithium_ion_half_cell_tests.py | 2 +- .../test_lithium_ion/test_basic_models.py | 8 +-- .../test_lithium_ion/test_electrode_soh.py | 4 +- .../test_lithium_ion/test_mpm_half_cell.py | 18 +++--- .../test_lithium_ion/test_spme_half_cell.py | 2 +- .../test_parameters_with_default_models.py | 2 +- .../test_size_distribution_parameters.py | 12 ++-- tests/unit/test_plotting/test_quick_plot.py | 4 +- 23 files changed, 108 insertions(+), 136 deletions(-) diff --git a/examples/scripts/compare_lithium_ion_half_cell.py b/examples/scripts/compare_lithium_ion_half_cell.py index b6396724f4..c38c819483 100644 --- a/examples/scripts/compare_lithium_ion_half_cell.py +++ b/examples/scripts/compare_lithium_ion_half_cell.py @@ -7,9 +7,9 @@ # load models models = [ - pybamm.lithium_ion.SPM({"working electrode": "positive"}), - pybamm.lithium_ion.SPMe({"working electrode": "positive"}), - pybamm.lithium_ion.DFN({"working electrode": "positive"}), + pybamm.lithium_ion.SPM({"half-cell": "true"}), + pybamm.lithium_ion.SPMe({"half-cell": "true"}), + pybamm.lithium_ion.DFN({"half-cell": "true"}), ] # create and run simulations diff --git a/pybamm/expression_tree/averages.py b/pybamm/expression_tree/averages.py index 6ada30d47a..e063b16c2a 100644 --- a/pybamm/expression_tree/averages.py +++ b/pybamm/expression_tree/averages.py @@ -273,7 +273,7 @@ def r_average(symbol): # "positive electrode", take the r-average of the child then broadcast back elif isinstance(symbol, pybamm.SecondaryBroadcast) and symbol.domains[ "secondary" - ] in [["positive electrode"], ["negative electrode"], ["working electrode"]]: + ] in [["positive electrode"], ["negative electrode"]]: child = symbol.orphans[0] child_av = pybamm.r_average(child) return pybamm.PrimaryBroadcast(child_av, symbol.domains["secondary"]) diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 4ea4611133..440a129bd2 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -49,6 +49,10 @@ class BatteryModelOptions(pybamm.FuzzyDict): * "electrolyte conductivity" : str Can be "default" (default), "full", "leading order", "composite" or "integrated". + * "half-cell" : str + Can be "false" (default) for a standard battery or "true" for a + half-cell where the negative electrode is replaced with a lithium metal + counter electrode. * "hydrolysis" : str Whether to include hydrolysis in the model. Only implemented for lead-acid models. Can be "false" (default) or "true". If "true", then @@ -168,10 +172,6 @@ class BatteryModelOptions(pybamm.FuzzyDict): solve an algebraic equation for it. Default is "false", unless "SEI film resistance" is distributed in which case it is automatically set to "true". - * "working electrode": str - Which electrode(s) intercalates and which is counter. If "both" - (default), the model is a standard battery. Otherwise can be "negative" - or "positive" to indicate a half-cell model. * "x-average side reactions": str Whether to average the side reactions (SEI growth, lithium plating and the respective porosity change) over the x-axis in Single Particle @@ -199,6 +199,7 @@ def __init__(self, extra_options): "composite", "integrated", ], + "half-cell": ["false", "true"], "hydrolysis": ["false", "true"], "intercalation kinetics": [ "symmetric Butler-Volmer", @@ -262,7 +263,6 @@ def __init__(self, extra_options): "surface form": ["false", "differential", "algebraic"], "thermal": ["isothermal", "lumped", "x-lumped", "x-full"], "total interfacial current density as a state": ["false", "true"], - "working electrode": ["both", "negative", "positive"], "x-average side reactions": ["false", "true"], } @@ -495,7 +495,7 @@ def __init__(self, extra_options): "mechanics model" ) - if options["working electrode"] != "both": + if options["half-cell"] == "true": if options["thermal"] == "x-full": raise pybamm.OptionError( "X-full thermal submodel is not compatible with half-cell models" @@ -506,10 +506,6 @@ def __init__(self, extra_options): f"X-lumped thermal submodels do not yet support {n}D " "current collectors in a half-cell configuration" ) - elif options["SEI on cracks"] == "true": - raise NotImplementedError( - "SEI on cracks not yet implemented for half-cell models" - ) if options["particle phases"] != "1": if not ( @@ -530,7 +526,7 @@ def __init__(self, extra_options): # Check options are valid for option, value in options.items(): - if option in ["working electrode"]: + if option in ["half-cell"]: # is this exception still necessary? pass else: if isinstance(value, str) or option in [ @@ -597,11 +593,9 @@ def phases(self): @cached_property def whole_cell_domains(self): - if self["working electrode"] == "positive": + if self["half-cell"] == "true": return ["separator", "positive electrode"] - elif self["working electrode"] == "negative": - return ["negative electrode", "separator"] - elif self["working electrode"] == "both": + else: return ["negative electrode", "separator", "positive electrode"] @property diff --git a/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py b/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py index cda7c02731..47916c2ffc 100644 --- a/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py +++ b/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py @@ -38,11 +38,6 @@ class BasicDFNHalfCell(BaseModel): def __init__(self, options=None, name="Doyle-Fuller-Newman half cell model"): super().__init__(options, name) - if self.options["working electrode"] not in ["negative", "positive"]: - raise ValueError( - "The option 'working electrode' should be either 'positive'" - " or 'negative'" - ) pybamm.citations.register("Marquis2019") # `param` is a class containing all the relevant parameters and functions for # this model. These are purely symbolic at this stage, and will be set by the diff --git a/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py b/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py index 75f9878fff..1cc0293298 100644 --- a/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py +++ b/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py @@ -25,21 +25,17 @@ class ElectrodeSOHHalfCell(pybamm.BaseModel): expansion. Journal of Power Sources, 427, 101-111. """ - def __init__(self, working_electrode, name="Electrode-specific SOH model"): - self.working_electrode = working_electrode + def __init__(self, name="Electrode-specific SOH model"): pybamm.citations.register("Mohtat2019") super().__init__(name) - param = pybamm.LithiumIonParameters({"working electrode": working_electrode}) + param = pybamm.LithiumIonParameters({"half-cell": "true"}) x_100 = pybamm.Variable("x_100", bounds=(0, 1)) x_0 = pybamm.Variable("x_0", bounds=(0, 1)) Q_w = pybamm.InputParameter("Q_w") T_ref = param.T_ref - if working_electrode == "negative": # pragma: no cover - raise NotImplementedError - elif working_electrode == "positive": - U_w = param.p.prim.U - Q = Q_w * (x_100 - x_0) + U_w = param.p.prim.U + Q = Q_w * (x_100 - x_0) V_max = param.voltage_high_cut V_min = param.voltage_low_cut diff --git a/pybamm/models/full_battery_models/lithium_ion/mpm.py b/pybamm/models/full_battery_models/lithium_ion/mpm.py index 3286a9c03f..9b42007bdc 100644 --- a/pybamm/models/full_battery_models/lithium_ion/mpm.py +++ b/pybamm/models/full_battery_models/lithium_ion/mpm.py @@ -53,6 +53,6 @@ def __init__(self, options=None, name="Many-Particle Model", build=True): def default_parameter_values(self): default_params = super().default_parameter_values default_params = pybamm.get_size_distribution_parameters( - default_params, electrode=self.options["working electrode"] + default_params, halfcell=self.options["half-cell"] ) return default_params diff --git a/pybamm/models/full_battery_models/lithium_metal/dfn.py b/pybamm/models/full_battery_models/lithium_metal/dfn.py index a7ab8b99e2..eb11345c90 100644 --- a/pybamm/models/full_battery_models/lithium_metal/dfn.py +++ b/pybamm/models/full_battery_models/lithium_metal/dfn.py @@ -29,7 +29,7 @@ def __init__( self, options=None, name="Doyle-Fuller-Newman lithium metal model", build=True ): options = options or {} - options["working electrode"] = "positive" + options["half-cell"] = "positive" super().__init__(options, name, build=False) if build: diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 479bc44e70..6b08214ff4 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -30,40 +30,45 @@ def __init__(self, param, options, phase="primary", cracks=False): def get_coupled_variables(self, variables): # Update some common variables + zero_av = pybamm.PrimaryBroadcast(0, "current collector") + zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") if self.reaction_loc != "interface": - j_sei_av = variables[ - f"X-averaged {self.reaction_name}interfacial current density [A.m-2]" - ] - j_sei = variables[ - f"{self.reaction_name}interfacial current density [A.m-2]" - ] variables.update( { f"X-averaged negative electrode {self.reaction_name}interfacial " - "current density [A.m-2]": j_sei_av, + "current density": variables[ + f"X-averaged {self.reaction_name}interfacial current density" + ], + f"Negative electrode {self.reaction_name}interfacial current " + "density": variables[ + f"{self.reaction_name}interfacial current density" + ], f"Negative electrode {self.reaction_name}interfacial current " - "density [A.m-2]": j_sei, + "density [A.m-2]": variables[ + f"{self.reaction_name}interfacial current density [A.m-2]" + ], } ) + variables.update( + self._get_standard_volumetric_current_density_variables(variables) + ) - zero_av = pybamm.PrimaryBroadcast(0, "current collector") - zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") 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 [A.m-2]": zero_av, + "volumetric interfacial current density": zero_av, f"Positive electrode {self.reaction} " - "volumetric interfacial current density [A.m-3]": zero, + "volumetric interfacial current density": zero, } ) - variables.update( - self._get_standard_volumetric_current_density_variables(variables) - ) - return variables def _get_standard_thickness_variables(self, L_inner, L_outer): @@ -157,7 +162,7 @@ def _get_standard_concentration_variables(self, variables): else: # m * (mol/m4) = mol/m3 (n is a bulk quantity) a = variables[ - f"Negative electrode {self.phase_name}" + f"{Domain} electrode {self.phase_name}" "surface area to volume ratio [m-1]" ] L_to_n_inner = a / phase_param.V_bar_inner diff --git a/pybamm/parameters/lithium_ion_parameters.py b/pybamm/parameters/lithium_ion_parameters.py index 43901eff2b..799bcb3561 100644 --- a/pybamm/parameters/lithium_ion_parameters.py +++ b/pybamm/parameters/lithium_ion_parameters.py @@ -16,14 +16,14 @@ class LithiumIonParameters(BaseParameters): A dictionary of options to be passed to the parameters. The options that can be set are listed below. + * "half-cell": str + Can be "false" (default) for a standard battery or "true" for a + half-cell where the negative electrode is replaced with a lithium metal + counter electrode. * "particle shape" : str, optional Sets the model shape of the electrode particles. This is used to calculate the surface area to volume ratio. Can be "spherical" (default). TODO: implement "cylindrical" and "platelet". - * "working electrode": str - Which electrode(s) intercalates and which is counter. If "both" - (default), the model is a standard battery. Otherwise can be "negative" - or "positive" to indicate a half-cell model. """ diff --git a/pybamm/parameters/size_distribution_parameters.py b/pybamm/parameters/size_distribution_parameters.py index c45bd3b11e..4eb0c34618 100644 --- a/pybamm/parameters/size_distribution_parameters.py +++ b/pybamm/parameters/size_distribution_parameters.py @@ -16,7 +16,7 @@ def get_size_distribution_parameters( R_min_p=None, R_max_n=None, R_max_p=None, - electrode="both", + halfcell="false", ): """ A convenience method to add standard area-weighted particle-size distribution @@ -60,7 +60,7 @@ def get_size_distribution_parameters( "positive" to indicate a half-cell model, in which case size distribution parameters are only added for a single electrode. """ - if electrode in ["both", "negative"]: + if halfcell == "false": # Radii from given parameter set R_n_typ = param["Negative particle radius [m]"] @@ -89,35 +89,33 @@ def f_a_dist_n(R): }, check_already_exists=False, ) - if electrode in ["both", "positive"]: - # Radii from given parameter set - R_p_typ = param["Positive particle radius [m]"] - - # Set the mean particle radii - R_p_av = R_p_av or R_p_typ - - # Minimum radii - R_min_p = R_min_p or np.max([0, 1 - sd_p * 5]) - - # Max radii - R_max_p = R_max_p or (1 + sd_p * 5) - - # Area-weighted particle-size distribution - def f_a_dist_p(R): - return lognormal(R, R_p_av, sd_p * R_p_av) - - param.update( - { - "Positive area-weighted mean particle radius [m]": R_p_av, - "Positive area-weighted particle-size " - + "standard deviation [m]": sd_p * R_p_av, - "Positive minimum particle radius [m]": R_min_p * R_p_av, - "Positive maximum particle radius [m]": R_max_p * R_p_av, - "Positive area-weighted " - + "particle-size distribution [m-1]": f_a_dist_p, - }, - check_already_exists=False, - ) + # Radii from given parameter set + R_p_typ = param["Positive particle radius [m]"] + + # Set the mean particle radii + R_p_av = R_p_av or R_p_typ + + # Minimum radii + R_min_p = R_min_p or np.max([0, 1 - sd_p * 5]) + + # Max radii + R_max_p = R_max_p or (1 + sd_p * 5) + + # Area-weighted particle-size distribution + def f_a_dist_p(R): + return lognormal(R, R_p_av, sd_p * R_p_av) + + param.update( + { + "Positive area-weighted mean particle radius [m]": R_p_av, + "Positive area-weighted particle-size " + + "standard deviation [m]": sd_p * R_p_av, + "Positive minimum particle radius [m]": R_min_p * R_p_av, + "Positive maximum particle radius [m]": R_max_p * R_p_av, + "Positive area-weighted " + "particle-size distribution [m-1]": f_a_dist_p, + }, + check_already_exists=False, + ) return param diff --git a/pybamm/simulation.py b/pybamm/simulation.py index 9ae217552e..003193012e 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -934,7 +934,7 @@ def _get_esoh_solver(self, calc_esoh): calc_esoh is False or isinstance(self.model, pybamm.lead_acid.BaseModel) or isinstance(self.model, pybamm.equivalent_circuit.Thevenin) - or self.model.options["working electrode"] != "both" + or self.model.options["half-cell"] == "true" ): return None diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py index 0c203c9fc7..ebb6f334c9 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py @@ -7,7 +7,7 @@ class BaseIntegrationTestLithiumIonHalfCell: def run_basic_processing_test(self, options, **kwargs): - options["working electrode"] = "positive" + options["half-cell"] = "true" model = self.model(options) modeltest = tests.StandardModelTest(model, **kwargs) modeltest.test_all(skip_output_tests=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py index c13efff621..0738a96cbd 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py @@ -10,7 +10,7 @@ class TestBasicHalfCellModels(TestCase): def test_runs_Xu2019(self): - options = {"working electrode": "positive"} + options = {"half-cell": "true"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) # create geometry @@ -40,7 +40,7 @@ def test_runs_Xu2019(self): def test_runs_OKane2022(self): # load model - options = {"working electrode": "positive"} + options = {"half-cell": "true"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) # create geometry diff --git a/tests/unit/test_experiments/test_simulation_with_experiment.py b/tests/unit/test_experiments/test_simulation_with_experiment.py index a48f6f08e8..d3caf4c887 100644 --- a/tests/unit/test_experiments/test_simulation_with_experiment.py +++ b/tests/unit/test_experiments/test_simulation_with_experiment.py @@ -583,7 +583,7 @@ def test_run_experiment_half_cell(self): experiment = pybamm.Experiment( [("Discharge at C/20 until 3.5V", "Charge at 1C until 3.8 V")] ) - model = pybamm.lithium_ion.SPM({"working electrode": "positive"}) + model = pybamm.lithium_ion.SPM({"half-cell": "true"}) sim = pybamm.Simulation( model, experiment=experiment, diff --git a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py index ee51f864c4..01f975024b 100644 --- a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py +++ b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py @@ -23,6 +23,7 @@ 'current collector': 'uniform' (possible: ['uniform', 'potential pair', 'potential pair quite conductive']) 'dimensionality': 0 (possible: [0, 1, 2]) 'electrolyte conductivity': 'default' (possible: ['default', 'full', 'leading order', 'composite', 'integrated']) +'half-cell': 'false' (possible: ['false', 'true']) 'hydrolysis': 'false' (possible: ['false', 'true']) 'intercalation kinetics': 'symmetric Butler-Volmer' (possible: ['symmetric Butler-Volmer', 'asymmetric Butler-Volmer', 'linear', 'Marcus', 'Marcus-Hush-Chidsey']) 'interface utilisation': 'full' (possible: ['full', 'constant', 'current-driven']) @@ -44,7 +45,6 @@ 'surface form': 'differential' (possible: ['false', 'differential', 'algebraic']) 'thermal': 'x-full' (possible: ['isothermal', 'lumped', 'x-lumped', 'x-full']) 'total interfacial current density as a state': 'false' (possible: ['false', 'true']) -'working electrode': 'both' (possible: ['both', 'negative', 'positive']) 'x-average side reactions': 'false' (possible: ['false', 'true']) """ # noqa: E501 @@ -291,13 +291,6 @@ def test_options(self): # SEI on cracks with self.assertRaisesRegex(pybamm.OptionError, "SEI on cracks"): pybamm.BaseBatteryModel({"SEI on cracks": "bad SEI on cracks"}) - with self.assertRaisesRegex(NotImplementedError, "SEI on cracks not yet"): - pybamm.BaseBatteryModel( - { - "SEI on cracks": "true", - "working electrode": "positive", - } - ) # plating model with self.assertRaisesRegex(pybamm.OptionError, "lithium plating"): @@ -350,15 +343,13 @@ def test_options(self): # thermal half-cell with self.assertRaisesRegex(pybamm.OptionError, "X-full"): - pybamm.BaseBatteryModel( - {"thermal": "x-full", "working electrode": "positive"} - ) + pybamm.BaseBatteryModel({"thermal": "x-full", "half-cell": "true"}) with self.assertRaisesRegex(pybamm.OptionError, "X-lumped"): pybamm.BaseBatteryModel( { "dimensionality": 2, "thermal": "x-lumped", - "working electrode": "positive", + "half-cell": "true", } ) @@ -456,16 +447,11 @@ def test_domain_phase_options(self): self.assertEqual(options.positive.secondary["particle mechanics"], "none") def test_whole_cell_domains(self): - options = BatteryModelOptions({"working electrode": "positive"}) + options = BatteryModelOptions({"half-cell": "true"}) self.assertEqual( options.whole_cell_domains, ["separator", "positive electrode"] ) - options = BatteryModelOptions({"working electrode": "negative"}) - self.assertEqual( - options.whole_cell_domains, ["negative electrode", "separator"] - ) - options = BatteryModelOptions({}) self.assertEqual( options.whole_cell_domains, diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py index 24ccd55d79..8684708c3e 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py @@ -7,7 +7,7 @@ class BaseUnitTestLithiumIonHalfCell: def check_well_posedness(self, options): if self.model is not None: - options["working electrode"] = "positive" + options["half-cell"] = "true" model = self.model(options) model.check_well_posedness() diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py index 6808709d92..a7b868c6ff 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py @@ -19,12 +19,12 @@ def test_spm_well_posed(self): model.check_well_posedness() def test_dfn_half_cell_well_posed(self): - options = {"working electrode": "positive"} + options = {"half-cell": "true"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) model.check_well_posedness() def test_dfn_half_cell_simulation_with_experiment_error(self): - options = {"working electrode": "positive"} + options = {"half-cell": "true"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) experiment = pybamm.Experiment( [("Discharge at C/10 for 10 hours or until 3.5 V")] @@ -36,9 +36,7 @@ def test_dfn_half_cell_simulation_with_experiment_error(self): pybamm.Simulation(model, experiment=experiment) def test_basic_dfn_half_cell_simulation(self): - model = pybamm.lithium_ion.BasicDFNHalfCell( - options={"working electrode": "positive"} - ) + model = pybamm.lithium_ion.BasicDFNHalfCell(options={"half-cell": "true"}) param = pybamm.ParameterValues("OKane2022") param["Current function [A]"] = 2.5 sim = pybamm.Simulation(model=model, parameter_values=param) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index 1fae0db77d..75112745ba 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -139,9 +139,9 @@ def test_error(self): class TestElectrodeSOHHalfCell(TestCase): def test_known_solution(self): - model = pybamm.lithium_ion.ElectrodeSOHHalfCell("positive") + model = pybamm.lithium_ion.ElectrodeSOHHalfCell() - param = pybamm.LithiumIonParameters({"working electrode": "positive"}) + param = pybamm.LithiumIonParameters({"half-cell": "true"}) parameter_values = pybamm.ParameterValues("Xu2019") sim = pybamm.Simulation(model, parameter_values=parameter_values) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py index 364120c741..93c5afea44 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py @@ -8,18 +8,18 @@ class TestMPM(TestCase): def test_well_posed(self): - options = {"thermal": "isothermal", "working electrode": "positive"} + options = {"thermal": "isothermal", "half-cell": "true"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() # Test build after init - model = pybamm.lithium_ion.MPM({"working electrode": "positive"}, build=False) + model = pybamm.lithium_ion.MPM({"half-cell": "true"}, build=False) model.build_model() model.check_well_posedness() def test_default_parameter_values(self): # check default parameters are added correctly - model = pybamm.lithium_ion.MPM({"working electrode": "positive"}) + model = pybamm.lithium_ion.MPM({"half-cell": "true"}) self.assertEqual( model.default_parameter_values[ "Positive area-weighted mean particle radius [m]" @@ -28,19 +28,19 @@ def test_default_parameter_values(self): ) def test_lumped_thermal_model_1D(self): - options = {"thermal": "lumped", "working electrode": "positive"} + options = {"thermal": "lumped", "half-cell": "true"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_particle_uniform(self): - options = {"particle": "uniform profile", "working electrode": "positive"} + options = {"particle": "uniform profile", "half-cell": "true"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_differential_surface_form(self): options = { "surface form": "differential", - "working electrode": "positive", + "half-cell": "true", } model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() @@ -48,12 +48,12 @@ def test_differential_surface_form(self): class TestMPMExternalCircuits(TestCase): def test_well_posed_voltage(self): - options = {"operating mode": "voltage", "working electrode": "positive"} + options = {"operating mode": "voltage", "half-cell": "true"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_well_posed_power(self): - options = {"operating mode": "power", "working electrode": "positive"} + options = {"operating mode": "power", "half-cell": "true"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() @@ -65,7 +65,7 @@ def external_circuit_function(variables): options = { "operating mode": external_circuit_function, - "working electrode": "positive", + "half-cell": "true", } model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py index f1930df026..16d4128fb1 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py @@ -1,6 +1,6 @@ # # Tests for the lithium-ion half-cell SPMe model -# This is achieved by using the {"working electrode": "positive"} option +# This is achieved by using the {"half-cell": "true"} option # import pybamm import unittest diff --git a/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py b/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py index 062d5caa24..398ab32ad5 100644 --- a/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py +++ b/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py @@ -31,7 +31,7 @@ def test_parameter_values_with_model(self): "ORegan2022": pybamm.lithium_ion.DFN(), "Prada2013": pybamm.lithium_ion.DFN(), "Ramadass2004": pybamm.lithium_ion.DFN(), - "Xu2019": pybamm.lithium_ion.DFN({"working electrode": "positive"}), + "Xu2019": pybamm.lithium_ion.DFN({"half-cell": "true"}), } # Loop over each parameter set, testing that parameters can be set diff --git a/tests/unit/test_parameters/test_size_distribution_parameters.py b/tests/unit/test_parameters/test_size_distribution_parameters.py index 8f8851bfaa..d07df9f8d1 100644 --- a/tests/unit/test_parameters/test_size_distribution_parameters.py +++ b/tests/unit/test_parameters/test_size_distribution_parameters.py @@ -13,15 +13,15 @@ def test_parameter_values(self): values = pybamm.lithium_ion.BaseModel().default_parameter_values param = pybamm.LithiumIonParameters() - # add distribution parameter values for negative electrode - values = pybamm.get_size_distribution_parameters(values, electrode="negative") + # add distribution parameter values for positive electrode + values = pybamm.get_size_distribution_parameters(values, halfcell="true") - # check positive parameters aren't there yet + # check negative parameters aren't there yet with self.assertRaises(KeyError): - values["Positive maximum particle radius [m]"] + values["Negative maximum particle radius [m]"] - # now add distribution parameter values for positive electrode - values = pybamm.get_size_distribution_parameters(values, electrode="positive") + # now add distribution parameter values for negative electrode + values = pybamm.get_size_distribution_parameters(values, halfcell="false") # check parameters diff --git a/tests/unit/test_plotting/test_quick_plot.py b/tests/unit/test_plotting/test_quick_plot.py index db7de574dc..6acf78b861 100644 --- a/tests/unit/test_plotting/test_quick_plot.py +++ b/tests/unit/test_plotting/test_quick_plot.py @@ -303,7 +303,7 @@ def test_loqs_spme(self): for model in [ pybamm.lithium_ion.SPMe(), pybamm.lead_acid.LOQS(), - pybamm.lithium_ion.SPMe({"working electrode": "positive"}), + pybamm.lithium_ion.SPMe({"half-cell": "true"}), ]: geometry = model.default_geometry param = model.default_parameter_values @@ -343,7 +343,7 @@ def test_loqs_spme(self): # test quick plot of particle for spme if ( model.name == "Single Particle Model with electrolyte" - and model.options["working electrode"] != "positive" + and model.options["half-cell"] == "false" ): output_variables = [ "X-averaged negative particle concentration [mol.m-3]", From 78001e81eecc38919364190940e095e0e51fab76 Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Mon, 12 Jun 2023 11:00:25 +0100 Subject: [PATCH 2/7] Added graphite half-cell parameter files --- .../Chen2020_composite_halfcell.py | 377 +++++++++++++++++ .../Ecker2015_graphite_halfcell.py | 396 ++++++++++++++++++ .../full_battery_models/base_battery_model.py | 20 +- .../lead_acid/base_lead_acid_model.py | 10 +- .../lithium_ion/base_lithium_ion_model.py | 134 +++--- .../full_battery_models/lithium_metal/dfn.py | 2 +- .../submodels/electrode/ohm/li_metal.py | 6 +- .../interface/lithium_plating/base_plating.py | 74 ++-- .../interface/lithium_plating/no_plating.py | 6 +- .../interface/lithium_plating/plating.py | 63 +-- .../submodels/interface/sei/base_sei.py | 128 +++--- .../submodels/interface/sei/constant_sei.py | 4 +- .../models/submodels/interface/sei/no_sei.py | 4 +- .../submodels/interface/sei/sei_growth.py | 85 ++-- .../submodels/interface/sei/total_sei.py | 15 +- setup.py | 2 + 16 files changed, 1079 insertions(+), 247 deletions(-) create mode 100644 pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py create mode 100644 pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py diff --git a/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py b/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py new file mode 100644 index 0000000000..73d79da8a4 --- /dev/null +++ b/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py @@ -0,0 +1,377 @@ +import pybamm +import os + + +def graphite_LGM50_electrolyte_exchange_current_density_Chen2020( + c_e, c_s_surf, c_s_max, T +): + """ + Exchange-current density for Butler-Volmer reactions between graphite and LiPF6 in + EC:DMC. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + c_e : :class:`pybamm.Symbol` + Electrolyte concentration [mol.m-3] + c_s_surf : :class:`pybamm.Symbol` + Particle concentration [mol.m-3] + c_s_max : :class:`pybamm.Symbol` + Maximum particle concentration [mol.m-3] + T : :class:`pybamm.Symbol` + Temperature [K] + + Returns + ------- + :class:`pybamm.Symbol` + Exchange-current density [A.m-2] + """ + m_ref = 6.48e-7 # (A/m2)(m3/mol)**1.5 - includes ref concentrations + E_r = 35000 + arrhenius = pybamm.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) + + return ( + m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 + ) + + +def silicon_ocp_lithiation_Mark2016(sto): + """ + silicon Open-circuit Potential (OCP) as a a function of the + stochiometry. The fit is taken from the Enertech cell [1], which is only accurate + for 0 < sto < 1. + + References + ---------- + .. [1] Verbrugge M, Baker D, Xiao X. Formulation for the treatment of multiple + electrochemical reactions and associated speciation for the Lithium-Silicon + electrode[J]. Journal of The Electrochemical Society, 2015, 163(2): A262. + + Parameters + ---------- + sto: double + Stochiometry of material (li-fraction) + + Returns + ------- + :class:`pybamm.Symbol` + OCP [V] + """ + p1 = -96.63 + p2 = 372.6 + p3 = -587.6 + p4 = 489.9 + p5 = -232.8 + p6 = 62.99 + p7 = -9.286 + p8 = 0.8633 + + U_lithiation = ( + p1 * sto**7 + + p2 * sto**6 + + p3 * sto**5 + + p4 * sto**4 + + p5 * sto**3 + + p6 * sto**2 + + p7 * sto + + p8 + ) + return U_lithiation + + +def silicon_ocp_delithiation_Mark2016(sto): + """ + silicon Open-circuit Potential (OCP) as a a function of the + stochiometry. The fit is taken from the Enertech cell [1], which is only accurate + for 0 < sto < 1. + + References + ---------- + .. [1] Verbrugge M, Baker D, Xiao X. Formulation for the treatment of multiple + electrochemical reactions and associated speciation for the Lithium-Silicon + electrode[J]. Journal of The Electrochemical Society, 2015, 163(2): A262. + + Parameters + ---------- + sto: double + Stochiometry of material (li-fraction) + + Returns + ------- + :class:`pybamm.Symbol` + OCP [V] + """ + p1 = -51.02 + p2 = 161.3 + p3 = -205.7 + p4 = 140.2 + p5 = -58.76 + p6 = 16.87 + p7 = -3.792 + p8 = 0.9937 + + U_delithiation = ( + p1 * sto**7 + + p2 * sto**6 + + p3 * sto**5 + + p4 * sto**4 + + p5 * sto**3 + + p6 * sto**2 + + p7 * sto + + p8 + ) + return U_delithiation + + +def silicon_LGM50_electrolyte_exchange_current_density_Chen2020( + c_e, c_s_surf, c_s_max, T +): + """ + Exchange-current density for Butler-Volmer reactions between silicon and LiPF6 in + EC:DMC. + + References + ---------- + .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the + Electrochemical Society 167 (2020): 080534. + + Parameters + ---------- + c_e : :class:`pybamm.Symbol` + Electrolyte concentration [mol.m-3] + c_s_surf : :class:`pybamm.Symbol` + Particle concentration [mol.m-3] + c_s_max : :class:`pybamm.Symbol` + Maximum particle concentration [mol.m-3] + T : :class:`pybamm.Symbol` + Temperature [K] + + Returns + ------- + :class:`pybamm.Symbol` + Exchange-current density [A.m-2] + """ + + m_ref = ( + 6.48e-7 * 28700 / 278000 + ) # (A/m2)(m3/mol)**1.5 - includes ref concentrations + E_r = 35000 + arrhenius = pybamm.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) + + return ( + m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 + ) + + +def electrolyte_diffusivity_Nyman2008(c_e, T): + """ + Diffusivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data + comes from [1] + + References + ---------- + .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and + modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," + Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + D_c_e = 8.794e-11 * (c_e / 1000) ** 2 - 3.972e-10 * (c_e / 1000) + 4.862e-10 + + # Nyman et al. (2008) does not provide temperature dependence + + return D_c_e + + +def electrolyte_conductivity_Nyman2008(c_e, T): + """ + Conductivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data + comes from [1]. + + References + ---------- + .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and + modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," + Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + sigma_e = ( + 0.1297 * (c_e / 1000) ** 3 - 2.51 * (c_e / 1000) ** 1.5 + 3.329 * (c_e / 1000) + ) + + # Nyman et al. (2008) does not provide temperature dependence + + return sigma_e + + +# Load data in the appropriate format +path, _ = os.path.split(os.path.abspath(__file__)) +graphite_ocp_Enertech_Ai2020_data = pybamm.parameters.process_1D_data( + "graphite_ocp_Enertech_Ai2020.csv", path=path +) + + +def graphite_ocp_Enertech_Ai2020(sto): + name, (x, y) = graphite_ocp_Enertech_Ai2020_data + return pybamm.Interpolant(x, y, sto, name=name, interpolator="cubic") + + +# Call dict via a function to avoid errors when editing in place +def get_parameter_values(): + """ + Parameters for a composite graphite/silicon electrode, from the paper + + Weilong Ai, Niall Kirkaldy, Yang Jiang, Gregory Offer, Huizhi Wang, and Billy + Wu. A composite electrode model for lithium-ion batteries with silicon/graphite + negative electrodes. Journal of Power Sources, 527:231142, 2022. URL: + https://www.sciencedirect.com/science/article/pii/S0378775322001604, + doi:https://doi.org/10.1016/j.jpowsour.2022.231142. + + based on the paper + + Chang-Hui Chen, Ferran Brosa Planella, Kieran O'Regan, Dominika Gastol, W. + Dhammika Widanage, and Emma Kendrick. Development of Experimental Techniques for + Parameterization of Multi-scale Lithium-ion Battery Models. Journal of The + Electrochemical Society, 167(8):080534, 2020. doi:10.1149/1945-7111/ab9050. + + and references therein. + + SEI parameters are example parameters for composite SEI on silicon/graphite. Both + phases use the same values, from the paper. + + Xiao Guang Yang, Yongjun Leng, Guangsheng Zhang, Shanhai Ge, and Chao Yang Wang. + Modeling of lithium plating induced aging of lithium-ion batteries: transition + from linear to nonlinear aging. Journal of Power Sources, 360:28–40, 2017. + doi:10.1016/j.jpowsour.2017.05.110. + + """ + + return { + "chemistry": "lithium_ion", + # sei + "Primary: Ratio of lithium moles to SEI moles": 2.0, + "Primary: Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Primary: Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Primary: SEI resistivity [Ohm.m]": 200000.0, + "Primary: Initial inner SEI thickness [m]": 2.5e-09, + "Primary: Initial outer SEI thickness [m]": 2.5e-09, + "Primary: EC initial concentration in electrolyte [mol.m-3]": 4541.0, + "Primary: EC diffusivity [m2.s-1]": 2e-18, + "Primary: SEI kinetic rate constant [m.s-1]": 1e-12, + "Primary: SEI open-circuit potential [V]": 0.4, + "Primary: SEI growth activation energy [J.mol-1]": 0.0, + "Secondary: Ratio of lithium moles to SEI moles": 2.0, + "Secondary: Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Secondary: Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Secondary: SEI resistivity [Ohm.m]": 200000.0, + "Secondary: Initial inner SEI thickness [m]": 2.5e-09, + "Secondary: Initial outer SEI thickness [m]": 2.5e-09, + "Secondary: EC initial concentration in electrolyte [mol.m-3]": 4541.0, + "Secondary: EC diffusivity [m2.s-1]": 2e-18, + "Secondary: SEI kinetic rate constant [m.s-1]": 1e-12, + "Secondary: SEI open-circuit potential [V]": 0.4, + "Secondary: SEI growth activation energy [J.mol-1]": 0.0, + # cell + "Positive current collector thickness [m]": 1.2e-05, + "Positive electrode thickness [m]": 8.52e-05, + "Separator thickness [m]": 1.2e-05, + "Electrode height [m]": 0.065, + "Electrode width [m]": 1.58, + "Cell cooling surface area [m2]": 0.00531, + "Cell volume [m3]": 2.42e-05, + "Cell thermal expansion coefficient [m.K-1]": 1.1e-06, + "Positive current collector conductivity [S.m-1]": 58411000.0, + "Positive current collector density [kg.m-3]": 8960.0, + "Positive current collector specific heat capacity [J.kg-1.K-1]": 385.0, + "Positive current collector thermal conductivity [W.m-1.K-1]": 401.0, + "Nominal cell capacity [A.h]": 5.0, + "Current function [A]": 5.0, + "Contact resistance [Ohm]": 0, + # positive electrode + "Positive electrode conductivity [S.m-1]": 215.0, + "Primary: Maximum concentration in positive electrode [mol.m-3]": 28700.0, + "Primary: Initial concentration in positive electrode [mol.m-3]": 27700.0, + "Primary: Positive electrode diffusivity [m2.s-1]": 5.5e-14, + "Primary: Positive electrode OCP [V]": graphite_ocp_Enertech_Ai2020, + "Negative electrode porosity": 0.25, + "Primary: Positive electrode active material volume fraction": 0.735, + "Primary: Positive particle radius [m]": 5.86e-06, + "Positive electrode Bruggeman coefficient (electrolyte)": 1.5, + "Positive electrode Bruggeman coefficient (electrode)": 0, + "Positive electrode charge transfer coefficient": 0.5, + "Positive electrode double-layer capacity [F.m-2]": 0.2, + "Primary: Positive electrode exchange-current density [A.m-2]" + "": graphite_LGM50_electrolyte_exchange_current_density_Chen2020, + "Primary: Positive electrode density [kg.m-3]": 1657.0, + "Positive electrode specific heat capacity [J.kg-1.K-1]": 700.0, + "Positive electrode thermal conductivity [W.m-1.K-1]": 1.7, + "Primary: Positive electrode OCP entropic change [V.K-1]": 0.0, + "Secondary: Maximum concentration in positive electrode [mol.m-3]": 278000.0, + "Secondary: Initial concentration in positive electrode [mol.m-3]": 276610.0, + "Secondary: Positive electrode diffusivity [m2.s-1]": 1.67e-14, + "Secondary: Positive electrode lithiation OCP [V]" + "": silicon_ocp_lithiation_Mark2016, + "Secondary: Positive electrode delithiation OCP [V]" + "": silicon_ocp_delithiation_Mark2016, + "Secondary: Positive electrode active material volume fraction": 0.015, + "Secondary: Positive particle radius [m]": 1.52e-06, + "Secondary: Positive electrode exchange-current density [A.m-2]" + "": silicon_LGM50_electrolyte_exchange_current_density_Chen2020, + "Secondary: Positive electrode density [kg.m-3]": 2650.0, + "Secondary: Positive electrode OCP entropic change [V.K-1]": 0.0, + # separator + "Separator porosity": 0.47, + "Separator Bruggeman coefficient (electrolyte)": 1.5, + "Separator density [kg.m-3]": 397.0, + "Separator specific heat capacity [J.kg-1.K-1]": 700.0, + "Separator thermal conductivity [W.m-1.K-1]": 0.16, + # electrolyte + "Initial concentration in electrolyte [mol.m-3]": 1000.0, + "Cation transference number": 0.2594, + "Thermodynamic factor": 1.0, + "Electrolyte diffusivity [m2.s-1]": electrolyte_diffusivity_Nyman2008, + "Electrolyte conductivity [S.m-1]": electrolyte_conductivity_Nyman2008, + # experiment + "Reference temperature [K]": 298.15, + "Total heat transfer coefficient [W.m-2.K-1]": 10.0, + "Ambient temperature [K]": 298.15, + "Number of electrodes connected in parallel to make a cell": 1.0, + "Number of cells connected in series to make a battery": 1.0, + "Lower voltage cut-off [V]": 2.5, + "Upper voltage cut-off [V]": 4.2, + "Initial concentration in positive electrode [mol.m-3]": 29866.0, + "Initial temperature [K]": 298.15, + # citations + "citations": ["Chen2020", "Ai2022"], + } diff --git a/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py b/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py new file mode 100644 index 0000000000..d3180ac92b --- /dev/null +++ b/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py @@ -0,0 +1,396 @@ +import pybamm + + +def graphite_diffusivity_Ecker2015(sto, T): + """ + Graphite diffusivity as a function of stochiometry [1, 2, 3]. + + References + ---------- + .. [1] 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. + .. [2] 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. + .. [3] Richardson, Giles, et. al. "Generalised single particle models for + high-rate operation of graded lithium-ion electrodes: Systematic derivation + and validation." Electrochemica Acta 339 (2020): 135862 + + Parameters + ---------- + sto: :class:`pybamm.Symbol` + Electrode stochiometry + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + D_ref = 8.4e-13 * pybamm.exp(-11.3 * sto) + 8.2e-15 + E_D_s = 3.03e4 + arrhenius = pybamm.exp(-E_D_s / (pybamm.constants.R * T)) * pybamm.exp( + E_D_s / (pybamm.constants.R * 296) + ) + + return D_ref * arrhenius + + +def graphite_ocp_Ecker2015(sto): + """ + Graphite OCP as a function of stochiometry [1, 2, 3]. + + References + ---------- + .. [1] 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. + .. [2] 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. + .. [3] Richardson, Giles, et. al. "Generalised single particle models for + high-rate operation of graded lithium-ion electrodes: Systematic derivation + and validation." Electrochemica Acta 339 (2020): 135862 + + Parameters + ---------- + sto: :class:`pybamm.Symbol` + Electrode stochiometry + + Returns + ------- + :class:`pybamm.Symbol` + Open-circuit potential + """ + + # Graphite electrode from Ecker, Kabitz, Laresgoiti et al. + # Analytical fit (WebPlotDigitizer + gnuplot) + a = 0.716502 + b = 369.028 + c = 0.12193 + d = 35.6478 + e = 0.0530947 + g = 0.0169644 + h = 27.1365 + i = 0.312832 + j = 0.0199313 + k = 28.5697 + m = 0.614221 + n = 0.931153 + o = 36.328 + p = 1.10743 + q = 0.140031 + r = 0.0189193 + s = 21.1967 + t = 0.196176 + + u_eq = ( + a * pybamm.exp(-b * sto) + + c * pybamm.exp(-d * (sto - e)) + - r * pybamm.tanh(s * (sto - t)) + - g * pybamm.tanh(h * (sto - i)) + - j * pybamm.tanh(k * (sto - m)) + - n * pybamm.exp(o * (sto - p)) + + q + ) + + return u_eq + + +def graphite_electrolyte_exchange_current_density_Ecker2015(c_e, c_s_surf, c_s_max, T): + """ + Exchange-current density for Butler-Volmer reactions between graphite and LiPF6 in + EC:DMC. + + References + ---------- + .. [1] 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. + .. [2] 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. + .. [3] Richardson, Giles, et. al. "Generalised single particle models for + high-rate operation of graded lithium-ion electrodes: Systematic derivation + and validation." Electrochemica Acta 339 (2020): 135862 + + Parameters + ---------- + c_e : :class:`pybamm.Symbol` + Electrolyte concentration [mol.m-3] + c_s_surf : :class:`pybamm.Symbol` + Particle concentration [mol.m-3] + c_s_max : :class:`pybamm.Symbol` + Maximum particle concentration [mol.m-3] + T : :class:`pybamm.Symbol` + Temperature [K] + + Returns + ------- + :class:`pybamm.Symbol` + Exchange-current density [A.m-2] + """ + + k_ref = 1.11 * 1e-10 + + # multiply by Faraday's constant to get correct units + m_ref = ( + pybamm.constants.F * k_ref + ) # (A/m2)(m3/mol)**1.5 - includes ref concentrations + E_r = 53400 + + arrhenius = pybamm.exp(-E_r / (pybamm.constants.R * T)) * pybamm.exp( + E_r / (pybamm.constants.R * 296.15) + ) + + return ( + m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 + ) + + +def electrolyte_diffusivity_Ecker2015(c_e, T): + """ + Diffusivity of LiPF6 in EC:DMC as a function of ion concentration [1, 2, 3]. + + References + ---------- + .. [1] 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. + .. [2] 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. + .. [3] Richardson, Giles, et. al. "Generalised single particle models for + high-rate operation of graded lithium-ion electrodes: Systematic derivation + and validation." Electrochemica Acta 339 (2020): 135862 + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + # The diffusivity epends on the electrolyte conductivity + inputs = {"Electrolyte concentration [mol.m-3]": c_e, "Temperature [K]": T} + sigma_e = pybamm.FunctionParameter("Electrolyte conductivity [S.m-1]", inputs) + + D_c_e = ( + (pybamm.constants.k_b / (pybamm.constants.F * pybamm.constants.q_e)) + * sigma_e + * T + / c_e + ) + + return D_c_e + + +def electrolyte_conductivity_Ecker2015(c_e, T): + """ + Conductivity of LiPF6 in EC:DMC as a function of ion concentration [1, 2, 3]. + + References + ---------- + .. [1] 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. + .. [2] 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. + .. [3] Richardson, Giles, et. al. "Generalised single particle models for + high-rate operation of graded lithium-ion electrodes: Systematic derivation + and validation." Electrochemica Acta 339 (2020): 135862 + + Parameters + ---------- + c_e: :class:`pybamm.Symbol` + Dimensional electrolyte concentration + T: :class:`pybamm.Symbol` + Dimensional temperature + + Returns + ------- + :class:`pybamm.Symbol` + Solid diffusivity + """ + + # mol/m^3 to mol/l + cm = 1e-3 * c_e + + # value at T = 296K + sigma_e_296 = 0.2667 * cm**3 - 1.2983 * cm**2 + 1.7919 * cm + 0.1726 + + # add temperature dependence + E_k_e = 1.71e4 + C = 296 * pybamm.exp(E_k_e / (pybamm.constants.R * 296)) + sigma_e = C * sigma_e_296 * pybamm.exp(-E_k_e / (pybamm.constants.R * T)) / T + + return sigma_e + + +# Call dict via a function to avoid errors when editing in place +def get_parameter_values(): + """ + Parameters for a graphite half-cell based on a Kokam SLPB 75106100 cell, from 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. + + The tab placement parameters are taken from measurements in + + Hales, Alastair, et al. "The cell cooling coefficient: a standard to define heat + rejection from lithium-ion batteries." Journal of The Electrochemical Society + 166.12 (2019): A2383. + + The thermal material properties are for a 5 Ah power pouch cell by Kokam. The data + are extracted from + + Zhao, Y., et al. "Modeling the effects of thermal gradients induced by tab and + surface cooling on lithium ion cell performance."" Journal of The + Electrochemical Society, 165.13 (2018): A3169-A3178. + + Graphite electrode parameters + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + The fits to data for the electrode and electrolyte properties are those provided + by Dr. Simon O'Kane in the paper: + + Richardson, Giles, et. al. "Generalised single particle models for high-rate + operation of graded lithium-ion electrodes: Systematic derivation and + validation." Electrochemica Acta 339 (2020): 135862 + + SEI parameters are example parameters for SEI growth from the papers: + + + Ramadass, P., Haran, B., Gomadam, P. M., White, R., & Popov, B. N. (2004). + Development of first principles capacity fade model for Li-ion cells. Journal of + the Electrochemical Society, 151(2), A196-A203. + + Ploehn, H. J., Ramadass, P., & White, R. E. (2004). Solvent diffusion model for + aging of lithium-ion battery cells. Journal of The Electrochemical Society, + 151(3), A456-A462. + + Single, F., Latz, A., & Horstmann, B. (2018). Identifying the mechanism of + continued growth of the solid-electrolyte interphase. ChemSusChem, 11(12), + 1950-1955. + + Safari, M., Morcrette, M., Teyssot, A., & Delacour, C. (2009). Multimodal + Physics- Based Aging Model for Life Prediction of Li-Ion Batteries. Journal of + The Electrochemical Society, 156(3), + + 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. + + Note: this parameter set does not claim to be representative of the true parameter + values. Instead these are parameter values that were used to fit SEI models to + observed experimental data in the referenced papers. + """ + + return { + "chemistry": "lithium_ion", + # sei + "Ratio of lithium moles to SEI moles": 2.0, + "Inner SEI reaction proportion": 0.5, + "Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, + "Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, + "SEI reaction exchange current density [A.m-2]": 1.5e-07, + "SEI resistivity [Ohm.m]": 200000.0, + "Outer SEI solvent diffusivity [m2.s-1]": 2.5000000000000002e-22, + "Bulk solvent concentration [mol.m-3]": 2636.0, + "Inner SEI open-circuit potential [V]": 0.1, + "Outer SEI open-circuit potential [V]": 0.8, + "Inner SEI electron conductivity [S.m-1]": 8.95e-14, + "Inner SEI lithium interstitial diffusivity [m2.s-1]": 1e-20, + "Lithium interstitial reference concentration [mol.m-3]": 15.0, + "Initial inner SEI thickness [m]": 2.5e-09, + "Initial outer SEI thickness [m]": 2.5e-09, + "EC initial concentration in electrolyte [mol.m-3]": 4541.0, + "EC diffusivity [m2.s-1]": 2e-18, + "SEI kinetic rate constant [m.s-1]": 1e-12, + "SEI open-circuit potential [V]": 0.4, + "SEI growth activation energy [J.mol-1]": 0.0, + "Positive electrode reaction-driven LAM factor [m3.mol-1]": 0.0, + # cell + "Positive current collector thickness [m]": 1.4e-05, + "Positive electrode thickness [m]": 7.4e-05, + "Separator thickness [m]": 2e-05, + "Electrode height [m]": 0.101, + "Electrode width [m]": 0.085, + "Positive tab width [m]": 0.007, + "Positive tab centre y-coordinate [m]": 0.0045, + "Positive tab centre z-coordinate [m]": 0.101, + "Cell cooling surface area [m2]": 0.0172, + "Cell volume [m3]": 1.52e-06, + "Positive current collector conductivity [S.m-1]": 58411000.0, + "Positive current collector density [kg.m-3]": 8933.0, + "Positive current collector specific heat capacity [J.kg-1.K-1]": 385.0, + "Positive current collector thermal conductivity [W.m-1.K-1]": 398.0, + "Nominal cell capacity [A.h]": 0.15625, + "Current function [A]": 0.15652, + "Contact resistance [Ohm]": 0, + # positive electrode + "Positive electrode conductivity [S.m-1]": 14.0, + "Maximum concentration in positive electrode [mol.m-3]": 31920.0, + "Positive electrode diffusivity [m2.s-1]": graphite_diffusivity_Ecker2015, + "Positive electrode OCP [V]": graphite_ocp_Ecker2015, + "Positive electrode porosity": 0.329, + "Positive electrode active material volume fraction": 0.372403, + "Positive particle radius [m]": 1.37e-05, + "Positive electrode Bruggeman coefficient (electrolyte)": 1.6372789338386007, + "Positive electrode Bruggeman coefficient (electrode)": 0.0, + "Positive electrode exchange-current density [A.m-2]" + "": graphite_electrolyte_exchange_current_density_Ecker2015, + "Positive electrode density [kg.m-3]": 1555.0, + "Positive electrode specific heat capacity [J.kg-1.K-1]": 1437.0, + "Positive electrode thermal conductivity [W.m-1.K-1]": 1.58, + "Positive electrode OCP entropic change [V.K-1]": 0.0, + # separator + "Separator porosity": 0.508, + "Separator Bruggeman coefficient (electrolyte)": 1.9804586773134945, + "Separator density [kg.m-3]": 1017.0, + "Separator specific heat capacity [J.kg-1.K-1]": 1978.0, + "Separator thermal conductivity [W.m-1.K-1]": 0.34, + # electrolyte + "Initial concentration in electrolyte [mol.m-3]": 1000.0, + "Cation transference number": 0.26, + "Thermodynamic factor": 1.0, + "Electrolyte diffusivity [m2.s-1]": electrolyte_diffusivity_Ecker2015, + "Electrolyte conductivity [S.m-1]": electrolyte_conductivity_Ecker2015, + # experiment + "Reference temperature [K]": 296.15, + "Positive current collector surface heat transfer coefficient [W.m-2.K-1]" + "": 10.0, + "Positive tab heat transfer coefficient [W.m-2.K-1]": 10.0, + "Edge heat transfer coefficient [W.m-2.K-1]": 10.0, + "Total heat transfer coefficient [W.m-2.K-1]": 10.0, + "Ambient temperature [K]": 298.15, + "Number of electrodes connected in parallel to make a cell": 1.0, + "Number of cells connected in series to make a battery": 1.0, + "Lower voltage cut-off [V]": 2.5, + "Upper voltage cut-off [V]": 4.2, + "Initial concentration in positive electrode [mol.m-3]": 26120.05, + "Initial temperature [K]": 298.15, + # citations + "citations": [ + "Ecker2015i", + "Ecker2015ii", + "Zhao2018", + "Hales2019", + "Richardson2020", + ], + } diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 440a129bd2..1cbed0d11b 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -292,13 +292,19 @@ def __init__(self, extra_options): # The "SEI film resistance" option will still be overridden by extra_options if # provided - # Change the default for particle mechanics based on which SEI on cracks and LAM - # options are provided - # return "false" and "none" respectively if options not given + # Change the default for particle mechanics based on which half-cell, + # SEI on cracks and LAM options are provided + # return "false", "false" and "none" respectively if options not given + half_cell_option = extra_options.get("half-cell", "false") SEI_cracks_option = extra_options.get("SEI on cracks", "false") LAM_opt = extra_options.get("loss of active material", "none") - if SEI_cracks_option == "true": - if "stress-driven" in LAM_opt or "stress and reaction-driven" in LAM_opt: + if "true" in SEI_cracks_option: + if half_cell_option == "true": + default_options["particle mechanics"] = ( # is this right? + "none", + "swelling and cracking", + ) + elif "stress-driven" in LAM_opt or "stress and reaction-driven" in LAM_opt: default_options["particle mechanics"] = ( "swelling and cracking", "swelling only", @@ -342,6 +348,7 @@ def __init__(self, extra_options): default_options["surface form"] = "algebraic" # The "surface form" option will still be overridden by # extra_options if provided + # Change default SEI model based on which lithium plating option is provided # return "none" if option not given plating_option = extra_options.get("lithium plating", "none") @@ -541,12 +548,15 @@ def __init__(self, extra_options): in [ "intercalation kinetics", "interface utilisation", + "lithium plating", "loss of active material", "open-circuit potential", "particle", "particle mechanics", "particle phases", "particle size", + "SEI", + "SEI on cracks", "stress-induced diffusion", ] and isinstance(value, tuple) 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 ecc173f97e..c0b5d1935c 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 @@ -97,10 +97,16 @@ def set_active_material_submodel(self): ) def set_sei_submodel(self): - self.submodels["sei"] = pybamm.sei.NoSEI(self.param, self.options) + for domain in ["negative", "positive"]: + self.submodels[f"{domain} sei"] = pybamm.sei.NoSEI( + self.param, domain, self.options + ) def set_lithium_plating_submodel(self): - self.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating(self.param) + for domain in ["negative", "positive"]: + self.submodels[ + f"{domain} lithium plating" + ] = pybamm.lithium_plating.NoPlating(self.param, domain) def set_total_interface_submodel(self): self.submodels["total interface"] = pybamm.interface.TotalInterfacialCurrent( 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 cc615dacf7..22bae5510e 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 @@ -159,13 +159,16 @@ def set_degradation_variables(self): # Lithium lost to side reactions # Different way of measuring LLI but should give same value - n_Li_lost_sei = self.variables["Loss of lithium to SEI [mol]"] - n_Li_lost_reactions = n_Li_lost_sei - if "negative electrode" in domains: + n_Li_lost_neg_sei = self.variables["Loss of lithium to negative SEI [mol]"] + n_Li_lost_pos_sei = self.variables["Loss of lithium to positive SEI [mol]"] + n_Li_lost_reactions = n_Li_lost_neg_sei + n_Li_lost_pos_sei + for domain in domains: n_Li_lost_sei_cracks = self.variables[ - "Loss of lithium to SEI on cracks [mol]" + f"Loss of lithium to {domain} SEI on cracks [mol]" + ] + n_Li_lost_pl = self.variables[ + f"Loss of lithium to {domain} lithium plating [mol]" ] - n_Li_lost_pl = self.variables["Loss of lithium to lithium plating [mol]"] n_Li_lost_reactions += n_Li_lost_sei_cracks + n_Li_lost_pl self.variables.update( @@ -197,8 +200,10 @@ def set_summary_variables(self): "Total lithium lost [mol]", "Total lithium lost from particles [mol]", "Total lithium lost from electrolyte [mol]", - "Loss of lithium to SEI [mol]", - "Loss of capacity to SEI [A.h]", + "Loss of lithium to negative SEI [mol]", + "Loss of capacity to negative SEI [A.h]", + "Loss of lithium to positive SEI [mol]", + "Loss of capacity to positive SEI [A.h]", "Total lithium lost to side reactions [mol]", "Total capacity lost to side reactions [A.h]", # Resistance @@ -210,16 +215,20 @@ def set_summary_variables(self): "Negative electrode capacity [A.h]", "Loss of active material in negative electrode [%]", "Total lithium in negative electrode [mol]", - "Loss of lithium to lithium plating [mol]", - "Loss of capacity to lithium plating [A.h]", - "Loss of lithium to SEI on cracks [mol]", - "Loss of capacity to SEI on cracks [A.h]", + "Loss of lithium to negative lithium plating [mol]", + "Loss of capacity to negative lithium plating [A.h]", + "Loss of lithium to negative SEI on cracks [mol]", + "Loss of capacity to negative SEI on cracks [A.h]", ] if self.options.electrode_types["positive"] == "porous": summary_variables += [ "Positive electrode capacity [A.h]", "Loss of active material in positive electrode [%]", "Total lithium in positive electrode [mol]", + "Loss of lithium to positive lithium plating [mol]", + "Loss of capacity to positive lithium plating [A.h]", + "Loss of lithium to positive SEI on cracks [mol]", + "Loss of capacity to positive SEI on cracks [A.h]", ] self.summary_variables = summary_variables @@ -243,56 +252,75 @@ def set_open_circuit_potential_submodel(self): ) def set_sei_submodel(self): - if self.options.electrode_types["negative"] == "planar": - reaction_loc = "interface" - elif self.options["x-average side reactions"] == "true": - reaction_loc = "x-average" - else: - reaction_loc = "full electrode" - - 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) + for domain in ["negative", "positive"]: + if self.options.electrode_types[domain] == "planar": + reaction_loc = "interface" + elif self.options["x-average side reactions"] == "true": + reaction_loc = "x-average" else: - 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 + reaction_loc = "full electrode" + + phases = self.options.phases[domain] + for phase in phases: + if self.options["SEI"] == "none": + submodel = pybamm.sei.NoSEI(self.param, domain, self.options, phase) + elif self.options["SEI"] == "constant": + submodel = pybamm.sei.ConstantSEI( + self.param, domain, self.options, phase ) else: submodel = pybamm.sei.SEIGrowth( - self.param, reaction_loc, self.options, phase, cracks=True + self.param, + domain, + reaction_loc, + self.options, + phase, + cracks=False, ) - self.submodels[f"{phase} sei on cracks"] = submodel + self.submodels[f"{domain} {phase} sei"] = submodel + # Do not set "sei on cracks" submodel for a planar electrode. For porous + # electrodes, "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, domain, self.options, phase, cracks=True + ) + else: + submodel = pybamm.sei.SEIGrowth( + self.param, + domain, + reaction_loc, + self.options, + phase, + cracks=True, + ) + self.submodels[f"{domain} {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 - ) + if len(phases) > 1: + self.submodels[f"{domain} total sei"] = pybamm.sei.TotalSEI( + self.param, self.options + ) + self.submodels[f"{domain} 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": - self.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating( - self.param, self.options - ) - else: - x_average = self.options["x-average side reactions"] == "true" - self.submodels["lithium plating"] = pybamm.lithium_plating.Plating( - self.param, x_average, self.options - ) + domains = [d for d in self.options.whole_cell_domains if d != "separator"] + for domain in domains: + if self.options["lithium plating"] == "none": + self.submodels[ + f"{domain} lithium plating" + ] = pybamm.lithium_plating.NoPlating(self.param, domain, self.options) + else: + x_average = self.options["x-average side reactions"] == "true" + self.submodels[ + f"{domain} lithium plating" + ] = pybamm.lithium_plating.Plating( + self.param, domain, x_average, self.options + ) def set_total_interface_submodel(self): self.submodels["total interface"] = pybamm.interface.TotalInterfacialCurrent( diff --git a/pybamm/models/full_battery_models/lithium_metal/dfn.py b/pybamm/models/full_battery_models/lithium_metal/dfn.py index eb11345c90..923be6fbe9 100644 --- a/pybamm/models/full_battery_models/lithium_metal/dfn.py +++ b/pybamm/models/full_battery_models/lithium_metal/dfn.py @@ -29,7 +29,7 @@ def __init__( self, options=None, name="Doyle-Fuller-Newman lithium metal model", build=True ): options = options or {} - options["half-cell"] = "positive" + options["half-cell"] = "true" super().__init__(options, name, build=False) if build: diff --git a/pybamm/models/submodels/electrode/ohm/li_metal.py b/pybamm/models/submodels/electrode/ohm/li_metal.py index e03ed770ad..31cdc7284e 100644 --- a/pybamm/models/submodels/electrode/ohm/li_metal.py +++ b/pybamm/models/submodels/electrode/ohm/li_metal.py @@ -76,9 +76,10 @@ def set_initial_conditions(self, variables): self.initial_conditions = {delta_phi: delta_phi_init} def set_rhs(self, variables): + Domain = self.domain.capitalize() if self.options["surface form"] == "differential": j_pl = variables["Lithium metal plating current density [A.m-2]"] - j_sei = variables["SEI interfacial current density [A.m-2]"] + j_sei = variables[f"{Domain} SEI interfacial current density [A.m-2]"] sum_j = j_pl + j_sei i_cc = variables["Current collector current density [A.m-2]"] @@ -91,9 +92,10 @@ def set_rhs(self, variables): self.rhs[delta_phi] = 1 / C_dl * (i_cc - sum_j) def set_algebraic(self, variables): + Domain = self.domain.capitalize() if self.options["surface form"] != "differential": # also catches "false" j_pl = variables["Lithium metal plating current density [A.m-2]"] - j_sei = variables["SEI interfacial current density [A.m-2]"] + j_sei = variables[f"{Domain} SEI interfacial current density [A.m-2]"] sum_j = j_pl + j_sei i_cc = variables["Current collector current density [A.m-2]"] diff --git a/pybamm/models/submodels/interface/lithium_plating/base_plating.py b/pybamm/models/submodels/interface/lithium_plating/base_plating.py index 6d848b6b70..07cf25e302 100644 --- a/pybamm/models/submodels/interface/lithium_plating/base_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/base_plating.py @@ -26,56 +26,44 @@ class BasePlating(BaseInterface): how to model it". Physical Chemistry: Chemical Physics, 24:7909, 2022 """ - def __init__(self, param, options=None): + def __init__(self, domain, param, options=None): reaction = "lithium plating" - domain = "negative" super().__init__(param, domain, reaction, options=options) def get_coupled_variables(self, variables): # Update some common variables + domain, Domain = self.domain_Domain - if self.options.electrode_types["negative"] == "porous": - j_plating = variables["Lithium plating interfacial current density [A.m-2]"] + if self.options.electrode_types[domain] == "porous": + j_plating = variables[ + f"{Domain} lithium plating interfacial current density [A.m-2]" + ] j_plating_av = variables[ - "X-averaged lithium plating interfacial current density [A.m-2]" + f"X-averaged {domain} lithium plating " + "interfacial current density [A.m-2]" ] if self.options.negative["particle phases"] == "1": - a = variables["Negative electrode surface area to volume ratio [m-1]"] + a = variables[f"{Domain} electrode surface area to volume ratio [m-1]"] else: a = variables[ - "Negative electrode primary surface area to volume ratio [m-1]" + f"{Domain} electrode primary surface area to volume ratio [m-1]" ] a_j_plating = a * j_plating a_j_plating_av = pybamm.x_average(a_j_plating) variables.update( { - "Negative electrode lithium plating interfacial current " + f"{Domain} electrode lithium plating interfacial current " "density [A.m-2]": j_plating, - "X-averaged negative electrode lithium plating " + f"X-averaged {domain} electrode lithium plating " "interfacial current density [A.m-2]": j_plating_av, - "Lithium plating volumetric " + f"{Domain} lithium plating volumetric " "interfacial current density [A.m-3]": a_j_plating, - "X-averaged lithium plating volumetric " + f"X-averaged {domain} lithium plating volumetric " "interfacial current density [A.m-3]": a_j_plating_av, } ) - zero_av = pybamm.PrimaryBroadcast(0, "current collector") - zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") - variables.update( - { - "X-averaged positive electrode lithium plating " - "interfacial current density [A.m-2]": zero_av, - "X-averaged positive electrode lithium plating volumetric " - "interfacial current density [A.m-3]": zero_av, - "Positive electrode lithium plating " - "interfacial current density [A.m-2]": zero, - "Positive electrode lithium plating volumetric " - "interfacial current density [A.m-3]": zero, - } - ) - variables.update( self._get_standard_volumetric_current_density_variables(variables) ) @@ -96,6 +84,7 @@ def _get_standard_concentration_variables(self, c_plated_Li, c_dead_Li): The variables which can be derived from the plated lithium thickness. """ param = self.param + domain, Domain = self.domain_Domain # Set scales to one for the "no plating" model so that they are not required # by parameter values in general @@ -116,18 +105,19 @@ def _get_standard_concentration_variables(self, c_plated_Li, c_dead_Li): Q_dead_Li = c_dead_Li_av * param.n.L * param.L_y * param.L_z variables = { - "Lithium plating concentration [mol.m-3]": c_plated_Li, - "X-averaged lithium plating concentration [mol.m-3]": c_plated_Li_av, - "Dead lithium concentration [mol.m-3]": c_dead_Li, - "X-averaged dead lithium concentration [mol.m-3]": c_dead_Li_av, - "Lithium plating thickness [m]": L_plated_Li, - "X-averaged lithium plating thickness [m]": L_plated_Li_av, - "Dead lithium thickness [m]": L_dead_Li, - "X-averaged dead lithium thickness [m]": L_dead_Li_av, - "Loss of lithium to lithium plating [mol]": (Q_plated_Li + Q_dead_Li), - "Loss of capacity to lithium plating [A.h]": (Q_plated_Li + Q_dead_Li) - * param.F - / 3600, + f"{Domain} lithium plating concentration [mol.m-3]": c_plated_Li, + f"X-averaged {domain} lithium plating " + "concentration [mol.m-3]": c_plated_Li_av, + f"{Domain} dead lithium concentration [mol.m-3]": c_dead_Li, + f"X-averaged {domain} dead lithium concentration [mol.m-3]": c_dead_Li_av, + f"{Domain} lithium plating thickness [m]": L_plated_Li, + f"X-averaged {domain} lithium plating thickness [m]": L_plated_Li_av, + f"{Domain} Dead lithium thickness [m]": L_dead_Li, + f"X-averaged {domain} dead lithium thickness [m]": L_dead_Li_av, + f"Loss of lithium to {domain} lithium plating " + "[mol]": (Q_plated_Li + Q_dead_Li), + f"Loss of capacity to {domain} lithium plating " + "[A.h]": (Q_plated_Li + Q_dead_Li) * param.F / 3600, } return variables @@ -145,13 +135,13 @@ def _get_standard_reaction_variables(self, j_stripping): variables : dict The variables which can be derived from the plated lithium thickness. """ - # Set scales to one for the "no plating" model so that they are not required - # by parameter values in general + domain, Domain = self.domain_Domain j_stripping_av = pybamm.x_average(j_stripping) variables = { - "Lithium plating interfacial current density [A.m-2]": j_stripping, - "X-averaged lithium plating " + f"{Domain} lithium plating interfacial current density " + "[A.m-2]": j_stripping, + f"X-averaged {domain} lithium plating " "interfacial current density [A.m-2]": j_stripping_av, } diff --git a/pybamm/models/submodels/interface/lithium_plating/no_plating.py b/pybamm/models/submodels/interface/lithium_plating/no_plating.py index 1a2f59808a..ad7fd7301a 100644 --- a/pybamm/models/submodels/interface/lithium_plating/no_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/no_plating.py @@ -16,12 +16,12 @@ class NoPlating(BasePlating): A dictionary of options to be passed to the model. """ - def __init__(self, param, options=None): - super().__init__(param, options=options) + def __init__(self, domain, param, options=None): + super().__init__(param, domain, options=options) def get_fundamental_variables(self): zero = pybamm.FullBroadcast( - pybamm.Scalar(0), "negative electrode", "current collector" + pybamm.Scalar(0), f"{self.domain} electrode", "current collector" ) variables = self._get_standard_concentration_variables(zero, zero) variables.update(self._get_standard_overpotential_variables(zero)) diff --git a/pybamm/models/submodels/interface/lithium_plating/plating.py b/pybamm/models/submodels/interface/lithium_plating/plating.py index c519115fc8..1ce898a737 100644 --- a/pybamm/models/submodels/interface/lithium_plating/plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/plating.py @@ -28,35 +28,36 @@ class Plating(BasePlating): how to model it". Physical Chemistry: Chemical Physics, 24:7909, 2022 """ - def __init__(self, param, x_average, options): - super().__init__(param, options) + def __init__(self, param, domain, x_average, options): + super().__init__(param, domain, options=options) self.x_average = x_average pybamm.citations.register("OKane2020") pybamm.citations.register("OKane2022") def get_fundamental_variables(self): + domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li_av = pybamm.Variable( - "X-averaged lithium plating concentration [mol.m-3]", + f"X-averaged {domain} lithium plating concentration [mol.m-3]", domain="current collector", scale=self.param.c_Li_typ, ) - c_plated_Li = pybamm.PrimaryBroadcast(c_plated_Li_av, "negative electrode") + c_plated_Li = pybamm.PrimaryBroadcast(c_plated_Li_av, f"{domain} electrode") c_dead_Li_av = pybamm.Variable( - "X-averaged dead lithium concentration [mol.m-3]", + f"X-averaged {domain} dead lithium concentration [mol.m-3]", domain="current collector", ) - c_dead_Li = pybamm.PrimaryBroadcast(c_dead_Li_av, "negative electrode") + c_dead_Li = pybamm.PrimaryBroadcast(c_dead_Li_av, f"{domain} electrode") else: c_plated_Li = pybamm.Variable( - "Lithium plating concentration [mol.m-3]", - domain="negative electrode", + f"{Domain} lithium plating concentration [mol.m-3]", + domain=f"{domain} electrode", auxiliary_domains={"secondary": "current collector"}, scale=self.param.c_Li_typ, ) c_dead_Li = pybamm.Variable( - "Dead lithium concentration [mol.m-3]", - domain="negative electrode", + f"{Domain} dead lithium concentration [mol.m-3]", + domain=f"{domain} electrode", auxiliary_domains={"secondary": "current collector"}, ) @@ -66,11 +67,12 @@ def get_fundamental_variables(self): def get_coupled_variables(self, variables): param = self.param - delta_phi = variables["Negative electrode surface potential difference [V]"] - c_e_n = variables["Negative electrolyte concentration [mol.m-3]"] - T = variables["Negative electrode temperature [K]"] - eta_sei = variables["SEI film overpotential [V]"] - c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] + Domain = self.domain.capitalize() + delta_phi = variables[f"{Domain} electrode surface potential difference [V]"] + c_e_n = variables[f"{Domain} electrolyte concentration [mol.m-3]"] + T = variables[f"{Domain} electrode temperature [K]"] + eta_sei = variables[f"{Domain} SEI film overpotential [V]"] + c_plated_Li = variables[f" {Domain} lithium plating concentration [mol.m-3]"] j0_stripping = param.j0_stripping(c_e_n, c_plated_Li, T) j0_plating = param.j0_plating(c_e_n, c_plated_Li, T) @@ -99,23 +101,27 @@ def get_coupled_variables(self, variables): return variables def set_rhs(self, variables): + domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li = variables[ - "X-averaged lithium plating concentration [mol.m-3]" + f"X-averaged {domain} lithium plating concentration [mol.m-3]" + ] + c_dead_Li = variables[ + f"X-averaged {domain} dead lithium concentration [mol.m-3]" ] - c_dead_Li = variables["X-averaged dead lithium concentration [mol.m-3]"] a_j_stripping = variables[ - "X-averaged lithium plating volumetric " + f"X-averaged {domain} lithium plating volumetric " "interfacial current density [A.m-3]" ] - L_sei = variables["X-averaged total SEI thickness [m]"] + L_sei = variables[f"X-averaged {domain} total SEI thickness [m]"] else: - c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] - c_dead_Li = variables["Dead lithium concentration [mol.m-3]"] + c_plated_Li = variables[f"{Domain} lithium plating concentration [mol.m-3]"] + c_dead_Li = variables[f"{Domain} dead lithium concentration [mol.m-3]"] a_j_stripping = variables[ - "Lithium plating volumetric interfacial current density [A.m-3]" + f"{Domain} lithium plating volumetric " + "interfacial current density [A.m-3]" ] - L_sei = variables["Total SEI thickness [m]"] + L_sei = variables[f"{Domain} total SEI thickness [m]"] # In the partially reversible plating model, coupling term turns reversible # lithium into dead lithium. In other plating models, it is zero. @@ -131,14 +137,17 @@ def set_rhs(self, variables): } def set_initial_conditions(self, variables): + domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li = variables[ - "X-averaged lithium plating concentration [mol.m-3]" + f"X-averaged {domain} lithium plating concentration [mol.m-3]" + ] + c_dead_Li = variables[ + f"X-averaged {domain} dead lithium concentration [mol.m-3]" ] - c_dead_Li = variables["X-averaged dead lithium concentration [mol.m-3]"] else: - c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] - c_dead_Li = variables["Dead lithium concentration [mol.m-3]"] + c_plated_Li = variables[f"{Domain} lithium plating concentration [mol.m-3]"] + c_dead_Li = variables[f"{domain} dead lithium concentration [mol.m-3]"] c_plated_Li_0 = self.param.c_plated_Li_0 zero = pybamm.Scalar(0) diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 6b08214ff4..9c57721d99 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -20,53 +20,37 @@ class BaseModel(BaseInterface): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__(self, param, options, phase="primary", cracks=False): + def __init__(self, param, domain, 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, phase=phase) def get_coupled_variables(self, variables): # Update some common variables - zero_av = pybamm.PrimaryBroadcast(0, "current collector") - zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") + domain, Domain = self.domain_Domain if self.reaction_loc != "interface": + j_sei_av = variables[ + f"X-averaged {domain} electrode {self.reaction_name}interfacial" + " current density [A.m-2]" + ] + j_sei = variables[ + f"{Domain} electrode {self.reaction_name}interfacial current" + " density [A.m-2]" + ] variables.update( { - f"X-averaged negative electrode {self.reaction_name}interfacial " - "current density": variables[ - f"X-averaged {self.reaction_name}interfacial current density" - ], - f"Negative electrode {self.reaction_name}interfacial current " - "density": variables[ - f"{self.reaction_name}interfacial current density" - ], - f"Negative electrode {self.reaction_name}interfacial current " - "density [A.m-2]": variables[ - f"{self.reaction_name}interfacial current density [A.m-2]" - ], + f"X-averaged {domain} electrode {self.reaction_name}interfacial " + "current density [A.m-2]": j_sei_av, + f"{Domain} electrode {self.reaction_name}interfacial current " + "density [A.m-2]": j_sei, } ) - variables.update( - self._get_standard_volumetric_current_density_variables(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, - } + self._get_standard_volumetric_current_density_variables(variables) ) return variables @@ -88,9 +72,10 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): variables : dict The variables which can be derived from the SEI thicknesses. """ + domain, Domain = self.domain_Domain variables = { - f"Inner {self.reaction_name}thickness [m]": L_inner, - f"Outer {self.reaction_name}thickness [m]": L_outer, + f"{Domain} inner {self.reaction_name}thickness [m]": L_inner, + f"{Domain} outer {self.reaction_name}thickness [m]": L_outer, } if self.reaction_loc != "interface": @@ -98,8 +83,10 @@ 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_name}thickness [m]": L_inner_av, - f"X-averaged outer {self.reaction_name}thickness [m]": L_outer_av, + f"X-averaged {domain} inner {self.reaction_name}" + "thickness [m]": L_inner_av, + f"X-averaged {domain} outer {self.reaction_name}" + "thickness [m]": L_outer_av, } ) # Get variables related to the total thickness @@ -110,7 +97,7 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): def _get_standard_total_thickness_variables(self, L_sei): """Update variables related to total SEI thickness.""" - domain = self.domain + domain, Domain = self.domain_Domain if isinstance(self, pybamm.sei.NoSEI): R_sei = 1 @@ -118,15 +105,16 @@ def _get_standard_total_thickness_variables(self, L_sei): R_sei = self.phase_param.R_sei variables = { - f"{self.reaction_name}[m]": L_sei, - f"Total {self.reaction_name}thickness [m]": L_sei, + f"{Domain} {self.reaction_name}[m]": L_sei, + f"Total {domain} {self.reaction_name}thickness [m]": L_sei, } if self.reaction_loc != "interface": L_sei_av = pybamm.x_average(L_sei) variables.update( { - f"X-averaged {self.reaction_name}thickness [m]": L_sei_av, - f"X-averaged total {self.reaction_name}thickness [m]": L_sei_av, + f"X-averaged {domain} {self.reaction_name}thickness [m]": L_sei_av, + f"X-averaged total {domain} {self.reaction_name}" + "thickness [m]": L_sei_av, } ) if self.reaction == "SEI": @@ -140,7 +128,7 @@ def _get_standard_total_thickness_variables(self, L_sei): def _get_standard_concentration_variables(self, variables): """Update variables related to the SEI concentration.""" - Domain = self.domain.capitalize() + domain, Domain = self.domain_Domain phase_param = self.phase_param reaction_name = self.reaction_name @@ -180,8 +168,8 @@ def _get_standard_concentration_variables(self, variables): ) if self.reaction == "SEI": - L_inner = variables[f"Inner {reaction_name}thickness [m]"] - L_outer = variables[f"Outer {reaction_name}thickness [m]"] + L_inner = variables[f"{Domain} inner {reaction_name}thickness [m]"] + L_outer = variables[f"{Domain} outer {reaction_name}thickness [m]"] n_inner = L_inner * L_to_n_inner # inner SEI concentration n_outer = L_outer * L_to_n_outer # outer SEI concentration @@ -209,24 +197,25 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - f"Inner {reaction_name}concentration [mol.m-3]": n_inner, - f"X-averaged inner {reaction_name}" + f"{Domain} inner {reaction_name}concentration [mol.m-3]": n_inner, + f"X-averaged {domain} inner {reaction_name}" "concentration [mol.m-3]": n_inner_av, - f"Outer {reaction_name}concentration [mol.m-3]": n_outer, - f"X-averaged outer {reaction_name}" + f"{Domain} outer {reaction_name}concentration [mol.m-3]": n_outer, + f"X-averaged {domain} outer {reaction_name}" "concentration [mol.m-3]": n_outer_av, - f"{reaction_name}concentration [mol.m-3]": n_SEI, - f"X-averaged {reaction_name}concentration [mol.m-3]": n_SEI_xav, - f"Loss of lithium to {reaction_name}[mol]": Q_sei, - f"Loss of capacity to {reaction_name}[A.h]": Q_sei + f"{Domain} {reaction_name}concentration [mol.m-3]": n_SEI, + f"X-averaged {domain} {reaction_name}" + "concentration [mol.m-3]": n_SEI_xav, + f"Loss of lithium to {domain} {reaction_name}[mol]": Q_sei, + f"Loss of capacity to {domain} {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[f"Inner {reaction_name}thickness [m]"] - L_outer_cr = variables[f"Outer {reaction_name}thickness [m]"] + L_inner_cr = variables[f"{Domain} inner {reaction_name}thickness [m]"] + L_outer_cr = variables[f"{Domain} outer {reaction_name}thickness [m]"] roughness = variables[f"{Domain} electrode roughness ratio"] n_inner_cr = L_inner_cr * L_to_n_inner * (roughness - 1) @@ -258,17 +247,19 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - f"Inner {reaction_name}" "concentration [mol.m-3]": n_inner_cr, - f"X-averaged inner {reaction_name}" + f"{Domain} inner {reaction_name}" + "concentration [mol.m-3]": n_inner_cr, + f"X-averaged {domain} inner {reaction_name}" "concentration [mol.m-3]": n_inner_cr_av, - f"Outer {reaction_name}concentration [mol.m-3]": n_outer_cr, - f"X-averaged outer {reaction_name}" + f"{Domain} outer {reaction_name}" + "concentration [mol.m-3]": n_outer_cr, + f"X-averaged {domain} outer {reaction_name}" "concentration [mol.m-3]": n_outer_cr_av, - f"{reaction_name}" "concentration [mol.m-3]": n_SEI_cr, - f"X-averaged {reaction_name}" + f"{Domain} {reaction_name}" "concentration [mol.m-3]": n_SEI_cr, + f"X-averaged {domain} {reaction_name}" "concentration [mol.m-3]": n_SEI_cr_xav, - f"Loss of lithium to {reaction_name}[mol]": Q_sei_cr, - f"Loss of capacity to {reaction_name}[A.h]": Q_sei_cr + f"Loss of lithium to {domain} {reaction_name}[mol]": Q_sei_cr, + f"Loss of capacity to {domain} {reaction_name}[A.h]": Q_sei_cr * self.param.F / 3600, } @@ -293,25 +284,28 @@ def _get_standard_reaction_variables(self, j_inner, j_outer): variables : dict The variables which can be derived from the SEI currents. """ + domain, Domain = self.domain_Domain j_inner_av = pybamm.x_average(j_inner) j_outer_av = pybamm.x_average(j_outer) j_sei = j_inner + j_outer variables = { - f"Inner {self.reaction_name}interfacial current density [A.m-2]": j_inner, - f"X-averaged inner {self.reaction_name}" + f"{Domain} inner {self.reaction_name}" + "interfacial currentdensity [A.m-2]": j_inner, + f"X-averaged {domain} inner {self.reaction_name}" "interfacial current density [A.m-2]": j_inner_av, - f"Outer {self.reaction_name}interfacial current density [A.m-2]": j_outer, - f"X-averaged outer {self.reaction_name}" + f"{Domain} outer {self.reaction_name}" + "interfacial current density [A.m-2]": j_outer, + f"X-averaged {domain} outer {self.reaction_name}" "interfacial current density [A.m-2]": j_outer_av, - f"{self.reaction_name}interfacial current density [A.m-2]": j_sei, + f"{Domain} {self.reaction_name}interfacial current density [A.m-2]": j_sei, } if self.reaction_loc != "interface": j_sei_av = pybamm.x_average(j_sei) variables.update( { - f"X-averaged {self.reaction_name}" + f"X-averaged {domain} {self.reaction_name}" "interfacial current density [A.m-2]": j_sei_av, } ) diff --git a/pybamm/models/submodels/interface/sei/constant_sei.py b/pybamm/models/submodels/interface/sei/constant_sei.py index becaa511da..e6d9a58edd 100644 --- a/pybamm/models/submodels/interface/sei/constant_sei.py +++ b/pybamm/models/submodels/interface/sei/constant_sei.py @@ -23,8 +23,8 @@ class ConstantSEI(BaseModel): Phase of the particle (default is "primary") """ - def __init__(self, param, options, phase="primary"): - super().__init__(param, options=options, phase=phase) + def __init__(self, param, domain, options, phase="primary"): + super().__init__(param, domain, options=options, phase=phase) if self.options.electrode_types["negative"] == "planar": self.reaction_loc = "interface" else: diff --git a/pybamm/models/submodels/interface/sei/no_sei.py b/pybamm/models/submodels/interface/sei/no_sei.py index 463b58bac4..590af4b376 100644 --- a/pybamm/models/submodels/interface/sei/no_sei.py +++ b/pybamm/models/submodels/interface/sei/no_sei.py @@ -21,8 +21,8 @@ class NoSEI(BaseModel): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__(self, param, options, phase="primary", cracks=False): - super().__init__(param, options=options, phase=phase, cracks=cracks) + def __init__(self, param, domain, options, phase="primary", cracks=False): + super().__init__(param, domain, options=options, phase=phase, cracks=cracks) if self.options.electrode_types[self.domain] == "planar": 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 08ee2f06d2..c1607d56d2 100644 --- a/pybamm/models/submodels/interface/sei/sei_growth.py +++ b/pybamm/models/submodels/interface/sei/sei_growth.py @@ -32,8 +32,10 @@ class SEIGrowth(BaseModel): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__(self, param, reaction_loc, options, phase="primary", cracks=False): - super().__init__(param, options=options, phase=phase, cracks=cracks) + def __init__( + self, param, domain, reaction_loc, options, phase="primary", cracks=False + ): + super().__init__(param, domain, options=options, phase=phase, cracks=cracks) self.reaction_loc = reaction_loc if self.options["SEI"] == "ec reaction limited": pybamm.citations.register("Yang2017") @@ -41,28 +43,28 @@ def __init__(self, param, reaction_loc, options, phase="primary", cracks=False): pybamm.citations.register("Marquis2020") def get_fundamental_variables(self): + domain, Domain = self.domain_Domain Ls = [] for pos in ["inner", "outer"]: - Pos = pos.capitalize() scale = self.phase_param.L_sei_0 if self.reaction_loc == "x-average": L_av = pybamm.Variable( - f"X-averaged {pos} {self.reaction_name}thickness [m]", + f"X-averaged {domain} {pos} {self.reaction_name}thickness [m]", domain="current collector", scale=scale, ) L_av.print_name = f"L_{pos}_av" - L = pybamm.PrimaryBroadcast(L_av, "negative electrode") + L = pybamm.PrimaryBroadcast(L_av, f"{domain} electrode") elif self.reaction_loc == "full electrode": L = pybamm.Variable( - f"{Pos} {self.reaction_name}thickness [m]", - domain="negative electrode", + f"{Domain} {pos} {self.reaction_name}thickness [m]", + domain=f"{domain} electrode", auxiliary_domains={"secondary": "current collector"}, scale=scale, ) elif self.reaction_loc == "interface": L = pybamm.Variable( - f"{Pos} {self.reaction_name}thickness [m]", + f"{Domain} {pos} {self.reaction_name}thickness [m]", domain="current collector", scale=scale, ) @@ -81,34 +83,37 @@ def get_fundamental_variables(self): def get_coupled_variables(self, variables): param = self.param phase_param = self.phase_param + domain, Domain = self.domain_Domain + T = variables[f"{Domain} electrode temperature [K]"] # delta_phi = phi_s - phi_e - T = variables["Negative electrode temperature [K]"] if self.reaction_loc == "interface": delta_phi = variables[ "Lithium metal interface surface potential difference [V]" ] T = pybamm.boundary_value(T, "right") else: - delta_phi = variables["Negative electrode surface potential difference [V]"] + delta_phi = variables[ + f"{Domain} electrode surface potential difference [V]" + ] # 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 [A.m-2]" in variables: - j = variables["Negative electrode interfacial current density [A.m-2]"] + if f"{Domain} electrode interfacial current density [A.m-2]" in variables: + j = variables[f"{Domain} electrode interfacial current density [A.m-2]"] elif self.reaction_loc == "interface": j = variables["Lithium metal total interfacial current density [A.m-2]"] else: j = variables[ - "X-averaged negative electrode total " + f"X-averaged {domain} electrode total " "interfacial current density [A.m-2]" ] - L_sei_inner = variables[f"Inner {self.reaction_name}thickness [m]"] - L_sei_outer = variables[f"Outer {self.reaction_name}thickness [m]"] - L_sei = variables[f"Total {self.reaction_name}thickness [m]"] + L_sei_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] + L_sei_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] + L_sei = variables[f"Total {domain} {self.reaction_name}thickness [m]"] R_sei = phase_param.R_sei eta_SEI = delta_phi - phase_param.U_sei - j * L_sei * R_sei @@ -162,9 +167,9 @@ def get_coupled_variables(self, variables): c_ec_av = pybamm.x_average(c_ec) if self.reaction == "SEI on cracks": - name = "EC concentration on cracks [mol.m-3]" + name = f"{Domain} EC concentration on cracks [mol.m-3]" else: - name = "EC surface concentration [mol.m-3]" + name = f"{Domain} EC surface concentration [mol.m-3]" variables.update({name: c_ec, f"X-averaged {name}": c_ec_av}) if self.options["SEI"].startswith("ec reaction limited"): @@ -189,38 +194,45 @@ def get_coupled_variables(self, variables): def set_rhs(self, variables): phase_param = self.phase_param param = self.param + domain, Domain = self.domain_Domain if self.reaction_loc == "x-average": - L_inner = variables[f"X-averaged inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"X-averaged outer {self.reaction_name}thickness [m]"] + L_inner = variables[ + f"X-averaged {domain} inner {self.reaction_name}thickness [m]" + ] + L_outer = variables[ + f"X-averaged {domain} outer {self.reaction_name}thickness [m]" + ] j_inner = variables[ - f"X-averaged inner {self.reaction_name}" + f"X-averaged {domain} inner {self.reaction_name}" "interfacial current density [A.m-2]" ] j_outer = variables[ - f"X-averaged outer {self.reaction_name}" + f"X-averaged {domain} outer {self.reaction_name}" "interfacial current density [A.m-2]" ] else: - L_inner = variables[f"Inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"Outer {self.reaction_name}thickness [m]"] + L_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] j_inner = variables[ - f"Inner {self.reaction_name}interfacial current density [A.m-2]" + f"{Domain} inner {self.reaction_name}" + "interfacial current density [A.m-2]" ] j_outer = variables[ - f"Outer {self.reaction_name}interfacial current density [A.m-2]" + f"{Domain} outer {self.reaction_name}" + "interfacial current density [A.m-2]" ] # 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. if self.reaction == "SEI on cracks": if self.reaction_loc == "x-average": - l_cr = variables["X-averaged negative particle crack length [m]"] - dl_cr = variables["X-averaged negative particle cracking rate [m.s-1]"] + l_cr = variables[f"X-averaged {domain} particle crack length [m]"] + dl_cr = variables[f"X-averaged {domain} particle cracking rate [m.s-1]"] else: - l_cr = variables["Negative particle crack length [m]"] - dl_cr = variables["Negative particle cracking rate [m.s-1]"] + l_cr = variables[f"{Domain} particle crack length [m]"] + dl_cr = variables[f"{Domain} particle cracking rate [m.s-1]"] spreading_outer = ( dl_cr / l_cr * (self.phase_param.L_outer_crack_0 - L_outer) ) @@ -254,12 +266,17 @@ def set_rhs(self, variables): } def set_initial_conditions(self, variables): + domain, Domain = self.domain_Domain if self.reaction_loc == "x-average": - L_inner = variables[f"X-averaged inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"X-averaged outer {self.reaction_name}thickness [m]"] + L_inner = variables[ + f"X-averaged {domain} inner {self.reaction_name}thickness [m]" + ] + L_outer = variables[ + f"X-averaged {domain} outer {self.reaction_name}thickness [m]" + ] else: - L_inner = variables[f"Inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"Outer {self.reaction_name}thickness [m]"] + L_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] if self.reaction == "SEI on cracks": L_inner_0 = self.phase_param.L_inner_crack_0 diff --git a/pybamm/models/submodels/interface/sei/total_sei.py b/pybamm/models/submodels/interface/sei/total_sei.py index a3ebffa6a6..2a017b94a0 100644 --- a/pybamm/models/submodels/interface/sei/total_sei.py +++ b/pybamm/models/submodels/interface/sei/total_sei.py @@ -19,24 +19,25 @@ class TotalSEI(pybamm.BaseSubModel): See :class:`pybamm.BaseBatteryModel` """ - def __init__(self, param, options, cracks=False): + def __init__(self, param, domain, options, cracks=False): if cracks is True: self.reaction = "SEI on cracks" else: self.reaction = "SEI" - super().__init__(param, options=options) + super().__init__(param, domain, options=options) def get_coupled_variables(self, variables): - phases = self.options.phases["negative"] + domain, Domain = self.domain_Domain + 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 for variable_template in [ - f"Negative electrode {{}}{self.reaction} volumetric " + f"{Domain} electrode {{}}{self.reaction} volumetric " "interfacial current density [A.m-3]", - f"X-averaged negative electrode {{}}{self.reaction} volumetric " + f"X-averaged {domain} electrode {{}}{self.reaction} volumetric " "interfacial current density [A.m-3]", - f"Loss of lithium to {{}}{self.reaction} [mol]", - f"Loss of capacity to {{}}{self.reaction} [A.h]", + f"Loss of lithium to {domain} {{}}{self.reaction} [mol]", + f"Loss of capacity to {domain} {{}}{self.reaction} [A.h]", ]: sumvar = sum( variables[variable_template.format(phase + " ")] for phase in phases diff --git a/setup.py b/setup.py index 9eeb7b6114..6d89b34cad 100644 --- a/setup.py +++ b/setup.py @@ -250,7 +250,9 @@ def compile_KLU(): "Ai2020 = pybamm.input.parameters.lithium_ion.Ai2020:get_parameter_values", # noqa: E501 "Chen2020 = pybamm.input.parameters.lithium_ion.Chen2020:get_parameter_values", # noqa: E501 "Chen2020_composite = pybamm.input.parameters.lithium_ion.Chen2020_composite:get_parameter_values", # noqa: E501 + "Chen2020_composite_halfcell = pybamm.input.parameters.lithium_ion.Chen2020_composite_halfcell:get_parameter_values", # noqa: E501 "Ecker2015 = pybamm.input.parameters.lithium_ion.Ecker2015:get_parameter_values", # noqa: E501 + "Ecker2015_graphite_halfcell = pybamm.input.parameters.lithium_ion.Ecker2015_graphite_halfcell:get_parameter_values", # noqa: E501 "Marquis2019 = pybamm.input.parameters.lithium_ion.Marquis2019:get_parameter_values", # noqa: E501 "Mohtat2020 = pybamm.input.parameters.lithium_ion.Mohtat2020:get_parameter_values", # noqa: E501 "NCA_Kim2011 = pybamm.input.parameters.lithium_ion.NCA_Kim2011:get_parameter_values", # noqa: E501 From 6be07adbeca0b062ac0c2d397373db87b07a961e Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Thu, 29 Jun 2023 13:42:16 +0100 Subject: [PATCH 3/7] Revert "Added graphite half-cell parameter files" This reverts commit 78001e81eecc38919364190940e095e0e51fab76. --- .../Chen2020_composite_halfcell.py | 377 ----------------- .../Ecker2015_graphite_halfcell.py | 396 ------------------ .../full_battery_models/base_battery_model.py | 20 +- .../lead_acid/base_lead_acid_model.py | 10 +- .../lithium_ion/base_lithium_ion_model.py | 134 +++--- .../full_battery_models/lithium_metal/dfn.py | 2 +- .../submodels/electrode/ohm/li_metal.py | 6 +- .../interface/lithium_plating/base_plating.py | 74 ++-- .../interface/lithium_plating/no_plating.py | 6 +- .../interface/lithium_plating/plating.py | 63 ++- .../submodels/interface/sei/base_sei.py | 128 +++--- .../submodels/interface/sei/constant_sei.py | 4 +- .../models/submodels/interface/sei/no_sei.py | 4 +- .../submodels/interface/sei/sei_growth.py | 85 ++-- .../submodels/interface/sei/total_sei.py | 15 +- setup.py | 2 - 16 files changed, 247 insertions(+), 1079 deletions(-) delete mode 100644 pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py delete mode 100644 pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py diff --git a/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py b/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py deleted file mode 100644 index 73d79da8a4..0000000000 --- a/pybamm/input/parameters/lithium_ion/Chen2020_composite_halfcell.py +++ /dev/null @@ -1,377 +0,0 @@ -import pybamm -import os - - -def graphite_LGM50_electrolyte_exchange_current_density_Chen2020( - c_e, c_s_surf, c_s_max, T -): - """ - Exchange-current density for Butler-Volmer reactions between graphite and LiPF6 in - EC:DMC. - - References - ---------- - .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. - Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for - Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the - Electrochemical Society 167 (2020): 080534. - - Parameters - ---------- - c_e : :class:`pybamm.Symbol` - Electrolyte concentration [mol.m-3] - c_s_surf : :class:`pybamm.Symbol` - Particle concentration [mol.m-3] - c_s_max : :class:`pybamm.Symbol` - Maximum particle concentration [mol.m-3] - T : :class:`pybamm.Symbol` - Temperature [K] - - Returns - ------- - :class:`pybamm.Symbol` - Exchange-current density [A.m-2] - """ - m_ref = 6.48e-7 # (A/m2)(m3/mol)**1.5 - includes ref concentrations - E_r = 35000 - arrhenius = pybamm.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) - - return ( - m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 - ) - - -def silicon_ocp_lithiation_Mark2016(sto): - """ - silicon Open-circuit Potential (OCP) as a a function of the - stochiometry. The fit is taken from the Enertech cell [1], which is only accurate - for 0 < sto < 1. - - References - ---------- - .. [1] Verbrugge M, Baker D, Xiao X. Formulation for the treatment of multiple - electrochemical reactions and associated speciation for the Lithium-Silicon - electrode[J]. Journal of The Electrochemical Society, 2015, 163(2): A262. - - Parameters - ---------- - sto: double - Stochiometry of material (li-fraction) - - Returns - ------- - :class:`pybamm.Symbol` - OCP [V] - """ - p1 = -96.63 - p2 = 372.6 - p3 = -587.6 - p4 = 489.9 - p5 = -232.8 - p6 = 62.99 - p7 = -9.286 - p8 = 0.8633 - - U_lithiation = ( - p1 * sto**7 - + p2 * sto**6 - + p3 * sto**5 - + p4 * sto**4 - + p5 * sto**3 - + p6 * sto**2 - + p7 * sto - + p8 - ) - return U_lithiation - - -def silicon_ocp_delithiation_Mark2016(sto): - """ - silicon Open-circuit Potential (OCP) as a a function of the - stochiometry. The fit is taken from the Enertech cell [1], which is only accurate - for 0 < sto < 1. - - References - ---------- - .. [1] Verbrugge M, Baker D, Xiao X. Formulation for the treatment of multiple - electrochemical reactions and associated speciation for the Lithium-Silicon - electrode[J]. Journal of The Electrochemical Society, 2015, 163(2): A262. - - Parameters - ---------- - sto: double - Stochiometry of material (li-fraction) - - Returns - ------- - :class:`pybamm.Symbol` - OCP [V] - """ - p1 = -51.02 - p2 = 161.3 - p3 = -205.7 - p4 = 140.2 - p5 = -58.76 - p6 = 16.87 - p7 = -3.792 - p8 = 0.9937 - - U_delithiation = ( - p1 * sto**7 - + p2 * sto**6 - + p3 * sto**5 - + p4 * sto**4 - + p5 * sto**3 - + p6 * sto**2 - + p7 * sto - + p8 - ) - return U_delithiation - - -def silicon_LGM50_electrolyte_exchange_current_density_Chen2020( - c_e, c_s_surf, c_s_max, T -): - """ - Exchange-current density for Butler-Volmer reactions between silicon and LiPF6 in - EC:DMC. - - References - ---------- - .. [1] Chang-Hui Chen, Ferran Brosa Planella, Kieran O’Regan, Dominika Gastol, W. - Dhammika Widanage, and Emma Kendrick. "Development of Experimental Techniques for - Parameterization of Multi-scale Lithium-ion Battery Models." Journal of the - Electrochemical Society 167 (2020): 080534. - - Parameters - ---------- - c_e : :class:`pybamm.Symbol` - Electrolyte concentration [mol.m-3] - c_s_surf : :class:`pybamm.Symbol` - Particle concentration [mol.m-3] - c_s_max : :class:`pybamm.Symbol` - Maximum particle concentration [mol.m-3] - T : :class:`pybamm.Symbol` - Temperature [K] - - Returns - ------- - :class:`pybamm.Symbol` - Exchange-current density [A.m-2] - """ - - m_ref = ( - 6.48e-7 * 28700 / 278000 - ) # (A/m2)(m3/mol)**1.5 - includes ref concentrations - E_r = 35000 - arrhenius = pybamm.exp(E_r / pybamm.constants.R * (1 / 298.15 - 1 / T)) - - return ( - m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 - ) - - -def electrolyte_diffusivity_Nyman2008(c_e, T): - """ - Diffusivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data - comes from [1] - - References - ---------- - .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and - modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," - Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. - - Parameters - ---------- - c_e: :class:`pybamm.Symbol` - Dimensional electrolyte concentration - T: :class:`pybamm.Symbol` - Dimensional temperature - - Returns - ------- - :class:`pybamm.Symbol` - Solid diffusivity - """ - - D_c_e = 8.794e-11 * (c_e / 1000) ** 2 - 3.972e-10 * (c_e / 1000) + 4.862e-10 - - # Nyman et al. (2008) does not provide temperature dependence - - return D_c_e - - -def electrolyte_conductivity_Nyman2008(c_e, T): - """ - Conductivity of LiPF6 in EC:EMC (3:7) as a function of ion concentration. The data - comes from [1]. - - References - ---------- - .. [1] A. Nyman, M. Behm, and G. Lindbergh, "Electrochemical characterisation and - modelling of the mass transport phenomena in LiPF6-EC-EMC electrolyte," - Electrochim. Acta, vol. 53, no. 22, pp. 6356–6365, 2008. - - Parameters - ---------- - c_e: :class:`pybamm.Symbol` - Dimensional electrolyte concentration - T: :class:`pybamm.Symbol` - Dimensional temperature - - Returns - ------- - :class:`pybamm.Symbol` - Solid diffusivity - """ - - sigma_e = ( - 0.1297 * (c_e / 1000) ** 3 - 2.51 * (c_e / 1000) ** 1.5 + 3.329 * (c_e / 1000) - ) - - # Nyman et al. (2008) does not provide temperature dependence - - return sigma_e - - -# Load data in the appropriate format -path, _ = os.path.split(os.path.abspath(__file__)) -graphite_ocp_Enertech_Ai2020_data = pybamm.parameters.process_1D_data( - "graphite_ocp_Enertech_Ai2020.csv", path=path -) - - -def graphite_ocp_Enertech_Ai2020(sto): - name, (x, y) = graphite_ocp_Enertech_Ai2020_data - return pybamm.Interpolant(x, y, sto, name=name, interpolator="cubic") - - -# Call dict via a function to avoid errors when editing in place -def get_parameter_values(): - """ - Parameters for a composite graphite/silicon electrode, from the paper - - Weilong Ai, Niall Kirkaldy, Yang Jiang, Gregory Offer, Huizhi Wang, and Billy - Wu. A composite electrode model for lithium-ion batteries with silicon/graphite - negative electrodes. Journal of Power Sources, 527:231142, 2022. URL: - https://www.sciencedirect.com/science/article/pii/S0378775322001604, - doi:https://doi.org/10.1016/j.jpowsour.2022.231142. - - based on the paper - - Chang-Hui Chen, Ferran Brosa Planella, Kieran O'Regan, Dominika Gastol, W. - Dhammika Widanage, and Emma Kendrick. Development of Experimental Techniques for - Parameterization of Multi-scale Lithium-ion Battery Models. Journal of The - Electrochemical Society, 167(8):080534, 2020. doi:10.1149/1945-7111/ab9050. - - and references therein. - - SEI parameters are example parameters for composite SEI on silicon/graphite. Both - phases use the same values, from the paper. - - Xiao Guang Yang, Yongjun Leng, Guangsheng Zhang, Shanhai Ge, and Chao Yang Wang. - Modeling of lithium plating induced aging of lithium-ion batteries: transition - from linear to nonlinear aging. Journal of Power Sources, 360:28–40, 2017. - doi:10.1016/j.jpowsour.2017.05.110. - - """ - - return { - "chemistry": "lithium_ion", - # sei - "Primary: Ratio of lithium moles to SEI moles": 2.0, - "Primary: Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, - "Primary: Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, - "Primary: SEI resistivity [Ohm.m]": 200000.0, - "Primary: Initial inner SEI thickness [m]": 2.5e-09, - "Primary: Initial outer SEI thickness [m]": 2.5e-09, - "Primary: EC initial concentration in electrolyte [mol.m-3]": 4541.0, - "Primary: EC diffusivity [m2.s-1]": 2e-18, - "Primary: SEI kinetic rate constant [m.s-1]": 1e-12, - "Primary: SEI open-circuit potential [V]": 0.4, - "Primary: SEI growth activation energy [J.mol-1]": 0.0, - "Secondary: Ratio of lithium moles to SEI moles": 2.0, - "Secondary: Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, - "Secondary: Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, - "Secondary: SEI resistivity [Ohm.m]": 200000.0, - "Secondary: Initial inner SEI thickness [m]": 2.5e-09, - "Secondary: Initial outer SEI thickness [m]": 2.5e-09, - "Secondary: EC initial concentration in electrolyte [mol.m-3]": 4541.0, - "Secondary: EC diffusivity [m2.s-1]": 2e-18, - "Secondary: SEI kinetic rate constant [m.s-1]": 1e-12, - "Secondary: SEI open-circuit potential [V]": 0.4, - "Secondary: SEI growth activation energy [J.mol-1]": 0.0, - # cell - "Positive current collector thickness [m]": 1.2e-05, - "Positive electrode thickness [m]": 8.52e-05, - "Separator thickness [m]": 1.2e-05, - "Electrode height [m]": 0.065, - "Electrode width [m]": 1.58, - "Cell cooling surface area [m2]": 0.00531, - "Cell volume [m3]": 2.42e-05, - "Cell thermal expansion coefficient [m.K-1]": 1.1e-06, - "Positive current collector conductivity [S.m-1]": 58411000.0, - "Positive current collector density [kg.m-3]": 8960.0, - "Positive current collector specific heat capacity [J.kg-1.K-1]": 385.0, - "Positive current collector thermal conductivity [W.m-1.K-1]": 401.0, - "Nominal cell capacity [A.h]": 5.0, - "Current function [A]": 5.0, - "Contact resistance [Ohm]": 0, - # positive electrode - "Positive electrode conductivity [S.m-1]": 215.0, - "Primary: Maximum concentration in positive electrode [mol.m-3]": 28700.0, - "Primary: Initial concentration in positive electrode [mol.m-3]": 27700.0, - "Primary: Positive electrode diffusivity [m2.s-1]": 5.5e-14, - "Primary: Positive electrode OCP [V]": graphite_ocp_Enertech_Ai2020, - "Negative electrode porosity": 0.25, - "Primary: Positive electrode active material volume fraction": 0.735, - "Primary: Positive particle radius [m]": 5.86e-06, - "Positive electrode Bruggeman coefficient (electrolyte)": 1.5, - "Positive electrode Bruggeman coefficient (electrode)": 0, - "Positive electrode charge transfer coefficient": 0.5, - "Positive electrode double-layer capacity [F.m-2]": 0.2, - "Primary: Positive electrode exchange-current density [A.m-2]" - "": graphite_LGM50_electrolyte_exchange_current_density_Chen2020, - "Primary: Positive electrode density [kg.m-3]": 1657.0, - "Positive electrode specific heat capacity [J.kg-1.K-1]": 700.0, - "Positive electrode thermal conductivity [W.m-1.K-1]": 1.7, - "Primary: Positive electrode OCP entropic change [V.K-1]": 0.0, - "Secondary: Maximum concentration in positive electrode [mol.m-3]": 278000.0, - "Secondary: Initial concentration in positive electrode [mol.m-3]": 276610.0, - "Secondary: Positive electrode diffusivity [m2.s-1]": 1.67e-14, - "Secondary: Positive electrode lithiation OCP [V]" - "": silicon_ocp_lithiation_Mark2016, - "Secondary: Positive electrode delithiation OCP [V]" - "": silicon_ocp_delithiation_Mark2016, - "Secondary: Positive electrode active material volume fraction": 0.015, - "Secondary: Positive particle radius [m]": 1.52e-06, - "Secondary: Positive electrode exchange-current density [A.m-2]" - "": silicon_LGM50_electrolyte_exchange_current_density_Chen2020, - "Secondary: Positive electrode density [kg.m-3]": 2650.0, - "Secondary: Positive electrode OCP entropic change [V.K-1]": 0.0, - # separator - "Separator porosity": 0.47, - "Separator Bruggeman coefficient (electrolyte)": 1.5, - "Separator density [kg.m-3]": 397.0, - "Separator specific heat capacity [J.kg-1.K-1]": 700.0, - "Separator thermal conductivity [W.m-1.K-1]": 0.16, - # electrolyte - "Initial concentration in electrolyte [mol.m-3]": 1000.0, - "Cation transference number": 0.2594, - "Thermodynamic factor": 1.0, - "Electrolyte diffusivity [m2.s-1]": electrolyte_diffusivity_Nyman2008, - "Electrolyte conductivity [S.m-1]": electrolyte_conductivity_Nyman2008, - # experiment - "Reference temperature [K]": 298.15, - "Total heat transfer coefficient [W.m-2.K-1]": 10.0, - "Ambient temperature [K]": 298.15, - "Number of electrodes connected in parallel to make a cell": 1.0, - "Number of cells connected in series to make a battery": 1.0, - "Lower voltage cut-off [V]": 2.5, - "Upper voltage cut-off [V]": 4.2, - "Initial concentration in positive electrode [mol.m-3]": 29866.0, - "Initial temperature [K]": 298.15, - # citations - "citations": ["Chen2020", "Ai2022"], - } diff --git a/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py b/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py deleted file mode 100644 index d3180ac92b..0000000000 --- a/pybamm/input/parameters/lithium_ion/Ecker2015_graphite_halfcell.py +++ /dev/null @@ -1,396 +0,0 @@ -import pybamm - - -def graphite_diffusivity_Ecker2015(sto, T): - """ - Graphite diffusivity as a function of stochiometry [1, 2, 3]. - - References - ---------- - .. [1] 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. - .. [2] 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. - .. [3] Richardson, Giles, et. al. "Generalised single particle models for - high-rate operation of graded lithium-ion electrodes: Systematic derivation - and validation." Electrochemica Acta 339 (2020): 135862 - - Parameters - ---------- - sto: :class:`pybamm.Symbol` - Electrode stochiometry - T: :class:`pybamm.Symbol` - Dimensional temperature - - Returns - ------- - :class:`pybamm.Symbol` - Solid diffusivity - """ - - D_ref = 8.4e-13 * pybamm.exp(-11.3 * sto) + 8.2e-15 - E_D_s = 3.03e4 - arrhenius = pybamm.exp(-E_D_s / (pybamm.constants.R * T)) * pybamm.exp( - E_D_s / (pybamm.constants.R * 296) - ) - - return D_ref * arrhenius - - -def graphite_ocp_Ecker2015(sto): - """ - Graphite OCP as a function of stochiometry [1, 2, 3]. - - References - ---------- - .. [1] 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. - .. [2] 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. - .. [3] Richardson, Giles, et. al. "Generalised single particle models for - high-rate operation of graded lithium-ion electrodes: Systematic derivation - and validation." Electrochemica Acta 339 (2020): 135862 - - Parameters - ---------- - sto: :class:`pybamm.Symbol` - Electrode stochiometry - - Returns - ------- - :class:`pybamm.Symbol` - Open-circuit potential - """ - - # Graphite electrode from Ecker, Kabitz, Laresgoiti et al. - # Analytical fit (WebPlotDigitizer + gnuplot) - a = 0.716502 - b = 369.028 - c = 0.12193 - d = 35.6478 - e = 0.0530947 - g = 0.0169644 - h = 27.1365 - i = 0.312832 - j = 0.0199313 - k = 28.5697 - m = 0.614221 - n = 0.931153 - o = 36.328 - p = 1.10743 - q = 0.140031 - r = 0.0189193 - s = 21.1967 - t = 0.196176 - - u_eq = ( - a * pybamm.exp(-b * sto) - + c * pybamm.exp(-d * (sto - e)) - - r * pybamm.tanh(s * (sto - t)) - - g * pybamm.tanh(h * (sto - i)) - - j * pybamm.tanh(k * (sto - m)) - - n * pybamm.exp(o * (sto - p)) - + q - ) - - return u_eq - - -def graphite_electrolyte_exchange_current_density_Ecker2015(c_e, c_s_surf, c_s_max, T): - """ - Exchange-current density for Butler-Volmer reactions between graphite and LiPF6 in - EC:DMC. - - References - ---------- - .. [1] 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. - .. [2] 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. - .. [3] Richardson, Giles, et. al. "Generalised single particle models for - high-rate operation of graded lithium-ion electrodes: Systematic derivation - and validation." Electrochemica Acta 339 (2020): 135862 - - Parameters - ---------- - c_e : :class:`pybamm.Symbol` - Electrolyte concentration [mol.m-3] - c_s_surf : :class:`pybamm.Symbol` - Particle concentration [mol.m-3] - c_s_max : :class:`pybamm.Symbol` - Maximum particle concentration [mol.m-3] - T : :class:`pybamm.Symbol` - Temperature [K] - - Returns - ------- - :class:`pybamm.Symbol` - Exchange-current density [A.m-2] - """ - - k_ref = 1.11 * 1e-10 - - # multiply by Faraday's constant to get correct units - m_ref = ( - pybamm.constants.F * k_ref - ) # (A/m2)(m3/mol)**1.5 - includes ref concentrations - E_r = 53400 - - arrhenius = pybamm.exp(-E_r / (pybamm.constants.R * T)) * pybamm.exp( - E_r / (pybamm.constants.R * 296.15) - ) - - return ( - m_ref * arrhenius * c_e**0.5 * c_s_surf**0.5 * (c_s_max - c_s_surf) ** 0.5 - ) - - -def electrolyte_diffusivity_Ecker2015(c_e, T): - """ - Diffusivity of LiPF6 in EC:DMC as a function of ion concentration [1, 2, 3]. - - References - ---------- - .. [1] 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. - .. [2] 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. - .. [3] Richardson, Giles, et. al. "Generalised single particle models for - high-rate operation of graded lithium-ion electrodes: Systematic derivation - and validation." Electrochemica Acta 339 (2020): 135862 - - Parameters - ---------- - c_e: :class:`pybamm.Symbol` - Dimensional electrolyte concentration - T: :class:`pybamm.Symbol` - Dimensional temperature - - Returns - ------- - :class:`pybamm.Symbol` - Solid diffusivity - """ - - # The diffusivity epends on the electrolyte conductivity - inputs = {"Electrolyte concentration [mol.m-3]": c_e, "Temperature [K]": T} - sigma_e = pybamm.FunctionParameter("Electrolyte conductivity [S.m-1]", inputs) - - D_c_e = ( - (pybamm.constants.k_b / (pybamm.constants.F * pybamm.constants.q_e)) - * sigma_e - * T - / c_e - ) - - return D_c_e - - -def electrolyte_conductivity_Ecker2015(c_e, T): - """ - Conductivity of LiPF6 in EC:DMC as a function of ion concentration [1, 2, 3]. - - References - ---------- - .. [1] 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. - .. [2] 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. - .. [3] Richardson, Giles, et. al. "Generalised single particle models for - high-rate operation of graded lithium-ion electrodes: Systematic derivation - and validation." Electrochemica Acta 339 (2020): 135862 - - Parameters - ---------- - c_e: :class:`pybamm.Symbol` - Dimensional electrolyte concentration - T: :class:`pybamm.Symbol` - Dimensional temperature - - Returns - ------- - :class:`pybamm.Symbol` - Solid diffusivity - """ - - # mol/m^3 to mol/l - cm = 1e-3 * c_e - - # value at T = 296K - sigma_e_296 = 0.2667 * cm**3 - 1.2983 * cm**2 + 1.7919 * cm + 0.1726 - - # add temperature dependence - E_k_e = 1.71e4 - C = 296 * pybamm.exp(E_k_e / (pybamm.constants.R * 296)) - sigma_e = C * sigma_e_296 * pybamm.exp(-E_k_e / (pybamm.constants.R * T)) / T - - return sigma_e - - -# Call dict via a function to avoid errors when editing in place -def get_parameter_values(): - """ - Parameters for a graphite half-cell based on a Kokam SLPB 75106100 cell, from 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. - - The tab placement parameters are taken from measurements in - - Hales, Alastair, et al. "The cell cooling coefficient: a standard to define heat - rejection from lithium-ion batteries." Journal of The Electrochemical Society - 166.12 (2019): A2383. - - The thermal material properties are for a 5 Ah power pouch cell by Kokam. The data - are extracted from - - Zhao, Y., et al. "Modeling the effects of thermal gradients induced by tab and - surface cooling on lithium ion cell performance."" Journal of The - Electrochemical Society, 165.13 (2018): A3169-A3178. - - Graphite electrode parameters - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - The fits to data for the electrode and electrolyte properties are those provided - by Dr. Simon O'Kane in the paper: - - Richardson, Giles, et. al. "Generalised single particle models for high-rate - operation of graded lithium-ion electrodes: Systematic derivation and - validation." Electrochemica Acta 339 (2020): 135862 - - SEI parameters are example parameters for SEI growth from the papers: - - - Ramadass, P., Haran, B., Gomadam, P. M., White, R., & Popov, B. N. (2004). - Development of first principles capacity fade model for Li-ion cells. Journal of - the Electrochemical Society, 151(2), A196-A203. - - Ploehn, H. J., Ramadass, P., & White, R. E. (2004). Solvent diffusion model for - aging of lithium-ion battery cells. Journal of The Electrochemical Society, - 151(3), A456-A462. - - Single, F., Latz, A., & Horstmann, B. (2018). Identifying the mechanism of - continued growth of the solid-electrolyte interphase. ChemSusChem, 11(12), - 1950-1955. - - Safari, M., Morcrette, M., Teyssot, A., & Delacour, C. (2009). Multimodal - Physics- Based Aging Model for Life Prediction of Li-Ion Batteries. Journal of - The Electrochemical Society, 156(3), - - 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. - - Note: this parameter set does not claim to be representative of the true parameter - values. Instead these are parameter values that were used to fit SEI models to - observed experimental data in the referenced papers. - """ - - return { - "chemistry": "lithium_ion", - # sei - "Ratio of lithium moles to SEI moles": 2.0, - "Inner SEI reaction proportion": 0.5, - "Inner SEI partial molar volume [m3.mol-1]": 9.585e-05, - "Outer SEI partial molar volume [m3.mol-1]": 9.585e-05, - "SEI reaction exchange current density [A.m-2]": 1.5e-07, - "SEI resistivity [Ohm.m]": 200000.0, - "Outer SEI solvent diffusivity [m2.s-1]": 2.5000000000000002e-22, - "Bulk solvent concentration [mol.m-3]": 2636.0, - "Inner SEI open-circuit potential [V]": 0.1, - "Outer SEI open-circuit potential [V]": 0.8, - "Inner SEI electron conductivity [S.m-1]": 8.95e-14, - "Inner SEI lithium interstitial diffusivity [m2.s-1]": 1e-20, - "Lithium interstitial reference concentration [mol.m-3]": 15.0, - "Initial inner SEI thickness [m]": 2.5e-09, - "Initial outer SEI thickness [m]": 2.5e-09, - "EC initial concentration in electrolyte [mol.m-3]": 4541.0, - "EC diffusivity [m2.s-1]": 2e-18, - "SEI kinetic rate constant [m.s-1]": 1e-12, - "SEI open-circuit potential [V]": 0.4, - "SEI growth activation energy [J.mol-1]": 0.0, - "Positive electrode reaction-driven LAM factor [m3.mol-1]": 0.0, - # cell - "Positive current collector thickness [m]": 1.4e-05, - "Positive electrode thickness [m]": 7.4e-05, - "Separator thickness [m]": 2e-05, - "Electrode height [m]": 0.101, - "Electrode width [m]": 0.085, - "Positive tab width [m]": 0.007, - "Positive tab centre y-coordinate [m]": 0.0045, - "Positive tab centre z-coordinate [m]": 0.101, - "Cell cooling surface area [m2]": 0.0172, - "Cell volume [m3]": 1.52e-06, - "Positive current collector conductivity [S.m-1]": 58411000.0, - "Positive current collector density [kg.m-3]": 8933.0, - "Positive current collector specific heat capacity [J.kg-1.K-1]": 385.0, - "Positive current collector thermal conductivity [W.m-1.K-1]": 398.0, - "Nominal cell capacity [A.h]": 0.15625, - "Current function [A]": 0.15652, - "Contact resistance [Ohm]": 0, - # positive electrode - "Positive electrode conductivity [S.m-1]": 14.0, - "Maximum concentration in positive electrode [mol.m-3]": 31920.0, - "Positive electrode diffusivity [m2.s-1]": graphite_diffusivity_Ecker2015, - "Positive electrode OCP [V]": graphite_ocp_Ecker2015, - "Positive electrode porosity": 0.329, - "Positive electrode active material volume fraction": 0.372403, - "Positive particle radius [m]": 1.37e-05, - "Positive electrode Bruggeman coefficient (electrolyte)": 1.6372789338386007, - "Positive electrode Bruggeman coefficient (electrode)": 0.0, - "Positive electrode exchange-current density [A.m-2]" - "": graphite_electrolyte_exchange_current_density_Ecker2015, - "Positive electrode density [kg.m-3]": 1555.0, - "Positive electrode specific heat capacity [J.kg-1.K-1]": 1437.0, - "Positive electrode thermal conductivity [W.m-1.K-1]": 1.58, - "Positive electrode OCP entropic change [V.K-1]": 0.0, - # separator - "Separator porosity": 0.508, - "Separator Bruggeman coefficient (electrolyte)": 1.9804586773134945, - "Separator density [kg.m-3]": 1017.0, - "Separator specific heat capacity [J.kg-1.K-1]": 1978.0, - "Separator thermal conductivity [W.m-1.K-1]": 0.34, - # electrolyte - "Initial concentration in electrolyte [mol.m-3]": 1000.0, - "Cation transference number": 0.26, - "Thermodynamic factor": 1.0, - "Electrolyte diffusivity [m2.s-1]": electrolyte_diffusivity_Ecker2015, - "Electrolyte conductivity [S.m-1]": electrolyte_conductivity_Ecker2015, - # experiment - "Reference temperature [K]": 296.15, - "Positive current collector surface heat transfer coefficient [W.m-2.K-1]" - "": 10.0, - "Positive tab heat transfer coefficient [W.m-2.K-1]": 10.0, - "Edge heat transfer coefficient [W.m-2.K-1]": 10.0, - "Total heat transfer coefficient [W.m-2.K-1]": 10.0, - "Ambient temperature [K]": 298.15, - "Number of electrodes connected in parallel to make a cell": 1.0, - "Number of cells connected in series to make a battery": 1.0, - "Lower voltage cut-off [V]": 2.5, - "Upper voltage cut-off [V]": 4.2, - "Initial concentration in positive electrode [mol.m-3]": 26120.05, - "Initial temperature [K]": 298.15, - # citations - "citations": [ - "Ecker2015i", - "Ecker2015ii", - "Zhao2018", - "Hales2019", - "Richardson2020", - ], - } diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 1cbed0d11b..440a129bd2 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -292,19 +292,13 @@ def __init__(self, extra_options): # The "SEI film resistance" option will still be overridden by extra_options if # provided - # Change the default for particle mechanics based on which half-cell, - # SEI on cracks and LAM options are provided - # return "false", "false" and "none" respectively if options not given - half_cell_option = extra_options.get("half-cell", "false") + # Change the default for particle mechanics based on which SEI on cracks and LAM + # options are provided + # return "false" and "none" respectively if options not given SEI_cracks_option = extra_options.get("SEI on cracks", "false") LAM_opt = extra_options.get("loss of active material", "none") - if "true" in SEI_cracks_option: - if half_cell_option == "true": - default_options["particle mechanics"] = ( # is this right? - "none", - "swelling and cracking", - ) - elif "stress-driven" in LAM_opt or "stress and reaction-driven" in LAM_opt: + if SEI_cracks_option == "true": + if "stress-driven" in LAM_opt or "stress and reaction-driven" in LAM_opt: default_options["particle mechanics"] = ( "swelling and cracking", "swelling only", @@ -348,7 +342,6 @@ def __init__(self, extra_options): default_options["surface form"] = "algebraic" # The "surface form" option will still be overridden by # extra_options if provided - # Change default SEI model based on which lithium plating option is provided # return "none" if option not given plating_option = extra_options.get("lithium plating", "none") @@ -548,15 +541,12 @@ def __init__(self, extra_options): in [ "intercalation kinetics", "interface utilisation", - "lithium plating", "loss of active material", "open-circuit potential", "particle", "particle mechanics", "particle phases", "particle size", - "SEI", - "SEI on cracks", "stress-induced diffusion", ] and isinstance(value, tuple) 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 c0b5d1935c..ecc173f97e 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 @@ -97,16 +97,10 @@ def set_active_material_submodel(self): ) def set_sei_submodel(self): - for domain in ["negative", "positive"]: - self.submodels[f"{domain} sei"] = pybamm.sei.NoSEI( - self.param, domain, self.options - ) + self.submodels["sei"] = pybamm.sei.NoSEI(self.param, self.options) def set_lithium_plating_submodel(self): - for domain in ["negative", "positive"]: - self.submodels[ - f"{domain} lithium plating" - ] = pybamm.lithium_plating.NoPlating(self.param, domain) + self.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating(self.param) def set_total_interface_submodel(self): self.submodels["total interface"] = pybamm.interface.TotalInterfacialCurrent( 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 22bae5510e..cc615dacf7 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 @@ -159,16 +159,13 @@ def set_degradation_variables(self): # Lithium lost to side reactions # Different way of measuring LLI but should give same value - n_Li_lost_neg_sei = self.variables["Loss of lithium to negative SEI [mol]"] - n_Li_lost_pos_sei = self.variables["Loss of lithium to positive SEI [mol]"] - n_Li_lost_reactions = n_Li_lost_neg_sei + n_Li_lost_pos_sei - for domain in domains: + n_Li_lost_sei = self.variables["Loss of lithium to SEI [mol]"] + n_Li_lost_reactions = n_Li_lost_sei + if "negative electrode" in domains: n_Li_lost_sei_cracks = self.variables[ - f"Loss of lithium to {domain} SEI on cracks [mol]" - ] - n_Li_lost_pl = self.variables[ - f"Loss of lithium to {domain} lithium plating [mol]" + "Loss of lithium to SEI on cracks [mol]" ] + n_Li_lost_pl = self.variables["Loss of lithium to lithium plating [mol]"] n_Li_lost_reactions += n_Li_lost_sei_cracks + n_Li_lost_pl self.variables.update( @@ -200,10 +197,8 @@ def set_summary_variables(self): "Total lithium lost [mol]", "Total lithium lost from particles [mol]", "Total lithium lost from electrolyte [mol]", - "Loss of lithium to negative SEI [mol]", - "Loss of capacity to negative SEI [A.h]", - "Loss of lithium to positive SEI [mol]", - "Loss of capacity to positive SEI [A.h]", + "Loss of lithium to SEI [mol]", + "Loss of capacity to SEI [A.h]", "Total lithium lost to side reactions [mol]", "Total capacity lost to side reactions [A.h]", # Resistance @@ -215,20 +210,16 @@ def set_summary_variables(self): "Negative electrode capacity [A.h]", "Loss of active material in negative electrode [%]", "Total lithium in negative electrode [mol]", - "Loss of lithium to negative lithium plating [mol]", - "Loss of capacity to negative lithium plating [A.h]", - "Loss of lithium to negative SEI on cracks [mol]", - "Loss of capacity to negative SEI on cracks [A.h]", + "Loss of lithium to lithium plating [mol]", + "Loss of capacity to lithium plating [A.h]", + "Loss of lithium to SEI on cracks [mol]", + "Loss of capacity to SEI on cracks [A.h]", ] if self.options.electrode_types["positive"] == "porous": summary_variables += [ "Positive electrode capacity [A.h]", "Loss of active material in positive electrode [%]", "Total lithium in positive electrode [mol]", - "Loss of lithium to positive lithium plating [mol]", - "Loss of capacity to positive lithium plating [A.h]", - "Loss of lithium to positive SEI on cracks [mol]", - "Loss of capacity to positive SEI on cracks [A.h]", ] self.summary_variables = summary_variables @@ -252,75 +243,56 @@ def set_open_circuit_potential_submodel(self): ) def set_sei_submodel(self): - for domain in ["negative", "positive"]: - if self.options.electrode_types[domain] == "planar": - reaction_loc = "interface" - elif self.options["x-average side reactions"] == "true": - reaction_loc = "x-average" + if self.options.electrode_types["negative"] == "planar": + reaction_loc = "interface" + elif self.options["x-average side reactions"] == "true": + reaction_loc = "x-average" + else: + reaction_loc = "full electrode" + + 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: - reaction_loc = "full electrode" - - phases = self.options.phases[domain] - for phase in phases: - if self.options["SEI"] == "none": - submodel = pybamm.sei.NoSEI(self.param, domain, self.options, phase) - elif self.options["SEI"] == "constant": - submodel = pybamm.sei.ConstantSEI( - self.param, domain, self.options, phase + 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, - domain, - reaction_loc, - self.options, - phase, - cracks=False, + self.param, reaction_loc, self.options, phase, cracks=True ) - self.submodels[f"{domain} {phase} sei"] = submodel - # Do not set "sei on cracks" submodel for a planar electrode. For porous - # electrodes, "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, domain, self.options, phase, cracks=True - ) - else: - submodel = pybamm.sei.SEIGrowth( - self.param, - domain, - reaction_loc, - self.options, - phase, - cracks=True, - ) - self.submodels[f"{domain} {phase} sei on cracks"] = submodel + self.submodels[f"{phase} sei on cracks"] = submodel - if len(phases) > 1: - self.submodels[f"{domain} total sei"] = pybamm.sei.TotalSEI( - self.param, self.options - ) - self.submodels[f"{domain} total sei on cracks"] = pybamm.sei.TotalSEI( - self.param, self.options, cracks=True - ) + 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): - domains = [d for d in self.options.whole_cell_domains if d != "separator"] - for domain in domains: - if self.options["lithium plating"] == "none": - self.submodels[ - f"{domain} lithium plating" - ] = pybamm.lithium_plating.NoPlating(self.param, domain, self.options) - else: - x_average = self.options["x-average side reactions"] == "true" - self.submodels[ - f"{domain} lithium plating" - ] = pybamm.lithium_plating.Plating( - self.param, domain, x_average, self.options - ) + if self.options["lithium plating"] == "none": + self.submodels["lithium plating"] = pybamm.lithium_plating.NoPlating( + self.param, self.options + ) + else: + x_average = self.options["x-average side reactions"] == "true" + self.submodels["lithium plating"] = pybamm.lithium_plating.Plating( + self.param, x_average, self.options + ) def set_total_interface_submodel(self): self.submodels["total interface"] = pybamm.interface.TotalInterfacialCurrent( diff --git a/pybamm/models/full_battery_models/lithium_metal/dfn.py b/pybamm/models/full_battery_models/lithium_metal/dfn.py index 923be6fbe9..eb11345c90 100644 --- a/pybamm/models/full_battery_models/lithium_metal/dfn.py +++ b/pybamm/models/full_battery_models/lithium_metal/dfn.py @@ -29,7 +29,7 @@ def __init__( self, options=None, name="Doyle-Fuller-Newman lithium metal model", build=True ): options = options or {} - options["half-cell"] = "true" + options["half-cell"] = "positive" super().__init__(options, name, build=False) if build: diff --git a/pybamm/models/submodels/electrode/ohm/li_metal.py b/pybamm/models/submodels/electrode/ohm/li_metal.py index 31cdc7284e..e03ed770ad 100644 --- a/pybamm/models/submodels/electrode/ohm/li_metal.py +++ b/pybamm/models/submodels/electrode/ohm/li_metal.py @@ -76,10 +76,9 @@ def set_initial_conditions(self, variables): self.initial_conditions = {delta_phi: delta_phi_init} def set_rhs(self, variables): - Domain = self.domain.capitalize() if self.options["surface form"] == "differential": j_pl = variables["Lithium metal plating current density [A.m-2]"] - j_sei = variables[f"{Domain} SEI interfacial current density [A.m-2]"] + j_sei = variables["SEI interfacial current density [A.m-2]"] sum_j = j_pl + j_sei i_cc = variables["Current collector current density [A.m-2]"] @@ -92,10 +91,9 @@ def set_rhs(self, variables): self.rhs[delta_phi] = 1 / C_dl * (i_cc - sum_j) def set_algebraic(self, variables): - Domain = self.domain.capitalize() if self.options["surface form"] != "differential": # also catches "false" j_pl = variables["Lithium metal plating current density [A.m-2]"] - j_sei = variables[f"{Domain} SEI interfacial current density [A.m-2]"] + j_sei = variables["SEI interfacial current density [A.m-2]"] sum_j = j_pl + j_sei i_cc = variables["Current collector current density [A.m-2]"] diff --git a/pybamm/models/submodels/interface/lithium_plating/base_plating.py b/pybamm/models/submodels/interface/lithium_plating/base_plating.py index e71432b10e..61119b3a0f 100644 --- a/pybamm/models/submodels/interface/lithium_plating/base_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/base_plating.py @@ -17,44 +17,56 @@ class BasePlating(BaseInterface): A dictionary of options to be passed to the model. """ - def __init__(self, domain, param, options=None): + def __init__(self, param, options=None): reaction = "lithium plating" + domain = "negative" super().__init__(param, domain, reaction, options=options) def get_coupled_variables(self, variables): # Update some common variables - domain, Domain = self.domain_Domain - if self.options.electrode_types[domain] == "porous": - j_plating = variables[ - f"{Domain} lithium plating interfacial current density [A.m-2]" - ] + if self.options.electrode_types["negative"] == "porous": + j_plating = variables["Lithium plating interfacial current density [A.m-2]"] j_plating_av = variables[ - f"X-averaged {domain} lithium plating " - "interfacial current density [A.m-2]" + "X-averaged lithium plating interfacial current density [A.m-2]" ] if self.options.negative["particle phases"] == "1": - a = variables[f"{Domain} electrode surface area to volume ratio [m-1]"] + a = variables["Negative electrode surface area to volume ratio [m-1]"] else: a = variables[ - f"{Domain} electrode primary surface area to volume ratio [m-1]" + "Negative electrode primary surface area to volume ratio [m-1]" ] a_j_plating = a * j_plating a_j_plating_av = pybamm.x_average(a_j_plating) variables.update( { - f"{Domain} electrode lithium plating interfacial current " + "Negative electrode lithium plating interfacial current " "density [A.m-2]": j_plating, - f"X-averaged {domain} electrode lithium plating " + "X-averaged negative electrode lithium plating " "interfacial current density [A.m-2]": j_plating_av, - f"{Domain} lithium plating volumetric " + "Lithium plating volumetric " "interfacial current density [A.m-3]": a_j_plating, - f"X-averaged {domain} lithium plating volumetric " + "X-averaged lithium plating volumetric " "interfacial current density [A.m-3]": a_j_plating_av, } ) + zero_av = pybamm.PrimaryBroadcast(0, "current collector") + zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") + variables.update( + { + "X-averaged positive electrode lithium plating " + "interfacial current density [A.m-2]": zero_av, + "X-averaged positive electrode lithium plating volumetric " + "interfacial current density [A.m-3]": zero_av, + "Positive electrode lithium plating " + "interfacial current density [A.m-2]": zero, + "Positive electrode lithium plating volumetric " + "interfacial current density [A.m-3]": zero, + } + ) + variables.update( self._get_standard_volumetric_current_density_variables(variables) ) @@ -75,7 +87,6 @@ def _get_standard_concentration_variables(self, c_plated_Li, c_dead_Li): The variables which can be derived from the plated lithium thickness. """ param = self.param - domain, Domain = self.domain_Domain # Set scales to one for the "no plating" model so that they are not required # by parameter values in general @@ -96,19 +107,18 @@ def _get_standard_concentration_variables(self, c_plated_Li, c_dead_Li): Q_dead_Li = c_dead_Li_av * param.n.L * param.L_y * param.L_z variables = { - f"{Domain} lithium plating concentration [mol.m-3]": c_plated_Li, - f"X-averaged {domain} lithium plating " - "concentration [mol.m-3]": c_plated_Li_av, - f"{Domain} dead lithium concentration [mol.m-3]": c_dead_Li, - f"X-averaged {domain} dead lithium concentration [mol.m-3]": c_dead_Li_av, - f"{Domain} lithium plating thickness [m]": L_plated_Li, - f"X-averaged {domain} lithium plating thickness [m]": L_plated_Li_av, - f"{Domain} Dead lithium thickness [m]": L_dead_Li, - f"X-averaged {domain} dead lithium thickness [m]": L_dead_Li_av, - f"Loss of lithium to {domain} lithium plating " - "[mol]": (Q_plated_Li + Q_dead_Li), - f"Loss of capacity to {domain} lithium plating " - "[A.h]": (Q_plated_Li + Q_dead_Li) * param.F / 3600, + "Lithium plating concentration [mol.m-3]": c_plated_Li, + "X-averaged lithium plating concentration [mol.m-3]": c_plated_Li_av, + "Dead lithium concentration [mol.m-3]": c_dead_Li, + "X-averaged dead lithium concentration [mol.m-3]": c_dead_Li_av, + "Lithium plating thickness [m]": L_plated_Li, + "X-averaged lithium plating thickness [m]": L_plated_Li_av, + "Dead lithium thickness [m]": L_dead_Li, + "X-averaged dead lithium thickness [m]": L_dead_Li_av, + "Loss of lithium to lithium plating [mol]": (Q_plated_Li + Q_dead_Li), + "Loss of capacity to lithium plating [A.h]": (Q_plated_Li + Q_dead_Li) + * param.F + / 3600, } return variables @@ -126,13 +136,13 @@ def _get_standard_reaction_variables(self, j_stripping): variables : dict The variables which can be derived from the plated lithium thickness. """ - domain, Domain = self.domain_Domain + # Set scales to one for the "no plating" model so that they are not required + # by parameter values in general j_stripping_av = pybamm.x_average(j_stripping) variables = { - f"{Domain} lithium plating interfacial current density " - "[A.m-2]": j_stripping, - f"X-averaged {domain} lithium plating " + "Lithium plating interfacial current density [A.m-2]": j_stripping, + "X-averaged lithium plating " "interfacial current density [A.m-2]": j_stripping_av, } diff --git a/pybamm/models/submodels/interface/lithium_plating/no_plating.py b/pybamm/models/submodels/interface/lithium_plating/no_plating.py index ad7fd7301a..1a2f59808a 100644 --- a/pybamm/models/submodels/interface/lithium_plating/no_plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/no_plating.py @@ -16,12 +16,12 @@ class NoPlating(BasePlating): A dictionary of options to be passed to the model. """ - def __init__(self, domain, param, options=None): - super().__init__(param, domain, options=options) + def __init__(self, param, options=None): + super().__init__(param, options=options) def get_fundamental_variables(self): zero = pybamm.FullBroadcast( - pybamm.Scalar(0), f"{self.domain} electrode", "current collector" + pybamm.Scalar(0), "negative electrode", "current collector" ) variables = self._get_standard_concentration_variables(zero, zero) variables.update(self._get_standard_overpotential_variables(zero)) diff --git a/pybamm/models/submodels/interface/lithium_plating/plating.py b/pybamm/models/submodels/interface/lithium_plating/plating.py index d8d8e891e6..d4d3980153 100644 --- a/pybamm/models/submodels/interface/lithium_plating/plating.py +++ b/pybamm/models/submodels/interface/lithium_plating/plating.py @@ -19,36 +19,35 @@ class Plating(BasePlating): A dictionary of options to be passed to the model. """ - def __init__(self, param, domain, x_average, options): - super().__init__(param, domain, options=options) + def __init__(self, param, x_average, options): + super().__init__(param, options) self.x_average = x_average pybamm.citations.register("OKane2020") pybamm.citations.register("OKane2022") def get_fundamental_variables(self): - domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li_av = pybamm.Variable( - f"X-averaged {domain} lithium plating concentration [mol.m-3]", + "X-averaged lithium plating concentration [mol.m-3]", domain="current collector", scale=self.param.c_Li_typ, ) - c_plated_Li = pybamm.PrimaryBroadcast(c_plated_Li_av, f"{domain} electrode") + c_plated_Li = pybamm.PrimaryBroadcast(c_plated_Li_av, "negative electrode") c_dead_Li_av = pybamm.Variable( - f"X-averaged {domain} dead lithium concentration [mol.m-3]", + "X-averaged dead lithium concentration [mol.m-3]", domain="current collector", ) - c_dead_Li = pybamm.PrimaryBroadcast(c_dead_Li_av, f"{domain} electrode") + c_dead_Li = pybamm.PrimaryBroadcast(c_dead_Li_av, "negative electrode") else: c_plated_Li = pybamm.Variable( - f"{Domain} lithium plating concentration [mol.m-3]", - domain=f"{domain} electrode", + "Lithium plating concentration [mol.m-3]", + domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, scale=self.param.c_Li_typ, ) c_dead_Li = pybamm.Variable( - f"{Domain} dead lithium concentration [mol.m-3]", - domain=f"{domain} electrode", + "Dead lithium concentration [mol.m-3]", + domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, ) @@ -58,12 +57,11 @@ def get_fundamental_variables(self): def get_coupled_variables(self, variables): param = self.param - Domain = self.domain.capitalize() - delta_phi = variables[f"{Domain} electrode surface potential difference [V]"] - c_e_n = variables[f"{Domain} electrolyte concentration [mol.m-3]"] - T = variables[f"{Domain} electrode temperature [K]"] - eta_sei = variables[f"{Domain} SEI film overpotential [V]"] - c_plated_Li = variables[f" {Domain} lithium plating concentration [mol.m-3]"] + delta_phi = variables["Negative electrode surface potential difference [V]"] + c_e_n = variables["Negative electrolyte concentration [mol.m-3]"] + T = variables["Negative electrode temperature [K]"] + eta_sei = variables["SEI film overpotential [V]"] + c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] j0_stripping = param.j0_stripping(c_e_n, c_plated_Li, T) j0_plating = param.j0_plating(c_e_n, c_plated_Li, T) @@ -92,27 +90,23 @@ def get_coupled_variables(self, variables): return variables def set_rhs(self, variables): - domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li = variables[ - f"X-averaged {domain} lithium plating concentration [mol.m-3]" - ] - c_dead_Li = variables[ - f"X-averaged {domain} dead lithium concentration [mol.m-3]" + "X-averaged lithium plating concentration [mol.m-3]" ] + c_dead_Li = variables["X-averaged dead lithium concentration [mol.m-3]"] a_j_stripping = variables[ - f"X-averaged {domain} lithium plating volumetric " + "X-averaged lithium plating volumetric " "interfacial current density [A.m-3]" ] - L_sei = variables[f"X-averaged {domain} total SEI thickness [m]"] + L_sei = variables["X-averaged total SEI thickness [m]"] else: - c_plated_Li = variables[f"{Domain} lithium plating concentration [mol.m-3]"] - c_dead_Li = variables[f"{Domain} dead lithium concentration [mol.m-3]"] + c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] + c_dead_Li = variables["Dead lithium concentration [mol.m-3]"] a_j_stripping = variables[ - f"{Domain} lithium plating volumetric " - "interfacial current density [A.m-3]" + "Lithium plating volumetric interfacial current density [A.m-3]" ] - L_sei = variables[f"{Domain} total SEI thickness [m]"] + L_sei = variables["Total SEI thickness [m]"] # In the partially reversible plating model, coupling term turns reversible # lithium into dead lithium. In other plating models, it is zero. @@ -128,17 +122,14 @@ def set_rhs(self, variables): } def set_initial_conditions(self, variables): - domain, Domain = self.domain_Domain if self.x_average is True: c_plated_Li = variables[ - f"X-averaged {domain} lithium plating concentration [mol.m-3]" - ] - c_dead_Li = variables[ - f"X-averaged {domain} dead lithium concentration [mol.m-3]" + "X-averaged lithium plating concentration [mol.m-3]" ] + c_dead_Li = variables["X-averaged dead lithium concentration [mol.m-3]"] else: - c_plated_Li = variables[f"{Domain} lithium plating concentration [mol.m-3]"] - c_dead_Li = variables[f"{domain} dead lithium concentration [mol.m-3]"] + c_plated_Li = variables["Lithium plating concentration [mol.m-3]"] + c_dead_Li = variables["Dead lithium concentration [mol.m-3]"] c_plated_Li_0 = self.param.c_plated_Li_0 zero = pybamm.Scalar(0) diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 9c57721d99..6b08214ff4 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -20,37 +20,53 @@ class BaseModel(BaseInterface): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__(self, param, domain, options, phase="primary", 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, phase=phase) def get_coupled_variables(self, variables): # Update some common variables - domain, Domain = self.domain_Domain + zero_av = pybamm.PrimaryBroadcast(0, "current collector") + zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") if self.reaction_loc != "interface": - j_sei_av = variables[ - f"X-averaged {domain} electrode {self.reaction_name}interfacial" - " current density [A.m-2]" - ] - j_sei = variables[ - f"{Domain} electrode {self.reaction_name}interfacial current" - " density [A.m-2]" - ] variables.update( { - f"X-averaged {domain} electrode {self.reaction_name}interfacial " - "current density [A.m-2]": j_sei_av, - f"{Domain} electrode {self.reaction_name}interfacial current " - "density [A.m-2]": j_sei, + f"X-averaged negative electrode {self.reaction_name}interfacial " + "current density": variables[ + f"X-averaged {self.reaction_name}interfacial current density" + ], + f"Negative electrode {self.reaction_name}interfacial current " + "density": variables[ + f"{self.reaction_name}interfacial current density" + ], + f"Negative electrode {self.reaction_name}interfacial current " + "density [A.m-2]": variables[ + f"{self.reaction_name}interfacial current density [A.m-2]" + ], } ) + variables.update( + self._get_standard_volumetric_current_density_variables(variables) + ) variables.update( - self._get_standard_volumetric_current_density_variables(variables) + { + 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, + } ) return variables @@ -72,10 +88,9 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): variables : dict The variables which can be derived from the SEI thicknesses. """ - domain, Domain = self.domain_Domain variables = { - f"{Domain} inner {self.reaction_name}thickness [m]": L_inner, - f"{Domain} outer {self.reaction_name}thickness [m]": L_outer, + f"Inner {self.reaction_name}thickness [m]": L_inner, + f"Outer {self.reaction_name}thickness [m]": L_outer, } if self.reaction_loc != "interface": @@ -83,10 +98,8 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): L_outer_av = pybamm.x_average(L_outer) variables.update( { - f"X-averaged {domain} inner {self.reaction_name}" - "thickness [m]": L_inner_av, - f"X-averaged {domain} outer {self.reaction_name}" - "thickness [m]": L_outer_av, + f"X-averaged inner {self.reaction_name}thickness [m]": L_inner_av, + f"X-averaged outer {self.reaction_name}thickness [m]": L_outer_av, } ) # Get variables related to the total thickness @@ -97,7 +110,7 @@ def _get_standard_thickness_variables(self, L_inner, L_outer): def _get_standard_total_thickness_variables(self, L_sei): """Update variables related to total SEI thickness.""" - domain, Domain = self.domain_Domain + domain = self.domain if isinstance(self, pybamm.sei.NoSEI): R_sei = 1 @@ -105,16 +118,15 @@ def _get_standard_total_thickness_variables(self, L_sei): R_sei = self.phase_param.R_sei variables = { - f"{Domain} {self.reaction_name}[m]": L_sei, - f"Total {domain} {self.reaction_name}thickness [m]": L_sei, + f"{self.reaction_name}[m]": L_sei, + f"Total {self.reaction_name}thickness [m]": L_sei, } if self.reaction_loc != "interface": L_sei_av = pybamm.x_average(L_sei) variables.update( { - f"X-averaged {domain} {self.reaction_name}thickness [m]": L_sei_av, - f"X-averaged total {domain} {self.reaction_name}" - "thickness [m]": L_sei_av, + f"X-averaged {self.reaction_name}thickness [m]": L_sei_av, + f"X-averaged total {self.reaction_name}thickness [m]": L_sei_av, } ) if self.reaction == "SEI": @@ -128,7 +140,7 @@ def _get_standard_total_thickness_variables(self, L_sei): def _get_standard_concentration_variables(self, variables): """Update variables related to the SEI concentration.""" - domain, Domain = self.domain_Domain + Domain = self.domain.capitalize() phase_param = self.phase_param reaction_name = self.reaction_name @@ -168,8 +180,8 @@ def _get_standard_concentration_variables(self, variables): ) if self.reaction == "SEI": - L_inner = variables[f"{Domain} inner {reaction_name}thickness [m]"] - L_outer = variables[f"{Domain} outer {reaction_name}thickness [m]"] + L_inner = variables[f"Inner {reaction_name}thickness [m]"] + L_outer = variables[f"Outer {reaction_name}thickness [m]"] n_inner = L_inner * L_to_n_inner # inner SEI concentration n_outer = L_outer * L_to_n_outer # outer SEI concentration @@ -197,25 +209,24 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - f"{Domain} inner {reaction_name}concentration [mol.m-3]": n_inner, - f"X-averaged {domain} inner {reaction_name}" + f"Inner {reaction_name}concentration [mol.m-3]": n_inner, + f"X-averaged inner {reaction_name}" "concentration [mol.m-3]": n_inner_av, - f"{Domain} outer {reaction_name}concentration [mol.m-3]": n_outer, - f"X-averaged {domain} outer {reaction_name}" + f"Outer {reaction_name}concentration [mol.m-3]": n_outer, + f"X-averaged outer {reaction_name}" "concentration [mol.m-3]": n_outer_av, - f"{Domain} {reaction_name}concentration [mol.m-3]": n_SEI, - f"X-averaged {domain} {reaction_name}" - "concentration [mol.m-3]": n_SEI_xav, - f"Loss of lithium to {domain} {reaction_name}[mol]": Q_sei, - f"Loss of capacity to {domain} {reaction_name}[A.h]": Q_sei + f"{reaction_name}concentration [mol.m-3]": n_SEI, + f"X-averaged {reaction_name}concentration [mol.m-3]": n_SEI_xav, + 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[f"{Domain} inner {reaction_name}thickness [m]"] - L_outer_cr = variables[f"{Domain} outer {reaction_name}thickness [m]"] + L_inner_cr = variables[f"Inner {reaction_name}thickness [m]"] + L_outer_cr = variables[f"Outer {reaction_name}thickness [m]"] roughness = variables[f"{Domain} electrode roughness ratio"] n_inner_cr = L_inner_cr * L_to_n_inner * (roughness - 1) @@ -247,19 +258,17 @@ def _get_standard_concentration_variables(self, variables): variables.update( { - f"{Domain} inner {reaction_name}" - "concentration [mol.m-3]": n_inner_cr, - f"X-averaged {domain} inner {reaction_name}" + f"Inner {reaction_name}" "concentration [mol.m-3]": n_inner_cr, + f"X-averaged inner {reaction_name}" "concentration [mol.m-3]": n_inner_cr_av, - f"{Domain} outer {reaction_name}" - "concentration [mol.m-3]": n_outer_cr, - f"X-averaged {domain} outer {reaction_name}" + f"Outer {reaction_name}concentration [mol.m-3]": n_outer_cr, + f"X-averaged outer {reaction_name}" "concentration [mol.m-3]": n_outer_cr_av, - f"{Domain} {reaction_name}" "concentration [mol.m-3]": n_SEI_cr, - f"X-averaged {domain} {reaction_name}" + f"{reaction_name}" "concentration [mol.m-3]": n_SEI_cr, + f"X-averaged {reaction_name}" "concentration [mol.m-3]": n_SEI_cr_xav, - f"Loss of lithium to {domain} {reaction_name}[mol]": Q_sei_cr, - f"Loss of capacity to {domain} {reaction_name}[A.h]": Q_sei_cr + 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, } @@ -284,28 +293,25 @@ def _get_standard_reaction_variables(self, j_inner, j_outer): variables : dict The variables which can be derived from the SEI currents. """ - domain, Domain = self.domain_Domain j_inner_av = pybamm.x_average(j_inner) j_outer_av = pybamm.x_average(j_outer) j_sei = j_inner + j_outer variables = { - f"{Domain} inner {self.reaction_name}" - "interfacial currentdensity [A.m-2]": j_inner, - f"X-averaged {domain} inner {self.reaction_name}" + f"Inner {self.reaction_name}interfacial current density [A.m-2]": j_inner, + f"X-averaged inner {self.reaction_name}" "interfacial current density [A.m-2]": j_inner_av, - f"{Domain} outer {self.reaction_name}" - "interfacial current density [A.m-2]": j_outer, - f"X-averaged {domain} outer {self.reaction_name}" + f"Outer {self.reaction_name}interfacial current density [A.m-2]": j_outer, + f"X-averaged outer {self.reaction_name}" "interfacial current density [A.m-2]": j_outer_av, - f"{Domain} {self.reaction_name}interfacial current density [A.m-2]": j_sei, + f"{self.reaction_name}interfacial current density [A.m-2]": j_sei, } if self.reaction_loc != "interface": j_sei_av = pybamm.x_average(j_sei) variables.update( { - f"X-averaged {domain} {self.reaction_name}" + f"X-averaged {self.reaction_name}" "interfacial current density [A.m-2]": j_sei_av, } ) diff --git a/pybamm/models/submodels/interface/sei/constant_sei.py b/pybamm/models/submodels/interface/sei/constant_sei.py index e6d9a58edd..becaa511da 100644 --- a/pybamm/models/submodels/interface/sei/constant_sei.py +++ b/pybamm/models/submodels/interface/sei/constant_sei.py @@ -23,8 +23,8 @@ class ConstantSEI(BaseModel): Phase of the particle (default is "primary") """ - def __init__(self, param, domain, options, phase="primary"): - super().__init__(param, domain, options=options, phase=phase) + def __init__(self, param, options, phase="primary"): + super().__init__(param, options=options, phase=phase) if self.options.electrode_types["negative"] == "planar": self.reaction_loc = "interface" else: diff --git a/pybamm/models/submodels/interface/sei/no_sei.py b/pybamm/models/submodels/interface/sei/no_sei.py index 590af4b376..463b58bac4 100644 --- a/pybamm/models/submodels/interface/sei/no_sei.py +++ b/pybamm/models/submodels/interface/sei/no_sei.py @@ -21,8 +21,8 @@ class NoSEI(BaseModel): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__(self, param, domain, options, phase="primary", cracks=False): - super().__init__(param, domain, options=options, phase=phase, cracks=cracks) + def __init__(self, param, options, phase="primary", cracks=False): + super().__init__(param, options=options, phase=phase, cracks=cracks) if self.options.electrode_types[self.domain] == "planar": 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 b28fd5c346..8fd07a286a 100644 --- a/pybamm/models/submodels/interface/sei/sei_growth.py +++ b/pybamm/models/submodels/interface/sei/sei_growth.py @@ -29,10 +29,8 @@ class SEIGrowth(BaseModel): Whether this is a submodel for standard SEI or SEI on cracks """ - def __init__( - self, param, domain, reaction_loc, options, phase="primary", cracks=False - ): - super().__init__(param, domain, options=options, phase=phase, 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 if self.options["SEI"] == "ec reaction limited": pybamm.citations.register("Yang2017") @@ -40,28 +38,28 @@ def __init__( pybamm.citations.register("Marquis2020") def get_fundamental_variables(self): - domain, Domain = self.domain_Domain Ls = [] for pos in ["inner", "outer"]: + Pos = pos.capitalize() scale = self.phase_param.L_sei_0 if self.reaction_loc == "x-average": L_av = pybamm.Variable( - f"X-averaged {domain} {pos} {self.reaction_name}thickness [m]", + f"X-averaged {pos} {self.reaction_name}thickness [m]", domain="current collector", scale=scale, ) L_av.print_name = f"L_{pos}_av" - L = pybamm.PrimaryBroadcast(L_av, f"{domain} electrode") + L = pybamm.PrimaryBroadcast(L_av, "negative electrode") elif self.reaction_loc == "full electrode": L = pybamm.Variable( - f"{Domain} {pos} {self.reaction_name}thickness [m]", - domain=f"{domain} electrode", + f"{Pos} {self.reaction_name}thickness [m]", + domain="negative electrode", auxiliary_domains={"secondary": "current collector"}, scale=scale, ) elif self.reaction_loc == "interface": L = pybamm.Variable( - f"{Domain} {pos} {self.reaction_name}thickness [m]", + f"{Pos} {self.reaction_name}thickness [m]", domain="current collector", scale=scale, ) @@ -80,37 +78,34 @@ def get_fundamental_variables(self): def get_coupled_variables(self, variables): param = self.param phase_param = self.phase_param - domain, Domain = self.domain_Domain - T = variables[f"{Domain} electrode temperature [K]"] # delta_phi = phi_s - phi_e + T = variables["Negative electrode temperature [K]"] if self.reaction_loc == "interface": delta_phi = variables[ "Lithium metal interface surface potential difference [V]" ] T = pybamm.boundary_value(T, "right") else: - delta_phi = variables[ - f"{Domain} electrode surface potential difference [V]" - ] + delta_phi = variables["Negative electrode surface potential difference [V]"] # 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 f"{Domain} electrode interfacial current density [A.m-2]" in variables: - j = variables[f"{Domain} electrode interfacial current density [A.m-2]"] + if "Negative electrode interfacial current density [A.m-2]" in variables: + j = variables["Negative electrode interfacial current density [A.m-2]"] elif self.reaction_loc == "interface": j = variables["Lithium metal total interfacial current density [A.m-2]"] else: j = variables[ - f"X-averaged {domain} electrode total " + "X-averaged negative electrode total " "interfacial current density [A.m-2]" ] - L_sei_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] - L_sei_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] - L_sei = variables[f"Total {domain} {self.reaction_name}thickness [m]"] + L_sei_inner = variables[f"Inner {self.reaction_name}thickness [m]"] + L_sei_outer = variables[f"Outer {self.reaction_name}thickness [m]"] + L_sei = variables[f"Total {self.reaction_name}thickness [m]"] R_sei = phase_param.R_sei eta_SEI = delta_phi - phase_param.U_sei - j * L_sei * R_sei @@ -164,9 +159,9 @@ def get_coupled_variables(self, variables): c_ec_av = pybamm.x_average(c_ec) if self.reaction == "SEI on cracks": - name = f"{Domain} EC concentration on cracks [mol.m-3]" + name = "EC concentration on cracks [mol.m-3]" else: - name = f"{Domain} EC surface concentration [mol.m-3]" + name = "EC surface concentration [mol.m-3]" variables.update({name: c_ec, f"X-averaged {name}": c_ec_av}) if self.options["SEI"].startswith("ec reaction limited"): @@ -191,45 +186,38 @@ def get_coupled_variables(self, variables): def set_rhs(self, variables): phase_param = self.phase_param param = self.param - domain, Domain = self.domain_Domain if self.reaction_loc == "x-average": - L_inner = variables[ - f"X-averaged {domain} inner {self.reaction_name}thickness [m]" - ] - L_outer = variables[ - f"X-averaged {domain} outer {self.reaction_name}thickness [m]" - ] + L_inner = variables[f"X-averaged inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"X-averaged outer {self.reaction_name}thickness [m]"] j_inner = variables[ - f"X-averaged {domain} inner {self.reaction_name}" + f"X-averaged inner {self.reaction_name}" "interfacial current density [A.m-2]" ] j_outer = variables[ - f"X-averaged {domain} outer {self.reaction_name}" + f"X-averaged outer {self.reaction_name}" "interfacial current density [A.m-2]" ] else: - L_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] + L_inner = variables[f"Inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"Outer {self.reaction_name}thickness [m]"] j_inner = variables[ - f"{Domain} inner {self.reaction_name}" - "interfacial current density [A.m-2]" + f"Inner {self.reaction_name}interfacial current density [A.m-2]" ] j_outer = variables[ - f"{Domain} outer {self.reaction_name}" - "interfacial current density [A.m-2]" + f"Outer {self.reaction_name}interfacial current density [A.m-2]" ] # 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. if self.reaction == "SEI on cracks": if self.reaction_loc == "x-average": - l_cr = variables[f"X-averaged {domain} particle crack length [m]"] - dl_cr = variables[f"X-averaged {domain} particle cracking rate [m.s-1]"] + l_cr = variables["X-averaged negative particle crack length [m]"] + dl_cr = variables["X-averaged negative particle cracking rate [m.s-1]"] else: - l_cr = variables[f"{Domain} particle crack length [m]"] - dl_cr = variables[f"{Domain} particle cracking rate [m.s-1]"] + l_cr = variables["Negative particle crack length [m]"] + dl_cr = variables["Negative particle cracking rate [m.s-1]"] spreading_outer = ( dl_cr / l_cr * (self.phase_param.L_outer_crack_0 - L_outer) ) @@ -263,17 +251,12 @@ def set_rhs(self, variables): } def set_initial_conditions(self, variables): - domain, Domain = self.domain_Domain if self.reaction_loc == "x-average": - L_inner = variables[ - f"X-averaged {domain} inner {self.reaction_name}thickness [m]" - ] - L_outer = variables[ - f"X-averaged {domain} outer {self.reaction_name}thickness [m]" - ] + L_inner = variables[f"X-averaged inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"X-averaged outer {self.reaction_name}thickness [m]"] else: - L_inner = variables[f"{Domain} inner {self.reaction_name}thickness [m]"] - L_outer = variables[f"{Domain} outer {self.reaction_name}thickness [m]"] + L_inner = variables[f"Inner {self.reaction_name}thickness [m]"] + L_outer = variables[f"Outer {self.reaction_name}thickness [m]"] if self.reaction == "SEI on cracks": L_inner_0 = self.phase_param.L_inner_crack_0 diff --git a/pybamm/models/submodels/interface/sei/total_sei.py b/pybamm/models/submodels/interface/sei/total_sei.py index 2a017b94a0..a3ebffa6a6 100644 --- a/pybamm/models/submodels/interface/sei/total_sei.py +++ b/pybamm/models/submodels/interface/sei/total_sei.py @@ -19,25 +19,24 @@ class TotalSEI(pybamm.BaseSubModel): See :class:`pybamm.BaseBatteryModel` """ - def __init__(self, param, domain, options, cracks=False): + def __init__(self, param, options, cracks=False): if cracks is True: self.reaction = "SEI on cracks" else: self.reaction = "SEI" - super().__init__(param, domain, options=options) + super().__init__(param, options=options) def get_coupled_variables(self, variables): - domain, Domain = self.domain_Domain - phases = self.options.phases[domain] + 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"{Domain} electrode {{}}{self.reaction} volumetric " + f"Negative electrode {{}}{self.reaction} volumetric " "interfacial current density [A.m-3]", - f"X-averaged {domain} electrode {{}}{self.reaction} volumetric " + f"X-averaged negative electrode {{}}{self.reaction} volumetric " "interfacial current density [A.m-3]", - f"Loss of lithium to {domain} {{}}{self.reaction} [mol]", - f"Loss of capacity to {domain} {{}}{self.reaction} [A.h]", + 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 diff --git a/setup.py b/setup.py index abcc5e2dd5..93b723438e 100644 --- a/setup.py +++ b/setup.py @@ -256,9 +256,7 @@ def compile_KLU(): "Ai2020 = pybamm.input.parameters.lithium_ion.Ai2020:get_parameter_values", # noqa: E501 "Chen2020 = pybamm.input.parameters.lithium_ion.Chen2020:get_parameter_values", # noqa: E501 "Chen2020_composite = pybamm.input.parameters.lithium_ion.Chen2020_composite:get_parameter_values", # noqa: E501 - "Chen2020_composite_halfcell = pybamm.input.parameters.lithium_ion.Chen2020_composite_halfcell:get_parameter_values", # noqa: E501 "Ecker2015 = pybamm.input.parameters.lithium_ion.Ecker2015:get_parameter_values", # noqa: E501 - "Ecker2015_graphite_halfcell = pybamm.input.parameters.lithium_ion.Ecker2015_graphite_halfcell:get_parameter_values", # noqa: E501 "Marquis2019 = pybamm.input.parameters.lithium_ion.Marquis2019:get_parameter_values", # noqa: E501 "Mohtat2020 = pybamm.input.parameters.lithium_ion.Mohtat2020:get_parameter_values", # noqa: E501 "NCA_Kim2011 = pybamm.input.parameters.lithium_ion.NCA_Kim2011:get_parameter_values", # noqa: E501 From 19238b941cf22f044be7f3a3935749286db230c9 Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Thu, 29 Jun 2023 13:42:30 +0100 Subject: [PATCH 4/7] Revert "fixed tests" This reverts commit cf53ff1d9e74eda7e68bc65b5dea5c18f7fcf872. --- .../scripts/compare_lithium_ion_half_cell.py | 6 +- pybamm/expression_tree/averages.py | 2 +- .../full_battery_models/base_battery_model.py | 24 +++++--- .../lithium_ion/basic_dfn_half_cell.py | 5 ++ .../lithium_ion/electrode_soh_half_cell.py | 12 ++-- .../full_battery_models/lithium_ion/mpm.py | 2 +- .../full_battery_models/lithium_metal/dfn.py | 2 +- .../submodels/interface/sei/base_sei.py | 39 ++++++------ pybamm/parameters/lithium_ion_parameters.py | 8 +-- .../size_distribution_parameters.py | 60 ++++++++++--------- pybamm/simulation.py | 2 +- .../base_lithium_ion_half_cell_tests.py | 2 +- .../test_basic_half_cell_models.py | 4 +- .../test_simulation_with_experiment.py | 2 +- .../test_base_battery_model.py | 22 +++++-- .../base_lithium_ion_half_cell_tests.py | 2 +- .../test_lithium_ion/test_basic_models.py | 8 ++- .../test_lithium_ion/test_electrode_soh.py | 4 +- .../test_lithium_ion/test_mpm_half_cell.py | 18 +++--- .../test_lithium_ion/test_spme_half_cell.py | 2 +- .../test_parameters_with_default_models.py | 2 +- .../test_size_distribution_parameters.py | 12 ++-- tests/unit/test_plotting/test_quick_plot.py | 4 +- 23 files changed, 136 insertions(+), 108 deletions(-) diff --git a/examples/scripts/compare_lithium_ion_half_cell.py b/examples/scripts/compare_lithium_ion_half_cell.py index c38c819483..b6396724f4 100644 --- a/examples/scripts/compare_lithium_ion_half_cell.py +++ b/examples/scripts/compare_lithium_ion_half_cell.py @@ -7,9 +7,9 @@ # load models models = [ - pybamm.lithium_ion.SPM({"half-cell": "true"}), - pybamm.lithium_ion.SPMe({"half-cell": "true"}), - pybamm.lithium_ion.DFN({"half-cell": "true"}), + pybamm.lithium_ion.SPM({"working electrode": "positive"}), + pybamm.lithium_ion.SPMe({"working electrode": "positive"}), + pybamm.lithium_ion.DFN({"working electrode": "positive"}), ] # create and run simulations diff --git a/pybamm/expression_tree/averages.py b/pybamm/expression_tree/averages.py index e063b16c2a..6ada30d47a 100644 --- a/pybamm/expression_tree/averages.py +++ b/pybamm/expression_tree/averages.py @@ -273,7 +273,7 @@ def r_average(symbol): # "positive electrode", take the r-average of the child then broadcast back elif isinstance(symbol, pybamm.SecondaryBroadcast) and symbol.domains[ "secondary" - ] in [["positive electrode"], ["negative electrode"]]: + ] in [["positive electrode"], ["negative electrode"], ["working electrode"]]: child = symbol.orphans[0] child_av = pybamm.r_average(child) return pybamm.PrimaryBroadcast(child_av, symbol.domains["secondary"]) diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py index 440a129bd2..4ea4611133 100644 --- a/pybamm/models/full_battery_models/base_battery_model.py +++ b/pybamm/models/full_battery_models/base_battery_model.py @@ -49,10 +49,6 @@ class BatteryModelOptions(pybamm.FuzzyDict): * "electrolyte conductivity" : str Can be "default" (default), "full", "leading order", "composite" or "integrated". - * "half-cell" : str - Can be "false" (default) for a standard battery or "true" for a - half-cell where the negative electrode is replaced with a lithium metal - counter electrode. * "hydrolysis" : str Whether to include hydrolysis in the model. Only implemented for lead-acid models. Can be "false" (default) or "true". If "true", then @@ -172,6 +168,10 @@ class BatteryModelOptions(pybamm.FuzzyDict): solve an algebraic equation for it. Default is "false", unless "SEI film resistance" is distributed in which case it is automatically set to "true". + * "working electrode": str + Which electrode(s) intercalates and which is counter. If "both" + (default), the model is a standard battery. Otherwise can be "negative" + or "positive" to indicate a half-cell model. * "x-average side reactions": str Whether to average the side reactions (SEI growth, lithium plating and the respective porosity change) over the x-axis in Single Particle @@ -199,7 +199,6 @@ def __init__(self, extra_options): "composite", "integrated", ], - "half-cell": ["false", "true"], "hydrolysis": ["false", "true"], "intercalation kinetics": [ "symmetric Butler-Volmer", @@ -263,6 +262,7 @@ def __init__(self, extra_options): "surface form": ["false", "differential", "algebraic"], "thermal": ["isothermal", "lumped", "x-lumped", "x-full"], "total interfacial current density as a state": ["false", "true"], + "working electrode": ["both", "negative", "positive"], "x-average side reactions": ["false", "true"], } @@ -495,7 +495,7 @@ def __init__(self, extra_options): "mechanics model" ) - if options["half-cell"] == "true": + if options["working electrode"] != "both": if options["thermal"] == "x-full": raise pybamm.OptionError( "X-full thermal submodel is not compatible with half-cell models" @@ -506,6 +506,10 @@ def __init__(self, extra_options): f"X-lumped thermal submodels do not yet support {n}D " "current collectors in a half-cell configuration" ) + elif options["SEI on cracks"] == "true": + raise NotImplementedError( + "SEI on cracks not yet implemented for half-cell models" + ) if options["particle phases"] != "1": if not ( @@ -526,7 +530,7 @@ def __init__(self, extra_options): # Check options are valid for option, value in options.items(): - if option in ["half-cell"]: # is this exception still necessary? + if option in ["working electrode"]: pass else: if isinstance(value, str) or option in [ @@ -593,9 +597,11 @@ def phases(self): @cached_property def whole_cell_domains(self): - if self["half-cell"] == "true": + if self["working electrode"] == "positive": return ["separator", "positive electrode"] - else: + elif self["working electrode"] == "negative": + return ["negative electrode", "separator"] + elif self["working electrode"] == "both": return ["negative electrode", "separator", "positive electrode"] @property diff --git a/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py b/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py index f2ff35d96d..786ae559e5 100644 --- a/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py +++ b/pybamm/models/full_battery_models/lithium_ion/basic_dfn_half_cell.py @@ -33,6 +33,11 @@ class BasicDFNHalfCell(BaseModel): def __init__(self, options=None, name="Doyle-Fuller-Newman half cell model"): super().__init__(options, name) + if self.options["working electrode"] not in ["negative", "positive"]: + raise ValueError( + "The option 'working electrode' should be either 'positive'" + " or 'negative'" + ) pybamm.citations.register("Marquis2019") # `param` is a class containing all the relevant parameters and functions for # this model. These are purely symbolic at this stage, and will be set by the diff --git a/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py b/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py index fce7e3ffc7..ed55a2d621 100644 --- a/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py +++ b/pybamm/models/full_battery_models/lithium_ion/electrode_soh_half_cell.py @@ -21,17 +21,21 @@ class ElectrodeSOHHalfCell(pybamm.BaseModel): """ - def __init__(self, name="Electrode-specific SOH model"): + def __init__(self, working_electrode, name="Electrode-specific SOH model"): + self.working_electrode = working_electrode pybamm.citations.register("Mohtat2019") super().__init__(name) - param = pybamm.LithiumIonParameters({"half-cell": "true"}) + param = pybamm.LithiumIonParameters({"working electrode": working_electrode}) x_100 = pybamm.Variable("x_100", bounds=(0, 1)) x_0 = pybamm.Variable("x_0", bounds=(0, 1)) Q_w = pybamm.InputParameter("Q_w") T_ref = param.T_ref - U_w = param.p.prim.U - Q = Q_w * (x_100 - x_0) + if working_electrode == "negative": # pragma: no cover + raise NotImplementedError + elif working_electrode == "positive": + U_w = param.p.prim.U + Q = Q_w * (x_100 - x_0) V_max = param.opc_soc_100_dimensional V_min = param.opc_soc_0_dimensional diff --git a/pybamm/models/full_battery_models/lithium_ion/mpm.py b/pybamm/models/full_battery_models/lithium_ion/mpm.py index 5147ec366b..4ad0d58eb8 100644 --- a/pybamm/models/full_battery_models/lithium_ion/mpm.py +++ b/pybamm/models/full_battery_models/lithium_ion/mpm.py @@ -47,6 +47,6 @@ def __init__(self, options=None, name="Many-Particle Model", build=True): def default_parameter_values(self): default_params = super().default_parameter_values default_params = pybamm.get_size_distribution_parameters( - default_params, halfcell=self.options["half-cell"] + default_params, electrode=self.options["working electrode"] ) return default_params diff --git a/pybamm/models/full_battery_models/lithium_metal/dfn.py b/pybamm/models/full_battery_models/lithium_metal/dfn.py index eb11345c90..a7ab8b99e2 100644 --- a/pybamm/models/full_battery_models/lithium_metal/dfn.py +++ b/pybamm/models/full_battery_models/lithium_metal/dfn.py @@ -29,7 +29,7 @@ def __init__( self, options=None, name="Doyle-Fuller-Newman lithium metal model", build=True ): options = options or {} - options["half-cell"] = "positive" + options["working electrode"] = "positive" super().__init__(options, name, build=False) if build: diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py index 6b08214ff4..479bc44e70 100644 --- a/pybamm/models/submodels/interface/sei/base_sei.py +++ b/pybamm/models/submodels/interface/sei/base_sei.py @@ -30,45 +30,40 @@ def __init__(self, param, options, phase="primary", cracks=False): def get_coupled_variables(self, variables): # Update some common variables - zero_av = pybamm.PrimaryBroadcast(0, "current collector") - zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") if self.reaction_loc != "interface": + j_sei_av = variables[ + f"X-averaged {self.reaction_name}interfacial current density [A.m-2]" + ] + j_sei = variables[ + f"{self.reaction_name}interfacial current density [A.m-2]" + ] variables.update( { f"X-averaged negative electrode {self.reaction_name}interfacial " - "current density": variables[ - f"X-averaged {self.reaction_name}interfacial current density" - ], - f"Negative electrode {self.reaction_name}interfacial current " - "density": variables[ - f"{self.reaction_name}interfacial current density" - ], + "current density [A.m-2]": j_sei_av, f"Negative electrode {self.reaction_name}interfacial current " - "density [A.m-2]": variables[ - f"{self.reaction_name}interfacial current density [A.m-2]" - ], + "density [A.m-2]": j_sei, } ) - variables.update( - self._get_standard_volumetric_current_density_variables(variables) - ) + zero_av = pybamm.PrimaryBroadcast(0, "current collector") + zero = pybamm.FullBroadcast(0, "positive electrode", "current collector") 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, + "volumetric interfacial current density [A.m-2]": zero_av, f"Positive electrode {self.reaction} " - "volumetric interfacial current density": zero, + "volumetric interfacial current density [A.m-3]": zero, } ) + variables.update( + self._get_standard_volumetric_current_density_variables(variables) + ) + return variables def _get_standard_thickness_variables(self, L_inner, L_outer): @@ -162,7 +157,7 @@ def _get_standard_concentration_variables(self, variables): else: # m * (mol/m4) = mol/m3 (n is a bulk quantity) a = variables[ - f"{Domain} electrode {self.phase_name}" + f"Negative electrode {self.phase_name}" "surface area to volume ratio [m-1]" ] L_to_n_inner = a / phase_param.V_bar_inner diff --git a/pybamm/parameters/lithium_ion_parameters.py b/pybamm/parameters/lithium_ion_parameters.py index f47b89935d..d035ad6c7d 100644 --- a/pybamm/parameters/lithium_ion_parameters.py +++ b/pybamm/parameters/lithium_ion_parameters.py @@ -16,14 +16,14 @@ class LithiumIonParameters(BaseParameters): A dictionary of options to be passed to the parameters. The options that can be set are listed below. - * "half-cell": str - Can be "false" (default) for a standard battery or "true" for a - half-cell where the negative electrode is replaced with a lithium metal - counter electrode. * "particle shape" : str, optional Sets the model shape of the electrode particles. This is used to calculate the surface area to volume ratio. Can be "spherical" (default). TODO: implement "cylindrical" and "platelet". + * "working electrode": str + Which electrode(s) intercalates and which is counter. If "both" + (default), the model is a standard battery. Otherwise can be "negative" + or "positive" to indicate a half-cell model. """ diff --git a/pybamm/parameters/size_distribution_parameters.py b/pybamm/parameters/size_distribution_parameters.py index 4eb0c34618..c45bd3b11e 100644 --- a/pybamm/parameters/size_distribution_parameters.py +++ b/pybamm/parameters/size_distribution_parameters.py @@ -16,7 +16,7 @@ def get_size_distribution_parameters( R_min_p=None, R_max_n=None, R_max_p=None, - halfcell="false", + electrode="both", ): """ A convenience method to add standard area-weighted particle-size distribution @@ -60,7 +60,7 @@ def get_size_distribution_parameters( "positive" to indicate a half-cell model, in which case size distribution parameters are only added for a single electrode. """ - if halfcell == "false": + if electrode in ["both", "negative"]: # Radii from given parameter set R_n_typ = param["Negative particle radius [m]"] @@ -89,33 +89,35 @@ def f_a_dist_n(R): }, check_already_exists=False, ) - # Radii from given parameter set - R_p_typ = param["Positive particle radius [m]"] - - # Set the mean particle radii - R_p_av = R_p_av or R_p_typ - - # Minimum radii - R_min_p = R_min_p or np.max([0, 1 - sd_p * 5]) - - # Max radii - R_max_p = R_max_p or (1 + sd_p * 5) - - # Area-weighted particle-size distribution - def f_a_dist_p(R): - return lognormal(R, R_p_av, sd_p * R_p_av) - - param.update( - { - "Positive area-weighted mean particle radius [m]": R_p_av, - "Positive area-weighted particle-size " - + "standard deviation [m]": sd_p * R_p_av, - "Positive minimum particle radius [m]": R_min_p * R_p_av, - "Positive maximum particle radius [m]": R_max_p * R_p_av, - "Positive area-weighted " + "particle-size distribution [m-1]": f_a_dist_p, - }, - check_already_exists=False, - ) + if electrode in ["both", "positive"]: + # Radii from given parameter set + R_p_typ = param["Positive particle radius [m]"] + + # Set the mean particle radii + R_p_av = R_p_av or R_p_typ + + # Minimum radii + R_min_p = R_min_p or np.max([0, 1 - sd_p * 5]) + + # Max radii + R_max_p = R_max_p or (1 + sd_p * 5) + + # Area-weighted particle-size distribution + def f_a_dist_p(R): + return lognormal(R, R_p_av, sd_p * R_p_av) + + param.update( + { + "Positive area-weighted mean particle radius [m]": R_p_av, + "Positive area-weighted particle-size " + + "standard deviation [m]": sd_p * R_p_av, + "Positive minimum particle radius [m]": R_min_p * R_p_av, + "Positive maximum particle radius [m]": R_max_p * R_p_av, + "Positive area-weighted " + + "particle-size distribution [m-1]": f_a_dist_p, + }, + check_already_exists=False, + ) return param diff --git a/pybamm/simulation.py b/pybamm/simulation.py index b4951d02b4..0315bd8144 100644 --- a/pybamm/simulation.py +++ b/pybamm/simulation.py @@ -940,7 +940,7 @@ def _get_esoh_solver(self, calc_esoh): calc_esoh is False or isinstance(self.model, pybamm.lead_acid.BaseModel) or isinstance(self.model, pybamm.equivalent_circuit.Thevenin) - or self.model.options["half-cell"] == "true" + or self.model.options["working electrode"] != "both" ): return None diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py index ebb6f334c9..0c203c9fc7 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py @@ -7,7 +7,7 @@ class BaseIntegrationTestLithiumIonHalfCell: def run_basic_processing_test(self, options, **kwargs): - options["half-cell"] = "true" + options["working electrode"] = "positive" model = self.model(options) modeltest = tests.StandardModelTest(model, **kwargs) modeltest.test_all(skip_output_tests=True) diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py index 0738a96cbd..c13efff621 100644 --- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py +++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_basic_half_cell_models.py @@ -10,7 +10,7 @@ class TestBasicHalfCellModels(TestCase): def test_runs_Xu2019(self): - options = {"half-cell": "true"} + options = {"working electrode": "positive"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) # create geometry @@ -40,7 +40,7 @@ def test_runs_Xu2019(self): def test_runs_OKane2022(self): # load model - options = {"half-cell": "true"} + options = {"working electrode": "positive"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) # create geometry diff --git a/tests/unit/test_experiments/test_simulation_with_experiment.py b/tests/unit/test_experiments/test_simulation_with_experiment.py index 9276072811..460d82c8c6 100644 --- a/tests/unit/test_experiments/test_simulation_with_experiment.py +++ b/tests/unit/test_experiments/test_simulation_with_experiment.py @@ -561,7 +561,7 @@ def test_run_experiment_half_cell(self): experiment = pybamm.Experiment( [("Discharge at C/20 until 3.5V", "Charge at 1C until 3.8 V")] ) - model = pybamm.lithium_ion.SPM({"half-cell": "true"}) + model = pybamm.lithium_ion.SPM({"working electrode": "positive"}) sim = pybamm.Simulation( model, experiment=experiment, diff --git a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py index 01f975024b..ee51f864c4 100644 --- a/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py +++ b/tests/unit/test_models/test_full_battery_models/test_base_battery_model.py @@ -23,7 +23,6 @@ 'current collector': 'uniform' (possible: ['uniform', 'potential pair', 'potential pair quite conductive']) 'dimensionality': 0 (possible: [0, 1, 2]) 'electrolyte conductivity': 'default' (possible: ['default', 'full', 'leading order', 'composite', 'integrated']) -'half-cell': 'false' (possible: ['false', 'true']) 'hydrolysis': 'false' (possible: ['false', 'true']) 'intercalation kinetics': 'symmetric Butler-Volmer' (possible: ['symmetric Butler-Volmer', 'asymmetric Butler-Volmer', 'linear', 'Marcus', 'Marcus-Hush-Chidsey']) 'interface utilisation': 'full' (possible: ['full', 'constant', 'current-driven']) @@ -45,6 +44,7 @@ 'surface form': 'differential' (possible: ['false', 'differential', 'algebraic']) 'thermal': 'x-full' (possible: ['isothermal', 'lumped', 'x-lumped', 'x-full']) 'total interfacial current density as a state': 'false' (possible: ['false', 'true']) +'working electrode': 'both' (possible: ['both', 'negative', 'positive']) 'x-average side reactions': 'false' (possible: ['false', 'true']) """ # noqa: E501 @@ -291,6 +291,13 @@ def test_options(self): # SEI on cracks with self.assertRaisesRegex(pybamm.OptionError, "SEI on cracks"): pybamm.BaseBatteryModel({"SEI on cracks": "bad SEI on cracks"}) + with self.assertRaisesRegex(NotImplementedError, "SEI on cracks not yet"): + pybamm.BaseBatteryModel( + { + "SEI on cracks": "true", + "working electrode": "positive", + } + ) # plating model with self.assertRaisesRegex(pybamm.OptionError, "lithium plating"): @@ -343,13 +350,15 @@ def test_options(self): # thermal half-cell with self.assertRaisesRegex(pybamm.OptionError, "X-full"): - pybamm.BaseBatteryModel({"thermal": "x-full", "half-cell": "true"}) + pybamm.BaseBatteryModel( + {"thermal": "x-full", "working electrode": "positive"} + ) with self.assertRaisesRegex(pybamm.OptionError, "X-lumped"): pybamm.BaseBatteryModel( { "dimensionality": 2, "thermal": "x-lumped", - "half-cell": "true", + "working electrode": "positive", } ) @@ -447,11 +456,16 @@ def test_domain_phase_options(self): self.assertEqual(options.positive.secondary["particle mechanics"], "none") def test_whole_cell_domains(self): - options = BatteryModelOptions({"half-cell": "true"}) + options = BatteryModelOptions({"working electrode": "positive"}) self.assertEqual( options.whole_cell_domains, ["separator", "positive electrode"] ) + options = BatteryModelOptions({"working electrode": "negative"}) + self.assertEqual( + options.whole_cell_domains, ["negative electrode", "separator"] + ) + options = BatteryModelOptions({}) self.assertEqual( options.whole_cell_domains, diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py index 8684708c3e..24ccd55d79 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_half_cell_tests.py @@ -7,7 +7,7 @@ class BaseUnitTestLithiumIonHalfCell: def check_well_posedness(self, options): if self.model is not None: - options["half-cell"] = "true" + options["working electrode"] = "positive" model = self.model(options) model.check_well_posedness() diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py index a7b868c6ff..6808709d92 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_basic_models.py @@ -19,12 +19,12 @@ def test_spm_well_posed(self): model.check_well_posedness() def test_dfn_half_cell_well_posed(self): - options = {"half-cell": "true"} + options = {"working electrode": "positive"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) model.check_well_posedness() def test_dfn_half_cell_simulation_with_experiment_error(self): - options = {"half-cell": "true"} + options = {"working electrode": "positive"} model = pybamm.lithium_ion.BasicDFNHalfCell(options=options) experiment = pybamm.Experiment( [("Discharge at C/10 for 10 hours or until 3.5 V")] @@ -36,7 +36,9 @@ def test_dfn_half_cell_simulation_with_experiment_error(self): pybamm.Simulation(model, experiment=experiment) def test_basic_dfn_half_cell_simulation(self): - model = pybamm.lithium_ion.BasicDFNHalfCell(options={"half-cell": "true"}) + model = pybamm.lithium_ion.BasicDFNHalfCell( + options={"working electrode": "positive"} + ) param = pybamm.ParameterValues("OKane2022") param["Current function [A]"] = 2.5 sim = pybamm.Simulation(model=model, parameter_values=param) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py index 37aa4cda48..f0db6a3a6d 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_electrode_soh.py @@ -158,9 +158,9 @@ def test_error(self): class TestElectrodeSOHHalfCell(TestCase): def test_known_solution(self): - model = pybamm.lithium_ion.ElectrodeSOHHalfCell() + model = pybamm.lithium_ion.ElectrodeSOHHalfCell("positive") - param = pybamm.LithiumIonParameters({"half-cell": "true"}) + param = pybamm.LithiumIonParameters({"working electrode": "positive"}) parameter_values = pybamm.ParameterValues("Xu2019") sim = pybamm.Simulation(model, parameter_values=parameter_values) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py index 93c5afea44..364120c741 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm_half_cell.py @@ -8,18 +8,18 @@ class TestMPM(TestCase): def test_well_posed(self): - options = {"thermal": "isothermal", "half-cell": "true"} + options = {"thermal": "isothermal", "working electrode": "positive"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() # Test build after init - model = pybamm.lithium_ion.MPM({"half-cell": "true"}, build=False) + model = pybamm.lithium_ion.MPM({"working electrode": "positive"}, build=False) model.build_model() model.check_well_posedness() def test_default_parameter_values(self): # check default parameters are added correctly - model = pybamm.lithium_ion.MPM({"half-cell": "true"}) + model = pybamm.lithium_ion.MPM({"working electrode": "positive"}) self.assertEqual( model.default_parameter_values[ "Positive area-weighted mean particle radius [m]" @@ -28,19 +28,19 @@ def test_default_parameter_values(self): ) def test_lumped_thermal_model_1D(self): - options = {"thermal": "lumped", "half-cell": "true"} + options = {"thermal": "lumped", "working electrode": "positive"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_particle_uniform(self): - options = {"particle": "uniform profile", "half-cell": "true"} + options = {"particle": "uniform profile", "working electrode": "positive"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_differential_surface_form(self): options = { "surface form": "differential", - "half-cell": "true", + "working electrode": "positive", } model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() @@ -48,12 +48,12 @@ def test_differential_surface_form(self): class TestMPMExternalCircuits(TestCase): def test_well_posed_voltage(self): - options = {"operating mode": "voltage", "half-cell": "true"} + options = {"operating mode": "voltage", "working electrode": "positive"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() def test_well_posed_power(self): - options = {"operating mode": "power", "half-cell": "true"} + options = {"operating mode": "power", "working electrode": "positive"} model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() @@ -65,7 +65,7 @@ def external_circuit_function(variables): options = { "operating mode": external_circuit_function, - "half-cell": "true", + "working electrode": "positive", } model = pybamm.lithium_ion.MPM(options) model.check_well_posedness() diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py index 16d4128fb1..f1930df026 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spme_half_cell.py @@ -1,6 +1,6 @@ # # Tests for the lithium-ion half-cell SPMe model -# This is achieved by using the {"half-cell": "true"} option +# This is achieved by using the {"working electrode": "positive"} option # import pybamm import unittest diff --git a/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py b/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py index 398ab32ad5..062d5caa24 100644 --- a/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py +++ b/tests/unit/test_parameters/test_parameter_sets/test_parameters_with_default_models.py @@ -31,7 +31,7 @@ def test_parameter_values_with_model(self): "ORegan2022": pybamm.lithium_ion.DFN(), "Prada2013": pybamm.lithium_ion.DFN(), "Ramadass2004": pybamm.lithium_ion.DFN(), - "Xu2019": pybamm.lithium_ion.DFN({"half-cell": "true"}), + "Xu2019": pybamm.lithium_ion.DFN({"working electrode": "positive"}), } # Loop over each parameter set, testing that parameters can be set diff --git a/tests/unit/test_parameters/test_size_distribution_parameters.py b/tests/unit/test_parameters/test_size_distribution_parameters.py index d07df9f8d1..8f8851bfaa 100644 --- a/tests/unit/test_parameters/test_size_distribution_parameters.py +++ b/tests/unit/test_parameters/test_size_distribution_parameters.py @@ -13,15 +13,15 @@ def test_parameter_values(self): values = pybamm.lithium_ion.BaseModel().default_parameter_values param = pybamm.LithiumIonParameters() - # add distribution parameter values for positive electrode - values = pybamm.get_size_distribution_parameters(values, halfcell="true") + # add distribution parameter values for negative electrode + values = pybamm.get_size_distribution_parameters(values, electrode="negative") - # check negative parameters aren't there yet + # check positive parameters aren't there yet with self.assertRaises(KeyError): - values["Negative maximum particle radius [m]"] + values["Positive maximum particle radius [m]"] - # now add distribution parameter values for negative electrode - values = pybamm.get_size_distribution_parameters(values, halfcell="false") + # now add distribution parameter values for positive electrode + values = pybamm.get_size_distribution_parameters(values, electrode="positive") # check parameters diff --git a/tests/unit/test_plotting/test_quick_plot.py b/tests/unit/test_plotting/test_quick_plot.py index 6acf78b861..db7de574dc 100644 --- a/tests/unit/test_plotting/test_quick_plot.py +++ b/tests/unit/test_plotting/test_quick_plot.py @@ -303,7 +303,7 @@ def test_loqs_spme(self): for model in [ pybamm.lithium_ion.SPMe(), pybamm.lead_acid.LOQS(), - pybamm.lithium_ion.SPMe({"half-cell": "true"}), + pybamm.lithium_ion.SPMe({"working electrode": "positive"}), ]: geometry = model.default_geometry param = model.default_parameter_values @@ -343,7 +343,7 @@ def test_loqs_spme(self): # test quick plot of particle for spme if ( model.name == "Single Particle Model with electrolyte" - and model.options["half-cell"] == "false" + and model.options["working electrode"] != "positive" ): output_variables = [ "X-averaged negative particle concentration [mol.m-3]", From 11d27156e32b399a454e4931dcef39943ca177f0 Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Thu, 4 Jan 2024 19:51:31 +0000 Subject: [PATCH 5/7] Restored original experiment protocol to coupled degradation example notebook --- .../notebooks/models/coupled-degradation.ipynb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/source/examples/notebooks/models/coupled-degradation.ipynb b/docs/source/examples/notebooks/models/coupled-degradation.ipynb index 1551a79a64..8c083d986a 100644 --- a/docs/source/examples/notebooks/models/coupled-degradation.ipynb +++ b/docs/source/examples/notebooks/models/coupled-degradation.ipynb @@ -105,22 +105,21 @@ "cycle_number = 10\n", "exp = pybamm.Experiment(\n", " [\n", - " \"Hold at 4.2 V until C/100\",\n", - " \"Rest for 4 hours\",\n", - " \"Discharge at 0.1C until 2.5 V\", # initial capacity check\n", - " \"Charge at 0.3C until 4.2 V\",\n", - " \"Hold at 4.2 V until C/100\",\n", + " \"Hold at 4.2 V until C/100 (5 minute period)\",\n", + " \"Rest for 4 hours (5 minute period)\",\n", + " \"Discharge at 0.1C until 2.5 V (5 minute period)\", # initial capacity check\n", + " \"Charge at 0.3C until 4.2 V (5 minute period)\",\n", + " \"Hold at 4.2 V until C/100 (5 minute period)\",\n", " ]\n", " + [\n", " (\n", " \"Discharge at 1C until 2.5 V\", # ageing cycles\n", - " \"Charge at 0.3C until 4.2 V\",\n", - " \"Hold at 4.2 V until C/100\",\n", + " \"Charge at 0.3C until 4.2 V (5 minute period)\",\n", + " \"Hold at 4.2 V until C/100 (5 minute period)\",\n", " )\n", " ]\n", " * cycle_number\n", - " + [\"Discharge at 0.1C until 2.5 V\"], # final capacity check\n", - " period=\"5 minutes\",\n", + " + [\"Discharge at 0.1C until 2.5 V (5 minute period)\"], # final capacity check\n", ")\n", "sim = pybamm.Simulation(model, parameter_values=param, experiment=exp, var_pts=var_pts)\n", "sol = sim.solve()" From 78f13f94b80533af1e319be9e456ae8c85716f76 Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Thu, 4 Jan 2024 20:02:50 +0000 Subject: [PATCH 6/7] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2f5c2bab..57f988a112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ## Bug fixes +- Reverted a change to the coupled degradation example notebook that caused it to be unstable for large numbers of cycles ([#3689](https://github.com/pybamm-team/PyBaMM/pull/3689)) - Fixed a bug where simulations using the CasADi-based solvers would fail randomly with the half-cell model ([#3494](https://github.com/pybamm-team/PyBaMM/pull/3494)) - Fixed bug that made identical Experiment steps with different end times crash ([#3516](https://github.com/pybamm-team/PyBaMM/pull/3516)) - Fixed bug in calculation of theoretical energy that made it very slow ([#3506](https://github.com/pybamm-team/PyBaMM/pull/3506)) From a5f2d68fb3d00c7dfc4be3ec02d246f20e7df14b Mon Sep 17 00:00:00 2001 From: DrSOKane Date: Fri, 5 Jan 2024 15:51:54 +0000 Subject: [PATCH 7/7] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57f988a112..599e1fc696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## Bug fixes -- Reverted a change to the coupled degradation example notebook that caused it to be unstable for large numbers of cycles ([#3689](https://github.com/pybamm-team/PyBaMM/pull/3689)) +- Reverted a change to the coupled degradation example notebook that caused it to be unstable for large numbers of cycles ([#3691](https://github.com/pybamm-team/PyBaMM/pull/3691)) - Fixed a bug where simulations using the CasADi-based solvers would fail randomly with the half-cell model ([#3494](https://github.com/pybamm-team/PyBaMM/pull/3494)) - Fixed bug that made identical Experiment steps with different end times crash ([#3516](https://github.com/pybamm-team/PyBaMM/pull/3516)) - Fixed bug in calculation of theoretical energy that made it very slow ([#3506](https://github.com/pybamm-team/PyBaMM/pull/3506))