@@ -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