Repository: pybamm-team/PyBaMM

Issue:
SEI transfer coefficient
The SEI transfer coefficient for `reaction limited` and `EC reaction limited` have the transfer coefficient hardcoded to 0.5. This value is a modelling assumption (see [Safari et al. 2009](https://iopscience.iop.org/article/10.1149/1.3043429/pdf)), so I think we should allow to define it in the parameter values so it can be changed.

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2ee1d24d..560886789 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 ## Features
 
+- SEI reactions can now be asymmetric ([#2425](https://github.com/pybamm-team/PyBaMM/pull/2425))
 - Third-party parameter sets can be added by registering entry points to `pybamm_parameter_set` ([#2396](https://github.com/pybamm-team/PyBaMM/pull/2396))
 - Added three-dimensional interpolation ([#2380](https://github.com/pybamm-team/PyBaMM/pull/2380))
 
diff --git a/benchmarks/different_model_options.py b/benchmarks/different_model_options.py
index 06af90003..b32839817 100644
--- a/benchmarks/different_model_options.py
+++ b/benchmarks/different_model_options.py
@@ -124,10 +124,12 @@ class TimeBuildModelSEI:
             "none",
             "constant",
             "reaction limited",
+            "reaction limited (asymmetric)",
             "solvent-diffusion limited",
             "electron-migration limited",
             "interstitial-diffusion limited",
             "ec reaction limited",
+            "ec reaction limited (asymmetric)",
         ],
     )
 
@@ -143,10 +145,12 @@ class TimeSolveSEI:
             "none",
             "constant",
             "reaction limited",
+            "reaction limited (asymmetric)",
             "solvent-diffusion limited",
             "electron-migration limited",
             "interstitial-diffusion limited",
             "ec reaction limited",
+            "ec reaction limited (asymmetric)",
         ],
     )
 
diff --git a/examples/scripts/calendar_ageing.py b/examples/scripts/calendar_ageing.py
index 5201f0a7c..b33b4d35c 100644
--- a/examples/scripts/calendar_ageing.py
+++ b/examples/scripts/calendar_ageing.py
@@ -7,10 +7,12 @@ models = [
     pb.lithium_ion.SPM({"SEI": "reaction limited"}),
     pb.lithium_ion.SPMe({"SEI": "reaction limited"}),
     pb.lithium_ion.SPM(
-        {"SEI": "reaction limited", "surface form": "algebraic"}, name="Algebraic SPM"
+        {"SEI": "reaction limited", "surface form": "algebraic"},
+        name="Algebraic SPM",
     ),
     pb.lithium_ion.SPMe(
-        {"SEI": "reaction limited", "surface form": "algebraic"}, name="Algebraic SPMe"
+        {"SEI": "reaction limited", "surface form": "algebraic"},
+        name="Algebraic SPMe",
     ),
     pb.lithium_ion.DFN({"SEI": "reaction limited"}),
 ]
diff --git a/pybamm/models/full_battery_models/base_battery_model.py b/pybamm/models/full_battery_models/base_battery_model.py
index a838f7f4e..ccb47e745 100644
--- a/pybamm/models/full_battery_models/base_battery_model.py
+++ b/pybamm/models/full_battery_models/base_battery_model.py
@@ -121,9 +121,10 @@ class BatteryModelOptions(pybamm.FuzzyDict):
 
                 - "none": :class:`pybamm.sei.NoSEI` (no SEI growth)
                 - "constant": :class:`pybamm.sei.Constant` (constant SEI thickness)
-                - "reaction limited", "solvent-diffusion limited",\
-                    "electron-migration limited", "interstitial-diffusion limited", \
-                    or "ec reaction limited": :class:`pybamm.sei.SEIGrowth`
+                - "reaction limited", "reaction limited (asymmetric)", \
+                    "solvent-diffusion limited", "electron-migration limited", \
+                    "interstitial-diffusion limited", "ec reaction limited" \
+                    or "ec reaction limited (asymmetric)": :class:`pybamm.sei.SEIGrowth`
             * "SEI film resistance" : str
                 Set the submodel for additional term in the overpotential due to SEI.
                 The default value is "none" if the "SEI" option is "none", and
@@ -255,10 +256,12 @@ class BatteryModelOptions(pybamm.FuzzyDict):
                 "none",
                 "constant",
                 "reaction limited",
+                "reaction limited (asymmetric)",
                 "solvent-diffusion limited",
                 "electron-migration limited",
                 "interstitial-diffusion limited",
                 "ec reaction limited",
+                "ec reaction limited (asymmetric)",
             ],
             "SEI film resistance": ["none", "distributed", "average"],
             "SEI on cracks": ["false", "true"],
diff --git a/pybamm/models/submodels/interface/sei/base_sei.py b/pybamm/models/submodels/interface/sei/base_sei.py
index f3f796a86..2f3744193 100644
--- a/pybamm/models/submodels/interface/sei/base_sei.py
+++ b/pybamm/models/submodels/interface/sei/base_sei.py
@@ -198,8 +198,9 @@ class BaseModel(BaseInterface):
                 )
             v_bar = phase_param.v_bar
             z_sei = phase_param.z_sei
-            # Set scales for the "EC Reaction Limited" model
-            if self.options["SEI"] == "ec reaction limited":
+            # Set scales for the "EC Reaction Limited" models (both symmetric and
+            # asymmetric)
+            if self.options["SEI"].startswith("ec reaction limited"):
                 L_inner_0 = 0
                 L_outer_0 = 1
                 L_inner_crack_0 = 0
diff --git a/pybamm/models/submodels/interface/sei/sei_growth.py b/pybamm/models/submodels/interface/sei/sei_growth.py
index dcd991919..9e8b81237 100644
--- a/pybamm/models/submodels/interface/sei/sei_growth.py
+++ b/pybamm/models/submodels/interface/sei/sei_growth.py
@@ -56,7 +56,7 @@ class SEIGrowth(BaseModel):
 
         L_inner, L_outer = Ls
 
-        if self.options["SEI"] == "ec reaction limited":
+        if self.options["SEI"].startswith("ec reaction limited"):
             L_inner = 0 * L_inner  # Set L_inner to zero, copying domains
 
         variables = self._get_standard_thickness_variables(L_inner, L_outer)
@@ -100,9 +100,16 @@ class SEIGrowth(BaseModel):
         # Thermal prefactor for reaction, interstitial and EC models
         prefactor = 1 / (1 + self.param.Theta * T)
 
-        if self.options["SEI"] == "reaction limited":
+        # Define alpha_SEI depending on whether it is symmetric or asymmetric. This
+        # applies to "reaction limited" and "EC reaction limited"
+        if self.options["SEI"].endswith("(asymmetric)"):
+            alpha_SEI = phase_param.alpha_SEI
+        else:
+            alpha_SEI = 0.5
+
+        if self.options["SEI"].startswith("reaction limited"):
             C_sei = phase_param.C_sei_reaction
-            j_sei = -(1 / C_sei) * pybamm.exp(-0.5 * prefactor * eta_SEI)
+            j_sei = -(1 / C_sei) * pybamm.exp(-alpha_SEI * prefactor * eta_SEI)
 
         elif self.options["SEI"] == "electron-migration limited":
             U_inner = phase_param.U_inner_electron
@@ -117,7 +124,7 @@ class SEIGrowth(BaseModel):
             C_sei = phase_param.C_sei_solvent
             j_sei = -1 / (C_sei * L_sei_outer)
 
-        elif self.options["SEI"] == "ec reaction limited":
+        elif self.options["SEI"].startswith("ec reaction limited"):
             C_sei_ec = phase_param.C_sei_ec
             C_ec = phase_param.C_ec
 
@@ -129,7 +136,7 @@ class SEIGrowth(BaseModel):
             # so
             #  j_sei = -C_sei_ec * exp() / (1 + L_sei * C_ec * C_sei_ec * exp())
             #  c_ec = 1 / (1 + L_sei * C_ec * C_sei_ec * exp())
-            C_sei_exp = C_sei_ec * pybamm.exp(-0.5 * prefactor * eta_SEI)
+            C_sei_exp = C_sei_ec * pybamm.exp(-alpha_SEI * prefactor * eta_SEI)
             j_sei = -C_sei_exp / (1 + L_sei * C_ec * C_sei_exp)
             c_ec = 1 / (1 + L_sei * C_ec * C_sei_exp)
 
@@ -150,7 +157,7 @@ class SEIGrowth(BaseModel):
                 }
             )
 
