Skip to content

Commit 422834e

Browse files
committed
dsl: Add support for Borders of uneven thickness
1 parent 4354077 commit 422834e

1 file changed

Lines changed: 47 additions & 18 deletions

File tree

devito/types/grid.py

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -920,12 +920,17 @@ class Border(SubDomainSet):
920920
dimension, but only on the left of the y. One can also supply a single dimension on
921921
which to construct a border, using `dims=x` or similar.
922922
923+
Corners can be included, excluded, or overlapped by setting the `corners` kwarg.
924+
923925
Parameters
924926
----------
925927
grid : Grid
926928
The computational grid on which the border should be constructed
927-
border : int
928-
The thickness of the border in gridpoints
929+
border : int, tuple of int, or tuple of tuple of int
930+
The thickness of the border in gridpoints. A tuple with thickness for each
931+
dimension can be supplied if different thicknesses are required per-dimension.
932+
A tuple of tuples can also be supplied for more granular control of left and
933+
right border thicknesses for each dimension.
929934
dims : Dimension, dict, or None, optional
930935
The dimensions on which a border should be applied. Default is None, corresponding
931936
to borders on both sides of all dimensions.
@@ -1008,13 +1013,16 @@ class Border(SubDomainSet):
10081013
DimSpec = None | dict[Dimension, Dimension | str]
10091014
ParsedDimSpec = frozendict[Dimension, Dimension | str]
10101015

1011-
def __init__(self, grid: Grid, border: int | np.integer,
1016+
BorderInt = int | np.integer
1017+
BorderSpec = BorderInt | tuple[BorderInt] | tuple[tuple[BorderInt]]
1018+
ParsedBorderSpec = tuple[tuple[BorderInt]]
1019+
1020+
def __init__(self, grid: Grid, border: BorderSpec,
10121021
dims: DimSpec = None, name: str = 'border',
10131022
corners: str = 'nooverlap') -> None:
10141023

10151024
self._name = name
1016-
# FIXME: Needs to accept int, tuple of int, or tuple of tuple of int
1017-
self._border = border
1025+
self._border = Border._parse_border(border, grid)
10181026
self._border_dims = Border._parse_dims(dims, grid)
10191027

10201028
if corners not in ('overlap', 'nooverlap', 'nocorners'):
@@ -1054,6 +1062,27 @@ def _parse_dims(dims: DimSpec, grid: Grid) -> ParsedDimSpec:
10541062

10551063
return frozendict(_border_dims)
10561064

1065+
@staticmethod
1066+
def _parse_border(border: BorderSpec, grid: Grid) -> ParsedBorderSpec:
1067+
if isinstance(border, (int, np.integer)):
1068+
return ((border, border),)*len(grid.dimensions)
1069+
1070+
else: # Tuple guaranteed by typing
1071+
if not len(border) == len(grid.dimensions):
1072+
raise ValueError("Length of border thickness specification should "
1073+
"match number of dimensions")
1074+
retval = [] # FIXME: make a better name later
1075+
for b, d in zip(border, grid.dimensions):
1076+
if isinstance(b, tuple):
1077+
if not len(b) == 2:
1078+
raise ValueError(f"{b}: more than two thicknesses supplied "
1079+
f"for dimension {d}")
1080+
retval.append(b)
1081+
else:
1082+
retval.append((b, b))
1083+
1084+
return tuple(retval)
1085+
10571086
def _build_domains(self, grid: Grid) -> tuple[int, tuple[np.ndarray]]:
10581087
"""
10591088
Constructs the bounds and ndomains kwargs for the SubDomainSet.
@@ -1066,25 +1095,25 @@ def _build_domains(self, grid: Grid) -> tuple[int, tuple[np.ndarray]]:
10661095
def _build_domains_overlap(self, grid: Grid) -> tuple[int, tuple[np.ndarray]]:
10671096

10681097
bounds = []
1069-
for i, (d, s) in enumerate(zip(grid.dimensions, grid.shape)):
1098+
for i, (d, s, b) in enumerate(zip(grid.dimensions, grid.shape, self.border)):
10701099

10711100
if d in self.border_dims:
10721101
side = self.border_dims[d]
10731102

10741103
if isinstance(side, Dimension):
1075-
bounds_l = [0 if j != 2*i else s-self.border
1104+
bounds_l = [0 if j != 2*i else s - b[0]
10761105
for j in range(2*len(grid.dimensions))]
1077-
bounds_r = [0 if j != 2*i+1 else s-self.border
1106+
bounds_r = [0 if j != 2*i+1 else s - b[1]
10781107
for j in range(2*len(grid.dimensions))]
10791108

10801109
bounds.extend([bounds_l, bounds_r])
10811110

10821111
elif side == 'left':
1083-
bounds.append([0 if j != 2*i else s-self.border
1112+
bounds.append([0 if j != 2*i else s - b[0]
10841113
for j in range(2*len(grid.dimensions))])
10851114

10861115
elif side == 'right':
1087-
bounds.append([0 if j != 2*i+1 else s-self.border
1116+
bounds.append([0 if j != 2*i+1 else s - b[1]
10881117
for j in range(2*len(grid.dimensions))])
10891118

10901119
else:
@@ -1099,23 +1128,23 @@ def _build_domains_nooverlap(self, grid: Grid) -> tuple[int, tuple[np.ndarray]]:
10991128
# Unpack the user-provided specification into a set of sides (on which
11001129
# a cartesian product is taken) and a mapper from those sides to a set of
11011130
# bounds for each dimension.
1102-
for d, s in zip(grid.dimensions, grid.shape):
1131+
for d, s, b in zip(grid.dimensions, grid.shape, self.border):
11031132
if d in self.border_dims:
11041133
side = self.border_dims[d]
11051134

11061135
if isinstance(side, Dimension):
11071136
domain_map[d] = (LEFT, CENTER, RIGHT)
1108-
interval_map[d] = {LEFT: (0, s-self.border),
1109-
CENTER: (self.border, self.border),
1110-
RIGHT: (s-self.border, 0)}
1137+
interval_map[d] = {LEFT: (0, s - b[0]),
1138+
CENTER: (b[0], b[1]),
1139+
RIGHT: (s - b[1], 0)}
11111140
elif side == 'left':
11121141
domain_map[d] = (LEFT, CENTER)
1113-
interval_map[d] = {LEFT: (0, s-self.border),
1114-
CENTER: (self.border, 0)}
1142+
interval_map[d] = {LEFT: (0, s - b[0]),
1143+
CENTER: (b[0], 0)}
11151144
elif side == 'right':
11161145
domain_map[d] = (CENTER, RIGHT)
1117-
interval_map[d] = {CENTER: (0, self.border),
1118-
RIGHT: (s-self.border, 0)}
1146+
interval_map[d] = {CENTER: (0, b[1]),
1147+
RIGHT: (s - b[1], 0)}
11191148
else:
11201149
raise ValueError(f"Unrecognised side value {side}")
11211150
else:

0 commit comments

Comments
 (0)