Skip to content

Commit 1a6aeda

Browse files
authored
Merge pull request #1423 from pybamm-team/newman-tobias
Newman tobias model
2 parents 0808b3e + d14a041 commit 1a6aeda

File tree

15 files changed

+675
-10
lines changed

15 files changed

+675
-10
lines changed

CHANGELOG.md

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

33
## Features
44

5+
- Added `NewmanTobias` li-ion battery model ([#1423](https://github.com/pybamm-team/PyBaMM/pull/1423))
56
- Added `plot_voltage_components` to easily plot the component overpotentials that make up the voltage ([#1419](https://github.com/pybamm-team/PyBaMM/pull/1419))
67
- Made `QuickPlot` more customizable and added an example ([#1419](https://github.com/pybamm-team/PyBaMM/pull/1419))
78
- `Solution` objects can now be created by stepping *different* models ([#1408](https://github.com/pybamm-team/PyBaMM/pull/1408))

docs/source/models/lithium_ion/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Lithium-ion Models
77
spm
88
spme
99
dfn
10+
newman_tobias
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Newman-Tobias
2+
=============
3+
4+
.. autoclass:: pybamm.lithium_ion.NewmanTobias
5+
:members:

examples/scripts/compare_lithium_ion.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
pybamm.set_logging_level("INFO")
77

88
# load models
9-
models = [pybamm.lithium_ion.SPM(), pybamm.lithium_ion.SPMe(), pybamm.lithium_ion.DFN()]
9+
models = [
10+
pybamm.lithium_ion.SPM(),
11+
pybamm.lithium_ion.SPMe(),
12+
pybamm.lithium_ion.DFN(),
13+
pybamm.lithium_ion.NewmanTobias(),
14+
]
1015

1116
# create and run simulations
1217
sims = []

pybamm/CITATIONS.txt

+22-1
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,25 @@ doi={10.1149/2.0661810jes}
358358
year={2004},
359359
publisher={IOP Publishing},
360360
doi={10.1149/1.1634273},
361-
}
361+
}
362+
363+
@article{Newman1962,
364+
title={Theoretical analysis of current distribution in porous electrodes},
365+
author={Newman, John S and Tobias, Charles W},
366+
journal={Journal of The Electrochemical Society},
367+
volume={109},
368+
number={12},
369+
pages={1183},
370+
year={1962},
371+
publisher={IOP Publishing}
372+
}
373+
374+
@article{Chu2020,
375+
title={Parameterization of prismatic lithium--iron--phosphate cells through a streamlined thermal/electrochemical model},
376+
author={Chu, Howie N and Kim, Sun Ung and Rahimian, Saeed Khaleghi and Siegel, Jason B and Monroe, Charles W},
377+
journal={Journal of Power Sources},
378+
volume={453},
379+
pages={227787},
380+
year={2020},
381+
publisher={Elsevier}
382+
}

pybamm/models/full_battery_models/base_battery_model.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class Options(pybamm.FuzzyDict):
3333
* "dimensionality" : int
3434
Sets the dimension of the current collector problem. Can be 0
3535
(default), 1 or 2.
36+
* "electrolyte conductivity" : str
37+
Can be "default" (default), "full", "leading order", "composite" or
38+
"integrated".
3639
* "external submodels" : list
3740
A list of the submodels that you would like to supply an external
3841
variable for instead of solving in PyBaMM. The entries of the lists
@@ -131,9 +134,6 @@ class Options(pybamm.FuzzyDict):
131134
solve an algebraic equation for it. Default is "false", unless "SEI film
132135
resistance" is distributed in which case it is automatically set to
133136
"true".
134-
* "electrolyte conductivity" : str
135-
Can be "default" (default), "full", "leading order", "composite" or
136-
"integrated"
137137
138138
**Extends:** :class:`dict`
139139
"""
@@ -459,7 +459,6 @@ def set_standard_output_variables(self):
459459
# Spatial
460460
var = pybamm.standard_spatial_vars
461461
L_x = self.param.L_x
462-
L_y = self.param.L_y
463462
L_z = self.param.L_z
464463
self.variables.update(
465464
{
@@ -476,8 +475,9 @@ def set_standard_output_variables(self):
476475
if self.options["dimensionality"] == 1:
477476
self.variables.update({"z": var.z, "z [m]": var.z * L_z})
478477
elif self.options["dimensionality"] == 2:
478+
# Note: both y and z are scaled with L_z
479479
self.variables.update(
480-
{"y": var.y, "y [m]": var.y * L_y, "z": var.z, "z [m]": var.z * L_z}
480+
{"y": var.y, "y [m]": var.y * L_z, "z": var.z, "z [m]": var.z * L_z}
481481
)
482482

483483
# Initialize "total reaction" variables
@@ -511,7 +511,7 @@ def build_fundamental_and_external(self):
511511
)
512512
self.variables.update(submodel.get_fundamental_variables())
513513

514-
# set the submodels that are external
514+
# Set the submodels that are external
515515
for sub in self.options["external submodels"]:
516516
self.submodels[sub].external = True
517517

pybamm/models/full_battery_models/lithium_ion/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .spm import SPM
66
from .spme import SPMe
77
from .dfn import DFN
8+
from .newman_tobias import NewmanTobias
89
from .basic_dfn import BasicDFN
910
from .basic_spm import BasicSPM
1011
from .basic_dfn_half_cell import BasicDFNHalfCell
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#
2+
# Newman Tobias Model
3+
#
4+
import pybamm
5+
from .dfn import DFN
6+
7+
8+
class NewmanTobias(DFN):
9+
"""
10+
Newman-Tobias model of a lithium-ion battery based on the formulation in [1]_.
11+
This model assumes a uniform concentration profile in the electrolyte.
12+
Unlike the model posed in [1]_, this model accounts for nonlinear Butler-Volmer
13+
kinetics. It also tracks the average concentration in the solid phase in each
14+
electrode, which is equivalent to including an equation for the local state of
15+
charge as in [2]_. The user can pass the "particle" option to include mass
16+
transport in the particles.
17+
18+
Parameters
19+
----------
20+
options : dict, optional
21+
A dictionary of options to be passed to the model.
22+
name : str, optional
23+
The name of the model.
24+
build : bool, optional
25+
Whether to build the model on instantiation. Default is True. Setting this
26+
option to False allows users to change any number of the submodels before
27+
building the complete model (submodels cannot be changed after the model is
28+
built).
29+
30+
References
31+
----------
32+
.. [1] JS Newman and CW Tobias. "Theoretical Analysis of Current Distribution
33+
in Porous Electrodes". Journal of The Electrochemical Society,
34+
109(12):A1183-A1191, 1962
35+
.. [2] HN Chu, SU Kim, SK Rahimian, JB Siegel and CW Monroe. "Parameterization
36+
of prismatic lithium–iron–phosphate cells through a streamlined
37+
thermal/electrochemical model". Journal of Power Sources, 453, p.227787,
38+
2020
39+
40+
41+
**Extends:** :class:`pybamm.lithium_ion.DFN`
42+
"""
43+
44+
def __init__(self, options=None, name="Newman-Tobias model", build=True):
45+
46+
# Set default option "uniform profile" for particle submodel. Other
47+
# default options are those given in `pybamm.Options` defined in
48+
# `base_battery_model.py`.
49+
options = options or {}
50+
if "particle" not in options:
51+
options["particle"] = "uniform profile"
52+
53+
super().__init__(options, name, build)
54+
55+
pybamm.citations.register("Newman1962")
56+
pybamm.citations.register("Chu2020")
57+
58+
def set_particle_submodel(self):
59+
60+
if self.options["particle"] == "Fickian diffusion":
61+
self.submodels["negative particle"] = pybamm.particle.FickianSingleParticle(
62+
self.param, "Negative"
63+
)
64+
self.submodels["positive particle"] = pybamm.particle.FickianSingleParticle(
65+
self.param, "Positive"
66+
)
67+
elif self.options["particle"] in [
68+
"uniform profile",
69+
"quadratic profile",
70+
"quartic profile",
71+
]:
72+
self.submodels[
73+
"negative particle"
74+
] = pybamm.particle.PolynomialSingleParticle(
75+
self.param, "Negative", self.options["particle"]
76+
)
77+
self.submodels[
78+
"positive particle"
79+
] = pybamm.particle.PolynomialSingleParticle(
80+
self.param, "Positive", self.options["particle"]
81+
)
82+
83+
def set_electrolyte_submodel(self):
84+
85+
surf_form = pybamm.electrolyte_conductivity.surface_potential_form
86+
87+
self.submodels[
88+
"electrolyte diffusion"
89+
] = pybamm.electrolyte_diffusion.ConstantConcentration(self.param)
90+
91+
if self.options["electrolyte conductivity"] not in ["default", "full"]:
92+
raise pybamm.OptionError(
93+
"electrolyte conductivity '{}' not suitable for Newman-Tobias".format(
94+
self.options["electrolyte conductivity"]
95+
)
96+
)
97+
98+
if self.options["surface form"] == "false":
99+
self.submodels[
100+
"electrolyte conductivity"
101+
] = pybamm.electrolyte_conductivity.Full(self.param)
102+
elif self.options["surface form"] == "differential":
103+
for domain in ["Negative", "Separator", "Positive"]:
104+
self.submodels[
105+
domain.lower() + " electrolyte conductivity"
106+
] = surf_form.FullDifferential(self.param, domain)
107+
elif self.options["surface form"] == "algebraic":
108+
for domain in ["Negative", "Separator", "Positive"]:
109+
self.submodels[
110+
domain.lower() + " electrolyte conductivity"
111+
] = surf_form.FullAlgebraic(self.param, domain)

pybamm/models/submodels/electrolyte_diffusion/constant_concentration.py

+16
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,19 @@ def get_coupled_variables(self, variables):
4545
variables.update(self._get_total_concentration_electrolyte(c_e, eps))
4646

4747
return variables
48+
49+
def set_boundary_conditions(self, variables):
50+
"""
51+
We provide boundary conditions even though the concentration is constant
52+
so that the gradient of the concentration has the correct shape after
53+
discretisation.
54+
"""
55+
56+
c_e = variables["Electrolyte concentration"]
57+
58+
self.boundary_conditions = {
59+
c_e: {
60+
"left": (pybamm.Scalar(0), "Neumann"),
61+
"right": (pybamm.Scalar(0), "Neumann"),
62+
}
63+
}

pybamm/simulation.py

+2
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ def plot(self, output_variables=None, quick_plot_vars=None, **kwargs):
832832
self._solution, output_variables=output_variables, **kwargs
833833
)
834834

835+
return self.quick_plot
836+
835837
@property
836838
def model(self):
837839
return self._model

tests/integration/test_models/standard_output_tests.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ def test_interfacial_current_average(self):
624624
axis=0,
625625
),
626626
self.i_cell / self.l_n,
627-
decimal=3,
627+
decimal=4,
628628
)
629629
np.testing.assert_array_almost_equal(
630630
np.mean(
@@ -633,7 +633,7 @@ def test_interfacial_current_average(self):
633633
axis=0,
634634
),
635635
-self.i_cell / self.l_p,
636-
decimal=3,
636+
decimal=4,
637637
)
638638

639639
def test_conservation(self):

tests/integration/test_models/test_full_battery_models/test_lithium_ion/test_dfn.py

+4
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ def positive_radius(x):
172172

173173
param["Negative particle radius [m]"] = negative_radius
174174
param["Positive particle radius [m]"] = positive_radius
175+
# Only get 3dp of accuracy in some tests at 1C with particle distribution
176+
# TODO: investigate if there is a bug or some way to improve the
177+
# implementation
178+
param["Current function [A]"] = 0.5 * param["Nominal cell capacity [A.h]"]
175179
modeltest = tests.StandardModelTest(model, parameter_values=param)
176180
modeltest.test_all()
177181

0 commit comments

Comments
 (0)