From cd11756dc7f25e770fbb58b968ff75ba13594359 Mon Sep 17 00:00:00 2001
From: Martin Bauer <martin.bauer@fau.de>
Date: Fri, 26 Jan 2018 12:16:49 +0100
Subject: [PATCH] New boundary handling

- plotting
- calculation of force on boundary
- vtk outpu
---
 boundaries/__init__.py                        |   2 +-
 .../boundary_handling.py                      | 204 ++++++++----
 boundaries/boundaryconditions.py              |  29 --
 boundaries/boundaryhandling.py                |  61 ----
 boundaries/boundaryhandling_old.py            | 280 ----------------
 boundaries/forceevaluation.py                 |  52 ---
 boundaries/handlinginterface.py               | 309 ------------------
 boundaries/periodicityhandling.py             |  69 ----
 geometry.py                                   |  14 +-
 lbstep.py                                     |  45 +--
 plot2d.py                                     |  86 +++--
 scenarios.py                                  |  41 +--
 12 files changed, 230 insertions(+), 962 deletions(-)
 rename boundary_handling.py => boundaries/boundary_handling.py (68%)
 delete mode 100644 boundaries/boundaryhandling.py
 delete mode 100644 boundaries/boundaryhandling_old.py
 delete mode 100644 boundaries/forceevaluation.py
 delete mode 100644 boundaries/handlinginterface.py
 delete mode 100644 boundaries/periodicityhandling.py

diff --git a/boundaries/__init__.py b/boundaries/__init__.py
index 7588d247..6d1d37c0 100644
--- a/boundaries/__init__.py
+++ b/boundaries/__init__.py
@@ -1,2 +1,2 @@
 from lbmpy.boundaries.boundaryconditions import NoSlip, UBB, FixedDensity, NeumannByCopy
-from lbmpy.boundaries.boundaryhandling import BoundaryHandling
\ No newline at end of file
+from lbmpy.boundaries.boundary_handling import BoundaryHandling
diff --git a/boundary_handling.py b/boundaries/boundary_handling.py
similarity index 68%
rename from boundary_handling.py
rename to boundaries/boundary_handling.py
index 07e0c29a..79eb83e3 100644
--- a/boundary_handling.py
+++ b/boundaries/boundary_handling.py
@@ -1,52 +1,11 @@
 import numpy as np
 
-from lbmpy.boundaries.boundary_kernel import generateIndexBoundaryKernelGeneric
+from lbmpy.stencils import inverseDirection
+from pystencils.cache import memorycache
 from pystencils import Field
+from lbmpy.boundaries import NoSlip
+from lbmpy.boundaries.boundary_kernel import generateIndexBoundaryKernelGeneric
 from lbmpy.boundaries.createindexlist import createBoundaryIndexArray, numpyDataTypeForBoundaryObject
-from pystencils.cache import memorycache
-
-
-class BoundaryDataSetter:
-
-    def __init__(self, indexArray, offset, stencil, ghostLayers, pdfArray):
-        self.indexArray = indexArray
-        self.offset = offset
-        self.stencil = np.array(stencil)
-        self.pdfArray = pdfArray.view()
-        self.pdfArray.flags.writeable = False
-
-        arrFieldNames = indexArray.dtype.names
-        self.dim = 3 if 'z' in arrFieldNames else 2
-        assert 'x' in arrFieldNames and 'y' in arrFieldNames and 'dir' in arrFieldNames, str(arrFieldNames)
-        self.boundaryDataNames = set(self.indexArray.dtype.names) - set(['x', 'y', 'z', 'dir'])
-        self.coordMap = {0: 'x', 1: 'y', 2: 'z'}
-        self.ghostLayers = ghostLayers
-
-    def fluidCellPositions(self, coord):
-        assert coord < self.dim
-        return self.indexArray[self.coordMap[coord]] + self.offset[coord] - self.ghostLayers + 0.5
-
-    @memorycache()
-    def linkOffsets(self):
-        return self.stencil[self.indexArray['dir']]
-
-    @memorycache()
-    def linkPositions(self, coord):
-        return self.fluidCellPositions(coord) + 0.5 * self.linkOffsets()[:, coord]
-
-    @memorycache()
-    def boundaryCellPositions(self, coord):
-        return self.fluidCellPositions(coord) + self.linkOffsets()[:, coord]
-
-    def __setitem__(self, key, value):
-        if key not in self.boundaryDataNames:
-            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (key, self.boundaryDataNames))
-        self.indexArray[key] = value
-
-    def __getitem__(self, item):
-        if item not in self.boundaryDataNames:
-            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (item, self.boundaryDataNames))
-        return self.indexArray[item]
 
 
 class BoundaryHandling:
@@ -56,7 +15,7 @@ class BoundaryHandling:
 
         self._lbMethod = lbMethod
         self._dataHandling = dataHandling
-        self._pdfFieldName = pdfFieldName
+        self._pdfArrayName = pdfFieldName
         self._flagFieldName = name + "Flags"
         self._indexArrayName = name + "IndexArrays"
         self._target = target
@@ -73,13 +32,17 @@ class BoundaryHandling:
                              "If you want to add multiple handlings, choose a different name.")
 
         gpu = self._target == 'gpu'
-        dataHandling.addArray(self._flagFieldName, dtype=np.int32, cpu=True, gpu=gpu)
+        dataHandling.addArray(self._flagFieldName, dtype=np.uint32, cpu=True, gpu=gpu)
         dataHandling.addCustomClass(self._indexArrayName, self.IndexFieldBlockData, cpu=True, gpu=gpu)
 
         ffGhostLayers = self._dataHandling.ghostLayersOfField(self._flagFieldName)
         for b in self._dataHandling.iterate(ghostLayers=ffGhostLayers):
             b[self._flagFieldName].fill(self._fluidFlag)
 
+    @property
+    def shape(self):
+        return self._dataHandling.shape
+
     @property
     def dim(self):
         return self._lbMethod.dim
@@ -88,11 +51,24 @@ class BoundaryHandling:
     def lbMethod(self):
         return self._lbMethod
 
+    @property
+    def dataHandling(self):
+        return self._dataHandling
+
     @property
     def boundaryObjects(self):
         return tuple(self._boundaryObjectToName.keys())
 
-    def setBoundary(self, boundaryObject, sliceObj=None, maskCallback=None, ghostLayers=True):
+    @property
+    def flagArrayName(self):
+        return self._flagFieldName
+
+    def getBoundaryNameToFlagDict(self):
+        result = {bObj.name: bInfo.flag for bObj, bInfo in self._boundaryObjectToBoundaryInfo.items()}
+        result['fluid'] = self._fluidFlag
+        return result
+
+    def setBoundary(self, boundaryObject, sliceObj=None, maskCallback=None, ghostLayers=True, innerGhostLayers=True):
         """
         Sets boundary using either a rectangular slice, a boolean mask or a combination of both
 
@@ -111,7 +87,7 @@ class BoundaryHandling:
         """
         flag = self._getFlagForBoundary(boundaryObject)
 
-        for b in self._dataHandling.iterate(sliceObj=sliceObj, ghostLayers=ghostLayers):
+        for b in self._dataHandling.iterate(sliceObj, ghostLayers=ghostLayers, innerGhostLayers=innerGhostLayers):
             flagArr = b[self._flagFieldName]
             if maskCallback is not None:
                 mask = maskCallback(*b.midpointArrays)
@@ -121,10 +97,12 @@ class BoundaryHandling:
 
         self._dirty = True
 
-    def getBoundaryNameToFlagDict(self):
-        result = {bObj.name: bInfo.flag for bObj, bInfo in self._boundaryObjectToBoundaryInfo.values()}
-        result['fluid'] = self._fluidFlag
-        return result
+    def forceOnBoundary(self, boundaryObj):
+        if isinstance(boundaryObj, NoSlip):
+            return self._forceOnNoSlip(boundaryObj)
+        else:
+            self.__call__()
+            return self._forceOnBoundary(boundaryObj)
 
     def prepare(self):
         if not self._dirty:
@@ -147,17 +125,88 @@ class BoundaryHandling:
 
         for b in self._dataHandling.iterate(gpu=self._target == 'gpu'):
             for bObj, idxArr in b[self._indexArrayName].boundaryObjectToIndexList.items():
-                kwargs[self._pdfFieldName] = b[self._pdfFieldName]
+                kwargs[self._pdfArrayName] = b[self._pdfArrayName]
                 self._boundaryObjectToBoundaryInfo[bObj].kernel(indexField=idxArr, **kwargs)
 
