Skip to content

Commit

Permalink
#617 cheb grid
Browse files Browse the repository at this point in the history
  • Loading branch information
rtimms committed Sep 13, 2019
1 parent 6ce4aec commit 01b5ab4
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 5 deletions.
75 changes: 75 additions & 0 deletions examples/scripts/SPM_compare_particle_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import pybamm
import numpy as np
import matplotlib.pyplot as plt

pybamm.set_logging_level("INFO")

# load model
model = pybamm.lithium_ion.SPM()

# create geometry
geometry = model.default_geometry

# load parameter values and process model and geometry
param = model.default_parameter_values
param.process_model(model)
param.process_geometry(geometry)

# set mesh
submesh_types = {
"negative electrode": pybamm.Uniform1DSubMesh,
"separator": pybamm.Uniform1DSubMesh,
"positive electrode": pybamm.Uniform1DSubMesh,
"negative particle": pybamm.Chebyshev1DSubMesh,
"positive particle": pybamm.Chebyshev1DSubMesh,
"current collector": pybamm.SubMesh0D,
}
var = pybamm.standard_spatial_vars
var_pts = {var.x_n: 10, var.x_s: 10, var.x_p: 10, var.r_n: 9, var.r_p: 9}
mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

# discretise model
disc = pybamm.Discretisation(mesh, model.default_spatial_methods)
disc.process_model(model)

# solve model
t_eval = np.linspace(0, 0.2, 100)
solution = model.default_solver.solve(model, t_eval)

# plot
r_n = mesh["negative particle"][0].edges
c_n = pybamm.ProcessedVariable(
model.variables["X-average negative particle concentration [mol.m-3]"],
solution.t,
solution.y,
mesh=mesh,
)
r_p = mesh["positive particle"][0].edges
c_p = pybamm.ProcessedVariable(
model.variables["X-average positive particle concentration [mol.m-3]"],
solution.t,
solution.y,
mesh=mesh,
)
import ipdb; ipdb.set_trace()
fig, ax = plt.subplots(figsize=(15, 8))
plt.tight_layout()
plt.subplot(121)
plt.plot(
r_n,
np.zeros_like(r_n),
"ro",
mesh["negative particle"][0].nodes,
c_n(t=0.1, r=mesh["negative particle"][0].nodes),
"b-",
)
plt.subplot(122)
plt.plot(
r_p,
np.zeros_like(r_p),
"ro",
mesh["positive particle"][0].nodes,
c_p(t=0.1, r=mesh["positive particle"][0].nodes),
"b-",
)
plt.show()
2 changes: 1 addition & 1 deletion pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def version(formatted=False):
from .discretisations.discretisation import Discretisation
from .meshes.meshes import Mesh
from .meshes.zero_dimensional_submesh import SubMesh0D
from .meshes.one_dimensional_submeshes import SubMesh1D, Uniform1DSubMesh
from .meshes.one_dimensional_submeshes import SubMesh1D, Uniform1DSubMesh, Chebyshev1DSubMesh
from .meshes.scikit_fem_submeshes import Scikit2DSubMesh, have_scikit_fem

