Tip
An interactive online version of this notebook is available, which can be accessed via
Alternatively, you may download this notebook and run it offline.
Test the parameter set of the Enertech cells#
In this notebook, we show how to use pybamm to reproduce the experimental results for the Enertech cells (LCO-G). To see all of the models and submodels available in PyBaMM, please take a look at the documentation here.
[1]:
%pip install "pybamm[plot,cite]" -q # install PyBaMM if it is not installed
import pybamm
import os
import matplotlib.pyplot as plt
os.chdir(pybamm.__path__[0] + "/..")
Note: you may need to restart the kernel to use updated packages.
An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.
Let’s load the DFN and include particle swelling
[2]:
model = pybamm.lithium_ion.DFN(
options={
"particle": "Fickian diffusion",
"cell geometry": "arbitrary",
"thermal": "lumped",
"particle mechanics": "swelling only",
}
)
We can get the parameter set Ai2020
for the model, which includes the mechanical properties required by the mechanical model.
[3]:
# update parameters, making C-rate and input
param = pybamm.ParameterValues("Ai2020")
capacity = param["Nominal cell capacity [A.h]"]
param.update({"Current function [A]": capacity * pybamm.InputParameter("C-rate")})
# update the mesh
var = pybamm.standard_spatial_vars
var_pts = {
var.x_n: 50,
var.x_s: 50,
var.x_p: 50,
var.r_n: 21,
var.r_p: 21,
}
# define the simulation
sim = pybamm.Simulation(
model,
var_pts=var_pts,
parameter_values=param,
solver=pybamm.CasadiSolver(mode="fast"),
)
# solve for different C-rates
Crates = [0.5, 1, 2]
solutions = []
for Crate in Crates:
print(f"{Crate} C")
sol = sim.solve(t_eval=[0, 3600 / Crate * 1.05], inputs={"C-rate": Crate})
solutions.append(sol)
# unpack solutions
solution05C, solution1C, solution2C = solutions
0.5 C
1 C
2 C
Load experimental results of the Enertech cells (see [1])
[4]:
# load experimental results
import pandas as pd
data_loader = pybamm.DataLoader()
data_Disp_01C = pd.read_csv(
data_loader.get_data("0.1C_discharge_displacement.txt"),
delimiter="\s+",
header=None,
)
data_Disp_05C = pd.read_csv(
data_loader.get_data("0.5C_discharge_displacement.txt"),
delimiter="\s+",
header=None,
)
data_Disp_1C = pd.read_csv(
data_loader.get_data("1C_discharge_displacement.txt"), delimiter="\s+", header=None
)
data_Disp_2C = pd.read_csv(
data_loader.get_data("2C_discharge_displacement.txt"), delimiter="\s+", header=None
)
data_V_01C = pd.read_csv(
data_loader.get_data("0.1C_discharge_U.txt"), delimiter="\s+", header=None
)
data_V_05C = pd.read_csv(
data_loader.get_data("0.5C_discharge_U.txt"), delimiter="\s+", header=None
)
data_V_1C = pd.read_csv(
data_loader.get_data("1C_discharge_U.txt"), delimiter="\s+", header=None
)
data_V_2C = pd.read_csv(
data_loader.get_data("2C_discharge_U.txt"), delimiter="\s+", header=None
)
data_T_05C = pd.read_csv(
data_loader.get_data("0.5C_discharge_T.txt"), delimiter="\s+", header=None
)
data_T_1C = pd.read_csv(
data_loader.get_data("1C_discharge_T.txt"), delimiter="\s+", header=None
)
data_T_2C = pd.read_csv(
data_loader.get_data("2C_discharge_T.txt"), delimiter="\s+", header=None
)
Downloading file '0.1C_discharge_displacement.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/0.1C_discharge_displacement.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '0.5C_discharge_displacement.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/0.5C_discharge_displacement.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '1C_discharge_displacement.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/1C_discharge_displacement.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '2C_discharge_displacement.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/2C_discharge_displacement.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '0.1C_discharge_U.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/0.1C_discharge_U.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '0.5C_discharge_U.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/0.5C_discharge_U.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '1C_discharge_U.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/1C_discharge_U.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '2C_discharge_U.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/2C_discharge_U.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '0.5C_discharge_T.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/0.5C_discharge_T.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '1C_discharge_T.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/1C_discharge_T.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Downloading file '2C_discharge_T.txt' from 'https://github.com/pybamm-team/pybamm-data/releases/download/v1.0.0/2C_discharge_T.txt' to '/home/santa/.cache/pybamm/v1.0.0'.
Plot the results.
[7]:
t_all2C = solution2C["Time [h]"].entries
V_n2C = solution2C["Voltage [V]"].entries
T_n2C = (
solution2C["Volume-averaged cell temperature [K]"].entries
- param["Initial temperature [K]"]
)
L_x2C = solution2C["Cell thickness change [m]"].entries
t_all1C = solution1C["Time [h]"].entries
V_n1C = solution1C["Voltage [V]"].entries
T_n1C = (
solution1C["Volume-averaged cell temperature [K]"].entries
- param["Initial temperature [K]"]
)
L_x1C = solution1C["Cell thickness change [m]"].entries
t_all05C = solution05C["Time [h]"].entries
V_n05C = solution05C["Voltage [V]"].entries
T_n05C = (
solution05C["Volume-averaged cell temperature [K]"].entries
- param["Initial temperature [K]"]
)
L_x05C = solution05C["Cell thickness change [m]"].entries
f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 4))
ax1.plot(t_all2C, V_n2C, "r-", label="Simulation")
ax1.plot(
data_V_2C.values[::30, 0] / 3600,
data_V_2C.values[::30, 1],
"ro",
markerfacecolor="none",
label="Experiment",
)
ax1.plot(t_all05C, V_n05C, "g-")
ax1.plot(
data_V_05C.values[::100, 0] / 3600,
data_V_05C.values[::100, 1],
"go",
markerfacecolor="none",
)
ax1.plot(t_all1C, V_n1C, "b-")
ax1.plot(
data_V_1C.values[::50, 0] / 3600,
data_V_1C.values[::50, 1],
"bo",
markerfacecolor="none",
)
ax1.legend()
ax1.set_xlabel("Time [h]")
ax1.set_ylabel("Voltage [V]")
ax1.text(0.1, 3.2, r"2 C", {"color": "r", "fontsize": 14})
ax1.text(1.1, 3.2, r"1 C", {"color": "b", "fontsize": 14})
ax1.text(1.6, 3.2, r"0.5 C", {"color": "g", "fontsize": 14})
ax2.plot(t_all2C, T_n2C, "r-", label="Simulation")
ax2.plot(
data_T_2C.values[0:1754:50, 0] / 3600,
data_T_2C.values[0:1754:50, 1],
"ro",
markerfacecolor="none",
label="Experiment",
)
ax2.plot(t_all05C, T_n05C, "g-")
ax2.plot(
data_T_05C.values[0:7301:200, 0] / 3600,
data_T_05C.values[0:7301:200, 1],
"go",
markerfacecolor="none",
)
ax2.plot(t_all1C, T_n1C, "b-")
ax2.plot(
data_T_1C.values[0:3598:100, 0] / 3600,
data_T_1C.values[0:3598:100, 1],
"bo",
markerfacecolor="none",
)
ax2.legend()
ax2.set_xlabel("Time [h]")
ax2.set_ylabel("Temperature rise [K]")
ax2.text(0.5, 8, r"2 C", {"color": "r", "fontsize": 14})
ax2.text(0.8, 4.4, r"1 C", {"color": "b", "fontsize": 14})
ax2.text(1.5, 2, r"0.5 C", {"color": "g", "fontsize": 14})
ax3.plot(t_all2C, L_x2C, "r-", label="Simulation")
ax3.plot(
data_Disp_2C.values[0:1754:5, 0] / 3600,
data_Disp_2C.values[0:1754:5, 1] - data_Disp_2C.values[0, 1],
"ro",
markerfacecolor="none",
label="Experiment",
)
ax3.plot(t_all05C, L_x05C, "g-")
ax3.plot(
data_Disp_05C.values[0:1754:10, 0] / 3600,
data_Disp_05C.values[0:1754:10, 1] - data_Disp_05C.values[0, 1],
"go",
markerfacecolor="none",
)
ax3.plot(t_all1C, L_x1C, "b-")
ax3.plot(
data_Disp_1C.values[0:1754:10, 0] / 3600,
data_Disp_1C.values[0:1754:10, 1] - data_Disp_1C.values[0, 1],
"bo",
markerfacecolor="none",
)
ax3.legend()
ax3.set_xlabel("Time [h]")
ax3.set_ylabel("Thickness change [m]")
ax3.text(0.1, -0.0001, r"2 C", {"color": "r", "fontsize": 14})
ax3.text(0.9, -0.0001, r"1 C", {"color": "b", "fontsize": 14})
ax3.text(1.8, -0.0001, r"0.5 C", {"color": "g", "fontsize": 14})
f.tight_layout()
f.show()
Stress data below are from Fig. 6 in [1]
[8]:
E_n = param["Negative electrode Young's modulus [Pa]"]
E_p = param["Positive electrode Young's modulus [Pa]"]
cs_n_xav = solution2C["X-averaged negative particle concentration [mol.m-3]"].entries
cs_p_xav = solution2C["X-averaged positive particle concentration [mol.m-3]"].entries
st_surf_n = solution2C["Negative particle surface tangential stress [Pa]"].entries / E_n
st_surf_p = solution2C["Positive particle surface tangential stress [Pa]"].entries / E_p
data_st_n_2C = pd.read_csv(data_loader.get_data("stn_2C.txt"), delimiter=",", header=3)
data_st_p_2C = pd.read_csv(data_loader.get_data("stp_2C.txt"), delimiter=",", header=3)
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 3.5))
ax1.plot(t_all2C, st_surf_n[-1, :], "ro", markerfacecolor="none", label="Current")
ax1.plot(
data_st_n_2C.values[:, 0] / 3600,
data_st_n_2C.values[:, 1],
"r-",
label="Ai et al. 2020",
)
ax1.legend()
ax1.set_xlabel("Time [h]")
ax1.set_ylabel("$\sigma_{t,n}/E_n$")
ax2.plot(t_all2C, st_surf_p[0, :], "ro", markerfacecolor="none", label="Current")
ax2.plot(
data_st_p_2C.values[0:3601, 0] / 3600,
data_st_p_2C.values[0:3601, 1],
"r-",
label="Ai et al. 2020",
)
ax2.legend()
ax2.set_xlabel("Time [h]")
ax2.set_ylabel("$\sigma_{t,p}/E_p$")
f.tight_layout()
f.show()
References#
The relevant papers for this notebook are:
[21]:
pybamm.print_citations()
[1] Weilong Ai, Ludwig Kraft, Johannes Sturm, Andreas Jossen, and Billy Wu. Electrochemical thermal-mechanical modelling of stress inhomogeneity in lithium-ion pouch cells. Journal of The Electrochemical Society, 167(1):013512, 2019. doi:10.1149/2.0122001JES.
[2] Joel A. E. Andersson, Joris Gillis, Greg Horn, James B. Rawlings, and Moritz Diehl. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.
[3] Rutooj Deshpande, Mark Verbrugge, Yang-Tse Cheng, John Wang, and Ping Liu. Battery cycle life prediction with coupled chemical degradation and fatigue mechanics. Journal of the Electrochemical Society, 159(10):A1730, 2012. doi:10.1149/2.049210jes.
[4] Marc Doyle, Thomas F. Fuller, and John Newman. Modeling of galvanostatic charge and discharge of the lithium/polymer/insertion cell. Journal of the Electrochemical society, 140(6):1526–1533, 1993. doi:10.1149/1.2221597.
[5] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.
[6] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.
[7] Robert Timms, Scott G Marquis, Valentin Sulzer, Colin P. Please, and S Jonathan Chapman. Asymptotic Reduction of a Lithium-ion Pouch Cell Model. SIAM Journal on Applied Mathematics, 81(3):765–788, 2021. doi:10.1137/20M1336898.