+    def geometryToVTK(self, fileName='geometry', boundaries='all', ghostLayers=False):
+        """
+        Writes a VTK field where each cell with the given boundary is marked with 1, other cells are 0
+        This can be used to display the simulation geometry in Paraview
+        :param fileName: vtk filename
+        :param boundaries: boundary object, or special string 'fluid' for fluid cells or special string 'all' for all
+                         boundary conditions.
+                         can also  be a sequence, to write multiple boundaries to VTK file
+        :param ghostLayers: number of ghost layers to write, or True for all, False for none
+        """
+        if boundaries == 'all':
+            boundaries = list(self._boundaryObjectToBoundaryInfo.keys()) + ['fluid']
+        elif not hasattr(boundaries, "__len__"):
+            boundaries = [boundaries]
+
+        masksToName = {}
+        for b in boundaries:
+            if b == 'fluid':
+                masksToName[self._fluidFlag] = 'fluid'
+            else:
+                masksToName[self._boundaryObjectToBoundaryInfo[b].flag] = b.name
+
+        writer = self.dataHandling.vtkWriterFlags(fileName, self._flagFieldName, masksToName, ghostLayers=ghostLayers)
+        writer(1)
+
     # ------------------------------ Implementation Details ------------------------------------------------------------
 
+    def _forceOnNoSlip(self, boundaryObj):
+        dh = self._dataHandling
+        ffGhostLayers = dh.ghostLayersOfField(self._flagFieldName)
+        method = self._lbMethod
+        stencil = np.array(method.stencil)
+
+        result = np.zeros(self.dim)
+
+        for b in dh.iterate(ghostLayers=ffGhostLayers):
+            objToIndList = b[self._indexArrayName].boundaryObjectToIndexList
+            pdfArray = b[self._pdfArrayName]
+            if boundaryObj in objToIndList:
+                indArr = objToIndList[boundaryObj]
+                indices = [indArr[name] for name in ('x', 'y', 'z')[:method.dim]]
+                indices.append(indArr['dir'])
+                values = 2 * pdfArray[tuple(indices)]
+                forces = stencil[indArr['dir']] * values[:, np.newaxis]
+                result += forces.sum(axis=0)
+        return dh.reduceFloatSequence(list(result), 'sum')
+
+    def _forceOnBoundary(self, boundaryObj):
+        dh = self._dataHandling
+        ffGhostLayers = dh.ghostLayersOfField(self._flagFieldName)
+        method = self._lbMethod
+        stencil = np.array(method.stencil)
+        invDirection = np.array([method.stencil.index(inverseDirection(d))
+                                 for d in method.stencil])
+
+        result = np.zeros(self.dim)
+
+        for b in dh.iterate(ghostLayers=ffGhostLayers):
+            objToIndList = b[self._indexArrayName].boundaryObjectToIndexList
+            pdfArray = b[self._pdfArrayName]
+            if boundaryObj in objToIndList:
+                indArr = objToIndList[boundaryObj]
+                indices = [indArr[name] for name in ('x', 'y', 'z')[:method.dim]]
+                offsets = stencil[indArr['dir']]
+                invDir = invDirection[indArr['dir']]
+                fluidValues = pdfArray[tuple(indices) + (indArr['dir'],)]
+                boundaryValues = pdfArray[tuple(ind + offsets[:, i] for i, ind in enumerate(indices)) + (invDir,)]
+                values = fluidValues + boundaryValues
+                forces = stencil[indArr['dir']] * values[:, np.newaxis]
+                result += forces.sum(axis=0)
+
+        return dh.reduceFloatSequence(list(result), 'sum')
+
     def _getFlagForBoundary(self, boundaryObject):
         if boundaryObject not in self._boundaryObjectToBoundaryInfo:
             symbolicIndexField = Field.createGeneric('indexField', spatialDimensions=1,
                                                      dtype=numpyDataTypeForBoundaryObject(boundaryObject, self.dim))
 
-            spPdfField = self._dataHandling.fields[self._pdfFieldName]
+            spPdfField = self._dataHandling.fields[self._pdfArrayName]
             ast = generateIndexBoundaryKernelGeneric(spPdfField, symbolicIndexField, self._lbMethod,
                                                      boundaryObject, target=self._target, openMP=self._openMP)
 
@@ -172,7 +221,7 @@ class BoundaryHandling:
         ffGhostLayers = dh.ghostLayersOfField(self._flagFieldName)
         for b in dh.iterate(ghostLayers=ffGhostLayers):
             flagArr = b[self._flagFieldName]
-            pdfArr = b[self._pdfFieldName]
+            pdfArr = b[self._pdfArrayName]
             indexArrayBD = b[self._indexArrayName]
             indexArrayBD.clear()
             for bInfo in self._boundaryObjectToBoundaryInfo.values():
@@ -225,3 +274,46 @@ class BoundaryHandling:
                     gpuVersion[obj] = gpuarray.to_gpu(cpuArr)
                 else:
                     gpuVersion[obj].set(cpuArr)
+
+
+class BoundaryDataSetter:
+
+    def __init__(self, indexArray, offset, stencil, ghostLayers, pdfArray):
+        self.indexArray = indexArray
+        self.offset = offset
+        self.stencil = np.array(stencil)
+        self.pdfArray = pdfArray.view()
+        self.pdfArray.flags.writeable = False
+
+        arrFieldNames = indexArray.dtype.names
+        self.dim = 3 if 'z' in arrFieldNames else 2
+        assert 'x' in arrFieldNames and 'y' in arrFieldNames and 'dir' in arrFieldNames, str(arrFieldNames)
+        self.boundaryDataNames = set(self.indexArray.dtype.names) - set(['x', 'y', 'z', 'dir'])
+        self.coordMap = {0: 'x', 1: 'y', 2: 'z'}
+        self.ghostLayers = ghostLayers
+
+    def fluidCellPositions(self, coord):
+        assert coord < self.dim
+        return self.indexArray[self.coordMap[coord]] + self.offset[coord] - self.ghostLayers + 0.5
+
+    @memorycache()
+    def linkOffsets(self):
+        return self.stencil[self.indexArray['dir']]
+
+    @memorycache()
+    def linkPositions(self, coord):
+        return self.fluidCellPositions(coord) + 0.5 * self.linkOffsets()[:, coord]
+
+    @memorycache()
+    def boundaryCellPositions(self, coord):
+        return self.fluidCellPositions(coord) + self.linkOffsets()[:, coord]
+
+    def __setitem__(self, key, value):
+        if key not in self.boundaryDataNames:
+            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (key, self.boundaryDataNames))
+        self.indexArray[key] = value
+
+    def __getitem__(self, item):
+        if item not in self.boundaryDataNames:
+            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (item, self.boundaryDataNames))
+        return self.indexArray[item]
\ No newline at end of file
diff --git a/boundaries/boundaryconditions.py b/boundaries/boundaryconditions.py
index 9557acc5..ab8caf25 100644
--- a/boundaries/boundaryconditions.py
+++ b/boundaries/boundaryconditions.py
@@ -77,35 +77,6 @@ class NoSlip(Boundary):
         return self.name == other.name
 
 
-#class NoSlipFullWay(Boundary):
-#    """Full-way bounce back"""
-#
-#    @property
-#    def additionalData(self):
-#        return [('lastValue', createType("double"))]
-#
-#    @property
-#    def additionalDataInitKernelEquations(self):
-#        # TODO this function is not longer availabel
-#        # formulate as additionalDataInitCallback
-#        def kernelEqGetter(pdfField, directionSymbol, indexField, **kwargs):
-#            return [sp.Eq(indexField('lastValue'), pdfField(directionSymbol))]
-#        return kernelEqGetter
-#
-#    def __call__(self, pdfField, directionSymbol, lbMethod, indexField, **kwargs):
-#        neighbor = offsetFromDir(directionSymbol, lbMethod.dim)
-#        inverseDir = invDir(directionSymbol)
-#        return [sp.Eq(pdfField[neighbor](inverseDir), indexField('lastValue')),
-#                sp.Eq(indexField('lastValue'), pdfField(directionSymbol))]
-#
-#    def __hash__(self):
-#        # All boundaries of these class behave equal -> should also be equal
-#        return hash("NoSlipFullWay")
-#
-#    def __eq__(self, other):
-#        return type(other) == NoSlipFullWay
-
-
 class UBB(Boundary):
 
     """Velocity bounce back boundary condition, enforcing specified velocity at obstacle"""
