Skip to content

Commit cb1e5d0

Browse files
committed
#704 added new extrapolation methods
1 parent 094ee49 commit cb1e5d0

File tree

3 files changed

+155
-63
lines changed

3 files changed

+155
-63
lines changed

pybamm/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ def version(formatted=False):
229229
# Mesh and Discretisation classes
230230
#
231231
from .discretisations.discretisation import Discretisation
232+
from .discretisations.discretisation import has_bc_condition_of_form
232233
from .meshes.meshes import Mesh, SubMesh, MeshGenerator
233234
from .meshes.zero_dimensional_submesh import SubMesh0D
234235
from .meshes.one_dimensional_submeshes import (

pybamm/discretisations/discretisation.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
from scipy.sparse import block_diag, csr_matrix
88

99

10+
def has_bc_condition_of_form(symbol, side, bcs, form):
11+
12+
if symbol.id in bcs:
13+
return True
14+
if bcs[symbol.id][side][1] == form:
15+
return True
16+
else:
17+
return False
18+
19+
else:
20+
return False
21+
22+
1023
class Discretisation(object):
1124
"""The discretisation class, with methods to process a model and replace
1225
Spatial Operators with Matrices and Variables with StateVectors
@@ -707,7 +720,9 @@ def _process_symbol(self, symbol):
707720
mesh = self.mesh[symbol.children[0].domain[0]][0]
708721
if isinstance(mesh, pybamm.SubMesh1D):
709722
symbol.side = mesh.tabs[symbol.side]
710-
return child_spatial_method.boundary_value_or_flux(symbol, disc_child)
723+
return child_spatial_method.boundary_value_or_flux(
724+
symbol, disc_child, self.bcs
725+
)
711726

712727
else:
713728
return symbol._unary_new_copy(disc_child)

pybamm/spatial_methods/finite_volume.py

+138-62
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, mesh):
3636
super().__init__(mesh)
3737

3838
# there is no way to set this at the moment
39-
self.extrapolation = "linear"
39+
self.extrapolation = "quadratic"
4040

4141
# add npts_for_broadcast to mesh domains for this particular discretisation
4242
for dom in mesh.keys():
@@ -576,9 +576,9 @@ def add_ghost_nodes(self, symbol, discretised_symbol, bcs):
576576

577577
return pybamm.Matrix(matrix) @ discretised_symbol + bcs_vector
578578

579-
def boundary_value_or_flux(self, symbol, discretised_child):
579+
def boundary_value_or_flux(self, symbol, discretised_child, bcs=None):
580580
"""
581-
Uses linear extrapolation to get the boundary value or flux of a variable in the
581+
Uses extrapolation to get the boundary value or flux of a variable in the
582582
Finite Volume Method.
583583
584584
See :meth:`pybamm.SpatialMethod.boundary_value`
@@ -590,9 +590,14 @@ def boundary_value_or_flux(self, symbol, discretised_child):
590590
prim_pts = submesh_list[0].npts
591591
sec_pts = len(submesh_list)
592592

593+
if not bcs:
594+
bcs = {}
595+
593596
# Create submatrix to compute boundary values or fluxes
594597
if isinstance(symbol, pybamm.BoundaryValue):
595598

599+
# Derivation of extrapolation formula can be found at:
600+
# https://github.com/Scottmar93/extrapolation-coefficents/tree/master
596601
nodes = submesh_list[0].nodes
597602
edges = submesh_list[0].edges
598603

@@ -604,85 +609,140 @@ def boundary_value_or_flux(self, symbol, discretised_child):
604609
dxNm1 = submesh_list[0].d_nodes[-1]
605610
dxNm2 = submesh_list[0].d_nodes[-2]
606611

612+
child = symbol.child
613+
607614
if symbol.side == "left":
608615

609616
if self.extrapolation == "linear":
610617
# to find value at x* use formula:
611618
# f(x*) = f_1 - (dx0 / dx1) (f_2 - f_1)
612-
# where
613-
# dx0 is distance between edge and first node
614-
# dx1 is distance between first and second nodes
615619

616-
sub_matrix = csr_matrix(
617-
([1 + (dx0 / dx1), -(dx0 / dx1)], ([0, 0], [0, 1])),
618-
shape=(1, prim_pts),
619-
)
620+
if pybamm.has_bc_condition_of_form(
621+
child, symbol.side, bcs, "Neumann"
622+
):
623+
sub_matrix = csr_matrix(([1], ([0], [0])), shape=(1, prim_pts),)
624+
625+
additive = -dx0 * bcs[child.id][symbol.side][0]
626+
627+
else:
628+
sub_matrix = csr_matrix(
629+
([1 + (dx0 / dx1), -(dx0 / dx1)], ([0, 0], [0, 1])),
630+
shape=(1, prim_pts),
631+
)
632+
additive = pybamm.Scalar(0)
633+
620634
elif self.extrapolation == "quadratic":
621-
# to find value at x* use formula:
622-
# see mathematica notebook at:
623-
# https://github.com/Scottmar93/extrapolation-coefficents/tree/master
624-
# f(x*) = a f_1 + b f_2 + c f_3
625635

626-
a = (dx0 + dx1) * (dx0 + dx1 + dx2) / (dx1 * (dx1 + dx2))
627-
b = -dx0 * (dx0 + dx1 + dx2) / (dx1 * dx2)
628-
c = dx0 * (dx0 + dx1) / (dx2 * (dx1 + dx2))
636+
if pybamm.has_bc_condition_of_form(
637+
child, symbol.side, bcs, "Neumann"
638+
):
639+
a = (dx0 + dx1) ** 2 / (dx1 * (2 * dx0 + dx1))
640+
b = -(dx0 ** 2) / (2 * dx0 * dx1 + dx1 ** 2)
641+
alpha = -(dx0 * (dx0 + dx1)) / (2 * dx0 + dx1)
629642

630-
sub_matrix = csr_matrix(
631-
([a, b, c], ([0, 0, 0], [0, 1, 2])), shape=(1, prim_pts),
632-
)
643+
sub_matrix = csr_matrix(
644+
([a, b], ([0, 0], [0, 1])), shape=(1, prim_pts),
645+
)
646+
additive = alpha * bcs[child.id][symbol.side][0]
647+
648+
else:
649+
a = (dx0 + dx1) * (dx0 + dx1 + dx2) / (dx1 * (dx1 + dx2))
650+
b = -dx0 * (dx0 + dx1 + dx2) / (dx1 * dx2)
651+
c = dx0 * (dx0 + dx1) / (dx2 * (dx1 + dx2))
652+
653+
sub_matrix = csr_matrix(
654+
([a, b, c], ([0, 0, 0], [0, 1, 2])), shape=(1, prim_pts),
655+
)
656+
657+
additive = pybamm.Scalar(0)
633658
else:
634659
raise NotImplementedError
635660

636661
elif symbol.side == "right":
637662

638663
if self.extrapolation == "linear":
639-
# to find value at x* use formula:
640-
# f(x*) = f_N - (dxN / dxNm1) (f_N - f_Nm1)
641-
# where
642-
# dxN is distance between edge and last node
643-
# dxNm1 is distance between second lase and last nodes
644664

645-
sub_matrix = csr_matrix(
646-
(
647-
[-(dxN / dxNm1), 1 + (dxN / dxNm1)],
648-
([0, 0], [prim_pts - 2, prim_pts - 1]),
649-
),
650-
shape=(1, prim_pts),
651-
)
665+
if pybamm.has_bc_condition_of_form(
666+
child, symbol.side, bcs, "Neumann"
667+
):
668+
# use formula:
669+
# f(x*) = fN + dxN * f'(x*)
670+
sub_matrix = csr_matrix(
671+
([1], ([0], [prim_pts - 1]),), shape=(1, prim_pts),
672+
)
673+
additive = dxN * bcs[child.id][symbol.side][0]
674+
675+
elif pybamm.has_bc_condition_of_form(
676+
child, symbol.side, bcs, "Dirichlet"
677+
):
678+
# just use the value from the bc: f(x*)
679+
sub_matrix = csr_matrix((1, prim_pts))
680+
additive = bcs[child.id][symbol.side][0]
681+
682+
else:
683+
# to find value at x* use formula:
684+
# f(x*) = f_N - (dxN / dxNm1) (f_N - f_Nm1)
685+
sub_matrix = csr_matrix(
686+
(
687+
[-(dxN / dxNm1), 1 + (dxN / dxNm1)],
688+
([0, 0], [prim_pts - 2, prim_pts - 1]),
689+
),
690+
shape=(1, prim_pts),
691+
)
692+
additive = pybamm.Scalar(0)
652693
elif self.extrapolation == "quadratic":
653-
# to find value at x* use formula:
654-
# see mathematica notebook at:
655-
# https://github.com/Scottmar93/extrapolation-coefficents/tree/master
656-
# f(x*) = a f_N + b f_Nm1 + c f_Nm2
657-
658-
a = (
659-
(dxN + dxNm1)
660-
* (dxN + dxNm1 + dxNm2)
661-
/ (dxNm1 * (dxNm1 + dxNm2))
662-
)
663-
b = -dxN * (dxN + dxNm1 + dxNm2) / (dxNm1 * dxNm2)
664-
c = dxN * (dxN + dxNm1) / (dxNm2 * (dxNm1 + dxNm2))
665694

666-
sub_matrix = csr_matrix(
667-
(
668-
[c, b, a],
669-
([0, 0, 0], [prim_pts - 3, prim_pts - 2, prim_pts - 1]),
670-
),
671-
shape=(1, prim_pts),
672-
)
695+
if pybamm.has_bc_condition_of_form(
696+
child, symbol.side, bcs, "Neumann"
697+
):
698+
a = (dxN + dxNm1) ** 2 / (dxNm1 * (2 * dxN + dxNm1))
699+
b = -(dxN ** 2) / (2 * dxN * dxNm1 + dxNm1 ** 2)
700+
alpha = dxN * (dxN + dxNm1) / (2 * dxN + dxNm1)
701+
sub_matrix = csr_matrix(
702+
([b, a], ([0, 0], [prim_pts - 2, prim_pts - 1]),),
703+
shape=(1, prim_pts),
704+
)
705+
706+
additive = alpha * bcs[child.id][symbol.side][0]
707+
708+
else:
709+
a = (
710+
(dxN + dxNm1)
711+
* (dxN + dxNm1 + dxNm2)
712+
/ (dxNm1 * (dxNm1 + dxNm2))
713+
)
714+
b = -dxN * (dxN + dxNm1 + dxNm2) / (dxNm1 * dxNm2)
715+
c = dxN * (dxN + dxNm1) / (dxNm2 * (dxNm1 + dxNm2))
716+
717+
sub_matrix = csr_matrix(
718+
(
719+
[c, b, a],
720+
([0, 0, 0], [prim_pts - 3, prim_pts - 2, prim_pts - 1]),
721+
),
722+
shape=(1, prim_pts),
723+
)
724+
additive = pybamm.Scalar(0)
673725
else:
674726
raise NotImplementedError
675727

676728
elif isinstance(symbol, pybamm.BoundaryGradient):
677729
if symbol.side == "left":
678-
# use formula:
679-
# f'(x*) = (f_2 - f_1) / dx1
680-
# where dx1 = x_2 - x_1
681730

682731
if self.extrapolation == "linear":
683-
sub_matrix = (1 / dx1) * csr_matrix(
684-
([-1, 1], ([0, 0], [0, 1])), shape=(1, prim_pts)
685-
)
732+
733+
if pybamm.has_bc_condition_of_form(
734+
child, symbol.side, bcs, "Neumann"
735+
):
736+
# just use the value from the bc: f'(x*)
737+
sub_matrix = csr_matrix((1, prim_pts))
738+
additive = bcs[child.id][symbol.side][0]
739+
else:
740+
# use formula:
741+
# f'(x*) = (f_2 - f_1) / dx1
742+
sub_matrix = (1 / dx1) * csr_matrix(
743+
([-1, 1], ([0, 0], [0, 1])), shape=(1, prim_pts)
744+
)
745+
additive = pybamm.Scalar(0)
686746
elif self.extrapolation == "quadratic":
687747

688748
a = -(2 * dx0 + 2 * dx1 + dx2) / (dx1 ** 2 + dx1 * dx2)
@@ -692,17 +752,28 @@ def boundary_value_or_flux(self, symbol, discretised_child):
692752
sub_matrix = csr_matrix(
693753
([a, b, c], ([0, 0, 0], [0, 1, 2])), shape=(1, prim_pts)
694754
)
755+
additive = pybamm.Scalar(0)
695756
else:
696757
raise NotImplementedError
697758

698759
elif symbol.side == "right":
699-
dx = submesh_list[0].d_nodes[-1]
700760

701761
if self.extrapolation == "linear":
702-
sub_matrix = (1 / dx) * csr_matrix(
703-
([-1, 1], ([0, 0], [prim_pts - 2, prim_pts - 1])),
704-
shape=(1, prim_pts),
705-
)
762+
if pybamm.has_bc_condition_of_form(
763+
child, symbol.side, bcs, "Neumann"
764+
):
765+
# just use the value from the bc: f'(x*)
766+
sub_matrix = csr_matrix((1, prim_pts))
767+
additive = bcs[child.id][symbol.side][0]
768+
else:
769+
# use formula:
770+
# f'(x*) = (f_N - f_Nm1) / dxNm1
771+
sub_matrix = (1 / dxNm1) * csr_matrix(
772+
([-1, 1], ([0, 0], [prim_pts - 2, prim_pts - 1])),
773+
shape=(1, prim_pts),
774+
)
775+
additive = pybamm.Scalar(0)
776+
706777
elif self.extrapolation == "quadratic":
707778
a = (2 * dxN + 2 * dxNm1 + dxNm2) / (dxNm1 ** 2 + dxNm1 * dxNm2)
708779
b = -(2 * dxN + dxNm1 + dxNm2) / (dxNm1 * dxNm2)
@@ -715,6 +786,7 @@ def boundary_value_or_flux(self, symbol, discretised_child):
715786
),
716787
shape=(1, prim_pts),
717788
)
789+
additive = pybamm.Scalar(0)
718790
else:
719791
raise NotImplementedError
720792

@@ -730,6 +802,10 @@ def boundary_value_or_flux(self, symbol, discretised_child):
730802
boundary_value.domain = symbol.domain
731803
boundary_value.auxiliary_domains = symbol.auxiliary_domains
732804

805+
additive.domain = symbol.domain
806+
additive.auxiliary_domains = symbol.auxiliary_domains
807+
boundary_value += additive
808+
733809
return boundary_value
734810

735811
def process_binary_operators(self, bin_op, left, right, disc_left, disc_right):

0 commit comments

Comments
 (0)