import numpy as np
[docs]
class NMC532Fast:
def __init__(self, alpha_a: float, alpha_c: float, Li_max: float) -> None:
"""
Computationally fast NMC532 kinetic and transport properties.
Differs from `NMC532Slow` because the equilibrium potential is not
piecewise here, making it less accurate, but faster to evaluate.
Parameters
----------
alpha_a : float
Anodic symmetry factor in Butler-Volmer expression [-].
alpha_c : float
Cathodic symmetry factor in Butler-Volmer expression [-].
Li_max : float
Maximum lithium concentration in solid phase [kmol/m3].
"""
self.alpha_a = alpha_a
self.alpha_c = alpha_c
self.Li_max = Li_max
[docs]
def get_Ds(self, x: float | np.ndarray, T: float,
fluxdir: float | np.ndarray) -> float | np.ndarray:
"""
Calculate the lithium diffusivity in the solid phase given the local
intercalation fraction `x` and temperature `T`.
Parameters
----------
x : float | 1D array
Lithium intercalation fraction [-].
T : float
Battery temperature [K].
fluxdir : float | 1D array
Lithiation direction: +1 for lithiation, -1 for delithiation, 0 for
rest. Used for direction-dependent parameters. Ensure the zero case
is handled explicitly or via a default (lithiating or delithiating).
Returns
-------
Ds : float | 1D array
Lithium diffusivity in the solid phase [m2/s].
"""
from .. import Constants
c = Constants()
A = np.array([
-2.509010843479270e+2,
2.391026725259970e+3,
-4.868420267611360e+3,
-8.331104102921070e+1,
1.057636028329000e+4,
-1.268324548348120e+4,
5.016272167775530e+3,
9.824896659649480e+2,
-1.502439339070900e+3,
4.723709304247700e+2,
-6.526092046397090e+1,
])
Ds = np.exp(-30e6/c.R * (1./T - 1./303.15)) \
* 2.25 * 10.0**(np.polyval(A, x))
return Ds
[docs]
def get_i0(self, x: float | np.ndarray, C_Li: float | np.ndarray,
T: float, fluxdir: float | np.ndarray) -> float | np.ndarray:
"""
Calculate the exchange current density given the intercalation
fraction `x` at the particle surface, the local lithium ion
concentration `C_Li`, and temperature `T`. The input types for
`x` and `C_Li` should both be the same (i.e., both float or both
1D arrays).
Parameters
----------
x : float | 1D array
Lithium intercalation fraction at `r = R_s` [-].
C_Li : float | 1D array
Lithium ion concentration in the local electrolyte [kmol/m3].
T : float
Battery temperature [K].
fluxdir : float | 1D array
Lithiation direction: +1 for lithiation, -1 for delithiation, 0 for
rest. Used for direction-dependent parameters. Ensure the zero case
is handled explicitly or via a default (lithiating or delithiating).
Returns
-------
i0 : float | 1D array
Exchange current density [A/m2].
"""
from .. import Constants
c = Constants()
A = np.array([
1.650452829641290e+1,
-7.523567141488800e+1,
1.240524690073040e+2,
-9.416571081287610e+1,
3.249768821737960e+1,
-3.585290065824760e+0,
])
i0 = 9.*(C_Li/1.2)**self.alpha_a * np.polyval(A, x) \
* np.exp(-30e6/c.R * (1./T - 1./303.15))
return i0
[docs]
def get_Eeq(self, x: float | np.ndarray) -> float | np.ndarray:
"""
Calculate the equilibrium potential given the surface intercalation
fraction `x` at the particle surface.
Parameters
----------
x : float
Lithium intercalation fraction at `r = R_s` [-].
Returns
-------
Eeq : float
Equilibrium potential [V].
"""
A = np.array([
-3.640117692001490e+3,
1.317657544484270e+4,
-1.455742062291360e+4,
-1.571094264365090e+3,
1.265630978512400e+4,
-2.057808873526350e+3,
-1.074374333186190e+4,
8.698112755348720e+3,
-8.297904604107030e+2,
-2.073765547574810e+3,
1.190223421193310e+3,
-2.724851668445780e+2,
2.723409218042130e+1,
-4.158276603609060e+0,
5.314735633000300e+0,
])
B = np.array([
-5.573191762723310e-4,
6.560240842659690e+0,
4.148209275061330e+1,
])
Eeq = B[0]*np.exp(B[1] * x**B[2]) + np.polyval(A, x)
return Eeq
[docs]
def get_Mhyst(self, x: float | np.ndarray) -> float | np.ndarray:
"""
Calculate the hysteresis magnitude given the surface intercalation
fraction `x` at the particle surface.
Parameters
----------
x : float | 1D array
Lithium intercalation fraction at `r = R_s` [-].
Returns
-------
M_hyst : float | 1D array
Hysteresis magnitude [V].
"""
M_hyst = 0.03
if isinstance(x, np.ndarray):
M_hyst *= np.ones_like(x)
return M_hyst
[docs]
class NMC532Slow(NMC532Fast):
def __init__(self, alpha_a: float, alpha_c: float, Li_max: float) -> None:
"""
Computationally slow NMC532 kinetic and transport properties.
Differs from `NMC532Fast` because the equilibrium potential is
piecewise here, making it more accurate, but slower to evaluate.
Parameters
----------
alpha_a : float
Anodic symmetry factor in Butler-Volmer expression [-].
alpha_c : float
Cathodic symmetry factor in Butler-Volmer expression [-].
Li_max : float
Maximum lithium concentration in solid phase [kmol/m3].
"""
import os
import pandas as pd
from scipy.interpolate import CubicSpline
self.alpha_a = alpha_a
self.alpha_c = alpha_c
self.Li_max = Li_max
csvfile = os.path.dirname(__file__) + '/data/nmc532_ocv.csv'
df = pd.read_csv(csvfile).sort_values(by='x')
self.x_min = df['x'].min()
self.x_max = df['x'].max()
self._Eeq_spline = CubicSpline(df['x'], df['V'])
[docs]
def get_Eeq(self, x: float | np.ndarray) -> float | np.ndarray:
"""
Calculate the equilibrium potential given the surface intercalation
fraction `x` at the particle surface.
Parameters
----------
x : float
Lithium intercalation fraction at `r = R_s` [-].
Returns
-------
Eeq : float
Equilibrium potential [V].
"""
if isinstance(x, float):
if x < self.x_min or x > self.x_max:
raise ValueError(f'x is out of bounds [{self.x_min},'
f' {self.x_max}].')
if not isinstance(x, float):
if np.any(x < self.x_min) or np.any(x > self.x_max):
raise ValueError(f'x is out of bounds [{self.x_min},'
f' {self.x_max}].')
return self._Eeq_spline(x)