Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Newman tobias model #1423

Merged
merged 14 commits into from
Mar 12, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- Added `NewmanTobias` li-ion battery model ([#1423](https://github.com/pybamm-team/PyBaMM/pull/1423))
- Added `plot_voltage_components` to easily plot the component overpotentials that make up the voltage ([#1419](https://github.com/pybamm-team/PyBaMM/pull/1419))
- Made `QuickPlot` more customizable and added an example ([#1419](https://github.com/pybamm-team/PyBaMM/pull/1419))
- `Solution` objects can now be created by stepping *different* models ([#1408](https://github.com/pybamm-team/PyBaMM/pull/1408))
Expand Down
1 change: 1 addition & 0 deletions docs/source/models/lithium_ion/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Lithium-ion Models
spm
spme
dfn
newman_tobias
5 changes: 5 additions & 0 deletions docs/source/models/lithium_ion/newman_tobias.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Newman-Tobias
=============

.. autoclass:: pybamm.lithium_ion.NewmanTobias
:members:
7 changes: 6 additions & 1 deletion examples/scripts/compare_lithium_ion.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
pybamm.set_logging_level("INFO")

# load models
models = [pybamm.lithium_ion.SPM(), pybamm.lithium_ion.SPMe(), pybamm.lithium_ion.DFN()]
models = [
pybamm.lithium_ion.SPM(),
pybamm.lithium_ion.SPMe(),
pybamm.lithium_ion.DFN(),
pybamm.lithium_ion.NewmanTobias(),
]

# create and run simulations
sims = []
Expand Down
23 changes: 22 additions & 1 deletion pybamm/CITATIONS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -358,4 +358,25 @@ doi={10.1149/2.0661810jes}
year={2004},
publisher={IOP Publishing},
doi={10.1149/1.1634273},
}
}

@article{Newman1962,
title={Theoretical analysis of current distribution in porous electrodes},
author={Newman, John S and Tobias, Charles W},
journal={Journal of The Electrochemical Society},
volume={109},
number={12},
pages={1183},
year={1962},
publisher={IOP Publishing}
}

@article{Chu2020,
title={Parameterization of prismatic lithium--iron--phosphate cells through a streamlined thermal/electrochemical model},
author={Chu, Howie N and Kim, Sun Ung and Rahimian, Saeed Khaleghi and Siegel, Jason B and Monroe, Charles W},
journal={Journal of Power Sources},
volume={453},
pages={227787},
year={2020},
publisher={Elsevier}
}
12 changes: 6 additions & 6 deletions pybamm/models/full_battery_models/base_battery_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Options(pybamm.FuzzyDict):
* "dimensionality" : int
Sets the dimension of the current collector problem. Can be 0
(default), 1 or 2.
* "electrolyte conductivity" : str
Can be "default" (default), "full", "leading order", "composite" or
"integrated".
* "external submodels" : list
A list of the submodels that you would like to supply an external
variable for instead of solving in PyBaMM. The entries of the lists
Expand Down Expand Up @@ -131,9 +134,6 @@ class Options(pybamm.FuzzyDict):
solve an algebraic equation for it. Default is "false", unless "SEI film
resistance" is distributed in which case it is automatically set to
"true".
* "electrolyte conductivity" : str
Can be "default" (default), "full", "leading order", "composite" or
"integrated"

**Extends:** :class:`dict`
"""
Expand Down Expand Up @@ -459,7 +459,6 @@ def set_standard_output_variables(self):
# Spatial
var = pybamm.standard_spatial_vars
L_x = self.param.L_x
L_y = self.param.L_y
L_z = self.param.L_z
self.variables.update(
{
Expand All @@ -476,8 +475,9 @@ def set_standard_output_variables(self):
if self.options["dimensionality"] == 1:
self.variables.update({"z": var.z, "z [m]": var.z * L_z})
elif self.options["dimensionality"] == 2:
# Note: both y and z are scaled with L_z
self.variables.update(
{"y": var.y, "y [m]": var.y * L_y, "z": var.z, "z [m]": var.z * L_z}
{"y": var.y, "y [m]": var.y * L_z, "z": var.z, "z [m]": var.z * L_z}
)

# Initialize "total reaction" variables
Expand Down Expand Up @@ -511,7 +511,7 @@ def build_fundamental_and_external(self):
)
self.variables.update(submodel.get_fundamental_variables())

# set the submodels that are external
# Set the submodels that are external
for sub in self.options["external submodels"]:
self.submodels[sub].external = True

Expand Down
1 change: 1 addition & 0 deletions pybamm/models/full_battery_models/lithium_ion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .spm import SPM
from .spme import SPMe
from .dfn import DFN
from .newman_tobias import NewmanTobias
from .basic_dfn import BasicDFN
from .basic_spm import BasicSPM
from .basic_dfn_half_cell import BasicDFNHalfCell
111 changes: 111 additions & 0 deletions pybamm/models/full_battery_models/lithium_ion/newman_tobias.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#
# Newman Tobias Model
#
import pybamm
from .dfn import DFN


class NewmanTobias(DFN):
"""
Newman-Tobias model of a lithium-ion battery based on the formulation in [1]_.
This model assumes a uniform concentration profile in the electrolyte.
Unlike the model posed in [1]_, this model accounts for nonlinear Butler-Volmer
kinetics. It also tracks the average concentration in the solid phase in each
electrode, which is equivalent to including an equation for the local state of
charge as in [2]_. The user can pass the "particle" option to include mass
transport in the particles.

Parameters
----------
options : dict, optional
A dictionary of options to be passed to the model.
name : str, optional
The name of the model.
build : bool, optional
Whether to build the model on instantiation. Default is True. Setting this
option to False allows users to change any number of the submodels before
building the complete model (submodels cannot be changed after the model is
built).

References
----------
.. [1] JS Newman and CW Tobias. "Theoretical Analysis of Current Distribution
in Porous Electrodes". Journal of The Electrochemical Society,
109(12):A1183-A1191, 1962
.. [2] HN Chu, SU Kim, SK Rahimian, JB Siegel and CW Monroe. "Parameterization
of prismatic lithium–iron–phosphate cells through a streamlined
thermal/electrochemical model". Journal of Power Sources, 453, p.227787,
2020


**Extends:** :class:`pybamm.lithium_ion.DFN`
"""

def __init__(self, options=None, name="Newman-Tobias model", build=True):

# Set default option "uniform profile" for particle submodel. Other
# default options are those given in `pybamm.Options` defined in
# `base_battery_model.py`.
options = options or {}
if "particle" not in options:
options["particle"] = "uniform profile"

super().__init__(options, name, build)

pybamm.citations.register("Newman1962")
pybamm.citations.register("Chu2020")

def set_particle_submodel(self):

if self.options["particle"] == "Fickian diffusion":
self.submodels["negative particle"] = pybamm.particle.FickianSingleParticle(
self.param, "Negative"
)
self.submodels["positive particle"] = pybamm.particle.FickianSingleParticle(
self.param, "Positive"
)
elif self.options["particle"] in [
"uniform profile",
"quadratic profile",
"quartic profile",
]:
self.submodels[
"negative particle"
] = pybamm.particle.PolynomialSingleParticle(
self.param, "Negative", self.options["particle"]
)
self.submodels[
"positive particle"
] = pybamm.particle.PolynomialSingleParticle(
self.param, "Positive", self.options["particle"]
)

def set_electrolyte_submodel(self):

surf_form = pybamm.electrolyte_conductivity.surface_potential_form

self.submodels[
"electrolyte diffusion"
] = pybamm.electrolyte_diffusion.ConstantConcentration(self.param)

if self.options["electrolyte conductivity"] not in ["default", "full"]:
raise pybamm.OptionError(
"electrolyte conductivity '{}' not suitable for Newman-Tobias".format(
self.options["electrolyte conductivity"]
)
)

if self.options["surface form"] == "false":
self.submodels[
"electrolyte conductivity"
] = pybamm.electrolyte_conductivity.Full(self.param)
elif self.options["surface form"] == "differential":
for domain in ["Negative", "Separator", "Positive"]:
self.submodels[
domain.lower() + " electrolyte conductivity"
] = surf_form.FullDifferential(self.param, domain)
elif self.options["surface form"] == "algebraic":
for domain in ["Negative", "Separator", "Positive"]:
self.submodels[
domain.lower() + " electrolyte conductivity"
] = surf_form.FullAlgebraic(self.param, domain)
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,19 @@ def get_coupled_variables(self, variables):
variables.update(self._get_total_concentration_electrolyte(c_e, eps))

return variables

def set_boundary_conditions(self, variables):
"""
We provide boundary conditions even though the concentration is constant
so that the gradient of the concentration has the correct shape after
discretisation.
"""

c_e = variables["Electrolyte concentration"]

self.boundary_conditions = {
c_e: {
"left": (pybamm.Scalar(0), "Neumann"),
"right": (pybamm.Scalar(0), "Neumann"),
}
}
2 changes: 2 additions & 0 deletions pybamm/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,8 @@ def plot(self, output_variables=None, quick_plot_vars=None, **kwargs):
self._solution, output_variables=output_variables, **kwargs
)

return self.quick_plot

@property
def model(self):
return self._model
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/test_models/standard_output_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def test_interfacial_current_average(self):
axis=0,
),
self.i_cell / self.l_n,
decimal=3,
decimal=4,
)
np.testing.assert_array_almost_equal(
np.mean(
Expand All @@ -633,7 +633,7 @@ def test_interfacial_current_average(self):
axis=0,
),
-self.i_cell / self.l_p,
decimal=3,
decimal=4,
)

def test_conservation(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ def positive_radius(x):

param["Negative particle radius [m]"] = negative_radius
param["Positive particle radius [m]"] = positive_radius
# Only get 3dp of accuracy in some tests at 1C with particle distribution
# TODO: investigate if there is a bug or some way to improve the
# implementation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this related to what @brosaplanella was seeing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like it. you can get 4dp of accuracy everywhere else.

param["Current function [A]"] = 0.5 * param["Nominal cell capacity [A.h]"]
modeltest = tests.StandardModelTest(model, parameter_values=param)
modeltest.test_all()

Expand Down
Loading