PyBaMM EIS with coupled degradation models

Hello Everyone,

I was instructed in in this discussion to use PyBaMM-EIS.

However, when I tried to run PyBaMM-EIS with degradation submodels it gave me the following errors. The snipped code and error message are mentioned below.

Is it possible to guide me in this matter thank you in advance.

import matplotlib.pyplot as plt
import numpy as np
from scipy.fft import fft
import pybamm
import time as timer

# Set random seed
np.random.seed(0)

# Original function for degradation parameters
pp = pybamm.ParameterValues("OKane2022")

# Function to get Chen2020 parameters
def get_chen2020_parameters():
    parameter_values = pybamm.ParameterValues("Chen2020")
    return parameter_values

# Function to get OKane2022 degradation parameters with k_cr included
def get_okane2022_degradation_parameters():
    degradation_parameters = {
        'Dead lithium decay constant [s-1]': 1e-06,
        'Dead lithium decay rate [s-1]': pp["Dead lithium decay rate [s-1]"],
        'Initial plated lithium concentration [mol.m-3]': 0.0,
        'Lithium metal partial molar volume [m3.mol-1]': 1.3e-05,
        'Lithium plating kinetic rate constant [m.s-1]': 1e-09,
        'Lithium plating transfer coefficient': 0.65,
        'Negative electrode LAM constant exponential term': 2.0,
        'Negative electrode LAM constant proportional term [s-1]': 2.7778e-07,
        "Negative electrode Paris' law constant b": 1.12,
        "Negative electrode Paris' law constant m": 2.2,
        "Negative electrode Poisson's ratio": 0.3,
        "Negative electrode Young's modulus [Pa]": 15000000000.0,
        'Negative electrode cracking rate': pp["Negative electrode cracking rate"],  # Function
        'Negative electrode critical stress [Pa]': 60000000.0,
        'Negative electrode initial crack length [m]': 2e-08,
        'Negative electrode initial crack width [m]': 1.5e-08,
        'Negative electrode number of cracks per unit area [m-2]': 3180000000000000.0,
        'Negative electrode partial molar volume [m3.mol-1]': 3.1e-06,
        'Negative electrode reference concentration for free of deformation [mol.m-3]': 0.0,
        'Negative electrode volume change': pp["Negative electrode volume change"],
        'Positive electrode LAM constant exponential term': 2.0,
        'Positive electrode LAM constant proportional term [s-1]': 2.7778e-07,
        'Positive electrode OCP entropic change [V.K-1]': 0.0,
        "Positive electrode Paris' law constant b": 1.12,
        "Positive electrode Paris' law constant m": 2.2,
        "Positive electrode Poisson's ratio": 0.2,
        "Positive electrode Young's modulus [Pa]": 375000000000.0,
        'Positive electrode active material volume fraction': 0.665,
        'Positive electrode cracking rate': pp["Positive electrode cracking rate"],  # Function
        'Positive electrode critical stress [Pa]': 375000000.0,
        'Positive electrode initial crack length [m]': 2e-08,
        'Positive electrode initial crack width [m]': 1.5e-08,
        'Positive electrode number of cracks per unit area [m-2]': 3180000000000000.0,
        'Positive electrode partial molar volume [m3.mol-1]': 1.25e-05,
        'Positive electrode reference concentration for free of deformation [mol.m-3]': 0.0,
        'Positive electrode volume change': pp["Positive electrode volume change"],
        'Typical plated lithium concentration [mol.m-3]': 1000.0,
        'Exchange-current density for stripping [A.m-2]': pp["Exchange-current density for stripping [A.m-2]"],
        'Exchange-current density for plating [A.m-2]': pp["Exchange-current density for plating [A.m-2]"],
        "k_cr": pybamm.Parameter("k_cr"),  # Define as a parameter object
    }
    return degradation_parameters

# Graphite cracking rate function
def graphite_cracking_rate_Ai2020(T_dim):
    """
    Calculates the graphite cracking rate using an Arrhenius-like relationship.
    T_dim: Dimensional temperature (in Kelvin)
    """
    Eac_cr = 0  # Activation energy for cracking [J/mol], update if needed
    arrhenius = pybamm.exp(Eac_cr / pybamm.constants.R * (1 / T_dim - 1 / 298.15))
    return pybamm.Parameter("k_cr") * arrhenius

# Function to update parameters
def update_parameters():
    # Load Chen2020 parameters
    chen2020_params = get_chen2020_parameters()

    # Load OKane2022 degradation parameters
    okane2022_degradation_params = get_okane2022_degradation_parameters()

    # Update Chen2020 parameters with OKane2022 degradation parameters
    chen2020_params.update(okane2022_degradation_params, check_already_exists=False)

    # Add graphite cracking rate function
    chen2020_params["k_cr"] = 3.9e-20

    return chen2020_params

# Update the parameters
updated_parameters = update_parameters()

# Define the model with configuration
model = pybamm.lithium_ion.SPMe(
    {   
        "thermal": "lumped",  # Lumped thermal submodel
        "surface form": "differential",  # Adding capacitance to the model
        "SEI": "solvent-diffusion limited",
        "SEI porosity change": "true",
        "particle mechanics": "swelling and cracking",
        "SEI on cracks": "true",
    }
)

