From 77d1341093b5cb8a89c7114acbe52a862af76bda Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Tue, 8 Nov 2016 10:45:09 +0100
Subject: [PATCH] Phasefield: prepared extraction of staggered quantities
 computation

- support for different #ghost layers at each coordinate limit
---
 cpu/kernelcreation.py |  9 +++++++--
 field.py              |  5 +++++
 transformations.py    | 16 ++++++++++------
 3 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/cpu/kernelcreation.py b/cpu/kernelcreation.py
index 6407382ba..c959afe09 100644
--- a/cpu/kernelcreation.py
+++ b/cpu/kernelcreation.py
@@ -6,7 +6,8 @@ from pystencils.field import Field
 import pystencils.ast as ast
 
 
-def createKernel(listOfEquations, functionName="kernel", typeForSymbol=None, splitGroups=(), iterationSlice=None):
+def createKernel(listOfEquations, functionName="kernel", typeForSymbol=None, splitGroups=(),
+                 iterationSlice=None, ghostLayers=None):
     """
     Creates an abstract syntax tree for a kernel function, by taking a list of update rules.
 
@@ -21,6 +22,10 @@ def createKernel(listOfEquations, functionName="kernel", typeForSymbol=None, spl
     :param splitGroups: Specification on how to split up inner loop into multiple loops. For details see
            transformation :func:`pystencils.transformation.splitInnerLoop`
     :param iterationSlice: if not None, iteration is done only over this slice of the field
+    :param ghostLayers: a sequence of pairs for each coordinate with lower and upper nr of ghost layers
+                        if None, the number of ghost layers is determined automatically and assumed to be equal for a
+                        all dimensions
+
     :return: :class:`pystencils.ast.KernelFunction` node
     """
     if not typeForSymbol:
@@ -43,7 +48,7 @@ def createKernel(listOfEquations, functionName="kernel", typeForSymbol=None, spl
         field.setReadOnly()
 
     body = ast.Block(assignments)
-    code = makeLoopOverDomain(body, functionName, iterationSlice=iterationSlice)
+    code = makeLoopOverDomain(body, functionName, iterationSlice=iterationSlice, ghostLayers=ghostLayers)
 
     if splitGroups:
         typedSplitGroups = [[typeSymbol(s) for s in splitGroup] for splitGroup in splitGroups]
diff --git a/field.py b/field.py
index 3507cd02e..9c5dfba03 100644
--- a/field.py
+++ b/field.py
@@ -156,6 +156,11 @@ class Field:
     def __repr__(self):
         return self._fieldName
 
+    def neighbor(self, coordId, offset):
+        offsetList = [0] * self.spatialDimensions
+        offsetList[coordId] = offset
+        return Field.Access(self, tuple(offsetList))
+
     def __getitem__(self, offset):
         if type(offset) is np.ndarray:
             offset = tuple(offset)
diff --git a/transformations.py b/transformations.py
index 144390803..634d57760 100644
--- a/transformations.py
+++ b/transformations.py
@@ -9,12 +9,15 @@ from pystencils.slicing import normalizeSlice
 import pystencils.ast as ast
 
 
-def makeLoopOverDomain(body, functionName, iterationSlice=None):
+def makeLoopOverDomain(body, functionName, iterationSlice=None, ghostLayers=None):
     """
     Uses :class:`pystencils.field.Field.Access` to create (multiple) loops around given AST.
     :param body: list of nodes
     :param functionName: name of generated C function
     :param iterationSlice: if not None, iteration is done only over this slice of the field
+    :param ghostLayers: a sequence of pairs for each coordinate with lower and upper nr of ghost layers
+                        if None, the number of ghost layers is determined automatically and assumed to be equal for a
+                        all dimensions
     :return: :class:`LoopOverCoordinate` instance with nested loops, ordered according to field layouts
     """
     # find correct ordering by inspecting participating FieldAccesses
@@ -23,9 +26,6 @@ def makeLoopOverDomain(body, functionName, iterationSlice=None):
     fields = set(fieldList)
     loopOrder = getOptimalLoopOrdering(fields)
 
-    # find number of required ghost layers
-    requiredGhostLayers = max([fa.requiredGhostLayers for fa in fieldAccesses])
-
     shapes = set([f.spatialShape for f in fields])
 
     if len(shapes) > 1:
@@ -39,12 +39,16 @@ def makeLoopOverDomain(body, functionName, iterationSlice=None):
     if iterationSlice is not None:
         iterationSlice = normalizeSlice(iterationSlice, shape)
 
+    if ghostLayers is None:
+        requiredGhostLayers = max([fa.requiredGhostLayers for fa in fieldAccesses])
+        ghostLayers = [(requiredGhostLayers, requiredGhostLayers)] * len(loopOrder)
+
     currentBody = body
     lastLoop = None
     for i, loopCoordinate in enumerate(loopOrder):
         if iterationSlice is None:
-            begin = requiredGhostLayers
-            end = shape[loopCoordinate] - requiredGhostLayers
+            begin = ghostLayers[loopCoordinate][0]
+            end = shape[loopCoordinate] - ghostLayers[loopCoordinate][1]
             newLoop = ast.LoopOverCoordinate(currentBody, loopCoordinate, begin, end, 1)
             lastLoop = newLoop
             currentBody = ast.Block([lastLoop])
-- 
GitLab