-        if self.options["SEI"] == "ec reaction limited":
+        if self.options["SEI"].startswith("ec reaction limited"):
             inner_sei_proportion = 0
         else:
             inner_sei_proportion = phase_param.inner_sei_proportion
@@ -224,7 +231,7 @@ class SEIGrowth(BaseModel):
 
         Gamma_SEI = self.phase_param.Gamma_SEI
 
-        if self.options["SEI"] == "ec reaction limited":
+        if self.options["SEI"].startswith("ec reaction limited"):
             self.rhs = {L_outer: -Gamma_SEI * a * j_outer + spreading_outer}
         else:
             v_bar = self.phase_param.v_bar
@@ -247,7 +254,7 @@ class SEIGrowth(BaseModel):
         else:
             L_inner_0 = self.phase_param.L_inner_0
             L_outer_0 = self.phase_param.L_outer_0
-        if self.options["SEI"] == "ec reaction limited":
+        if self.options["SEI"].startswith("ec reaction limited"):
             self.initial_conditions = {L_outer: L_inner_0 + L_outer_0}
         else:
             self.initial_conditions = {L_inner: L_inner_0, L_outer: L_outer_0}
diff --git a/pybamm/parameters/lithium_ion_parameters.py b/pybamm/parameters/lithium_ion_parameters.py
index 7ffe53186..811a66a1d 100644
--- a/pybamm/parameters/lithium_ion_parameters.py
+++ b/pybamm/parameters/lithium_ion_parameters.py
@@ -704,6 +704,7 @@ class ParticleLithiumIonParameters(BaseParameters):
             self.E_sei_dimensional = pybamm.Parameter(
                 f"{pref}SEI growth activation energy [J.mol-1]"
             )