diff --git a/boundaries/boundaryhandling.py b/boundaries/boundaryhandling.py
deleted file mode 100644
index 5897cd17..00000000
--- a/boundaries/boundaryhandling.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import numpy as np
-from lbmpy.boundaries.handlinginterface import GenericBoundaryHandling, FlagFieldInterface
-from lbmpy.boundaries.periodicityhandling import PeriodicityHandling
-
-
-class BoundaryHandling(PeriodicityHandling, GenericBoundaryHandling):  # important: first periodicity, then boundary
-
-    def __init__(self, pdfField, domainShape, lbMethod, ghostLayers=1, target='cpu', openMP=True, flagDtype=np.uint32):
-        shapeWithGl = [a + 2 * ghostLayers for a in domainShape]
-        self.domainShape = domainShape
-        self.domainShapeWithGhostLayers = shapeWithGl
-        flagInterface = NumpyFlagFieldInterface(shapeWithGl, flagDtype)
-
-        GenericBoundaryHandling.__init__(self, flagInterface, pdfField, lbMethod, None, ghostLayers, target, openMP)
-        PeriodicityHandling.__init__(self, list(domainShape) + [len(lbMethod.stencil)], target=target)
-
-    def __call__(self, *args, **kwargs):
-        for cls in BoundaryHandling.__bases__:
-            cls.__call__(self, *args, **kwargs)
-
-    def prepare(self):
-        for cls in BoundaryHandling.__bases__:
-            cls.prepare(self)
-
-# ----------------------------------------------------------------------------------------------------------------------
-
-
-class NumpyFlagFieldInterface(FlagFieldInterface):
-
-    def __init__(self, shape, dtype=np.uint32):
-        self._array = np.ones(shape, dtype=dtype)
-        self._boundaryObjectToName = {}
-        self._boundaryObjectToFlag = {'fluid': dtype(2**0)}
-        self._nextFreeExponent = 1
-
-    @property
-    def array(self):
-        return self._array
-
-    def getFlag(self, boundaryObject):
-        if boundaryObject not in self._boundaryObjectToFlag:
-            self._boundaryObjectToFlag[boundaryObject] = 2 ** self._nextFreeExponent
-            self._nextFreeExponent += 1
-            name = self._makeBoundaryName(boundaryObject, self._boundaryObjectToName.values())
-            self._boundaryObjectToName[boundaryObject] = name
-
-        return self._boundaryObjectToFlag[boundaryObject]
-
-    def getName(self, boundaryObject):
-        return self._boundaryObjectToName[boundaryObject]
-
-    @property
-    def boundaryObjects(self):
-        return self._boundaryObjectToName.keys()
-
-    def clear(self):
-        self._array.fill(0)
-        self._boundaryObjectToName = {}
-        self._boundaryObjectToFlag = {'fluid': np.dtype(self._array.dtype)(2**0)}
-        self._nextFreeExponent = 1
-
diff --git a/boundaries/boundaryhandling_old.py b/boundaries/boundaryhandling_old.py
deleted file mode 100644
index 3be5b708..00000000
--- a/boundaries/boundaryhandling_old.py
+++ /dev/null
@@ -1,280 +0,0 @@
-import sympy as sp
-import numpy as np
-
-from lbmpy.stencils import getStencil
-from pystencils import TypedSymbol, Field
-from pystencils.backends.cbackend import CustomCppCode
-from lbmpy.boundaries.createindexlist import createBoundaryIndexList
-from pystencils.slicing import normalizeSlice, makeSlice
-
-INV_DIR_SYMBOL = TypedSymbol("invDir", "int")
-WEIGHTS_SYMBOL = TypedSymbol("weights", "double")
-
-
-class BoundaryHandling(object):
-    class BoundaryInfo(object):
-        def __init__(self, flag, object, kernel, ast):
-            self.flag = flag
-            self.object = object
-            self.kernel = kernel
-            self.ast = ast
-
-    def __init__(self, pdfField, domainShape, lbMethod, ghostLayers=1, target='cpu', openMP=True):
-        """
-        Class for managing boundary kernels
-
-        :param pdfField: pdf numpy array including ghost layers
-        :param domainShape: domain size without ghost layers
-        :param lbMethod: lattice Boltzmann method
-        :param ghostLayers: number of ghost layers
-        :param target: either 'cpu' or 'gpu'
-        """
-        symbolicPdfField = Field.createFromNumpyArray('pdfs', pdfField, indexDimensions=1)
-        assert pdfField.shape[:-1] == tuple(d + 2*ghostLayers for d in domainShape)
-
-        self._symbolicPdfField = symbolicPdfField
-        self._pdfField = pdfField
-        self._shapeWithGhostLayers = [d + 2 * ghostLayers for d in domainShape]
-        self._fluidFlag = 2 ** 0
-        self.flagField = np.full(self._shapeWithGhostLayers, self._fluidFlag, dtype=np.int32)
-        self._ghostLayers = ghostLayers
-        self._lbMethod = lbMethod
-
-        self._boundaryInfos = {}
-        self._periodicityKernels = []
-
-        self._dirty = False
-        self._periodicity = [False, False, False]
-        self._target = target
-        self.openMP = openMP
-        if target not in ('cpu', 'gpu'):
-            raise ValueError("Invalid target '%s' . Allowed values: 'cpu' or 'gpu'" % (target,))
-
-    @property
-    def periodicity(self):
-        """List that indicates for x,y (z) coordinate if domain is periodic in that direction"""
-        return self._periodicity
-
-    @property
-    def fluidFlag(self):
-        """Flag that is set where the lattice Boltzmann update should happen"""
-        return self._fluidFlag
-
-    def getFlag(self, boundaryObject):
-        """Flag that represents the given boundary."""
-        return self._boundaryInfos[boundaryObject].flag
-
-    def getBoundaries(self):
-        return [b.object for b in self._boundaryInfos.values()]
-
-    def setPeriodicity(self, x=False, y=False, z=False):
-        """Enable periodic boundary conditions at the border of the domain"""
-        for d in (x, y, z):
-            assert isinstance(d, bool)
-
-        self._periodicity = [x, y, z]
-        self._compilePeriodicityKernels()
-
-    def hasBoundary(self, boundaryObject):
-        """Returns boolean indicating if a boundary with that name exists"""
-        return boundaryObject in self._boundaryInfos
-
-    def setBoundary(self, boundaryObject, indexExpr=None, maskArr=None):
-        """
-        Sets boundary in a rectangular region (slice)
-
-        :param boundaryObject: boundary condition object or the string 'fluid' to remove boundary conditions
-        :param indexExpr: slice expression, where boundary should be set, see :mod:`pystencils.slicing`
-        :param maskArr: optional boolean (masked) array specifying where the boundary should be set
-        """
-        if indexExpr is None:
-            indexExpr = [slice(None, None, None)] * len(self.flagField.shape)
-        if boundaryObject == 'fluid':
-            flag = self._fluidFlag
-        else:
-            flag = self.addBoundary(boundaryObject)
-            assert flag != self._fluidFlag
-
-        indexExpr = normalizeSlice(indexExpr, self._shapeWithGhostLayers)
-        if maskArr is None:
-            self.flagField[indexExpr] = flag
-        else:
-            flagFieldView = self.flagField[indexExpr]
-            flagFieldView[maskArr] = flag
-        self._dirty = True
-
-    def addBoundary(self, boundaryObject):
-        """
-        Adds a boundary condition, i.e. reserves a flog in the flag field and returns that flag
-        If the boundary already exists, the existing flag is returned.
-        This flag can be logicalled or'ed to the boundaryHandling.flagField
-
-        :param boundaryObject: boundary condition object, see :mod:`lbmpy.boundaries.boundaryconditions`
-        """
-        if boundaryObject in self._boundaryInfos:
-            return self._boundaryInfos[boundaryObject].flag
-
-        newIdx = len(self._boundaryInfos) + 1  # +1 because 2**0 is reserved for fluid flag
-        boundaryInfo = self.BoundaryInfo(2 ** newIdx, boundaryObject, None, None)
-        self._boundaryInfos[boundaryObject] = boundaryInfo
-        self._dirty = True
-        return boundaryInfo.flag
-
-    def indices(self, dx=1.0, includeGhostLayers=False):
-        if not includeGhostLayers:
-            params = [np.arange(start=-1, stop=s-1) * dx for s in self.flagField.shape]
-        else:
-            params = [np.arange(s) * dx for s in self.flagField.shape]
-        return np.meshgrid(*params, indexing='ij')
-
-    def __call__(self, **kwargs):
-        """Run the boundary handling, all keyword args are passed through to the boundary sweeps"""
-        if self._dirty:
-            self.prepare()
-        for boundary in self._boundaryInfos.values():
-            boundary.kernel(**kwargs)
-        for k in self._periodicityKernels:
-            k(**kwargs)
-
-    def clear(self):
-        """Removes all boundaries and fills the domain with fluid"""
-        self.flagField.fill(self._fluidFlag)
-        self._dirty = False
-        self._boundaryInfos = {}
-
-    def prepare(self):
-        """Fills data structures to speed up the boundary handling and compiles all boundary kernels.
-        This is automatically done when first called. With this function this can be triggered before running."""
-        for boundary in self._boundaryInfos.values():
-            assert boundary.flag != self._fluidFlag
-            idxArray = createBoundaryIndexList(self.flagField, self._lbMethod.stencil,
-                                               boundary.flag, self._fluidFlag, self._ghostLayers)
-
-            dim = self._lbMethod.dim
-
-            if boundary.object.additionalData:
-                coordinateNames = ["x", "y", "z"][:dim]
-                indexArrDtype = np.dtype([(name, np.int32) for name in coordinateNames] +
-                                         [('dir', np.int32)] +
-                                         [(i[0], i[1].numpyDtype) for i in boundary.object.additionalData])
-                extendedIdxField = np.empty(len(idxArray), dtype=indexArrDtype)
-                for prop in coordinateNames + ['dir']:
-                    extendedIdxField[prop] = idxArray[prop]
-
-                idxArray = extendedIdxField
-                if boundary.object.additionalDataInitKernelEquations:
-                    initKernelAst = generateIndexBoundaryKernel(self._symbolicPdfField, idxArray, self._lbMethod,
-                                                                boundary.object, target='cpu',
-                                                                createInitializationKernel=True)
-                    from pystencils.cpu import makePythonFunction as makePythonCpuFunction
-                    initKernel = makePythonCpuFunction(initKernelAst, {'indexField': idxArray, 'pdfs': self._pdfField})
-                    initKernel()
-
-            ast = generateIndexBoundaryKernel(self._symbolicPdfField, idxArray, self._lbMethod, boundary.object,
-                                              target=self._target)
-            boundary.ast = ast
-            if self._target == 'cpu':
-                from pystencils.cpu import makePythonFunction as makePythonCpuFunction, addOpenMP
-                addOpenMP(ast, numThreads=self.openMP)
-                boundary.kernel = makePythonCpuFunction(ast, {'indexField': idxArray})
-            elif self._target == 'gpu':
-                from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
-                import pycuda.gpuarray as gpuarray
-                idxGpuField = gpuarray.to_gpu(idxArray)
-                boundary.kernel = makePythonGpuFunction(ast, {'indexField': idxGpuField})
-            else:
-                assert False
-        self._dirty = False
-
-    def invalidateIndexCache(self):
-        """Invalidates the cache for optimization data structures. When setting boundaries the cache is automatically
-        invalidated, so there is no need to call this function manually, as long as the flag field is not manually
-        modified."""
-        self._dirty = True
-
-    def _compilePeriodicityKernels(self):
-        self._periodicityKernels = []
-        dim = len(self.flagField.shape)
-        if dim == 2:
-            stencil = getStencil("D2Q9")
-        elif dim == 3:
-            stencil = getStencil("D3Q27")
-        else:
-            assert False
-
-        filteredStencil = []
-        for direction in stencil:
-            useDirection = True
-            if direction == (0, 0) or direction == (0, 0, 0):
-                useDirection = False
-            for component, periodicity in zip(direction, self._periodicity):
-                if not periodicity and component != 0:
-                    useDirection = False
-            if useDirection:
-                filteredStencil.append(direction)
-
-        if len(filteredStencil) > 0:
-            if self._target == 'cpu':
-                from pystencils.slicing import getPeriodicBoundaryFunctor
-                self._periodicityKernels.append(getPeriodicBoundaryFunctor(filteredStencil, ghostLayers=1))
-            elif self._target == 'gpu':
-                from pystencils.gpucuda.periodicity import getPeriodicBoundaryFunctor
-                self._periodicityKernels.append(getPeriodicBoundaryFunctor(filteredStencil, self.flagField.shape,
-                                                                           indexDimensions=1,
-                                                                           indexDimShape=len(self._lbMethod.stencil),
-                                                                           ghostLayers=1))
-            else:
-                assert False
-
-
-# -------------------------------------- Helper Functions --------------------------------------------------------------
-
-
-
-
-# ------------------------------------- Kernel Generation --------------------------------------------------------------
-
-class LbmMethodInfo(CustomCppCode):
-    def __init__(self, lbMethod):
-        stencil = lbMethod.stencil
-        symbolsDefined = set(offsetSymbols(lbMethod.dim) + [INV_DIR_SYMBOL, WEIGHTS_SYMBOL])
-
-        offsetSym = offsetSymbols(lbMethod.dim)
-        code = "\n"
-        for i in range(lbMethod.dim):
-            offsetStr = ", ".join([str(d[i]) for d in stencil])
-            code += "const int %s [] = { %s };\n" % (offsetSym[i].name, offsetStr)
-
-        invDirs = []
-        for direction in stencil:
-            inverseDir = tuple([-i for i in direction])
-            invDirs.append(str(stencil.index(inverseDir)))
-
-        code += "const int %s [] = { %s };\n" % (INV_DIR_SYMBOL.name, ", ".join(invDirs))
-        weights = [str(w.evalf()) for w in lbMethod.weights]
-        code += "const double %s [] = { %s };\n" % (WEIGHTS_SYMBOL.name, ",".join(weights))
-        super(LbmMethodInfo, self).__init__(code, symbolsRead=set(), symbolsDefined=symbolsDefined)
-
-
-def generateIndexBoundaryKernel(pdfField, indexArr, lbMethod, boundaryFunctor, target='cpu',
-                                createInitializationKernel=False):
-    indexField = Field.createFromNumpyArray("indexField", indexArr)
-
-    elements = [LbmMethodInfo(lbMethod)]
-    dirSymbol = TypedSymbol("dir", indexArr.dtype.fields['dir'][0])
-    boundaryEqList = [sp.Eq(dirSymbol, indexField[0]('dir'))]
-    if createInitializationKernel:
-        boundaryEqList += boundaryFunctor.additionalDataInitKernelEquations(pdfField=pdfField, directionSymbol=dirSymbol,
-                                                                            lbMethod=lbMethod, indexField=indexField)
-    else:
-        boundaryEqList += boundaryFunctor(pdfField=pdfField, directionSymbol=dirSymbol, lbMethod=lbMethod,
-                                          indexField=indexField)
-    elements += boundaryEqList
-
-    if target == 'cpu':
-        from pystencils.cpu import createIndexedKernel
-        return createIndexedKernel(elements, [indexField])
-    elif target == 'gpu':
-        from pystencils.gpucuda import createdIndexedCUDAKernel
-        return createdIndexedCUDAKernel(elements, [indexField])
-
diff --git a/boundaries/forceevaluation.py b/boundaries/forceevaluation.py
deleted file mode 100644
index 3e741d73..00000000
--- a/boundaries/forceevaluation.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import numpy as np
-from lbmpy.stencils import inverseDirection
-
-
-def calculateForceOnNoSlipBoundary(boundaryObject, boundaryHandling, pdfArray):
-    indArr = boundaryHandling.getBoundaryIndexArray(boundaryObject)
-    method = boundaryHandling.lbMethod
-
-    stencil = np.array(method.stencil)
-
-    if method.dim == 2:
-        x, y = indArr['x'], indArr['y']
-        values = 2 * pdfArray[x, y,  indArr['dir']]
-    else:
-        assert method.dim == 3
-        x, y, z = indArr['x'], indArr['y'], indArr['z']
-        values = 2 * pdfArray[x, y, z, indArr['dir']]
-
-    forces = stencil[indArr['dir']] * values[:, np.newaxis]
-    return forces.sum(axis=0)
-
-
-def calculateForceOnBoundary(boundaryObject, boundaryHandling, pdfArray):
-    indArr = boundaryHandling.getBoundaryIndexArray(boundaryObject)
-    method = boundaryHandling.lbMethod
-
-    stencil = np.array(method.stencil)
-    invDirection = np.array([method.stencil.index(inverseDirection(d))
-                            for d in method.stencil])
-
-    if method.dim == 2:
-        x, y = indArr['x'], indArr['y']
-        offsets = stencil[indArr['dir']]
-
-        fluidValues = pdfArray[x, y,  indArr['dir']]
-        boundaryValues = pdfArray[x + offsets[:, 0],
-                                  y + offsets[:, 1],
-                                  invDirection[indArr['dir']]]
-    else:
-        assert method.dim == 3
-        x, y, z = indArr['x'], indArr['y'], indArr['z']
-        offsets = stencil[indArr['dir']]
-
-        fluidValues = pdfArray[x, y, z, indArr['dir']]
-        boundaryValues = pdfArray[x + offsets[:, 0],
-                                  y + offsets[:, 1],
-                                  z + offsets[:, 2],
-                                  invDirection[indArr['dir']]]
-
-    values = fluidValues + boundaryValues
-    forces = stencil[indArr['dir']] * values[:, np.newaxis]
-    return forces.sum(axis=0)
diff --git a/boundaries/handlinginterface.py b/boundaries/handlinginterface.py
deleted file mode 100644
index 4653feda..00000000
--- a/boundaries/handlinginterface.py
+++ /dev/null
@@ -1,309 +0,0 @@
-import numpy as np
-
-from pystencils.field import Field
-from pystencils.slicing import normalizeSlice, shiftSlice
-from lbmpy.boundaries.boundary_kernel import generateIndexBoundaryKernel
-from lbmpy.boundaries.createindexlist import createBoundaryIndexList, boundaryIndexArrayCoordinateNames,\
-    numpyDataTypeForBoundaryObject
-from pystencils.cache import memorycache
-
-
-class FlagFieldInterface(object):
-
-    def getFlag(self, boundaryObject):
-        raise NotImplementedError()
-
-    def getName(self, boundaryObject):
-        raise NotImplementedError()
-
-    @property
-    def array(self):
-        raise NotImplementedError()
-
-    @property
-    def boundaryObjects(self):
-        raise NotImplementedError()
-
-    def clear(self):
-        raise NotImplementedError()
-
-    @staticmethod
-    def _makeBoundaryName(boundaryObject, existingNames):
-        baseName = boundaryObject.name
-        name = baseName
-        counter = 1
-        while name in existingNames:
-            name = "%s_%d" % (baseName, counter)
-            counter += 1
-
-        return name
-
-
-class BoundaryDataSetter:
-
-    def __init__(self, indexArray, offset, stencil, ghostLayers):
-        self.indexArray = indexArray
-        self.offset = offset
-        self.stencil = np.array(stencil)
-        arrFieldNames = indexArray.dtype.names
-        self.dim = 3 if 'z' in arrFieldNames else 2
-        assert 'x' in arrFieldNames and 'y' in arrFieldNames and 'dir' in arrFieldNames, str(arrFieldNames)
-        self.boundaryDataNames = set(self.indexArray.dtype.names) - set(['x', 'y', 'z', 'dir'])
-        self.coordMap = {0: 'x', 1: 'y', 2: 'z'}
-        self.ghostLayers = ghostLayers
-
-    def fluidCellPositions(self, coord):
-        assert coord < self.dim
-        return self.indexArray[self.coordMap[coord]] + self.offset[coord] - self.ghostLayers + 0.5
-
-    @memorycache()
-    def linkOffsets(self):
-        return self.stencil[self.indexArray['dir']]
-
-    @memorycache()
-    def linkPositions(self, coord):
-        return self.fluidCellPositions(coord) + 0.5 * self.linkOffsets()[:, coord]
-
-    @memorycache()
-    def boundaryCellPositions(self, coord):
-        return self.fluidCellPositions(coord) + self.linkOffsets()[:, coord]
-
-    def __setitem__(self, key, value):
-        if key not in self.boundaryDataNames:
-            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (key, self.boundaryDataNames))
-        self.indexArray[key] = value
-
-    def __getitem__(self, item):
-        if item not in self.boundaryDataNames:
-            raise KeyError("Invalid boundary data name %s. Allowed are %s" % (item, self.boundaryDataNames))
-        return self.indexArray[item]
-
-
-class GenericBoundaryHandling(object):
-
-    class BoundaryInfo(object):
-        def __init__(self, kernel, ast, indexArray, idxArrayForExecution, boundaryDataSetter):
-            self.kernel = kernel
-            self.ast = ast
-            self.indexArray = indexArray
-            self.idxArrayForExecution = idxArrayForExecution  # is different for GPU kernels
-            self.boundaryDataSetter = boundaryDataSetter
-
-    def __init__(self, flagFieldInterface, pdfField, lbMethod, offset=None, ghostLayers=1, target='cpu', openMP=True):
-        """
-        :param flagFieldInterface: implementation of FlagFieldInterface
-        :param pdfField: numpy array
-        :param lbMethod:
-        :param offset: offset that is added to all coordinates when calling callback functions to set up geometry
-                       or boundary data. This is used for waLBerla simulations to pass the block offset  
-        :param target: 'cpu' or 'gpu'
-        :param openMP:
-        """
-        self._flagFieldInterface = flagFieldInterface
-        self._pdfField = pdfField
-        self._lbMethod = lbMethod
-        self._target = target
-        self._openMP = openMP
-        self.ghostLayers = ghostLayers
-        self._dirty = False
-        self.offset = offset if offset else (0,) * lbMethod.dim
-        self._boundaryInfos = {}  # mapping of boundary object to boundary info
-        self._symbolicPdfField = Field.createFromNumpyArray('pdfs', pdfField, indexDimensions=1)
-        self.dim = lbMethod.dim
-
-        if target not in ('cpu', 'gpu'):
-            raise ValueError("Invalid target '%s' . Allowed values: 'cpu' or 'gpu'" % (target,))
-
-    @property
-    def boundaryObjects(self):
-        return self._flagFieldInterface.boundaryObjects
-
-    @property
-    def lbMethod(self):
-        return self._lbMethod
-
-    def getBoundaryNameToFlagDict(self):
-        result = {self._flagFieldInterface.getName(o): self._flagFieldInterface.getFlag(o) for o in self._boundaryInfos}
-        result['fluid'] = self._flagFieldInterface.getFlag('fluid')
-        return result
-
-    def hasBoundary(self, boundaryObject):
-        """Returns boolean indicating if this boundary exists in that handling"""
-        return boundaryObject in self._boundaryInfos
-
-    def __call__(self, **kwargs):
-        """
-        Runs boundary handling
-        :param kwargs: keyword arguments passed to boundary kernel
-        :return:
-        """
-        if self._dirty:
-            self.prepare()
-        for boundary in self._boundaryInfos.values():
-            if boundary.kernel:
-                boundary.kernel(indexField=boundary.idxArrayForExecution, **kwargs)
-
-    def getBoundaryIndexArray(self, boundaryObject):
-        return self._boundaryInfos[boundaryObject].indexArray
-
-    def clear(self):
-        """Removes all boundaries and fills the domain with fluid"""
-        self._flagFieldInterface.clear()
-        self._dirty = False
-        self._boundaryInfos = {}
-
-    def triggerReinitializationOfBoundaryData(self, **kwargs):
-        if self._dirty:
-            self.prepare()
-            return
-        else:
-            for boundaryObject, boundaryInfo in self._boundaryInfos.items():
-                self.__boundaryDataInitialization(boundaryInfo, boundaryObject, **kwargs)
-
-    def prepare(self):
-        """Compiles boundary kernels according to flag field. When setting boundaries the cache is automatically
-        invalidated, so there is no need to call this function manually, as long as the flag field is not manually
-        modified."""
-        if not self._dirty:
-            return
-
-        dim = self._lbMethod.dim
-        fluidFlag = self._flagFieldInterface.getFlag('fluid')
-
-        for boundaryObject in self._flagFieldInterface.boundaryObjects:
-            boundaryFlag = self._flagFieldInterface.getFlag(boundaryObject)
-            idxArray = createBoundaryIndexList(self.flagField, self._lbMethod.stencil,
-                                               boundaryFlag, fluidFlag, self.ghostLayers)
-            if len(idxArray) == 0:
-                continue
-
-            if boundaryObject.additionalData:
-                coordinateNames = boundaryIndexArrayCoordinateNames[:dim]
-                indexArrDtype = numpyDataTypeForBoundaryObject(boundaryObject, dim)
-                extendedIdxField = np.empty(len(idxArray), dtype=indexArrDtype)
-                for prop in coordinateNames + ['dir']:
-                    extendedIdxField[prop] = idxArray[prop]
-
-                idxArray = extendedIdxField
-
-            ast = generateIndexBoundaryKernel(self._symbolicPdfField, idxArray, self._lbMethod, boundaryObject,
-                                              target=self._target)
-
-            if self._target == 'cpu':
-                from pystencils.cpu import makePythonFunction as makePythonCpuFunction, addOpenMP
-                addOpenMP(ast, numThreads=self._openMP)
-                idxArrayForExecution = idxArray
-                kernel = makePythonCpuFunction(ast)
-            elif self._target == 'gpu':
-                from pystencils.gpucuda import makePythonFunction as makePythonGpuFunction
-                import pycuda.gpuarray as gpuarray
-                idxArrayForExecution = gpuarray.to_gpu(idxArray)
-                kernel = makePythonGpuFunction(ast)
-            else:
-                assert False
-
-            boundaryDataSetter = BoundaryDataSetter(idxArray, self.offset, self._lbMethod.stencil, self.ghostLayers)
-            boundaryInfo = GenericBoundaryHandling.BoundaryInfo(kernel, ast, idxArray,
-                                                                idxArrayForExecution, boundaryDataSetter)
-            self._boundaryInfos[boundaryObject] = boundaryInfo
-
-            if boundaryObject.additionalData:
-                self.__boundaryDataInitialization(boundaryInfo, boundaryObject)
-
-        self._dirty = False
-
-    def getMask(self, boundaryObject):
-        return np.bitwise_and(self._flagFieldInterface.array, self._flagFieldInterface.getFlag(boundaryObject))
-
-    @property
-    def flagField(self):
-        return self._flagFieldInterface.array
-
-    def reserveFlag(self, boundaryObject):
-        self._flagFieldInterface.getFlag(boundaryObject)
-
-    def setBoundary(self, boundaryObject, indexExpr=None, maskCallback=None, includeGhostLayers=True):
-        """
-        Sets boundary using either a rectangular slice, a boolean mask or a combination of both
-        
-        :param boundaryObject: instance of a boundary object that should be set 
-        :param indexExpr: a slice object (can be created with makeSlice[]) that selects a part of the domain where
-                          the boundary should be set. If none, the complete domain is selected which makes only sense
-                          if a maskCallback is passed. The slice can have ':' placeholders, which are interpreted 
-                          depending on the 'includeGhostLayers' parameter i.e. if it is True, the slice extends
-                          into the ghost layers
-        :param maskCallback: callback function getting x,y (z) parameters of the cell midpoints and returning a 
-                             boolean mask with True entries where boundary cells should be set. 
-                             The x, y, z arrays have 2D/3D shape such that they can be used directly 
-                             to create the boolean return array. i.e return x < 10 sets boundaries in cells with
-                             midpoint x coordinate smaller than 10.
-        :param includeGhostLayers: if this parameter is False, boundaries can not be set in the ghost
-                                   layer, because index 0 is the first inner layer and -1 is interpreted in the Python
-                                   way as maximum. If this parameter is True, the lower ghost layers have index 0, and
-                                   placeholders ':' in index expressions extend into the ghost layers.
-        """
-        if indexExpr is None:
-            indexExpr = [slice(None, None, None)] * len(self.flagField.shape)
-        if not includeGhostLayers:
-            domainSize = [i - 2 * self.ghostLayers for i in self._flagFieldInterface.array.shape]
-            indexExpr = normalizeSlice(indexExpr, domainSize)
-            indexExpr = shiftSlice(indexExpr, self.ghostLayers)
-        else:
-            indexExpr = normalizeSlice(indexExpr, self._flagFieldInterface.array.shape)
-
-        mask = None
-        if maskCallback is not None:
-            gridParams = []
-            for s, offset in zip(indexExpr, self.offset):
-                if isinstance(s, slice):
-                    gridParams.append(np.arange(s.start, s.stop) + offset + 0.5 - self.ghostLayers)
-                else:
-                    gridParams.append(s + offset + 0.5 - self.ghostLayers)
-            indices = np.meshgrid(*gridParams, indexing='ij')
-            mask = maskCallback(*indices)
-        return self._setBoundaryWithMaskArray(boundaryObject, indexExpr, mask)
-
-    def _setBoundaryWithMaskArray(self, boundaryObject, indexExpr=None, maskArr=None):
-        """
-        Sets boundary in a rectangular region (slice)
-
-        :param boundaryObject: boundary condition object or the string 'fluid' to remove boundary conditions
-        :param indexExpr: slice expression, where boundary should be set. ghost layers are expected to have coord=0
-        :param maskArr: optional boolean (masked) array specifying where the boundary should be set
-        """
-        if indexExpr is None:
-            indexExpr = [slice(None, None, None)] * len(self.flagField.shape)
-
-        flag = self._flagFieldInterface.getFlag(boundaryObject)
-        flagField = self._flagFieldInterface.array
-        indexExpr = normalizeSlice(indexExpr, flagField.shape)
-
-        if maskArr is None:
-            flagField[indexExpr] = flag
-        else:
-            flagFieldView = flagField[indexExpr].squeeze()
-            maskArr = maskArr.squeeze()
-            flagFieldView[maskArr] = flag
-        self._dirty = True
-
-    def _invalidateCache(self):
-        self._dirty = True
-
-    def __boundaryDataInitialization(self, boundaryInfo, boundaryObject, **kwargs):
-        if boundaryObject.additionalDataInitCallback:
-            boundaryObject.additionalDataInitCallback(boundaryInfo.boundaryDataSetter, **kwargs)
-
-        if boundaryObject.additionalDataInitKernelEquations:
-            initKernelAst = generateIndexBoundaryKernel(self._symbolicPdfField, boundaryInfo.indexArray, self._lbMethod,
-                                                        boundaryObject, target='cpu',
-                                                        createInitializationKernel=True)
-            from pystencils.cpu import makePythonFunction as makePythonCpuFunction
-            initKernel = makePythonCpuFunction(initKernelAst, {'indexField': boundaryInfo.indexArray,
-                                                               'pdfs': self._pdfField})
-            initKernel()
-
-        if self._target == 'gpu':
-            import pycuda.gpuarray as gpuarray
-            boundaryInfo.idxArrayForExecution = gpuarray.to_gpu(boundaryInfo.indexArray)
-        else:
-            boundaryInfo.idxArrayForExecution = boundaryInfo.indexArray
diff --git a/boundaries/periodicityhandling.py b/boundaries/periodicityhandling.py
deleted file mode 100644
index 428afcae..00000000
--- a/boundaries/periodicityhandling.py
+++ /dev/null
@@ -1,69 +0,0 @@
-from lbmpy.stencils import getStencil
-
-
-class PeriodicityHandling(object):
-    def __init__(self, fieldShape, periodicity=(False, False, False), target='cpu'):
-        self._spatialShape = fieldShape[:-1]
-        self._indexShape = fieldShape[-1]
-        self._periodicity = list(periodicity)
-        self._periodicityDirty = True
-        self._periodicityKernels = []
-        self._target = target
-
-    @property
-    def periodicity(self):
-        """List that indicates for x,y (z) coordinate if domain is periodic in that direction"""
-        return self._periodicity
-
-    def setPeriodicity(self, x=False, y=False, z=False):
-        """Enable periodic boundary conditions at the border of the domain"""
-        for d in (x, y, z):
-            assert isinstance(d, bool)
-
-        self._periodicity = [x, y, z]
-        self._periodicityDirty = True
-
-    def __call__(self, **kwargs):
-        if self._periodicityDirty:
-            self.prepare()
-        for k in self._periodicityKernels:
-            k(**kwargs)
-
-    def prepare(self):
-        if not self._periodicityDirty:
-            return
-
-        self._periodicityKernels = []
-        dim = len(self._spatialShape)
-        if dim == 2:
-            stencil = getStencil("D2Q9")
-        elif dim == 3:
-            stencil = getStencil("D3Q27")
-        else:
-            assert False
-
-        filteredStencil = []
-        for direction in stencil:
-            useDirection = True
-            if direction == (0, 0) or direction == (0, 0, 0):
-                useDirection = False
-            for component, periodicity in zip(direction, self._periodicity):
-                if not periodicity and component != 0:
-                    useDirection = False
-            if useDirection:
-                filteredStencil.append(direction)
-
-        if len(filteredStencil) > 0:
-            if self._target == 'cpu':
-                from pystencils.slicing import getPeriodicBoundaryFunctor
-                self._periodicityKernels.append(getPeriodicBoundaryFunctor(filteredStencil, ghostLayers=1))
-            elif self._target == 'gpu':
-                from pystencils.gpucuda.periodicity import getPeriodicBoundaryFunctor
-                self._periodicityKernels.append(getPeriodicBoundaryFunctor(filteredStencil, self._spatialShape,
-                                                                           indexDimensions=1,
-                                                                           indexDimShape=self._indexShape,
-                                                                           ghostLayers=1))
-            else:
-                assert False
-
-        self._periodicityDirty = False
diff --git a/geometry.py b/geometry.py
index 404aed0f..7533bf98 100644
--- a/geometry.py
+++ b/geometry.py
@@ -33,7 +33,7 @@ def addParabolicVelocityInflow(boundaryHandling, u_max, indexExpr, velCoord=0, d
             if i != velCoord:
                 boundaryData[name] = 0.0
         if diameter is None:
-            radius = int(round(min(sh for i, sh in enumerate(boundaryHandling.domainShape) if i != velCoord) / 2))
+            radius = int(round(min(sh for i, sh in enumerate(boundaryHandling.shape) if i != velCoord) / 2))
         else:
             radius = int(round(diameter / 2))
 
@@ -41,8 +41,8 @@ def addParabolicVelocityInflow(boundaryHandling, u_max, indexExpr, velCoord=0, d
             normalCoord1 = (velCoord + 1) % 3
             normalCoord2 = (velCoord + 2) % 3
             y, z = boundaryData.linkPositions(normalCoord1), boundaryData.linkPositions(normalCoord2)
-            centeredNormal1 = y - int(round(boundaryHandling.domainShape[normalCoord1] / 2))
-            centeredNormal2 = z - int(round(boundaryHandling.domainShape[normalCoord2] / 2))
+            centeredNormal1 = y - int(round(boundaryHandling.shape[normalCoord1] / 2))
+            centeredNormal2 = z - int(round(boundaryHandling.shape[normalCoord2] / 2))
             distToCenter = np.sqrt(centeredNormal1 ** 2 + centeredNormal2 ** 2)
         elif dim == 2:
             normalCoord = (velCoord + 1) % 2
@@ -68,7 +68,7 @@ def setupChannelWalls(boundaryHandling, diameterCallback, duct=False, wallBounda
         raise ValueError("For duct flows, passing a diameter callback does not make sense.")
 
     if not duct:
-        diameter = min(boundaryHandling.domainShape[1:])
+        diameter = min(boundaryHandling.shape[1:])
         addPipe(boundaryHandling, diameterCallback if diameterCallback else diameter, wallBoundary)
 
 
@@ -82,7 +82,7 @@ def addPipe(boundaryHandling, diameter, boundary=NoSlip()):
                      a array of same shape as the received xCoordArray, with the diameter for each x position
     :param boundary: boundary object that is set at the wall, defaults to NoSlip (bounce back)
     """
-    domainShape = boundaryHandling.domainShape
+    domainShape = boundaryHandling.shape
     dim = len(domainShape)
     assert dim in (2, 3)
 
@@ -123,11 +123,11 @@ def addBlackAndWhiteImage(boundaryHandling, imageFile, targetSlice=None, plane=(
     except ImportError:
         raise ImportError("scipy image read could not be imported! Install 'scipy' and 'pillow'")
 
-    domainSize = boundaryHandling.domainShape
+    domainSize = boundaryHandling.shape
     if targetSlice is None:
         targetSlice = [slice(None, None, None)] * len(domainSize)
 
-    dim = len(boundaryHandling.domainShape)
+    dim = boundaryHandling.dim
 
     imageSlice = normalizeSlice(targetSlice, domainSize)
     targetSize = [imageSlice[i].stop - imageSlice[i].start for i in plane]
diff --git a/lbstep.py b/lbstep.py
index b3a9a55d..2008b434 100644
--- a/lbstep.py
+++ b/lbstep.py
@@ -1,5 +1,5 @@
 import numpy as np
-from lbmpy.boundary_handling import BoundaryHandling
+from lbmpy.boundaries.boundary_handling import BoundaryHandling
 from lbmpy.creationfunctions import switchToSymbolicRelaxationRatesForEntropicMethods, createLatticeBoltzmannFunction, \
     updateWithDefaultParameters
 from lbmpy.simplificationfactory import createSimplificationStrategy
@@ -11,7 +11,7 @@ from pystencils import createKernel, makeSlice
 class LatticeBoltzmannStep:
 
     def __init__(self, domainSize=None, lbmKernel=None, periodicity=False,
-                 kernelParams={}, dataHandling=None, dataPrefix="", optimizationParams={}, **methodParameters):
+                 kernelParams={}, dataHandling=None, name="lbm", optimizationParams={}, **methodParameters):
 
         # --- Parameter normalization  ---
         if dataHandling is not None:
@@ -36,10 +36,10 @@ class LatticeBoltzmannStep:
             Q = len(getStencil(methodParameters['stencil']))
 
         self._dataHandling = dataHandling
-        self._pdfArrName = dataPrefix + "pdfSrc"
-        self._tmpArrName = dataPrefix + "pdfTmp"
-        self.velocityDataName = dataPrefix + "velocity"
-        self.densityDataName = dataPrefix + "density"
+        self._pdfArrName = name + "_pdfSrc"
+        self._tmpArrName = name + "_pdfTmp"
+        self.velocityDataName = name + "_velocity"
+        self.densityDataName = name + "_density"
 
         self._gpu = target == 'gpu'
         layout = optimizationParams['fieldLayout']
@@ -67,7 +67,7 @@ class LatticeBoltzmannStep:
         else:
             self._sync = dataHandling.synchronizationFunctionCPU([self._pdfArrName], methodParameters['stencil'])
         self._boundaryHandling = BoundaryHandling(self._lbmKernel.method, self._dataHandling, self._pdfArrName,
-                                                  name=dataPrefix + "_boundaryHandling",
+                                                  name=name + "_boundaryHandling",
                                                   target=target, openMP=optimizationParams['openMP'])
 
         # -- Macroscopic Value Kernels
@@ -79,6 +79,9 @@ class LatticeBoltzmannStep:
             b[self.densityDataName].fill(1.0)
             b[self.velocityDataName].fill(0.0)
 
+        # -- VTK output
+        self.vtkWriter = self.dataHandling.vtkWriter(name, [self.velocityDataName, self.densityDataName])
+
     @property
     def boundaryHandling(self):
         """Boundary handling instance of the scenario. Use this to change the boundary setup"""
@@ -96,6 +99,10 @@ class LatticeBoltzmannStep:
     def method(self):
         return self._lbmKernel.method
 
+    @property
+    def pdfArrayName(self):
+        return self._pdfArrName
+
     def _getSlice(self, dataName, sliceObj):
         if sliceObj is None:
             sliceObj = makeSlice[:, :] if self.dim == 2 else makeSlice[:, :, 0.5]
@@ -132,6 +139,9 @@ class LatticeBoltzmannStep:
             self.timeStep()
         self.postRun()
 
+    def writeVTK(self):
+        self.vtkWriter(self.timeStepsRun)
+
     def _compilerMacroscopicSetterAndGetter(self):
         lbMethod = self._lbmKernel.method
         D = lbMethod.dim
@@ -153,24 +163,3 @@ class LatticeBoltzmannStep:
         setterEqs = createSimplificationStrategy(lbMethod)(setterEqs)
         setterKernel = createKernel(setterEqs, target='cpu').compile()
         return getterKernel, setterKernel
-
-
-if __name__ == '__main__':
-    from pycuda import autoinit
-    from lbmpy.boundaries import NoSlip, UBB
-    from pystencils import makeSlice
-    step = LatticeBoltzmannStep((30, 30), relaxationRate=1.8, periodicity=True,
-                                optimizationParams={'target': 'cpu', 'openMP': False})
-
-    wall = NoSlip()
-    movingWall = UBB((0.001, 0))
-
-    bh = step.boundaryHandling
-    bh.setBoundary(wall, makeSlice[0, :])
-    bh.setBoundary(wall, makeSlice[-1, :])
-    bh.setBoundary(wall, makeSlice[:, 0])
-    bh.setBoundary(movingWall, makeSlice[:, -1])
-    bh.prepare()
-
-    step.run(4)
-    step.run(100)
diff --git a/plot2d.py b/plot2d.py
index 8b0a3634..a08bbaa3 100644
--- a/plot2d.py
+++ b/plot2d.py
@@ -1,56 +1,54 @@
 from pystencils.plot2d import *
-from pystencils.slicing import normalizeSlice
 
 
-def plotBoundaryHandling(boundaryHandling, indexExpr=None, boundaryNameToColor=None, legend=True):
+def boundaryHandling(boundaryHandlingObj, indexExpr=None, boundaryNameToColor=None, showLegend=True):
     """
     Shows boundary cells
 
-    :param boundaryHandling: instance of :class:`lbmpy.boundaries.BoundaryHandling`
+    :param boundaryHandlingObj: instance of :class:`lbmpy.boundaries.BoundaryHandling`
     :param indexExpr: for 3D boundary handling a slice expression has to be passed here to define the plane that
                       should be plotted
     :param boundaryNameToColor: optional dictionary mapping boundary names to colors
-    :param legend: if True legend for color->boundary name is added
+    :param showLegend: if True legend for color->boundary name is added
     """
     import matplotlib.pyplot as plt
 
-    boundaryHandling.prepare()
-
-    if len(boundaryHandling.flagField.shape) != 2 and indexExpr is None:
-        raise ValueError("To plot a 3D boundary handling a slice has to be passed")
-
-    if boundaryNameToColor:
-        fixedColors = boundaryNameToColor
-    else:
-        fixedColors = {
-            'fluid': '#56b4e9',
-            'NoSlip': '#999999',
-            'UBB': '#d55e00',
-            'FixedDensity': '#009e73',
-        }
-
-    boundaryNames = []
-    flagValues = []
-    for name, flagName in sorted(boundaryHandling.getBoundaryNameToFlagDict().items(), key=lambda l: l[1]):
-        boundaryNames.append(name)
-        flagValues.append(flagName)
-    defaultCycle = matplotlib.rcParams['axes.prop_cycle']
-    colorValues = [fixedColors[name] if name in fixedColors else cycle['color']
-                   for cycle, name in zip(defaultCycle, boundaryNames)]
-
-    cmap = matplotlib.colors.ListedColormap(colorValues)
-    bounds = np.array(flagValues, dtype=float) - 0.5
-    bounds = list(bounds) + [bounds[-1] + 1]
-    norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
-
-    flagField = boundaryHandling.flagField
-    if indexExpr:
-        flagField = flagField[normalizeSlice(indexExpr, flagField.shape)]
-    flagField = flagField.swapaxes(0, 1)
-    plt.imshow(flagField, interpolation='none', origin='lower',
-               cmap=cmap, norm=norm)
-
-    patches = [matplotlib.patches.Patch(color=color, label=name) for color, name in zip(colorValues, boundaryNames)]
-    plt.axis('equal')
-    if legend:
-        plt.legend(handles=patches, bbox_to_anchor=(1.02, 0.5), loc=2, borderaxespad=0.)
+    boundaryHandlingObj.prepare()
+
+    dh = boundaryHandlingObj.dataHandling
+    for flagArr in dh.gatherArray(boundaryHandlingObj.flagArrayName, indexExpr):
+        if len(flagArr.shape) != 2 and indexExpr is None:
+            raise ValueError("To plot a 3D boundary handling a slice has to be passed")
+
+        if boundaryNameToColor:
+            fixedColors = boundaryNameToColor
+        else:
+            fixedColors = {
+                'fluid': '#56b4e9',
+                'NoSlip': '#999999',
+                'UBB': '#d55e00',
+                'FixedDensity': '#009e73',
+            }
+
+        boundaryNames = []
+        flagValues = []
+        for name, flagName in sorted(boundaryHandlingObj.getBoundaryNameToFlagDict().items(), key=lambda l: l[1]):
+            boundaryNames.append(name)
+            flagValues.append(flagName)
+        defaultCycle = matplotlib.rcParams['axes.prop_cycle']
+        colorValues = [fixedColors[name] if name in fixedColors else cycle['color']
+                       for cycle, name in zip(defaultCycle, boundaryNames)]
+
+        cmap = matplotlib.colors.ListedColormap(colorValues)
+        bounds = np.array(flagValues, dtype=float) - 0.5
+        bounds = list(bounds) + [bounds[-1] + 1]
+        norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
+
+        flagArr = flagArr.swapaxes(0, 1)
+        plt.imshow(flagArr, interpolation='none', origin='lower',
+                   cmap=cmap, norm=norm)
+
+        patches = [matplotlib.patches.Patch(color=color, label=name) for color, name in zip(colorValues, boundaryNames)]
+        plt.axis('equal')
+        if showLegend:
+            plt.legend(handles=patches, bbox_to_anchor=(1.02, 0.5), loc=2, borderaxespad=0.)
diff --git a/scenarios.py b/scenarios.py
index a3f393ec..39b3b75b 100644
--- a/scenarios.py
+++ b/scenarios.py
@@ -55,7 +55,7 @@ def createFullyPeriodicFlow(initialVelocity, periodicityInKernel=False, lbmKerne
     if dataHandling is None:
         dataHandling = createDataHandling(parallel, domainSize, periodicity=not periodicityInKernel,
                                           defaultGhostLayers=1)
-    step = LatticeBoltzmannStep(dataHandling=dataHandling, lbmKernel=lbmKernel, **kwargs)
+    step = LatticeBoltzmannStep(dataHandling=dataHandling, name="periodicScenario", lbmKernel=lbmKernel, **kwargs)
     for b in step.dataHandling.iterate(ghostLayers=False):
         np.copyto(b[step.velocityDataName], initialVelocity[b.globalSlice])
     return step
@@ -76,7 +76,7 @@ def createLidDrivenCavity(domainSize=None, lidVelocity=0.005, lbmKernel=None, pa
     assert domainSize is not None or dataHandling is not None
     if dataHandling is None:
         dataHandling = createDataHandling(parallel, domainSize, periodicity=False, defaultGhostLayers=1)
-    step = LatticeBoltzmannStep(dataHandling=dataHandling, lbmKernel=lbmKernel, **kwargs)
+    step = LatticeBoltzmannStep(dataHandling=dataHandling, lbmKernel=lbmKernel, name="lidDrivenCavity" **kwargs)
 
     myUbb = UBB(velocity=[lidVelocity, 0, 0][:step.method.dim])
     step.boundaryHandling.setBoundary(myUbb, sliceFromDirection('N', step.dim))
@@ -86,7 +86,7 @@ def createLidDrivenCavity(domainSize=None, lidVelocity=0.005, lbmKernel=None, pa
     return step
 
 
-def createChannel(domainSize, force=None, pressureDifference=None, u_max=None, diameterCallback=None,
+def createChannel(domainSize=None, force=None, pressureDifference=None, u_max=None, diameterCallback=None,
                   duct=False, wallBoundary=NoSlip(), parallel=False, dataHandling=None, **kwargs):
     """
     Create a channel scenario (2D or 3D)
@@ -106,6 +106,8 @@ def createChannel(domainSize, force=None, pressureDifference=None, u_max=None, d
     :param parallel: True for distributed memory parallelization with waLBerla
     :param kwargs: all other keyword parameters are passed directly to scenario class.
     """
+    assert domainSize is not None or dataHandling is not None
+
     dim = len(domainSize)
     assert dim in (2, 3)
 
@@ -115,42 +117,29 @@ def createChannel(domainSize, force=None, pressureDifference=None, u_max=None, d
     periodicity = (True, False, False) if force else (False, False, False)
     if dataHandling is None:
         dataHandling = createDataHandling(parallel, domainSize, periodicity=periodicity[:dim], defaultGhostLayers=1)
-    step = LatticeBoltzmannStep(dataHandling=dataHandling, **kwargs)
-    boundaryHandling = step.boundaryHandling
+
     if force:
         kwargs['force'] = tuple([force, 0, 0][:dim])
-        boundaryHandling.setPeriodicity(True, False, False)
+        assert dataHandling.periodicity[0]
+        step = LatticeBoltzmannStep(dataHandling=dataHandling, name="forceDrivenChannel", **kwargs)
     elif pressureDifference:
         inflow = FixedDensity(1.0 + pressureDifference)
         outflow = FixedDensity(1.0)
-        boundaryHandling.setBoundary(inflow, sliceFromDirection('W', dim))
-        boundaryHandling.setBoundary(outflow, sliceFromDirection('E', dim))
+        step = LatticeBoltzmannStep(dataHandling=dataHandling, name="pressureDrivenChannel", **kwargs)
+        step.boundaryHandling.setBoundary(inflow, sliceFromDirection('W', dim))
+        step.boundaryHandling.setBoundary(outflow, sliceFromDirection('E', dim))
     elif u_max:
         if duct:
             raise NotImplementedError("Velocity inflow for duct flows not yet implemented")
-
+        step = LatticeBoltzmannStep(dataHandling=dataHandling, name="velocityDrivenChannel", **kwargs)
         diameter = diameterCallback(np.array([0]), domainSize)[0] if diameterCallback else min(domainSize[1:])
-        addParabolicVelocityInflow(boundaryHandling, u_max, sliceFromDirection('W', dim),
+        addParabolicVelocityInflow(step.boundaryHandling, u_max, sliceFromDirection('W', dim),
                                    velCoord=0, diameter=diameter)
         outflow = FixedDensity(1.0)
-        boundaryHandling.setBoundary(outflow, sliceFromDirection('E', dim))
+        step.boundaryHandling.setBoundary(outflow, sliceFromDirection('E', dim))
     else:
         assert False
 
-    setupChannelWalls(boundaryHandling, diameterCallback, duct, wallBoundary)
+    setupChannelWalls(step.boundaryHandling, diameterCallback, duct, wallBoundary)
     return step
 
-
-if __name__ == '__main__':
-    import pycuda.autoinit
-    import waLBerla as wlb
-    from pystencils.datahandling import ParallelDataHandling
-    blocks = wlb.createUniformBlockGrid(blocks=(2, 2, 1), cellsPerBlock=(20, 20, 1), oneBlockPerProcess=False)
-    dh = ParallelDataHandling(blocks, dim=2)
-    ldc = createLidDrivenCavity(relaxationRate=1.5, dataHandling=dh,
-                                optimizationParams={'openMP': 2, 'target': 'gpu', 'fieldLayout': 'f',
-                                                    'gpuIndexingParams': {'blockSize': (8, 8, 2)}})
-    ldc.boundaryHandling.prepare()
-
-    ldc.run(4)
-    ldc.run(100)
-- 
GitLab