Skip to content

Commit a975b9f

Browse files
Merge pull request #1209 from pybamm-team/lfp-parameters
Lfp parameters
2 parents bac8c71 + 90250c4 commit a975b9f

File tree

31 files changed

+671
-381
lines changed

31 files changed

+671
-381
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Features
44

5+
- Added parameter set for an A123 LFP cell ([#1209](https://github.com/pybamm-team/PyBaMM/pull/1209))
56
- Added variables related to equivalent circuit models ([#1204](https://github.com/pybamm-team/PyBaMM/pull/1204))
67
- Added an example script to check conservation of lithium ([#1186](https://github.com/pybamm-team/PyBaMM/pull/1186))
78
- Added `erf` and `erfc` functions ([#1184](https://github.com/pybamm-team/PyBaMM/pull/1184))

examples/scripts/compare_spectral_volume.py

+31-30
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
# load model
1010
# don't use new_copy
11-
models = [pybamm.lithium_ion.DFN(name="Finite Volume"),
12-
pybamm.lithium_ion.DFN(name="Spectral Volume")]
11+
models = [
12+
pybamm.lithium_ion.DFN(name="Finite Volume"),
13+
pybamm.lithium_ion.DFN(name="Spectral Volume"),
14+
]
1315

1416
# create geometry
1517
geometries = [m.default_geometry for m in models]
@@ -24,32 +26,31 @@
2426
var = pybamm.standard_spatial_vars
2527
var_pts = {var.x_n: 1, var.x_s: 1, var.x_p: 1, var.r_n: 1, var.r_p: 1}
2628
# the Finite Volume method also works on spectral meshes
27-
meshes = [pybamm.Mesh(
28-
geometry,
29-
{
30-
"negative particle": pybamm.MeshGenerator(
31-
pybamm.SpectralVolume1DSubMesh,
32-
{"order": order}
33-
),
34-
"positive particle": pybamm.MeshGenerator(
35-
pybamm.SpectralVolume1DSubMesh,
36-
{"order": order}
37-
),
38-
"negative electrode": pybamm.MeshGenerator(
39-
pybamm.SpectralVolume1DSubMesh,
40-
{"order": order}
41-
),
42-
"separator": pybamm.MeshGenerator(
43-
pybamm.SpectralVolume1DSubMesh,
44-
{"order": order}
45-
),
46-
"positive electrode": pybamm.MeshGenerator(
47-
pybamm.SpectralVolume1DSubMesh,
48-
{"order": order}
49-
),
50-
"current collector": pybamm.SubMesh0D,
51-
},
52-
var_pts) for geometry in geometries]
29+
meshes = [
30+
pybamm.Mesh(
31+
geometry,
32+
{
33+
"negative particle": pybamm.MeshGenerator(
34+
pybamm.SpectralVolume1DSubMesh, {"order": order}
35+
),
36+
"positive particle": pybamm.MeshGenerator(
37+
pybamm.SpectralVolume1DSubMesh, {"order": order}
38+
),
39+
"negative electrode": pybamm.MeshGenerator(
40+
pybamm.SpectralVolume1DSubMesh, {"order": order}
41+
),
42+
"separator": pybamm.MeshGenerator(
43+
pybamm.SpectralVolume1DSubMesh, {"order": order}
44+
),
45+
"positive electrode": pybamm.MeshGenerator(
46+
pybamm.SpectralVolume1DSubMesh, {"order": order}
47+
),
48+
"current collector": pybamm.SubMesh0D,
49+
},
50+
var_pts,
51+
)
52+
for geometry in geometries
53+
]
5354

5455
# discretise model
5556
disc_fv = pybamm.Discretisation(meshes[0], models[0].default_spatial_methods)
@@ -61,8 +62,8 @@
6162
"negative electrode": pybamm.SpectralVolume(order=order),
6263
"separator": pybamm.SpectralVolume(order=order),
6364
"positive electrode": pybamm.SpectralVolume(order=order),
64-
"current collector": pybamm.ZeroDimensionalSpatialMethod()
65-
}
65+
"current collector": pybamm.ZeroDimensionalSpatialMethod(),
66+
},
6667
)
6768

6869
disc_fv.process_model(models[0])

pybamm/CITATIONS.txt

+31-6
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,15 @@ year={2019},
7878
publisher={The Electrochemical Society}
7979
}
8080

81-
@article{Mohtat2020,
82-
author = {Mohtat, Peyman and Siegel, Jason B and Stefanopoulou, Anna G},
83-
title = {{High C-rate Differential Expansion and Voltage Model for Li-ion Batteries}},
84-
journal = {Submitted for publication},
85-
year = {2019},
86-
publisher={ECSarXiv}
81+
@article{mohtat2020differential,
82+
title={Differential Expansion and Voltage Model for Li-ion Batteries at Practical Charging Rates},
83+
author={Mohtat, Peyman and Lee, Suhak and Sulzer, Valentin and Siegel, Jason B and Stefanopoulou, Anna G},
84+
journal={Journal of The Electrochemical Society},
85+
volume={167},
86+
number={11},
87+
pages={110561},
88+
year={2020},
89+
publisher={IOP Publishing}
8790
}
8891

8992
@article{scikits-odes,
@@ -204,3 +207,25 @@ primaryClass={physics.app-ph},
204207
year={2005},
205208
publisher={IOP Publishing}
206209
}
210+
211+
@article{lain2019design,
212+
title={Design Strategies for High Power vs. High Energy Lithium Ion Cells},
213+
author={Lain, Michael J and Brandon, James and Kendrick, Emma},
214+
journal={Batteries},
215+
volume={5},
216+
number={4},
217+
pages={64},
218+
year={2019},
219+
publisher={Multidisciplinary Digital Publishing Institute}
220+
}
221+
222+
@article{prada2013simplified,
223+
title={A simplified electrochemical and thermal aging model of LiFePO4-graphite Li-ion batteries: power and capacity fade simulations},
224+
author={Prada, Eric and Di Domenico, D and Creff, Y and Bernard, J and Sauvant-Moynot, Val{\'e}rie and Huet, Fran{\c{c}}ois},
225+
journal={Journal of The Electrochemical Society},
226+
volume={160},
227+
number={4},
228+
pages={A616},
229+
year={2013},
230+
publisher={IOP Publishing}
231+
}

pybamm/expression_tree/operations/evaluate.py

+40-35
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
import numbers
1111
from platform import system
12+
1213
if system() != "Windows":
1314
import jax
1415

1516
from jax.config import config
17+
1618
config.update("jax_enable_x64", True)
1719

1820

@@ -95,30 +97,35 @@ def find_symbols(symbol, constant_symbols, variable_symbols, to_dense=False):
9597
dummy_eval_left = symbol.children[0].evaluate_for_shape()
9698
dummy_eval_right = symbol.children[1].evaluate_for_shape()
9799
if not to_dense and scipy.sparse.issparse(dummy_eval_left):
98-
symbol_str = "{0}.multiply({1})"\
99-
.format(children_vars[0], children_vars[1])
100+
symbol_str = "{0}.multiply({1})".format(
101+
children_vars[0], children_vars[1]
102+
)
100103
elif not to_dense and scipy.sparse.issparse(dummy_eval_right):
101-
symbol_str = "{1}.multiply({0})"\
102-
.format(children_vars[0], children_vars[1])
104+
symbol_str = "{1}.multiply({0})".format(
105+
children_vars[0], children_vars[1]
106+
)
103107
else:
104108
symbol_str = "{0} * {1}".format(children_vars[0], children_vars[1])
105109
elif isinstance(symbol, pybamm.Division):
106110
dummy_eval_left = symbol.children[0].evaluate_for_shape()
107111
if not to_dense and scipy.sparse.issparse(dummy_eval_left):
108-
symbol_str = "{0}.multiply(1/{1})"\
109-
.format(children_vars[0], children_vars[1])
112+
symbol_str = "{0}.multiply(1/{1})".format(
113+
children_vars[0], children_vars[1]
114+
)
110115
else:
111116
symbol_str = "{0} / {1}".format(children_vars[0], children_vars[1])
112117

113118
elif isinstance(symbol, pybamm.Inner):
114119
dummy_eval_left = symbol.children[0].evaluate_for_shape()
115120
dummy_eval_right = symbol.children[1].evaluate_for_shape()
116121
if not to_dense and scipy.sparse.issparse(dummy_eval_left):
117-
symbol_str = "{0}.multiply({1})"\
118-
.format(children_vars[0], children_vars[1])
122+
symbol_str = "{0}.multiply({1})".format(
123+
children_vars[0], children_vars[1]
124+
)
119125
elif not to_dense and scipy.sparse.issparse(dummy_eval_right):
120-
symbol_str = "{1}.multiply({0})"\
121-
.format(children_vars[0], children_vars[1])
126+
symbol_str = "{1}.multiply({0})".format(
127+
children_vars[0], children_vars[1]
128+
)
122129
else:
123130
symbol_str = "{0} * {1}".format(children_vars[0], children_vars[1])
124131

@@ -294,18 +301,20 @@ def __init__(self, symbol):
294301
# extract constants in generated function
295302
for i, symbol_id in enumerate(constants.keys()):
296303
const_name = id_to_python_variable(symbol_id, True)
297-
python_str = '{} = constants[{}]\n'.format(const_name, i) + python_str
304+
python_str = "{} = constants[{}]\n".format(const_name, i) + python_str
298305

299306
# constants passed in as an ordered dict, convert to list
300307
self._constants = list(constants.values())
301308

302309
# indent code
303-
python_str = ' ' + python_str
304-
python_str = python_str.replace('\n', '\n ')
310+
python_str = " " + python_str
311+
python_str = python_str.replace("\n", "\n ")
305312

306313
# add function def to first line
307-
python_str = 'def evaluate(constants, t=None, y=None, '\
308-
'y_dot=None, inputs=None, known_evals=None):\n' + python_str
314+
python_str = (
315+
"def evaluate(constants, t=None, y=None, "
316+
"y_dot=None, inputs=None, known_evals=None):\n" + python_str
317+
)
309318

310319
# calculate the final variable that will output the result of calling `evaluate`
311320
# on `symbol`
@@ -315,21 +324,18 @@ def __init__(self, symbol):
315324

316325
# add return line
317326
if symbol.is_constant() and isinstance(result_value, numbers.Number):
318-
python_str = python_str + '\n return ' + str(result_value)
327+
python_str = python_str + "\n return " + str(result_value)
319328
else:
320-
python_str = python_str + '\n return ' + result_var
329+
python_str = python_str + "\n return " + result_var
321330

322331
# store a copy of examine_jaxpr
323-
python_str = python_str + \
324-
'\nself._evaluate = evaluate'
332+
python_str = python_str + "\nself._evaluate = evaluate"
325333

326334
self._python_str = python_str
327335
self._symbol = symbol
328336

329337
# compile and run the generated python code,
330-
compiled_function = compile(
331-
python_str, result_var, "exec"
332-
)
338+
compiled_function = compile(python_str, result_var, "exec")
333339
exec(compiled_function)
334340

335341
def evaluate(self, t=None, y=None, y_dot=None, inputs=None, known_evals=None):
@@ -377,7 +383,7 @@ def __init__(self, symbol):
377383
constants, python_str = pybamm.to_python(symbol, debug=False, to_dense=True)
378384

379385
# replace numpy function calls to jax numpy calls
380-
python_str = python_str.replace('np.', 'jax.numpy.')
386+
python_str = python_str.replace("np.", "jax.numpy.")
381387

382388
# convert all numpy constants to device vectors
383389
for symbol_id in constants:
@@ -387,18 +393,20 @@ def __init__(self, symbol):
387393
# extract constants in generated function
388394
for i, symbol_id in enumerate(constants.keys()):
389395
const_name = id_to_python_variable(symbol_id, True)
390-
python_str = '{} = constants[{}]\n'.format(const_name, i) + python_str
396+
python_str = "{} = constants[{}]\n".format(const_name, i) + python_str
391397

392398
# constants passed in as an ordered dict, convert to list
393399
self._constants = list(constants.values())
394400

395401
# indent code
396-
python_str = ' ' + python_str
397-
python_str = python_str.replace('\n', '\n ')
402+
python_str = " " + python_str
403+
python_str = python_str.replace("\n", "\n ")
398404

399405
# add function def to first line
400-
python_str = 'def evaluate_jax(constants, t=None, y=None, '\
401-
'y_dot=None, inputs=None, known_evals=None):\n' + python_str
406+
python_str = (
407+
"def evaluate_jax(constants, t=None, y=None, "
408+
"y_dot=None, inputs=None, known_evals=None):\n" + python_str
409+
)
402410

403411
# calculate the final variable that will output the result of calling `evaluate`
404412
# on `symbol`
@@ -408,18 +416,15 @@ def __init__(self, symbol):
408416

409417
# add return line
410418
if symbol.is_constant() and isinstance(result_value, numbers.Number):
411-
python_str = python_str + '\n return ' + str(result_value)
419+
python_str = python_str + "\n return " + str(result_value)
412420
else:
413-
python_str = python_str + '\n return ' + result_var
421+
python_str = python_str + "\n return " + result_var
414422

415423
# store a copy of examine_jaxpr
416-
python_str = python_str + \
417-
'\nself._evaluate_jax = evaluate_jax'
424+
python_str = python_str + "\nself._evaluate_jax = evaluate_jax"
418425

419426
# compile and run the generated python code,
420-
compiled_function = compile(
421-
python_str, result_var, "exec"
422-
)
427+
compiled_function = compile(python_str, result_var, "exec")
423428
exec(compiled_function)
424429

425430
self._jit_evaluate = jax.jit(self._evaluate_jax, static_argnums=(0, 4, 5))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from pybamm import exp, constants, Parameter
2+
3+
4+
def LFP_electrolyte_exchange_current_density_kashkooli2017(c_e, c_s_surf, T): # , 1
5+
"""
6+
Exchange-current density for Butler-Volmer reactions between LFP and electrolyte
7+
8+
References
9+
----------
10+
.. [1] Kashkooli, A. G., Amirfazli, A., Farhad, S., Lee, D. U., Felicelli, S., Park,
11+
H. W., ... & Chen, Z. (2017). Representative volume element model of lithium-ion
12+
battery electrodes based on X-ray nano-tomography. Journal of Applied
13+
Electrochemistry, 47(3), 281-293.
14+
15+
Parameters
16+
----------
17+
c_e : :class:`pybamm.Symbol`
18+
Electrolyte concentration [mol.m-3]
19+
c_s_surf : :class:`pybamm.Symbol`
20+
Particle concentration [mol.m-3]
21+
T : :class:`pybamm.Symbol`
22+
Temperature [K]
23+
24+
Returns
25+
-------
26+
:class:`pybamm.Symbol`
27+
Exchange-current density [A.m-2]
28+
"""
29+
30+
m_ref = 6 * 10 ** (-7) # (A/m2)(mol/m3)**1.5 - includes ref concentrations
31+
E_r = 39570
32+
arrhenius = exp(E_r / constants.R * (1 / 298.15 - 1 / T))
33+
c_p_max = Parameter("Maximum concentration in positive electrode [mol.m-3]")
34+
35+
return (
36+
m_ref * arrhenius * c_e ** 0.5 * c_s_surf ** 0.5 * (c_p_max - c_s_surf) ** 0.5
37+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from pybamm import exp
2+
3+
4+
def LFP_ocp_ashfar2017(sto):
5+
"""
6+
Open-circuit potential for LFP
7+
8+
References
9+
----------
10+
.. [1] Afshar, S., Morris, K., & Khajepour, A. (2017). Efficient electrochemical
11+
model for lithium-ion cells. arXiv preprint arXiv:1709.03970.
12+
13+
Parameters
14+
----------
15+
sto : :class:`pybamm.Symbol`
16+
Stochiometry of material (li-fraction)
17+
18+
"""
19+
20+
c1 = -150 * sto
21+
c2 = -30 * (1 - sto)
22+
k = 3.4077 - 0.020269 * sto + 0.5 * exp(c1) - 0.9 * exp(c2)
23+
24+
return k
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Lithium Cobalt Oxide cathode parameters
2+
3+
Parameters for an LFP cathode, from the paper
4+
5+
> Prada, E., Di Domenico, D., Creff, Y., Bernard, J., Sauvant-Moynot, V., & Huet, F. (2013). A simplified electrochemical and thermal aging model of LiFePO4-graphite Li-ion batteries: power and capacity fade simulations. [Journal of The Electrochemical Society](https://doi.org/10.1149/2.053304jes), 160(4), A616.
6+
7+
and references therein. The functions used for OCP and exchange-current density are from separate references (documented within the functions), to provide better fit to data

0 commit comments

Comments
 (0)