#
Expand Down
118 changes: 114 additions & 4 deletions pybamm/meshes/one_dimensional_submeshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,12 @@ class Uniform1DSubMesh(SubMesh1D):
A dictionary that contains the limits of the spatial variables
npts : dict
A dictionary that contains the number of points to be used on each
spatial variable
spatial variable. Note: the number of nodes (located at the cell centres)
is npts, and the number of edges is npts+1.
"""

def __init__(self, lims, npts):

# currently accept lims and npts as dicts. This may get changed at a future
# date depending on the form of mesh we desire for 2D/3D

# check that only one variable passed in
if len(lims) != 1:
raise pybamm.GeometryError("lims should only contain a single variable")
Expand All @@ -52,3 +50,115 @@ def __init__(self, lims, npts):
coord_sys = spatial_var.coord_sys

super().__init__(edges, coord_sys=coord_sys)


class Chebyshev1DSubMesh(SubMesh1D):
"""
A class to generate a submesh on a 1D domain using Chebyshev nodes on the
interval (a, b), given by
.. math::
x_{k} = \\frac{1}{2}(a+b) + \\frac{1}{2}(b-a) \\cos(\\frac{2k-1}{2N}\\pi),
for k = 1, ..., N, where N is the number of nodes. Note: this mesh then
appends the boundary nodes, so that the mesh edges are given by
.. math ::
a < x_{1} < ... < x_{N} < b.
Parameters
----------
lims : dict
A dictionary that contains the limits of the spatial variables
npts : dict
A dictionary that contains the number of points to be used on each
spatial variable. Note: the number of nodes (located at the cell centres)
is npts, and the number of edges is npts+1.
"""

def __init__(self, lims, npts):

# check that only one variable passed in
if len(lims) != 1:
raise pybamm.GeometryError("lims should only contain a single variable")

spatial_var = list(lims.keys())[0]
spatial_lims = lims[spatial_var]
npts = npts[spatial_var.id]

# Create N Chebyshev nodes in the interval (a,b)
N = npts - 2
ii = np.array(range(1, N + 1))
a = spatial_lims["min"]
b = spatial_lims["max"]
x_cheb = (a + b) / 2 + (b - a) / 2 * np.cos((2 * ii - 1) * np.pi / 2 / N)

# Append the boundary nodes. Note: we need to flip the order the Chebyshev
# nodes as they are created in descending order.
edges = np.concatenate(([a], np.flip(x_cheb), [b]))
coord_sys = spatial_var.coord_sys

super().__init__(edges, coord_sys=coord_sys)


class ExponentialEdges1DSubMesh(SubMesh1D):
"""
A class to generate a submesh on a 1D domain in which the points are clustered
close to the boundaries using an exponential formula on the interval [a,b].
The first half of the interval is meshed using the gridpoints
.. math::
x_{k} = (\\frac{b}{2}-a) + \\frac{\\exp{\\alpha k / N} - 1}{\\exp{\\alpha} - 1}} + a,
for k = 1, ..., N, where N is the number of nodes. The grid spacing is then
reflected to contruct the grid on the full interval [a,b].
Parameters
----------
lims : dict
A dictionary that contains the limits of the spatial variables
npts : dict
A dictionary that contains the number of points to be used on each
spatial variable. Note: the number of nodes (located at the cell centres)
is npts, and the number of edges is npts+1.
"""

def __init__(self, lims, npts):

# check that only one variable passed in
if len(lims) != 1:
raise pybamm.GeometryError("lims should only contain a single variable")

spatial_var = list(lims.keys())[0]
spatial_lims = lims[spatial_var]
npts = npts[spatial_var.id]

# Strech factor. TODO: allows parameters to be passed to mesh
alpha = 1

# Mesh half-interval [a, b/2]
if npts % 2 == 0:
ii = np.array(range(0, int((npts) / 2)))
else:
ii = np.array(range(0, int((npts + 1) / 2)))

a = spatial_lims["min"]
b = spatial_lims["max"]
x_exp_left = (b / 2 - a) * (np.exp(alpha * ii / npts) - 1) / (
np.exp(alpha) - 1
) + a

# Refelct mesh
x_exp_right = b * np.ones_like(x_exp_left) - (x_exp_left[::-1] - a)

# Combine left and right halves of the mesh, adding a node at the centre
# if npts is even (odd number of edges)
if npts % 2 == 0:
edges = np.concatenate((x_exp_left, [(a + b) / 2], x_exp_right))
else:
edges = np.concatenate((x_exp_left, x_exp_right))
coord_sys = spatial_var.coord_sys

super().__init__(edges, coord_sys=coord_sys)
48 changes: 48 additions & 0 deletions tests/unit/test_meshes/test_chebyshev_submesh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pybamm
import unittest


class TestChebyshev1DSubMesh(unittest.TestCase):
def test_exceptions(self):
lims = [[0, 1], [0, 1]]
with self.assertRaises(pybamm.GeometryError):
pybamm.Chebyshev1DSubMesh(lims, None)

def test_mesh_creation_no_parameters(self):
r = pybamm.SpatialVariable(
"r", domain=["negative particle"], coord_sys="spherical polar"
)

geometry = {
"negative particle": {
"primary": {r: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}}
}
}

submesh_types = {"negative particle": pybamm.Chebyshev1DSubMesh}
var_pts = {r: 20}
mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

# create mesh
mesh = pybamm.Mesh(geometry, submesh_types, var_pts)

# check boundary locations
self.assertEqual(mesh["negative particle"][0].edges[0], 0)
self.assertEqual(mesh["negative particle"][0].edges[-1], 1)

# check number of edges and nodes
self.assertEqual(len(mesh["negative particle"][0].nodes), var_pts[r])
self.assertEqual(
len(mesh["negative particle"][0].edges),
len(mesh["negative particle"][0].nodes) + 1
)


if __name__ == "__main__":
print("Add -v for more debug output")
import sys

if "-v" in sys.argv:
debug = True
pybamm.settings.debug_mode = True
unittest.main()

0 comments on commit 01b5ab4

Please sign in to comment.