# Variable points
var_pts = {
    "x_n": 5,
    "x_s": 5,
    "x_p": 5,
    "r_n": 30,
    "r_p": 30,
}


# Simulation parameters
cycle_numbers = [50]


# Store results for Nyquist plot
impedance_data = {}

# Loop through each cycle count
for cycle_number in cycle_numbers:
    exp = pybamm.Experiment(
        [
            (
                "Discharge at 1C until 2.5 V",  # Ageing cycles
                "Charge at 0.6C until 4.2 V (5 minute period)",
                "Hold at 4.2 V until C/100 (5 minute period)",
            )
        ]
        * cycle_number
    )

    sim = pybamm.Simulation(
        model,
        parameter_values=updated_parameters,
        experiment=exp,
        solver=pybamm.CasadiSolver(mode='safe without grid'),
        var_pts=var_pts,
    )

    sol = sim.solve()

# Frequency domain
import pbeis
aged_model = model.set_initial_conditions_from(sol)

frequencies = np.logspace(-4, 2, 30)

methods = ["direct"]
impedances_freqs = []
for method in methods:
    start_time = timer.time()
    eis_sim = pbeis.EISSimulation(aged_model, parameter_values=updated_parameters)
    impedances_freq = eis_sim.solve(frequencies, method)
    end_time = timer.time()
    time_elapsed = end_time - start_time
    print(f"Frequency domain ({method}): ", time_elapsed, "s")
    impedances_freqs.append(impedances_freq)

---------------------------------------------------------------------------
ModelError                                Traceback (most recent call last)
Cell In[4], line 11
      9 for method in methods:
     10     start_time = timer.time()
---> 11     eis_sim = pbeis.EISSimulation(aged_model, parameter_values=updated_parameters)
     12     impedances_freq = eis_sim.solve(frequencies, method)
     13     end_time = timer.time()

File ~/pybamm-eis/pbeis/eis_simulation.py:61, in EISSimulation.__init__(self, model, parameter_values, geometry, submesh_types, var_pts, spatial_methods)
     52 parameter_values["Current function [A]"] = 0
     53 sim = pybamm.Simulation(
     54     self.model,
     55     geometry=geometry,
   (...)
     59     spatial_methods=spatial_methods,
     60 )
---> 61 sim.build()
     62 self.built_model = sim.built_model
     64 # Extract mass matrix and Jacobian

File ~/pybamm-eis/env/lib/python3.9/site-packages/pybamm/simulation.py:439, in Simulation.build(self, check_model, initial_soc)
    437 self._mesh = pybamm.Mesh(self._geometry, self._submesh_types, self._var_pts)
    438 self._disc = pybamm.Discretisation(self._mesh, self._spatial_methods)
--> 439 self._built_model = self._disc.process_model(
    440     self._model_with_set_params, inplace=False, check_model=check_model
    441 )

File ~/pybamm-eis/env/lib/python3.9/site-packages/pybamm/discretisations/discretisation.py:269, in Discretisation.process_model(self, model, inplace, check_model, remove_independent_variables_from_rhs)
    267 if check_model:
    268     pybamm.logger.verbose("Performing model checks for {}".format(model.name))
--> 269     self.check_model(model_disc)
    271 pybamm.logger.info("Finish discretising {}".format(model.name))
    273 # Record that the model has been discretised

File ~/pybamm-eis/env/lib/python3.9/site-packages/pybamm/discretisations/discretisation.py:1076, in Discretisation.check_model(self, model)
   1074 def check_model(self, model):
   1075     """Perform some basic checks to make sure the discretised model makes sense."""
-> 1076     self.check_initial_conditions(model)
   1077     self.check_variables(model)

File ~/pybamm-eis/env/lib/python3.9/site-packages/pybamm/discretisations/discretisation.py:1106, in Discretisation.check_initial_conditions(self, model)
   1104 for var in model.rhs.keys():
   1105     if model.rhs[var].shape != model.initial_conditions[var].shape:
-> 1106         raise pybamm.ModelError(
   1107             "rhs and initial conditions must have the same shape after "
   1108             "discretisation but rhs.shape = "
   1109             "{} and initial_conditions.shape = {} for variable '{}'.".format(
   1110                 model.rhs[var].shape, model.initial_conditions[var].shape, var
   1111             )
   1112         )
   1113 for var in model.algebraic.keys():
   1114     if model.algebraic[var].shape != model.initial_conditions[var].shape:

ModelError: rhs and initial conditions must have the same shape after discretisation but rhs.shape = (20, 1) and initial_conditions.shape = (30, 1) for variable 'X-averaged negative particle concentration'.

It looks like a mismatch in the var_pts. Can you pass var_pts to the EIS Simulation class?

Hi @brosaplanella ,

Thank you for your suggestion this error is now fixed. However, I have a another issue again.

I am trying to obtain EIS from pybamm-eis methods, however, the EIS curve is far different from the actual values of resistance 0.025 ohm in the datasheet of the LGM50 cell (Refer to the figure below).

Any comment from the PyBaMM team about this?

Thank you for your consideration and looking forward to hearing.