+            self.alpha_SEI = pybamm.Parameter(f"{pref}SEI growth transfer coefficient")
 
             # EC reaction
             self.c_ec_0_dim = pybamm.Parameter(


Test patch:
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 c8055ebcc..0c203c9fc 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
@@ -53,6 +53,15 @@ class BaseIntegrationTestLithiumIonHalfCell:
         options = {"SEI": "reaction limited"}
         self.run_basic_processing_test(options)
 
+    def test_sei_asymmetric_reaction_limited(self):
+        options = {"SEI": "reaction limited (asymmetric)"}
+        parameter_values = pybamm.ParameterValues("Xu2019")
+        parameter_values.update(
+            {"SEI growth transfer coefficient": 0.2},
+            check_already_exists=False,
+        )
+        self.run_basic_processing_test(options, parameter_values=parameter_values)
+
     def test_sei_solvent_diffusion_limited(self):
         options = {"SEI": "solvent-diffusion limited"}
         self.run_basic_processing_test(options)
@@ -69,6 +78,15 @@ class BaseIntegrationTestLithiumIonHalfCell:
         options = {"SEI": "ec reaction limited"}
         self.run_basic_processing_test(options)
 
+    def test_sei_asymmetric_ec_reaction_limited(self):
+        options = {"SEI": "ec reaction limited (asymmetric)"}
+        parameter_values = pybamm.ParameterValues("Xu2019")
+        parameter_values.update(
+            {"SEI growth transfer coefficient": 0.2},
+            check_already_exists=False,
+        )
+        self.run_basic_processing_test(options, parameter_values=parameter_values)
+
     def test_constant_utilisation(self):
         options = {"interface utilisation": "constant"}
         parameter_values = pybamm.ParameterValues("Xu2019")
diff --git a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
index 6d4676622..33766b485 100644
--- a/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
+++ b/tests/integration/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
@@ -169,6 +169,15 @@ class BaseIntegrationTestLithiumIon:
         options = {"SEI": "reaction limited"}
         self.run_basic_processing_test(options)
 
+    def test_sei_asymmetric_reaction_limited(self):
+        options = {"SEI": "reaction limited (asymmetric)"}
+        parameter_values = pybamm.ParameterValues("Marquis2019")
+        parameter_values.update(
+            {"SEI growth transfer coefficient": 0.2},
+            check_already_exists=False,
+        )
+        self.run_basic_processing_test(options, parameter_values=parameter_values)
+
     def test_sei_solvent_diffusion_limited(self):
         options = {"SEI": "solvent-diffusion limited"}
         self.run_basic_processing_test(options)
@@ -182,9 +191,24 @@ class BaseIntegrationTestLithiumIon:
         self.run_basic_processing_test(options)
 
     def test_sei_ec_reaction_limited(self):
-        options = {"SEI": "ec reaction limited", "SEI porosity change": "true"}
+        options = {
+            "SEI": "ec reaction limited",
+            "SEI porosity change": "true",
+        }
         self.run_basic_processing_test(options)
 
+    def test_sei_asymmetric_ec_reaction_limited(self):
+        options = {
+            "SEI": "ec reaction limited (asymmetric)",
+            "SEI porosity change": "true",
+        }
+        parameter_values = pybamm.ParameterValues("Marquis2019")
+        parameter_values.update(
+            {"SEI growth transfer coefficient": 0.2},
+            check_already_exists=False,
+        )
+        self.run_basic_processing_test(options, parameter_values=parameter_values)
+
     def test_loss_active_material_stress_negative(self):
         options = {"loss of active material": ("none", "stress-driven")}
         parameter_values = pybamm.ParameterValues("Ai2020")
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 2678bca5b..b2fa082f7 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
@@ -34,7 +34,7 @@ PRINT_OPTIONS_OUTPUT = """\
 'particle phases': '1' (possible: ['1', '2'])
 'particle shape': 'spherical' (possible: ['spherical', 'no particles'])
 'particle size': 'single' (possible: ['single', 'distribution'])
-'SEI': 'none' (possible: ['none', 'constant', 'reaction limited', 'solvent-diffusion limited', 'electron-migration limited', 'interstitial-diffusion limited', 'ec reaction limited'])
+'SEI': 'none' (possible: ['none', 'constant', 'reaction limited', 'reaction limited (asymmetric)', 'solvent-diffusion limited', 'electron-migration limited', 'interstitial-diffusion limited', 'ec reaction limited', 'ec reaction limited (asymmetric)'])
 'SEI film resistance': 'none' (possible: ['none', 'distributed', 'average'])
 'SEI on cracks': 'false' (possible: ['false', 'true'])
 'SEI porosity change': 'false' (possible: ['false', 'true'])
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 1635c2ea4..24ccd55d7 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
@@ -55,6 +55,10 @@ class BaseUnitTestLithiumIonHalfCell:
         options = {"SEI": "reaction limited"}
         self.check_well_posedness(options)
 
+    def test_well_posed_asymmetric_reaction_limited_sei(self):
+        options = {"SEI": "reaction limited (asymmetric)"}
+        self.check_well_posedness(options)
+
     def test_well_posed_solvent_diffusion_limited_sei(self):
         options = {"SEI": "solvent-diffusion limited"}
         self.check_well_posedness(options)
@@ -71,6 +75,10 @@ class BaseUnitTestLithiumIonHalfCell:
         options = {"SEI": "ec reaction limited"}
         self.check_well_posedness(options)
 
+    def test_well_posed_asymmetric_ec_reaction_limited_sei(self):
+        options = {"SEI": "ec reaction limited (asymmetric)"}
+        self.check_well_posedness(options)
+
     def test_well_posed_lumped_thermal(self):
         options = {"thermal": "lumped"}
         self.check_well_posedness(options)
diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
index c3188c289..7dbe12822 100644
--- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
+++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/base_lithium_ion_tests.py
@@ -147,8 +147,22 @@ class BaseUnitTestLithiumIon:
         options = {"SEI": "reaction limited"}
         self.check_well_posedness(options)
 
+    def test_well_posed_asymmetric_sei_reaction_limited(self):
+        options = {"SEI": "reaction limited (asymmetric)"}
+        self.check_well_posedness(options)
+
     def test_well_posed_sei_reaction_limited_average_film_resistance(self):
-        options = {"SEI": "reaction limited", "SEI film resistance": "average"}
+        options = {
+            "SEI": "reaction limited",
+            "SEI film resistance": "average",
+        }
+        self.check_well_posedness(options)
+
+    def test_well_posed_asymmetric_sei_reaction_limited_average_film_resistance(self):
+        options = {
+            "SEI": "reaction limited (asymmetric)",
+            "SEI film resistance": "average",
+        }
         self.check_well_posedness(options)
 
     def test_well_posed_sei_solvent_diffusion_limited(self):
@@ -164,7 +178,17 @@ class BaseUnitTestLithiumIon:
         self.check_well_posedness(options)
 
     def test_well_posed_sei_ec_reaction_limited(self):
-        options = {"SEI": "ec reaction limited", "SEI porosity change": "true"}
+        options = {
+            "SEI": "ec reaction limited",
+            "SEI porosity change": "true",
+        }
+        self.check_well_posedness(options)
+
+    def test_well_posed_sei_asymmetric_ec_reaction_limited(self):
+        options = {
+            "SEI": "ec reaction limited (asymmetric)",
+            "SEI porosity change": "true",
+        }
         self.check_well_posedness(options)
 
     def test_well_posed_mechanics_negative_cracking(self):
diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm.py
index 0798d095c..5c824164e 100644
--- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm.py
+++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_mpm.py
@@ -141,7 +141,10 @@ class TestMPMWithSEI(unittest.TestCase):
             pybamm.lithium_ion.MPM(options)
 
     def test_ec_reaction_limited_not_implemented(self):
-        options = {"SEI": "ec reaction limited", "SEI porosity change": "true"}
+        options = {
+            "SEI": "ec reaction limited",
+            "SEI porosity change": "true",
+        }
         with self.assertRaises(NotImplementedError):
             pybamm.lithium_ion.MPM(options)