diff --git a/python/mesa_pd.py b/python/mesa_pd.py
index 17c03fb8d7d9104f9b9c76546622982331b6f92f..b567d0876b4369a83fa172cdfba95d13cdfd9fee 100755
--- a/python/mesa_pd.py
+++ b/python/mesa_pd.py
@@ -1,7 +1,7 @@
 #! /usr/bin/env python3
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
+from mesa_pd import Module
 import mesa_pd.data as data
 import mesa_pd.kernel as kernel
 import mesa_pd.mpi as mpi
@@ -11,136 +11,107 @@ import numpy as np
 import os
 
 if __name__ == '__main__':
-   parser = argparse.ArgumentParser(description='Generate all necessary files for the waLBerla mesa_pd module.')
-   parser.add_argument('path', help='Where should the files be created?')
-   parser.add_argument("-f", "--force", help="Generate the files even if not inside a waLBerla directory.",
-                       action="store_true")
-   args = parser.parse_args()
-
-   if ((not os.path.isfile(args.path + "/src/walberla.h")) and (not args.force)):
-      raise RuntimeError(args.path + " is not the path to a waLBerla root directory! Specify -f to generate the files anyway.")
-
-   os.makedirs(args.path + "/src/mesa_pd/common", exist_ok = True)
-   os.makedirs(args.path + "/src/mesa_pd/data", exist_ok = True)
-   os.makedirs(args.path + "/src/mesa_pd/domain", exist_ok = True)
-   os.makedirs(args.path + "/src/mesa_pd/kernel", exist_ok = True)
-   os.makedirs(args.path + "/src/mesa_pd/mpi/notifications", exist_ok = True)
-   os.makedirs(args.path + "/src/mesa_pd/vtk", exist_ok = True)
-
-   shapes = ["Sphere", "HalfSpace", "CylindricalBoundary", "Box", "Ellipsoid"]
-
-   ps    = data.ParticleStorage()
-   ch    = data.ContactHistory()
-   lc    = data.LinkedCells()
-   slc   = data.SparseLinkedCells()
-   ss    = data.ShapeStorage(ps, shapes)
-   cs    = data.ContactStorage()
-
-   ps.addProperty("position",         "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-   ps.addProperty("linearVelocity",   "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-   ps.addProperty("invMass",          "walberla::real_t",        defValue="real_t(1)", syncMode="ON_GHOST_CREATION")
-   ps.addProperty("force",            "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("oldForce",         "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
-
-   ps.addProperty("shapeID",          "size_t",                  defValue="",          syncMode="ON_GHOST_CREATION")
-   ps.addProperty("rotation",         "walberla::mesa_pd::Rot3", defValue="",          syncMode="ALWAYS")
-   ps.addProperty("angularVelocity",  "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-   ps.addProperty("torque",           "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("oldTorque",        "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
-
-   ps.addInclude("blockforest/BlockForest.h")
-   ps.addProperty("currentBlock",     "blockforest::BlockID",    defValue="",          syncMode="NEVER")
-
-   ps.addProperty("type",             "uint_t",                  defValue="0",         syncMode="ON_GHOST_CREATION")
-
-   ps.addProperty("flags",            "walberla::mesa_pd::data::particle_flags::FlagT", defValue="", syncMode="ON_GHOST_CREATION")
-   ps.addProperty("nextParticle",     "int",                     defValue="-1",        syncMode="NEVER")
-
-   ps.addProperty("oldContactHistory", "std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory>", defValue="", syncMode="ALWAYS")
-   ps.addProperty("newContactHistory", "std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory>", defValue="", syncMode="NEVER")
-
-   ps.addProperty("temperature",      "walberla::real_t",        defValue="real_t(0)", syncMode="ALWAYS")
-   ps.addProperty("heatFlux",         "walberla::real_t",        defValue="real_t(0)", syncMode="NEVER")
-
-   # Properties for HCSITS
-   ps.addProperty("dv",               "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("dw",               "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-
-   # Properties for lbm_mesapd_coupling:
-   ps.addProperty("hydrodynamicForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("hydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("oldHydrodynamicForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-   ps.addProperty("oldHydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
-
-
-   ch.addProperty("tangentialSpringDisplacement", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
-   ch.addProperty("isSticking",                   "bool",                    defValue="false")
-   ch.addProperty("impactVelocityMagnitude",      "real_t",                  defValue="real_t(0)")
-
-   cs.addProperty("id1",              "walberla::id_t",          defValue = "walberla::id_t(-1)", syncMode="NEVER")
-   cs.addProperty("id2",              "walberla::id_t",          defValue = "walberla::id_t(-1)", syncMode="NEVER")
-   cs.addProperty("distance",         "real_t",                  defValue = "real_t(1)",          syncMode="NEVER")
-   cs.addProperty("normal",           "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("position",         "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("t",                "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("o",                "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("r1",               "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("r2",               "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("mu",               "real_t",                  defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("p",                "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("diag_nto",         "walberla::mesa_pd::Mat3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("diag_nto_inv",     "walberla::mesa_pd::Mat3", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("diag_to_inv",      "walberla::mesa_pd::Mat2", defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("diag_n_inv",       "real_t",                  defValue = "real_t(0)",          syncMode="NEVER")
-   cs.addProperty("p",                "walberla::mesa_pd::Vec3", defValue = "real_t(0)",          syncMode="NEVER")
-
-
-   kernels = []
-   kernels.append( kernel.DetectAndStoreContacts() )
-   kernels.append( kernel.DoubleCast(shapes) )
-   kernels.append( kernel.ExplicitEuler() )
-   kernels.append( kernel.ExplicitEulerWithShape() )
-   kernels.append( kernel.ForceLJ() )
-   kernels.append( kernel.HCSITSRelaxationStep() )
-   kernels.append( kernel.HeatConduction() )
-   kernels.append( kernel.InitParticlesForHCSITS() )
-   kernels.append( kernel.InitContactsForHCSITS() )
-   kernels.append( kernel.IntegrateParticlesHCSITS() )
-   kernels.append( kernel.InsertParticleIntoLinkedCells() )
-   kernels.append( kernel.InsertParticleIntoSparseLinkedCells() )
-   kernels.append( kernel.LinearSpringDashpot() )
-   kernels.append( kernel.NonLinearSpringDashpot() )
-   kernels.append( kernel.SingleCast(shapes) )
-   kernels.append( kernel.SpringDashpot() )
-   kernels.append( kernel.TemperatureIntegration() )
-   kernels.append( kernel.VelocityVerlet() )
-   kernels.append( kernel.VelocityVerletWithShape() )
-
-
-   ac = Accessor()
-   for k in kernels:
-      ac.mergeRequirements(k.getRequirements())
-   ac.printSummary()
-
-   comm = []
-   comm.append(mpi.BroadcastProperty())
-   comm.append(mpi.ClearNextNeighborSync())
-   comm.append(mpi.ReduceContactHistory())
-   comm.append(mpi.ReduceProperty())
-   comm.append(mpi.ShapePackUnpack(shapes))
-   comm.append(mpi.SyncGhostOwners(ps))
-   comm.append(mpi.SyncNextNeighbors(ps))
-
-
-   ps.generate(args.path + "/src/mesa_pd/")
-   ch.generate(args.path + "/src/mesa_pd/")
-   lc.generate(args.path + "/src/mesa_pd/")
-   slc.generate(args.path + "/src/mesa_pd/")
-   ss.generate(args.path + "/src/mesa_pd/")
-   cs.generate(args.path + "/src/mesa_pd/")
-
-   for k in kernels:
-      k.generate(args.path + "/src/mesa_pd/")
-
-   for c in comm:
-      c.generate(args.path + "/src/mesa_pd/")
+    parser = argparse.ArgumentParser(description='Generate all necessary files for the waLBerla mesa_pd module.')
+    parser.add_argument('path', help='Where should the files be created?')
+    args = parser.parse_args()
+
+    mpd = Module(args.path)
+    ps = mpd.add(data.ParticleStorage())
+    ps.set_shapes('Sphere', 'HalfSpace', 'CylindricalBoundary', 'Box', 'Ellipsoid')
+    ps.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+    ps.add_property("linearVelocity", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+    ps.add_property("invMass", "walberla::real_t", defValue="real_t(1)", syncMode="ON_GHOST_CREATION")
+    ps.add_property("force", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("oldForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
+
+    ps.add_property("shapeID", "size_t", defValue="", syncMode="ON_GHOST_CREATION")
+    ps.add_property("rotation", "walberla::mesa_pd::Rot3", defValue="", syncMode="ALWAYS")
+    ps.add_property("angularVelocity", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+    ps.add_property("torque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("oldTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
+
+    ps.add_include("blockforest/BlockForest.h")
+    ps.add_property("currentBlock", "blockforest::BlockID", defValue="", syncMode="NEVER")
+
+    ps.add_property("type", "uint_t", defValue="0", syncMode="ON_GHOST_CREATION")
+
+    ps.add_property("flags", "walberla::mesa_pd::data::particle_flags::FlagT", defValue="",
+                    syncMode="ON_GHOST_CREATION")
+    ps.add_property("nextParticle", "int", defValue="-1", syncMode="NEVER")
+
+    ps.add_property("oldContactHistory", "std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory>",
+                    defValue="", syncMode="ALWAYS")
+    ps.add_property("newContactHistory", "std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory>",
+                    defValue="", syncMode="NEVER")
+
+    ps.add_property("temperature", "walberla::real_t", defValue="real_t(0)", syncMode="ALWAYS")
+    ps.add_property("heatFlux", "walberla::real_t", defValue="real_t(0)", syncMode="NEVER")
+
+    # Properties for HCSITS
+    ps.add_property("dv", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("dw", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+
+    # Properties for lbm_mesapd_coupling:
+    ps.add_property("hydrodynamicForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("hydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("oldHydrodynamicForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    ps.add_property("oldHydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+
+    ch = mpd.add(data.ContactHistory())
+    ch.add_property("tangentialSpringDisplacement", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    ch.add_property("isSticking", "bool", defValue="false")
+    ch.add_property("impactVelocityMagnitude", "real_t", defValue="real_t(0)")
+
+    cs = mpd.add(data.ContactStorage())
+    cs.add_property("id1", "walberla::id_t", defValue="walberla::id_t(-1)")
+    cs.add_property("id2", "walberla::id_t", defValue="walberla::id_t(-1)")
+    cs.add_property("distance", "real_t", defValue="real_t(1)")
+    cs.add_property("normal", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("t", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("o", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("r1", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("r2", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("mu", "real_t", defValue="real_t(0)")
+    cs.add_property("p", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+    cs.add_property("diag_nto", "walberla::mesa_pd::Mat3", defValue="real_t(0)")
+    cs.add_property("diag_nto_inv", "walberla::mesa_pd::Mat3", defValue="real_t(0)")
+    cs.add_property("diag_to_inv", "walberla::mesa_pd::Mat2", defValue="real_t(0)")
+    cs.add_property("diag_n_inv", "real_t", defValue="real_t(0)")
+    cs.add_property("p", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
+
+    mpd.add(data.LinkedCells())
+    mpd.add(data.SparseLinkedCells())
+    mpd.add(data.ShapeStorage(ps))
+
+    mpd.add(kernel.DetectAndStoreContacts())
+    mpd.add(kernel.DoubleCast(ps))
+    mpd.add(kernel.ExplicitEuler())
+    mpd.add(kernel.ExplicitEulerWithShape())
+    mpd.add(kernel.ForceLJ())
+    mpd.add(kernel.HCSITSRelaxationStep())
+    mpd.add(kernel.HeatConduction())
+    mpd.add(kernel.InitParticlesForHCSITS())
+    mpd.add(kernel.InitContactsForHCSITS())
+    mpd.add(kernel.IntegrateParticlesHCSITS())
+    mpd.add(kernel.InsertParticleIntoLinkedCells())
+    mpd.add(kernel.InsertParticleIntoSparseLinkedCells())
+    mpd.add(kernel.LinearSpringDashpot())
+    mpd.add(kernel.NonLinearSpringDashpot())
+    mpd.add(kernel.SingleCast(ps))
+    mpd.add(kernel.SpringDashpot())
+    mpd.add(kernel.TemperatureIntegration())
+    mpd.add(kernel.VelocityVerlet())
+    mpd.add(kernel.VelocityVerletWithShape())
+
+    mpd.add(mpi.BroadcastProperty())
+    mpd.add(mpi.ClearNextNeighborSync())
+    mpd.add(mpi.Notifications(ps))
+    mpd.add(mpi.ReduceContactHistory())
+    mpd.add(mpi.ReduceProperty())
+    mpd.add(mpi.ShapePackUnpack(ps))
+    mpd.add(mpi.SyncGhostOwners(ps))
+    mpd.add(mpi.SyncNextNeighbors(ps))
+    mpd.add(mpi.SyncNextNeighborsNoGhosts(ps))
+
+    mpd.generate()
diff --git a/python/mesa_pd/Container.py b/python/mesa_pd/Container.py
deleted file mode 100644
index 3acd960e672d2fbfabf19e03a9b0b31356bfd7fe..0000000000000000000000000000000000000000
--- a/python/mesa_pd/Container.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from .Property import Property, unrollDimension
-from .utility import find, TerminalColor
-
-class Container:
-   def __init__(self):
-      """Base class for a container which manages includes and properties
-      """
-
-      self.properties = []
-      self.includes   = []
-
-   def addProperty(self, name, type, access="grs", defValue="", syncMode = "ALWAYS", dim=1):
-      prop = find( lambda x : x.name == name, self.properties )
-      if (prop == None):
-         #print(TerminalColor.GREEN + "creating particle property: {}".format(name) + TerminalColor.DEFAULT)
-         self.properties.append( Property(name, type, access=access, defValue=defValue, syncMode=syncMode, dim=dim) )
-      else:
-         if not (prop.type == type and prop.name == name and prop.defValue == defValue and prop.dim == dim):
-            raise RuntimeError(TerminalColor.RED + "property definition differs from previous one:\nPREVIOUS {}\nNEW {}".format(prop, Property(name, type, defValue=defValue, syncMode=syncMode, dim=dim)) + TerminalColor.DEFAULT)
-         print(TerminalColor.YELLOW + "reusing particle property: {}".format(name) + TerminalColor.DEFAULT)
-
-   def addInclude(self, include):
-      if (include in self.includes):
-         print(TerminalColor.YELLOW + "reusing particle include: {}".format(include) + TerminalColor.DEFAULT)
-      else:
-         #print(TerminalColor.GREEN + "creating particle include: {}".format(include) + TerminalColor.DEFAULT)
-         self.includes.append(include)
-
-   def unrollDimension(self):
-      self.properties = unrollDimension(self.properties)
diff --git a/python/mesa_pd/Module.py b/python/mesa_pd/Module.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc94b5fbf74c75521c5a1137225108a35db43ade
--- /dev/null
+++ b/python/mesa_pd/Module.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+from pathlib import Path
+import shutil
+import os
+
+
+class Module:
+    def __init__(self, path, module_name='mesa_pd'):
+        """Propery of a data strcuture
+
+        Parameters
+        ----------
+        path : str
+           path to the root waLBerla folder
+        module_name : str
+           name of the generated module
+        """
+
+        self.context = {}
+        self.context['path'] = Path(path).resolve()
+        self.context['name'] = module_name
+        self.context['module_path'] = self.context['path'] / 'src' / self.context['name']
+        self.context['test_path']   = self.context['path'] / 'tests' / self.context['name']
+
+        self.components = []
+
+    def add(self, component):
+        self.components.append(component)
+        return component
+
+    def rename(self):
+        for root, dirnames, filenames in os.walk(self.context['module_path']):
+            for filename in filenames:
+                filedata = None
+                print(f'{root}/{filename}')
+                with open(f'{root}/{filename}', 'r') as file:
+                    filedata = file.read()
+
+                filedata = filedata.replace('mesa_pd', self.context['name'])
+
+                with open(f'{root}/{filename}', 'w') as file:
+                    file.write(filedata)
+
+    def generate(self, folder_check=True):
+        if (folder_check):
+            print(f"This operation will overwrite the content of: {self.context['module_path']}")
+            answer = input("Continue? (y to confirm)")
+            if (answer != "y"):
+                return
+
+        mesa_pd_folder = (Path(__file__).parents[2] / 'src' / 'mesa_pd').resolve()
+        if (mesa_pd_folder != self.context['module_path']):
+            shutil.rmtree(self.context['module_path'])
+            shutil.copytree(mesa_pd_folder, self.context['module_path'])
+
+        for d in self.components:
+            d.generate(self.context)
+
+        self.rename()
\ No newline at end of file
diff --git a/python/mesa_pd/Property.py b/python/mesa_pd/Property.py
deleted file mode 100644
index d0da37d9fe21109f5ec01c307ca7e3ccf99de2b5..0000000000000000000000000000000000000000
--- a/python/mesa_pd/Property.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from .utility import TerminalColor
-
-class Property:
-   def __init__(self, name, type, access="grs", defValue="", syncMode = "ALWAYS", dim=1):
-      """Propery of a data strcuture
-
-      Parameters
-      ----------
-      name : str
-         name of the property
-      type : str
-         type of the property
-      access : str
-         'g' for getter (getName)
-         'r' for reference (getNameRef)
-         's' for setter (setName)
-         any combination is possible
-      defValue : str
-         default value the property should be initialized with
-      syncMode : str
-         'NEVER', this property does not have to be synced
-         'ON_GHOST_CREATION', this property must be synced on creation
-         'ON_OWNERSHIP_CHANGE', this property must be synced when the ownership changes
-         'ALWAYS', this property has to be synced in every iteration
-      dim : int
-         dimensions of the property
-      """
-
-      #sort access specifier and remove duplicates
-      foo = "".join(sorted(access))
-      access = ''.join([foo[i] for i in range(len(foo)-1) if foo[i+1]!= foo[i]]+[foo[-1]])
-
-      for acc in access:
-         if not (acc in ["g","s","r"]):
-            raise RuntimeError("{} is not a valid access specifier in {}".format(acc, access))
-
-      if (not syncMode in ["NEVER", "ON_GHOST_CREATION", "ON_OWNERSHIP_CHANGE", "ALWAYS"]):
-         raise RuntimeError(TerminalColor.RED + "{} is no valid sync for property: {}".format(syncMode, name) + TerminalColor.DEFAULT)
-
-      if (dim < 1):
-         raise RuntimeError(TerminalColor.RED + "dimension has to be >=1: {}".format(dim) + TerminalColor.DEFAULT)
-
-      self.name     = name
-      self.type     = type
-      self.access   = access
-      self.defValue = defValue
-      self.syncMode = syncMode
-      self.dim      = dim
-
-   def __str__(self):
-      return "name: {}, type: {}, access: {}, defValue: {}, syncMode: {}, dim: {}".format(self.name, self.type, self.access, self.defValue, self.syncMode, self.dim)
-
-   def getAccessFunction(self):
-      """Returns a list of accessor function names
-      """
-
-      funcs = []
-      if 'g' in self.access:
-         funcs.append("get" + self.name.capitalize())
-      if 's' in self.access:
-         funcs.append("set" + self.name.capitalize())
-      if 'r' in self.access:
-         funcs.append("get" + self.name.capitalize() + "Ref")
-      return funcs
-
-def unrollDimension(props):
-   """Unrolls all more dimensional properties into one dimensional properties
-
-   Iterates over all elements. Copies all one dimensional properties.
-   More dimensional properties get split into one dimensional properties with added suffix.
-
-   Parameters
-   ----------
-   props : list
-      list of properties to be unrolled
-
-   Returns
-   -------
-      list of unrolled properties
-   """
-
-   unrolled = []
-   for prop in props:
-      if (prop.dim == 1):
-         unrolled.append(Property(prop.name, prop.type, prop.access, prop.defValue, prop.syncMode, prop.dim))
-      else:
-         for d in range(prop.dim):
-            unrolled.append(Property("{}{}".format(prop.name,d), prop.type, prop.access, prop.defValue, prop.syncMode, 1))
-   return unrolled
diff --git a/python/mesa_pd/__init__.py b/python/mesa_pd/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d68cd8086f9c0494613e440877cdfb5531cfd55d 100644
--- a/python/mesa_pd/__init__.py
+++ b/python/mesa_pd/__init__.py
@@ -0,0 +1,3 @@
+from .Module import Module
+
+__all__ = ['Module']
\ No newline at end of file
diff --git a/python/mesa_pd/accessor/Accessor.py b/python/mesa_pd/accessor/Accessor.py
index 288ff22e44fd79dca925a41b0574bc228a5e2d0a..88c081895881f4258cb6e0cd9c88603f760e0303 100644
--- a/python/mesa_pd/accessor/Accessor.py
+++ b/python/mesa_pd/accessor/Accessor.py
@@ -1,53 +1,28 @@
 # -*- coding: utf-8 -*-
 
-from ..Property import Property
-from ..utility import find, TerminalColor, generateFile
 
-class Accessor:
-   def __init__(self):
-      self.properties = []
-
-   def require(self, name, type, access):
-      """requires that a certain property is accessible
-
-      Parameters
-      ----------
-      name : str
-         name of the property requested
-      type : str
-         type of the requested property
-      access : str
-         'g' for getter (getName)
-         'r' for reference (getNameRef)
-         's' for setter (setName)
-         any combination is possible
-
-      Example
-      -------
-      require("position", "walberla::mesa_pd::Vec3", "sg")
-      """
-
-      prop = find( lambda x : x.name == name, self.properties )
-      if (prop == None):
-         #print(TerminalColor.GREEN + "[{}] creating particle property: {}".format(info, name) + TerminalColor.DEFAULT)
-         self.properties.append( Property(name, type, access = access) )
-      else:
-         if not (prop.type == type):
-            raise RuntimeError(TerminalColor.RED + "requirement definition differs from previous one:\n{} {}\n{} {}".format(name, type, prop.name, prop.type) + TerminalColor.DEFAULT)
-         foo = "".join(sorted(access + prop.access))
-         prop.access = ''.join([foo[i] for i in range(len(foo)-1) if foo[i+1]!= foo[i]]+[foo[-1]])
-
-
-   def mergeRequirements(self, accessor):
-      for req in accessor.properties:
-         self.require( req.name, req.type, req.access )
-
-   def printSummary(self):
-      print("="*90)
-      print("Requirements for Accessor:")
-      print("")
-      print("{0: <30}{1: <30}{2: <30}".format("Name", "Type", "Access"))
-      print("="*90)
-      for gs in self.properties:
-         print("{0: <30.29}{1: <30.29}{2: <30.29}".format(gs.name, gs.type, gs.access))
-      print("="*90)
+def create_access(name, type, access):
+    """requires that a certain property is accessible
+
+    Parameters
+    ----------
+    name : str
+       name of the property requested
+    type : str
+       type of the requested property
+    access : str
+       'g' for getter (getName)
+       'r' for reference (getNameRef)
+       's' for setter (setName)
+       any combination is possible
+
+    Example
+    -------
+    create_access("position", "walberla::mesa_pd::Vec3", "sg")
+    """
+
+    for acc in access:
+        if not (acc in ["g", "s", "r"]):
+            raise RuntimeError(f"{acc} is not a valid access specifier in {access}")
+
+    return {'name': name, 'type': type, 'access': access}
diff --git a/python/mesa_pd/accessor/__init__.py b/python/mesa_pd/accessor/__init__.py
index 6e15afb2ca99c6458a219884f92719ed0ef96e52..1d37a364cdb9b44f9c021d4db272e45a062e0e61 100644
--- a/python/mesa_pd/accessor/__init__.py
+++ b/python/mesa_pd/accessor/__init__.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
 
-from .Accessor import Accessor
+from .Accessor import create_access
 
-__all__ = ['Accessor']
+__all__ = ['create_access']
diff --git a/python/mesa_pd/data/ContactHistory.py b/python/mesa_pd/data/ContactHistory.py
index 444da6bfc05ce1ea823376b3e41548bf1847d5d7..08e80a6f4d436299b861a6c18899d288705a8f97 100644
--- a/python/mesa_pd/data/ContactHistory.py
+++ b/python/mesa_pd/data/ContactHistory.py
@@ -1,28 +1,62 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..Container import Container
-from ..utility import generateFile
-
-class ContactHistory(Container):
-   def __init__(self):
-      super().__init__()
-
-   def generate(self, path):
-      self.unrollDimension()
-
-      print("="*90)
-      print("Creating ContactHistory Datastructure:")
-      print("")
-      print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
-      print("="*90)
-      for prop in self.properties:
-         print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop.type, prop.name, prop.defValue, prop.syncMode))
-      print("="*90)
-
-      context = dict()
-      context["includes"]    = self.includes
-      context["properties"]  = self.properties
-
-      generateFile(path, 'data/ContactHistory.templ.h', context, filename='data/ContactHistory.h')
-      generateFile(path, 'mpi/notifications/ContactHistoryNotification.templ.h', context)
+from ..utility import TerminalColor, find, generate_file
+
+
+def create_contact_history_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class ContactHistory():
+    def __init__(self):
+        self.context = {'includes': [], 'properties': []}
+
+    def add_property(self, name, type, defValue=""):
+        prop = find(lambda x: x['name'] == name, self.context['properties'])
+        if (prop == None):
+            # print(f"{TerminalColor.GREEN} creating property: {name} {TerminalColor.DEFAULT}")
+            self.context['properties'].append(create_contact_history_property(name, type, defValue=defValue))
+        else:
+            if not (prop['type'] == type and prop['name'] == name and prop['defValue'] == defValue):
+                new_prop = create_contact_history_property(name, type, defValue=defValue)
+                raise RuntimeError(
+                    f"{TerminalColor.RED} property definition differs from previous one:\nPREVIOUS {prop}\nNEW {new_prop} {TerminalColor.DEFAULT}")
+            print(f"{TerminalColor.YELLOW} reusing property: {name} {TerminalColor.DEFAULT}")
+
+    def add_include(self, include):
+        if (include in self.context['includes']):
+            print(f"{TerminalColor.YELLOW} reusing include: {include} {TerminalColor.DEFAULT}")
+        else:
+            # print(f"{TerminalColor.GREEN} creating include: {include} {TerminalColor.DEFAULT}")
+            self.context['includes'].append(include)
+
+    def print(self):
+        print("=" * 90)
+        print("Creating ContactHistory Datastructure:")
+        print("")
+        print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
+        print("=" * 90)
+        for prop in self.properties:
+            print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop.type, prop.name, prop.defValue,
+                                                                       prop.syncMode))
+        print("=" * 90)
+
+    def generate(self, module):
+        ctx = {}
+        ctx['module'] = module
+        ctx.update(self.context)
+
+        generate_file(module['module_path'], 'data/ContactHistory.templ.h', ctx, filename='data/ContactHistory.h')
+        generate_file(module['module_path'], 'mpi/notifications/ContactHistoryNotification.templ.h', ctx)
diff --git a/python/mesa_pd/data/ContactStorage.py b/python/mesa_pd/data/ContactStorage.py
index 2106759319ad24e71737118a3b8962eee8053b10..9dd669381367a17856505fca40f0bde7fdd7cfff 100644
--- a/python/mesa_pd/data/ContactStorage.py
+++ b/python/mesa_pd/data/ContactStorage.py
@@ -1,29 +1,63 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..Container import Container
-from ..utility import generateFile
-
-class ContactStorage(Container):
-   def __init__(self):
-      super().__init__()
-      self.addProperty("uid",                "walberla::id_t",           defValue = "walberla::id_t(-1)",   syncMode="NEVER")
-
-   def generate(self, path):
-      self.unrollDimension()
-
-      print("="*90)
-      print("Creating ContactStorage Datastructure:")
-      print("")
-      print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
-      print("="*90)
-      for prop in self.properties:
-         print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop.type, prop.name, prop.defValue, prop.syncMode))
-      print("="*90)
-
-      context = dict()
-      context["includes"]    = self.includes
-      context["properties"]  = self.properties
-
-      generateFile(path, 'data/ContactStorage.templ.h', context, filename='data/ContactStorage.h')
-      generateFile(path, 'data/ContactAccessor.templ.h', context, filename='data/ContactAccessor.h')
+from ..utility import TerminalColor, find, generate_file
+
+
+def create_contact_storage_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class ContactStorage():
+    def __init__(self):
+        self.context = {'includes': [], 'properties': []}
+        self.add_property("uid", "walberla::id_t", defValue="walberla::id_t(-1)")
+
+    def add_property(self, name, type, defValue=""):
+        prop = find(lambda x: x['name'] == name, self.context['properties'])
+        if (prop == None):
+            # print(f"{TerminalColor.GREEN} creating property: {name} {TerminalColor.DEFAULT}")
+            self.context['properties'].append(create_contact_storage_property(name, type, defValue=defValue))
+        else:
+            if not (prop['type'] == type and prop['name'] == name and prop['defValue'] == defValue):
+                new_prop = create_contact_storage_property(name, type, defValue=defValue)
+                raise RuntimeError(
+                    f"{TerminalColor.RED} property definition differs from previous one:\nPREVIOUS {prop}\nNEW {new_prop} {TerminalColor.DEFAULT}")
+            print(f"{TerminalColor.YELLOW} reusing property: {name} {TerminalColor.DEFAULT}")
+
+    def add_include(self, include):
+        if (include in self.context['includes']):
+            print(f"{TerminalColor.YELLOW} reusing include: {include} {TerminalColor.DEFAULT}")
+        else:
+            # print(f"{TerminalColor.GREEN} creating include: {include} {TerminalColor.DEFAULT}")
+            self.context['includes'].append(include)
+
+    def print(self):
+        print("=" * 90)
+        print("Creating ContactStorage Datastructure:")
+        print("")
+        print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
+        print("=" * 90)
+        for prop in self.properties:
+            print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop.type, prop.name, prop.defValue,
+                                                                       prop.syncMode))
+        print("=" * 90)
+
+    def generate(self, module):
+        ctx = {}
+        ctx['module'] = module
+        ctx.update(self.context)
+
+        generate_file(module['module_path'], 'data/ContactStorage.templ.h', ctx, filename='data/ContactStorage.h')
+        generate_file(module['module_path'], 'data/ContactAccessor.templ.h', ctx, filename='data/ContactAccessor.h')
diff --git a/python/mesa_pd/data/LinkedCells.py b/python/mesa_pd/data/LinkedCells.py
index 60ea2ec83c24669c67ec4744ad2c335f40f28d5f..5ff4c9b8d1477e347093394ad3e2fa5356359877 100644
--- a/python/mesa_pd/data/LinkedCells.py
+++ b/python/mesa_pd/data/LinkedCells.py
@@ -1,8 +1,10 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class LinkedCells:
-   def generate(self, path):
-      generateFile(path, 'data/LinkedCells.templ.h')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'data/LinkedCells.templ.h', ctx)
diff --git a/python/mesa_pd/data/ParticleStorage.py b/python/mesa_pd/data/ParticleStorage.py
index 7f9813a0606128eb581779b3c76fc7edefb8340d..61264f24085bcf6b5bdef73915303d9e06d0f1a5 100644
--- a/python/mesa_pd/data/ParticleStorage.py
+++ b/python/mesa_pd/data/ParticleStorage.py
@@ -1,49 +1,100 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..Container import Container
-from ..utility import generateFile
-
-class ParticleStorage(Container):
-   def __init__(self):
-      super().__init__()
-
-      self.addInclude("mesa_pd/data/Flags.h")
-
-      self.addProperty("uid",               "walberla::id_t",       defValue = "UniqueID<data::Particle>::invalidID()", syncMode="ALWAYS")
-      self.addProperty("position",          "walberla::mesa_pd::Vec3", defValue = "real_t(0)", syncMode="ALWAYS")
-      self.addProperty("interactionRadius", "walberla::real_t",     defValue = "real_t(0)", syncMode="ON_GHOST_CREATION")
-      self.addProperty("flags",             "walberla::mesa_pd::data::particle_flags::FlagT", defValue = "", syncMode="ON_GHOST_CREATION")
-      self.addProperty("owner",             "int",                  defValue = "-1", syncMode="ON_GHOST_CREATION")
-      self.addProperty("ghostOwners",       "std::unordered_set<walberla::mpi::MPIRank>", defValue = "", syncMode="ON_OWNERSHIP_CHANGE")
-
-   def generate(self, path):
-      self.unrollDimension()
-
-      print("="*90)
-      print("Creating Particle Datastructure:")
-      print("")
-      print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
-      print("="*90)
-      for prop in self.properties:
-         print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop.type, prop.name, prop.defValue, prop.syncMode))
-      print("="*90)
-
-      context = dict()
-      context["includes"]    = self.includes
-      context["properties"]  = self.properties
-
-      generateFile(path, 'data/ParticleStorage.templ.h', context, filename='data/ParticleStorage.h')
-      generateFile(path, 'data/ParticleAccessor.templ.h', context, filename='data/ParticleAccessor.h')
-
-      generateFile(path, 'mpi/notifications/ForceTorqueNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/HeatFluxNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParseMessage.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleCopyNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleGhostCopyNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/NewGhostParticleNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleMigrationNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleRemoteMigrationNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleRemovalInformationNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleRemovalNotification.templ.h', context)
-      generateFile(path, 'mpi/notifications/ParticleUpdateNotification.templ.h', context)
+from ..utility import find, TerminalColor, generate_file
+
+
+def create_particle_property(name, type, access="grs", defValue="", syncMode="ALWAYS"):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    access : str
+       'g' for getter (getName)
+       'r' for reference (getNameRef)
+       's' for setter (setName)
+       any combination is possible
+    defValue : str
+       default value the property should be initialized with
+    syncMode : str
+       'NEVER', this property does not have to be synced
+       'ON_GHOST_CREATION', this property must be synced on creation
+       'ON_OWNERSHIP_CHANGE', this property must be synced when the ownership changes
+       'ALWAYS', this property has to be synced in every iteration
+    """
+
+    # sort access specifier and remove duplicates
+    foo = "".join(sorted(access))
+    access = ''.join([foo[i] for i in range(len(foo) - 1) if foo[i + 1] != foo[i]] + [foo[-1]])
+
+    for acc in access:
+        if not (acc in ["g", "s", "r"]):
+            raise RuntimeError(f"{acc} is not a valid access specifier in {access}")
+
+    if (syncMode not in ["NEVER", "ON_GHOST_CREATION", "ON_OWNERSHIP_CHANGE", "ALWAYS"]):
+        raise RuntimeError(
+            f"{TerminalColor.RED}{syncMode} is no valid sync for property: {name}{TerminalColor.DEFAULT}")
+
+    return {'name': name, 'type': type, 'access': access, 'defValue': defValue, 'syncMode': syncMode}
+
+
+class ParticleStorage():
+    def __init__(self):
+        self.context = {'includes': [], 'properties': []}
+
+        self.add_include("mesa_pd/data/Flags.h")
+
+        self.add_property("uid", "walberla::id_t", defValue="UniqueID<data::Particle>::invalidID()", syncMode="ALWAYS")
+        self.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+        self.add_property("interactionRadius", "walberla::real_t", defValue="real_t(0)", syncMode="ON_GHOST_CREATION")
+        self.add_property("flags", "walberla::mesa_pd::data::particle_flags::FlagT", defValue="",
+                          syncMode="ON_GHOST_CREATION")
+        self.add_property("owner", "int", defValue="-1", syncMode="ON_GHOST_CREATION")
+        self.add_property("ghostOwners", "std::unordered_set<walberla::mpi::MPIRank>", defValue="",
+                          syncMode="ON_OWNERSHIP_CHANGE")
+
+    def set_shapes(self, *shapes):
+        self.context['shapes'] = list(shapes)
+
+    def add_property(self, name, type, access="grs", defValue="", syncMode="ALWAYS"):
+        prop = find(lambda x: x['name'] == name, self.context['properties'])
+        if (prop == None):
+            # print(f"{TerminalColor.GREEN} creating particle property: {name} {TerminalColor.DEFAULT}")
+            self.context['properties'].append(
+                create_particle_property(name, type, access=access, defValue=defValue, syncMode=syncMode))
+        else:
+            if not (prop['type'] == type and prop['name'] == name and prop['defValue'] == defValue):
+                new_prop = create_particle_property(name, type, defValue=defValue, syncMode=syncMode)
+                raise RuntimeError(
+                    f"{TerminalColor.RED} property definition differs from previous one:\nPREVIOUS {prop}\nNEW {new_prop} {TerminalColor.DEFAULT}")
+            print(f"{TerminalColor.YELLOW} reusing particle property: {name} {TerminalColor.DEFAULT}")
+
+    def add_include(self, include):
+        if (include in self.context['includes']):
+            print(f"{TerminalColor.YELLOW} reusing particle include: {include} {TerminalColor.DEFAULT}")
+        else:
+            # print(f"{TerminalColor.GREEN} creating particle include: {include} {TerminalColor.DEFAULT}")
+            self.context['includes'].append(include)
+
+    def print(self):
+        print("=" * 90)
+        print("Creating Particle Datastructure:")
+        print("")
+        print("{0: <20}{1: <30}{2: <20}{3: <10}".format("Type", "Name", "Def. Value", "SyncMode"))
+        print("=" * 90)
+        for prop in self.context['properties']:
+            print("{0: <20.19}{1: <30.29}{2: <20.19}{3: <10.9}".format(prop['type'], prop['name'], prop['defValue'],
+                                                                       prop['syncMode']))
+        print("=" * 90)
+
+    def get_context(self):
+        return self.context
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+
+        generate_file(module['module_path'], 'data/ParticleStorage.templ.h', ctx, filename='data/ParticleStorage.h')
+        generate_file(module['module_path'], 'data/ParticleAccessor.templ.h', ctx, filename='data/ParticleAccessor.h')
diff --git a/python/mesa_pd/data/ShapeStorage.py b/python/mesa_pd/data/ShapeStorage.py
index 74b4f598e7b869cd00c621f042dee61da83862c9..512cb2b29dfc4a2d55ae98e5429532a8b291e9f8 100644
--- a/python/mesa_pd/data/ShapeStorage.py
+++ b/python/mesa_pd/data/ShapeStorage.py
@@ -1,19 +1,20 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class ShapeStorage:
-   def __init__(self, p, shapes):
-      p.addProperty("shapeID",         "size_t",                  defValue="",          syncMode="ON_GHOST_CREATION")
-      p.addProperty("rotation",        "walberla::mesa_pd::Rot3", defValue="",          syncMode="ALWAYS")
-      p.addProperty("angularVelocity", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-      p.addProperty("torque",          "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
+    def __init__(self, particle_storage):
+        particle_storage.add_property("shapeID", "size_t", defValue="", syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("rotation", "walberla::mesa_pd::Rot3", defValue="", syncMode="ALWAYS")
+        particle_storage.add_property("angularVelocity", "walberla::mesa_pd::Vec3", defValue="real_t(0)",
+                                      syncMode="ALWAYS")
+        particle_storage.add_property("torque", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
 
-      self.shapes          = shapes
+        self.ps = particle_storage
 
-   def generate(self, path):
-      context = dict()
-      context["shapes"]          = self.shapes
+    def generate(self, module):
+        ctx = {'module': module, 'particle': self.ps.get_context()}
 
-      generateFile(path, 'data/ShapeStorage.templ.h', context)
+        generate_file(module['module_path'], 'data/ShapeStorage.templ.h', ctx)
diff --git a/python/mesa_pd/data/SparseLinkedCells.py b/python/mesa_pd/data/SparseLinkedCells.py
index 516acfd82d4ae94474604f238fdd7fcd13ab4052..b0ee85de8c5ef589c0fc5749a71a41cfe8931d35 100644
--- a/python/mesa_pd/data/SparseLinkedCells.py
+++ b/python/mesa_pd/data/SparseLinkedCells.py
@@ -1,8 +1,10 @@
 # -*- coding: utf-8 -*-
 
 import numpy as np
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class SparseLinkedCells:
-   def generate(self, path):
-      generateFile(path, 'data/SparseLinkedCells.templ.h')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'data/SparseLinkedCells.templ.h', ctx)
diff --git a/python/mesa_pd/data/__init__.py b/python/mesa_pd/data/__init__.py
index d09dc6e57c8eeae41b4462b161eca662667f9fea..b75df12ceb6ad1f2fb66bac271704f522c83397f 100644
--- a/python/mesa_pd/data/__init__.py
+++ b/python/mesa_pd/data/__init__.py
@@ -9,7 +9,6 @@ from .SparseLinkedCells import SparseLinkedCells
 
 __all__ = ['ContactHistory',
            'ContactStorage',
-           'GeometryStorage',
            'LinkedCells',
            'ParticleStorage',
            'SparseLinkedCells']
diff --git a/python/mesa_pd/kernel/DetectAndStoreContacts.py b/python/mesa_pd/kernel/DetectAndStoreContacts.py
index 8f6c3d9d89f20f91bd5a5651b15eed1639341cf2..4bcf797128ac2bb9f164d46c8d3c8e4ba98c6e7c 100644
--- a/python/mesa_pd/kernel/DetectAndStoreContacts.py
+++ b/python/mesa_pd/kernel/DetectAndStoreContacts.py
@@ -1,25 +1,18 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
-
-class DetectAndStoreContacts:
-
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("uid",             "walberla::id_t",                                    access="g")
-      self.accessor.require("flags",           "walberla::mesa_pd::data::particle_flags::FlagT",    access="g")
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("rotation",        "walberla::mesa_pd::Rot3",                           access="g")
-      self.accessor.require("shape",           "BaseShape*",                                        access="g")
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
 
+class DetectAndStoreContacts:
 
-   def getRequirements(self):
-      return self.accessor
-
+    def __init__(self):
+        self.context = {'interface': [create_access("uid", "walberla::id_t", access="g"),
+                                      create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"),
+                                      create_access("position", "walberla::mesa_pd::Vec3", access="g"),
+                                      create_access("rotation", "walberla::mesa_pd::Rot3", access="g"),
+                                      create_access("shape", "BaseShape*", access="g")]}
 
-   def generate(self, path):
-      context = dict()
-      context["interface"]        = self.accessor.properties
-      generateFile(path, 'kernel/DetectAndStoreContacts.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/DetectAndStoreContacts.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/DoubleCast.py b/python/mesa_pd/kernel/DoubleCast.py
index 4d132f79a994448bd64a8bb0bd0c2a773a9746e6..04722840bcca6fdd84ed6297368452b34bd6bcb7 100644
--- a/python/mesa_pd/kernel/DoubleCast.py
+++ b/python/mesa_pd/kernel/DoubleCast.py
@@ -1,19 +1,14 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class DoubleCast:
-   def __init__(self, shapes):
-      self.shapes = shapes
-      self.accessor = Accessor()
-      self.accessor.require("shape", "BaseShape*", access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class DoubleCast:
+    def __init__(self, particle_storage):
+        self.ps = particle_storage
+        self.context = {'interface': [create_access("shape", "BaseShape*", access="g")]}
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-      context["shapes"]    = self.shapes
-      generateFile(path, 'kernel/DoubleCast.templ.h', context)
+    def generate(self, module):
+        context = {'module': module, 'particle': self.ps.get_context(), **self.context}
+        generate_file(module['module_path'], 'kernel/DoubleCast.templ.h', context)
diff --git a/python/mesa_pd/kernel/ExplicitEuler.py b/python/mesa_pd/kernel/ExplicitEuler.py
index a610c7c273f56273edd30dc008a2f66c543cabea..b8cbb0eb252b2469a045e4480253119e4b8c77a0 100644
--- a/python/mesa_pd/kernel/ExplicitEuler.py
+++ b/python/mesa_pd/kernel/ExplicitEuler.py
@@ -1,26 +1,24 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class ExplicitEuler:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invMass",         "walberla::real_t",        access="g" )
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("flags",           "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class ExplicitEuler:
+    def __init__(self):
+        self.context = dict()
+        self.context['interface'] = [create_access("position", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("invMass", "walberla::real_t", access="g"),
+                                     create_access("force", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g")]
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-      generateFile(path, 'kernel/ExplicitEuler.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/ExplicitEuler.templ.h', ctx)
 
-      context["InterfaceTestName"] = "ExplicitEulerInterfaceCheck"
-      context["KernelInclude"] = "kernel/ExplicitEuler.h"
-      context["ExplicitInstantiation"] = "template void kernel::ExplicitEuler::operator()(const size_t p_idx1, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/ExplicitEulerInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "ExplicitEulerInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/ExplicitEuler.h"
+        ctx["ExplicitInstantiation"] = "template void kernel::ExplicitEuler::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/ExplicitEulerInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/ExplicitEulerWithShape.py b/python/mesa_pd/kernel/ExplicitEulerWithShape.py
index cdedaa2427da0006c3c5042aa2e1d1c3e8c28896..9179e7b7087ec9ef07d47ea128edac2c39832dcf 100644
--- a/python/mesa_pd/kernel/ExplicitEulerWithShape.py
+++ b/python/mesa_pd/kernel/ExplicitEulerWithShape.py
@@ -1,30 +1,28 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class ExplicitEulerWithShape:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invMass",         "walberla::real_t",        access="g" )
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("rotation",        "walberla::mesa_pd::Rot3", access="gs")
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invInertiaBF",    "walberla::mesa_pd::Mat3", access="g" )
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("flags",           "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class ExplicitEulerWithShape:
+    def __init__(self):
+        self.context = dict()
+        self.context['interface'] = [create_access("position", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("invMass", "walberla::real_t", access="g"),
+                                     create_access("force", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("rotation", "walberla::mesa_pd::Rot3", access="gs"),
+                                     create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("invInertiaBF", "walberla::mesa_pd::Mat3", access="g"),
+                                     create_access("torque", "walberla::mesa_pd::Vec3", access="gs"),
+                                     create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g")]
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-      generateFile(path, 'kernel/ExplicitEulerWithShape.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/ExplicitEulerWithShape.templ.h', ctx)
 
-      context["InterfaceTestName"] = "ExplicitEulerWithShapeInterfaceCheck"
-      context["KernelInclude"] = "kernel/ExplicitEulerWithShape.h"
-      context["ExplicitInstantiation"] = "template void kernel::ExplicitEulerWithShape::operator()(const size_t p_idx1, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "ExplicitEulerWithShapeInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/ExplicitEulerWithShape.h"
+        ctx["ExplicitInstantiation"] = "template void kernel::ExplicitEulerWithShape::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/ForceLJ.py b/python/mesa_pd/kernel/ForceLJ.py
index 4af22f77b06878f6df3231a4cf98aeb164df3f2a..45c22f298a655fd5828b335e99d84e9f4fc92557 100644
--- a/python/mesa_pd/kernel/ForceLJ.py
+++ b/python/mesa_pd/kernel/ForceLJ.py
@@ -1,26 +1,24 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class ForceLJ:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3", access="g")
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3", access="r" )
-      self.accessor.require("type",            "uint_t",              access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class ForceLJ:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["epsilon", "sigma"]
-      context["interface"]        = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["parameters"] = ["epsilon", "sigma"]
 
-      generateFile(path, 'kernel/ForceLJ.templ.h', context)
+        generate_file(module['module_path'], 'kernel/ForceLJ.templ.h', ctx)
 
-      context["InterfaceTestName"] = "ForceLJInterfaceCheck"
-      context["KernelInclude"] = "kernel/ForceLJ.h"
-      context["ExplicitInstantiation"] = "template void kernel::ForceLJ::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/ForceLJInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "ForceLJInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/ForceLJ.h"
+        ctx["ExplicitInstantiation"] = "template void kernel::ForceLJ::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/ForceLJInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/HCSITSRelaxationStep.py b/python/mesa_pd/kernel/HCSITSRelaxationStep.py
index bb80d20ac32ea60971076fa75d351a7110fa5d84..f29ae75cb53d1bf19d67e44915581769e69f8c0e 100644
--- a/python/mesa_pd/kernel/HCSITSRelaxationStep.py
+++ b/python/mesa_pd/kernel/HCSITSRelaxationStep.py
@@ -1,32 +1,43 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from ..Container import Container
-from mesa_pd.utility import generateFile
-
-class HCSITSRelaxationStep(Container):
-   def __init__(self):
-      super().__init__()
-      self.addProperty("maxSubIterations", "size_t", defValue = "20")
-      self.addProperty("relaxationModel", "RelaxationModel", defValue = "InelasticFrictionlessContact")
-      self.addProperty("deltaMax", "real_t", defValue = "0")
-      self.addProperty("cor", "real_t", defValue = "real_t(0.2)")
-
-      self.accessor = Accessor()
-      self.accessor.require("uid",             "walberla::id_t",                                    access="g")
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("invMass",         "walberla::real_t",                                  access="g" )
-      self.accessor.require("invInertia",      "walberla::mesa_pd::Mat3",                           access="g" )
-      self.accessor.require("dv",              "walberla::mesa_pd::Vec3",                           access="gr" )
-      self.accessor.require("dw",              "walberla::mesa_pd::Vec3",                           access="gr" )
-
-   def getRequirements(self):
-      return self.accessor
-
-   def generate(self, path):
-      context = dict()
-      context["properties"]      = self.properties
-      context["interface"]        = self.accessor.properties
-      generateFile(path, 'kernel/HCSITSRelaxationStep.templ.h', context)
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+def create_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class HCSITSRelaxationStep():
+    def __init__(self):
+        self.context = {'properties': [], 'interface': []}
+
+        self.context['properties'].append(create_property("maxSubIterations", "size_t", defValue="20"))
+        self.context['properties'].append(
+            create_property("relaxationModel", "RelaxationModel", defValue="InelasticFrictionlessContact"))
+        self.context['properties'].append(create_property("deltaMax", "real_t", defValue="0"))
+        self.context['properties'].append(create_property("cor", "real_t", defValue="real_t(0.2)"))
+
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("invMass", "walberla::real_t", access="g"))
+        self.context['interface'].append(create_access("invInertia", "walberla::mesa_pd::Mat3", access="g"))
+        self.context['interface'].append(create_access("dv", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("dw", "walberla::mesa_pd::Vec3", access="gr"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/HCSITSRelaxationStep.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/HeatConduction.py b/python/mesa_pd/kernel/HeatConduction.py
index 2a6f31337cc517d733a7aba100479dc840e388eb..1135cf1998bf735cdd5801756a43876ff31caae6 100644
--- a/python/mesa_pd/kernel/HeatConduction.py
+++ b/python/mesa_pd/kernel/HeatConduction.py
@@ -1,26 +1,25 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile, checkInterface
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class HeatConduction:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("temperature",     "walberla::real_t", access="g")
-      self.accessor.require("heatFlux",        "walberla::real_t", access="gsr")
-      self.accessor.require("type",            "uint_t",           access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class HeatConduction:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("temperature", "walberla::real_t", access="g"))
+        self.context['interface'].append(create_access("heatFlux", "walberla::real_t", access="gsr"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["conductance"]
-      context["interface"]        = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module,
+               **self.context,
+               "parameters": ["conductance"]}
 
-      generateFile(path, 'kernel/HeatConduction.templ.h', context)
+        generate_file(module['module_path'], 'kernel/HeatConduction.templ.h', ctx)
 
-      context["InterfaceTestName"] = "HeatConductionInterfaceCheck"
-      context["KernelInclude"] = "kernel/HeatConduction.h"
-      context["ExplicitInstantiation"] = "template void kernel::HeatConduction::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/HeatConductionInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "HeatConductionInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/HeatConduction.h"
+        ctx["ExplicitInstantiation"] = "template void kernel::HeatConduction::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/HeatConductionInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/InitContactsForHCSITS.py b/python/mesa_pd/kernel/InitContactsForHCSITS.py
index 54da9536a4a2f3569a429357abd02bd7466974a3..545975d4b53db576a45c8da2c4f267316fb53370 100644
--- a/python/mesa_pd/kernel/InitContactsForHCSITS.py
+++ b/python/mesa_pd/kernel/InitContactsForHCSITS.py
@@ -1,28 +1,40 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from ..Container import Container
-from mesa_pd.utility import generateFile
-
-class InitContactsForHCSITS(Container):
-   def __init__(self):
-      super().__init__()
-      self.addProperty("erp", "real_t", defValue = "real_t(0.8)")
-      self.addProperty("maximumPenetration", "real_t", defValue ="0")
-
-      self.paccessor = Accessor()
-      self.paccessor.require("uid",             "walberla::id_t",                                    access="g")
-      self.paccessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.paccessor.require("invInertia",      "walberla::mesa_pd::Mat3",                           access="g" )
-      self.paccessor.require("invMass",         "walberla::real_t",                                  access="g" )
-
-
-   def getRequirements(self):
-      return self.paccessor
-
-   def generate(self, path):
-      context = dict()
-      context["properties"]      = self.properties
-      context["material_parameters"] = ["friction"]
-      context["interface"]        = self.paccessor.properties
-      generateFile(path, 'kernel/InitContactsForHCSITS.templ.h', context)
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+def create_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class InitContactsForHCSITS:
+    def __init__(self):
+        self.context = {'properties': [], 'interface': []}
+
+        self.context['properties'].append(create_property("erp", "real_t", defValue="real_t(0.8)"))
+        self.context['properties'].append(create_property("maximumPenetration", "real_t", defValue="0"))
+
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("invInertia", "walberla::mesa_pd::Mat3", access="g"))
+        self.context['interface'].append(create_access("invMass", "walberla::real_t", access="g"))
+
+    def getRequirements(self):
+        return self.paccessor
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["material_parameters"] = ["friction"]
+        generate_file(module['module_path'], 'kernel/InitContactsForHCSITS.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/InitParticlesForHCSITS.py b/python/mesa_pd/kernel/InitParticlesForHCSITS.py
index ca0148cab20c55575aff787611076319ac2fe121..09cd05c8493e480a644d14dbcdd4f4e5c9c58231 100644
--- a/python/mesa_pd/kernel/InitParticlesForHCSITS.py
+++ b/python/mesa_pd/kernel/InitParticlesForHCSITS.py
@@ -1,31 +1,42 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from ..Container import Container
-from mesa_pd.utility import generateFile
-
-class InitParticlesForHCSITS(Container):
-   def __init__(self):
-      super().__init__()
-      self.addProperty("globalAcceleration", "walberla::mesa_pd::Vec3", defValue = "0")
-
-      self.accessor = Accessor()
-      self.accessor.require("uid",             "walberla::id_t",                                    access="g")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3",                           access="gr")
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3",                           access="gr")
-      self.accessor.require("invMass",         "walberla::real_t",                                  access="g" )
-      self.accessor.require("inertia",         "walberla::mesa_pd::Mat3",                           access="g" )
-      self.accessor.require("invInertia",      "walberla::mesa_pd::Mat3",                           access="g" )
-      self.accessor.require("dv",              "walberla::mesa_pd::Vec3",                           access="gr" )
-      self.accessor.require("dw",              "walberla::mesa_pd::Vec3",                           access="gr" )
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3",                           access="r" )
-
-   def getRequirements(self):
-      return self.accessor
-
-   def generate(self, path):
-      context = dict()
-      context["properties"]      = self.properties
-      context["interface"]       = self.accessor.properties
-      generateFile(path, 'kernel/InitParticlesForHCSITS.templ.h', context)
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+def create_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class InitParticlesForHCSITS:
+    def __init__(self):
+        self.context = {'properties': [], 'interface': []}
+
+        self.context['properties'].append(
+            create_property("globalAcceleration", "walberla::mesa_pd::Vec3", defValue="0"))
+
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("invMass", "walberla::real_t", access="g"))
+        self.context['interface'].append(create_access("inertia", "walberla::mesa_pd::Mat3", access="g"))
+        self.context['interface'].append(create_access("invInertia", "walberla::mesa_pd::Mat3", access="g"))
+        self.context['interface'].append(create_access("dv", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("dw", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="r"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/InitParticlesForHCSITS.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/InsertParticleIntoLinkedCells.py b/python/mesa_pd/kernel/InsertParticleIntoLinkedCells.py
index 794fab1df9678a6017eb97bbf3bd315cee92dd9f..d0ce662ee9ad9ab215a5f0709dde2c4913294833 100644
--- a/python/mesa_pd/kernel/InsertParticleIntoLinkedCells.py
+++ b/python/mesa_pd/kernel/InsertParticleIntoLinkedCells.py
@@ -1,19 +1,17 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class InsertParticleIntoLinkedCells:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",     "walberla::mesa_pd::Vec3",                        access="g")
-      self.accessor.require("flags",        "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
-      self.accessor.require("nextParticle", "size_t",                                     access="gs" )
 
-   def getRequirements(self):
-      return self.accessor
+class InsertParticleIntoLinkedCells:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
+        self.context['interface'].append(create_access("nextParticle", "size_t", access="gs"))
 
-   def generate(self, path):
-      context = dict()
-      context["interface"]        = self.accessor.properties
-      generateFile(path, 'kernel/InsertParticleIntoLinkedCells.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/InsertParticleIntoLinkedCells.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.py b/python/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.py
index 9bb841b01e00b688025030b4b84600f1bff2ffcd..a1c4d4bbe9ff7df2d816b8605cb57a62ab2b8317 100644
--- a/python/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.py
+++ b/python/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.py
@@ -1,19 +1,17 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class InsertParticleIntoSparseLinkedCells:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",     "walberla::mesa_pd::Vec3",                        access="g")
-      self.accessor.require("flags",        "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
-      self.accessor.require("nextParticle", "size_t",                                     access="gs" )
 
-   def getRequirements(self):
-      return self.accessor
+class InsertParticleIntoSparseLinkedCells:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
+        self.context['interface'].append(create_access("nextParticle", "size_t", access="gs"))
 
-   def generate(self, path):
-      context = dict()
-      context["interface"]        = self.accessor.properties
-      generateFile(path, 'kernel/InsertParticleIntoSparseLinkedCells.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/InsertParticleIntoSparseLinkedCells.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py b/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py
index b869d5decd30a3cf05a58981544d1309545f93f7..ac0f06bf58cf9c057a0555d3ada0e4d7da7cec74 100644
--- a/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py
+++ b/python/mesa_pd/kernel/IntegrateParticlesHCSITS.py
@@ -1,30 +1,41 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from ..Container import Container
-from mesa_pd.utility import generateFile
-
-class IntegrateParticlesHCSITS(Container):
-   def __init__(self):
-      super().__init__()
-      self.addProperty("speedLimiterActive", "bool", defValue = "false")
-      self.addProperty("speedLimitFactor",    "real_t", defValue ="real_t(1.0)")
-
-      self.accessor = Accessor()
-      self.accessor.require("uid",              "walberla::id_t",                                   access="g")
-      self.accessor.require("position",         "walberla::mesa_pd::Vec3",                          access="gr")
-      self.accessor.require("rotation",         "walberla::mesa_pd::Rot3",                          access="gr")
-      self.accessor.require("linearVelocity",   "walberla::mesa_pd::Vec3",                          access="gr")
-      self.accessor.require("angularVelocity",  "walberla::mesa_pd::Vec3",                          access="gr")
-      self.accessor.require("dv",               "walberla::mesa_pd::Vec3",                          access="g" )
-      self.accessor.require("dw",               "walberla::mesa_pd::Vec3",                          access="g" )
-      self.accessor.require("flags",            "walberla::mesa_pd::data::particle_flags::FlagT",   access="g")
-
-   def getRequirements(self):
-      return self.accessor
-
-   def generate(self, path):
-      context = dict()
-      context["properties"]      = self.properties
-      context["interface"]       = self.accessor.properties
-      generateFile(path, 'kernel/IntegrateParticlesHCSITS.templ.h', context)
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+def create_property(name, type, defValue=""):
+    """
+    Parameters
+    ----------
+    name : str
+       name of the property
+    type : str
+       type of the property
+    defValue : str
+       default value the property should be initialized with
+    """
+
+    return {'name': name, 'type': type, 'defValue': defValue}
+
+
+class IntegrateParticlesHCSITS():
+    def __init__(self):
+        self.context = {'properties': [], 'interface': []}
+
+        self.context['properties'].append(create_property("speedLimiterActive", "bool", defValue="false"))
+        self.context['properties'].append(create_property("speedLimitFactor", "real_t", defValue="real_t(1.0)"))
+
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("rotation", "walberla::mesa_pd::Rot3", access="gr"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gr"))
+        self.context['interface'].append(create_access("dv", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("dw", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/IntegrateParticlesHCSITS.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/LinearSpringDashpot.py b/python/mesa_pd/kernel/LinearSpringDashpot.py
index 430a58dc6d7fbbd1273112805d00e993f8aff4eb..747a83f6a978256c9b59535af4f4f438756ca6ef 100644
--- a/python/mesa_pd/kernel/LinearSpringDashpot.py
+++ b/python/mesa_pd/kernel/LinearSpringDashpot.py
@@ -1,26 +1,25 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class LinearSpringDashpot:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("uid",             "walberla::id_t",                                access="g")
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("type",            "uint_t",                                        access="g")
-      self.accessor.require("contactHistory",  "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs")
 
-   def getRequirements(self):
-      return self.accessor
+class LinearSpringDashpot:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
+        self.context['interface'].append(
+            create_access("contactHistory", "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["stiffnessN", "stiffnessT", "dampingN", "dampingT", "frictionCoefficientStatic", "frictionCoefficientDynamic"]
-      context["interface"]        = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["parameters"] = ["stiffnessN", "stiffnessT", "dampingN", "dampingT", "frictionCoefficientStatic",
+                             "frictionCoefficientDynamic"]
 
-      generateFile(path, 'kernel/LinearSpringDashpot.templ.h', context)
+        generate_file(module['module_path'], 'kernel/LinearSpringDashpot.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/NonLinearSpringDashpot.py b/python/mesa_pd/kernel/NonLinearSpringDashpot.py
index 490d05ff50e11831caaf34b6b5545f6cb3627ca6..33f46d44edf5ed8d0ad0eefb12a4a37b2bf5cc07 100644
--- a/python/mesa_pd/kernel/NonLinearSpringDashpot.py
+++ b/python/mesa_pd/kernel/NonLinearSpringDashpot.py
@@ -1,26 +1,25 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class NonLinearSpringDashpot:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("uid",             "walberla::id_t",                                access="g")
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("type",            "uint_t",                                        access="g")
-      self.accessor.require("contactHistory",  "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs")
 
-   def getRequirements(self):
-      return self.accessor
+class NonLinearSpringDashpot:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("uid", "walberla::id_t", access="g"))
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
+        self.context['interface'].append(
+            create_access("contactHistory", "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["lnCORsqr", "meff", "stiffnessT", "dampingT", "frictionCoefficientStatic", "frictionCoefficientDynamic"]
-      context["interface"]        = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["parameters"] = ["lnCORsqr", "meff", "stiffnessT", "dampingT", "frictionCoefficientStatic",
+                             "frictionCoefficientDynamic"]
 
-      generateFile(path, 'kernel/NonLinearSpringDashpot.templ.h', context)
+        generate_file(module['module_path'], 'kernel/NonLinearSpringDashpot.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/SingleCast.py b/python/mesa_pd/kernel/SingleCast.py
index ff253dfd082059dfce1af4d4c8558646e78e7319..1af856e59d0cf641222e06b36c924ee27542a9a9 100644
--- a/python/mesa_pd/kernel/SingleCast.py
+++ b/python/mesa_pd/kernel/SingleCast.py
@@ -1,19 +1,15 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class SingleCast:
-   def __init__(self, shapes):
-      self.shapes = shapes
-      self.accessor = Accessor()
-      self.accessor.require("shape", "BaseShape*", access="g")
-
-   def getRequirements(self):
-      return self.accessor
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-      context["shapes"]    = self.shapes
-      generateFile(path, 'kernel/SingleCast.templ.h', context)
+class SingleCast:
+    def __init__(self, particle_storage):
+        self.ps = particle_storage
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("shape", "BaseShape*", access="g"))
+
+    def generate(self, module):
+        ctx = {'module': module, 'particle': self.ps.get_context(), **self.context}
+        generate_file(module['module_path'], 'kernel/SingleCast.templ.h', ctx)
diff --git a/python/mesa_pd/kernel/SpringDashpot.py b/python/mesa_pd/kernel/SpringDashpot.py
index e7d90a8430a27c7dbfaa9c3dd4d7ef2e693ca7be..8b7a783b449be0dcf073650cb728572d1140499c 100644
--- a/python/mesa_pd/kernel/SpringDashpot.py
+++ b/python/mesa_pd/kernel/SpringDashpot.py
@@ -1,30 +1,30 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile, checkInterface
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class SpringDashpot:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3",                           access="g")
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3",                           access="r" )
-      self.accessor.require("type",            "uint_t",                                            access="g")
-      self.accessor.require("contactHistory",  "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs")
 
-   def getRequirements(self):
-      return self.accessor
+class SpringDashpot:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="g"))
+        self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="r"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
+        self.context['interface'].append(
+            create_access("contactHistory", "std::map<walberla::id_t, walberla::mesa_pd::Vec3>", access="gs"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["stiffness", "dampingN", "dampingT", "friction"]
-      context["interface"]        = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["parameters"] = ["stiffness", "dampingN", "dampingT", "friction"]
 
-      generateFile(path, 'kernel/SpringDashpot.templ.h', context)
+        generate_file(module['module_path'], 'kernel/SpringDashpot.templ.h', ctx)
 
-      context["InterfaceTestName"] = "SpringDashpotInterfaceCheck"
-      context["KernelInclude"] = "kernel/SpringDashpot.h"
-      context["ExplicitInstantiation"] = "template void kernel::SpringDashpot::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac, const Vec3& contactPoint, const Vec3& contactNormal, const real_t& penetrationDepth) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/SpringDashpotInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "SpringDashpotInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/SpringDashpot.h"
+        ctx[
+            "ExplicitInstantiation"] = "template void kernel::SpringDashpot::operator()(const size_t p_idx1, const size_t p_idx2, Accessor& ac, const Vec3& contactPoint, const Vec3& contactNormal, const real_t& penetrationDepth) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/SpringDashpotInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/TemperatureIntegration.py b/python/mesa_pd/kernel/TemperatureIntegration.py
index 68cca40f202a6b165e9e7537fb7f93e8ec4f1af5..426a9b3874805db5abac7b686baf791d0a46263a 100644
--- a/python/mesa_pd/kernel/TemperatureIntegration.py
+++ b/python/mesa_pd/kernel/TemperatureIntegration.py
@@ -1,25 +1,24 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class TemperatureIntegration:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("temperature",     "walberla::real_t", access="gs")
-      self.accessor.require("heatFlux",        "walberla::real_t", access="gs")
-      self.accessor.require("type",            "uint_t",           access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class TemperatureIntegration:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("temperature", "walberla::real_t", access="gs"))
+        self.context['interface'].append(create_access("heatFlux", "walberla::real_t", access="gs"))
+        self.context['interface'].append(create_access("type", "uint_t", access="g"))
 
-   def generate(self, path):
-      context = dict()
-      context["parameters"]       = ["invHeatCapacity"]
-      context["interface"] = self.accessor.properties
-      generateFile(path, 'kernel/TemperatureIntegration.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        ctx["parameters"] = ["invHeatCapacity"]
+        generate_file(module['module_path'], 'kernel/TemperatureIntegration.templ.h', ctx)
 
-      context["InterfaceTestName"] = "TemperatureIntegrationInterfaceCheck"
-      context["KernelInclude"] = "kernel/TemperatureIntegration.h"
-      context["ExplicitInstantiation"] = "template void kernel::TemperatureIntegration::operator()(const size_t p_idx1, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "TemperatureIntegrationInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/TemperatureIntegration.h"
+        ctx[
+            "ExplicitInstantiation"] = "template void kernel::TemperatureIntegration::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/VelocityVerlet.py b/python/mesa_pd/kernel/VelocityVerlet.py
index 391b770706978c145a4a8d6614c224b047301c70..8d2d42a35760d36beab36d965c86c0267272d129 100644
--- a/python/mesa_pd/kernel/VelocityVerlet.py
+++ b/python/mesa_pd/kernel/VelocityVerlet.py
@@ -1,29 +1,29 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
 
-class VelocityVerlet:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invMass",         "walberla::real_t",        access="g" )
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("oldForce",        "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("flags",           "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
 
-   def getRequirements(self):
-      return self.accessor
+class VelocityVerlet:
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("invMass", "walberla::real_t", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("oldForce", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
 
-      generateFile(path, 'kernel/VelocityVerlet.templ.h', context)
+        generate_file(module['module_path'], 'kernel/VelocityVerlet.templ.h', ctx)
 
-      context["InterfaceTestName"] = "VelocityVerletInterfaceCheck"
-      context["KernelInclude"] = "kernel/VelocityVerlet.h"
-      context["ExplicitInstantiation"] = "template void kernel::VelocityVerletPreForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;\n" +\
-      "template void kernel::VelocityVerletPostForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/VelocityVerletInterfaceCheck.cpp')
+        ctx["InterfaceTestName"] = "VelocityVerletInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/VelocityVerlet.h"
+        ctx[
+            "ExplicitInstantiation"] = "template void kernel::VelocityVerletPreForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;\n" + \
+                                       "template void kernel::VelocityVerletPostForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/VelocityVerletInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/VelocityVerletWithShape.py b/python/mesa_pd/kernel/VelocityVerletWithShape.py
index 8dbd50a0bbef25ef0fd635a6cb3835850eab4348..e3d21c59561f88fdc13c60552f025358dc99981c 100644
--- a/python/mesa_pd/kernel/VelocityVerletWithShape.py
+++ b/python/mesa_pd/kernel/VelocityVerletWithShape.py
@@ -1,36 +1,39 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
 
 class VelocityVerletWithShape:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("position",        "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("linearVelocity",  "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invMass",         "walberla::real_t",        access="g" )
-      self.accessor.require("force",           "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("oldForce",        "walberla::mesa_pd::Vec3", access="gs" )
-
-      self.accessor.require("rotation",        "walberla::mesa_pd::Rot3", access="gs")
-      self.accessor.require("angularVelocity", "walberla::mesa_pd::Vec3", access="gs")
-      self.accessor.require("invInertiaBF",    "walberla::mesa_pd::Mat3", access="g" )
-      self.accessor.require("torque",          "walberla::mesa_pd::Vec3", access="gs" )
-      self.accessor.require("oldTorque",       "walberla::mesa_pd::Vec3", access="gs" )
-
-      self.accessor.require("flags",           "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
-
-   def getRequirements(self):
-      return self.accessor
-
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-
-      generateFile(path, 'kernel/VelocityVerletWithShape.templ.h', context)
-
-      context["InterfaceTestName"] = "VelocityVerletWithShapeInterfaceCheck"
-      context["KernelInclude"] = "kernel/VelocityVerletWithShape.h"
-      context["ExplicitInstantiation"] = "template void kernel::VelocityVerletWithShapePreForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;\n" +\
-      "template void kernel::VelocityVerletWithShapePostForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;"
-      generateFile(path, 'tests/CheckInterface.templ.cpp', context, '../../tests/mesa_pd/kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp')
+    def __init__(self):
+        self.context = {'interface': []}
+        self.context['interface'].append(create_access("position", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("invMass", "walberla::real_t", access="g"))
+        self.context['interface'].append(create_access("force", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("oldForce", "walberla::mesa_pd::Vec3", access="gs"))
+
+        self.context['interface'].append(create_access("rotation", "walberla::mesa_pd::Rot3", access="gs"))
+        self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("invInertiaBF", "walberla::mesa_pd::Mat3", access="g"))
+        self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="gs"))
+        self.context['interface'].append(create_access("oldTorque", "walberla::mesa_pd::Vec3", access="gs"))
+
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
+
+    def getRequirements(self):
+        return self.accessor
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+
+        generate_file(module['module_path'], 'kernel/VelocityVerletWithShape.templ.h', ctx)
+
+        ctx["InterfaceTestName"] = "VelocityVerletWithShapeInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/VelocityVerletWithShape.h"
+        ctx[
+            "ExplicitInstantiation"] = "template void kernel::VelocityVerletWithShapePreForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;\n" + \
+                                       "template void kernel::VelocityVerletWithShapePostForceUpdate::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp')
diff --git a/python/mesa_pd/mpi/BroadcastProperty.py b/python/mesa_pd/mpi/BroadcastProperty.py
index 4e6109ae41b1cc931e3ce1c26ea1695eda4d96e5..99a2c1fd8c0fa7172208e802d3dce19f521a1006 100644
--- a/python/mesa_pd/mpi/BroadcastProperty.py
+++ b/python/mesa_pd/mpi/BroadcastProperty.py
@@ -1,7 +1,9 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class BroadcastProperty:
-   def generate(self, path):
-      generateFile(path, 'mpi/BroadcastProperty.templ.h')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/BroadcastProperty.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/ClearNextNeighborSync.py b/python/mesa_pd/mpi/ClearNextNeighborSync.py
index e78fecc881953472737b5004edf16a670f066fb0..0cb341c4af4d02c1c4982353039cb361331fcf37 100644
--- a/python/mesa_pd/mpi/ClearNextNeighborSync.py
+++ b/python/mesa_pd/mpi/ClearNextNeighborSync.py
@@ -1,18 +1,17 @@
 # -*- coding: utf-8 -*-
 
-from mesa_pd.accessor import Accessor
-from mesa_pd.utility import generateFile
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
 
 class ClearNextNeighborSync:
-   def __init__(self):
-      self.accessor = Accessor()
-      self.accessor.require("flags",        "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
-      self.accessor.require("ghostOwners",  "std::vector<int>",                           access="r")
+    def __init__(self):
+        self.context = {'properties': [], 'interface': []}
 
-   def getRequirements(self):
-      return self.accessor
+        self.context['interface'].append(
+            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g"))
+        self.context['interface'].append(create_access("ghostOwners", "std::vector<int>", access="r"))
 
-   def generate(self, path):
-      context = dict()
-      context["interface"] = self.accessor.properties
-      generateFile(path, 'mpi/ClearNextNeighborSync.templ.h', context)
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'mpi/ClearNextNeighborSync.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/Notifications.py b/python/mesa_pd/mpi/Notifications.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a87ee426c931d7c6627ddea582584a511e74061
--- /dev/null
+++ b/python/mesa_pd/mpi/Notifications.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+
+from ..utility import generate_file
+
+class Notifications:
+   def __init__(self, particle_storage):
+      self.ps = particle_storage
+
+   def generate(self, module):
+      ctx = {'module': module, 'particle' : self.ps.get_context()}
+
+      generate_file(module['module_path'], 'mpi/notifications/ForceTorqueNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/HeatFluxNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParseMessage.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleCopyNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleGhostCopyNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/NewGhostParticleNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleMigrationNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleRemoteMigrationNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleRemovalInformationNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleRemovalNotification.templ.h', ctx)
+      generate_file(module['module_path'], 'mpi/notifications/ParticleUpdateNotification.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/ReduceContactHistory.py b/python/mesa_pd/mpi/ReduceContactHistory.py
index f2a4c797ae46bce927ee96968d940ad64e6913a5..cf4e18582f5931d05693ba5d0db810bc71e8950e 100644
--- a/python/mesa_pd/mpi/ReduceContactHistory.py
+++ b/python/mesa_pd/mpi/ReduceContactHistory.py
@@ -1,7 +1,9 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class ReduceContactHistory:
-   def generate(self, path):
-      generateFile(path, 'mpi/ReduceContactHistory.templ.h')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/ReduceContactHistory.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/ReduceProperty.py b/python/mesa_pd/mpi/ReduceProperty.py
index b630a0dd05900d1d4b2b716d5951df0bf7c812bc..744661043a5c76667f147366b7c0f517d1df1af0 100644
--- a/python/mesa_pd/mpi/ReduceProperty.py
+++ b/python/mesa_pd/mpi/ReduceProperty.py
@@ -1,7 +1,9 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class ReduceProperty:
-   def generate(self, path):
-      generateFile(path, 'mpi/ReduceProperty.templ.h')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/ReduceProperty.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/ShapePackUnpack.py b/python/mesa_pd/mpi/ShapePackUnpack.py
index cb52010d7fd7e353f712ede06f204131c659f15e..2bb60d77e9e085a563da0f823f9327b37bf7aec7 100644
--- a/python/mesa_pd/mpi/ShapePackUnpack.py
+++ b/python/mesa_pd/mpi/ShapePackUnpack.py
@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class ShapePackUnpack:
-   def __init__(self, shapes):
-      self.shapes = shapes
-   def generate(self, path):
-      context = dict()
-      context["shapes"] = self.shapes
-      generateFile(path, 'mpi/ShapePackUnpack.templ.h', context)
+    def __init__(self, particle_storage):
+        self.ps = particle_storage
+
+    def generate(self, module):
+        ctx = {'module': module, 'particle': self.ps.get_context()}
+        generate_file(module['module_path'], 'mpi/ShapePackUnpack.templ.h', ctx)
diff --git a/python/mesa_pd/mpi/SyncGhostOwners.py b/python/mesa_pd/mpi/SyncGhostOwners.py
index 320a8f4c7d00f7bfaedec9698e22c5f21c5bca6d..e4c8b203f8c7b8ad869df532188c925c7a0801ed 100644
--- a/python/mesa_pd/mpi/SyncGhostOwners.py
+++ b/python/mesa_pd/mpi/SyncGhostOwners.py
@@ -1,16 +1,22 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class SyncGhostOwners:
-   def __init__(self, p):
-      p.addProperty("position",          "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-      p.addProperty("interactionRadius", "walberla::real_t",        defValue="real_t(0)", syncMode="ON_GHOST_CREATION")
-      p.addProperty("flags",             "walberla::mesa_pd::data::particle_flags::FlagT", defValue="", syncMode="ON_GHOST_CREATION")
-      p.addProperty("owner",             "int",                     defValue="-1",        syncMode="ON_GHOST_CREATION")
-      p.addProperty("ghostOwners",       "std::unordered_set<walberla::mpi::MPIRank>",    defValue="",          syncMode="NEVER")
-      p.addProperty("neighborState",     "std::unordered_set<walberla::mpi::MPIRank>",       defValue="",          syncMode="NEVER")
+    def __init__(self, particle_storage):
+        particle_storage.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+        particle_storage.add_property("interactionRadius", "walberla::real_t", defValue="real_t(0)",
+                                      syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("flags", "walberla::mesa_pd::data::particle_flags::FlagT", defValue="",
+                                      syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("owner", "int", defValue="-1", syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("ghostOwners", "std::unordered_set<walberla::mpi::MPIRank>", defValue="",
+                                      syncMode="NEVER")
+        particle_storage.add_property("neighborState", "std::unordered_set<walberla::mpi::MPIRank>", defValue="",
+                                      syncMode="NEVER")
 
-   def generate(self, path):
-      generateFile(path, 'mpi/SyncGhostOwners.templ.h')
-      generateFile(path, 'mpi/SyncGhostOwners.templ.cpp')
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/SyncGhostOwners.templ.h', ctx)
+        generate_file(module['module_path'], 'mpi/SyncGhostOwners.templ.cpp', ctx)
diff --git a/python/mesa_pd/mpi/SyncNextNeighbors.py b/python/mesa_pd/mpi/SyncNextNeighbors.py
index 703b9a2266609f87dc1ee120b855a02cfcfe6cd6..71a9e8cfa60ef23f49e8a1d18149250478f5fc22 100644
--- a/python/mesa_pd/mpi/SyncNextNeighbors.py
+++ b/python/mesa_pd/mpi/SyncNextNeighbors.py
@@ -1,15 +1,20 @@
 # -*- coding: utf-8 -*-
 
-from ..utility import generateFile
+from ..utility import generate_file
+
 
 class SyncNextNeighbors:
-   def __init__(self, p):
-      p.addProperty("position",          "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
-      p.addProperty("interactionRadius", "walberla::real_t",        defValue="real_t(0)", syncMode="ON_GHOST_CREATION")
-      p.addProperty("flags",             "walberla::mesa_pd::data::particle_flags::FlagT", defValue="", syncMode="ON_GHOST_CREATION")
-      p.addProperty("owner",             "int",                     defValue="-1",        syncMode="ON_GHOST_CREATION")
-      p.addProperty("ghostOwners",       "std::unordered_set<walberla::mpi::MPIRank>",    defValue="",          syncMode="NEVER")
-
-   def generate(self, path):
-      generateFile(path, 'mpi/SyncNextNeighbors.templ.h')
-      generateFile(path, 'mpi/SyncNextNeighbors.templ.cpp')
+    def __init__(self, particle_storage):
+        particle_storage.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+        particle_storage.add_property("interactionRadius", "walberla::real_t", defValue="real_t(0)",
+                                      syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("flags", "walberla::mesa_pd::data::particle_flags::FlagT", defValue="",
+                                      syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("owner", "int", defValue="-1", syncMode="ON_GHOST_CREATION")
+        particle_storage.add_property("ghostOwners", "std::unordered_set<walberla::mpi::MPIRank>", defValue="",
+                                      syncMode="NEVER")
+
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/SyncNextNeighbors.templ.h', ctx)
+        generate_file(module['module_path'], 'mpi/SyncNextNeighbors.templ.cpp', ctx)
diff --git a/python/mesa_pd/mpi/SyncNextNeighborsNoGhosts.py b/python/mesa_pd/mpi/SyncNextNeighborsNoGhosts.py
new file mode 100644
index 0000000000000000000000000000000000000000..d29a5471eef805f6553ccaa4bced31fe8a4031c8
--- /dev/null
+++ b/python/mesa_pd/mpi/SyncNextNeighborsNoGhosts.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+from ..utility import generate_file
+
+
+class SyncNextNeighborsNoGhosts:
+    def __init__(self, particle_storage):
+        particle_storage.add_property("position", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ALWAYS")
+        particle_storage.add_property("owner", "int", defValue="-1", syncMode="ON_GHOST_CREATION")
+
+    def generate(self, module):
+        ctx = {'module': module}
+        generate_file(module['module_path'], 'mpi/SyncNextNeighborsNoGhosts.templ.h', ctx)
+        generate_file(module['module_path'], 'mpi/SyncNextNeighborsNoGhosts.templ.cpp', ctx)
diff --git a/python/mesa_pd/mpi/__init__.py b/python/mesa_pd/mpi/__init__.py
index c319a8feca8c92c1e302a50b9bce2abb570b1461..223a70c04e5a71d119b2ad58a69167dafee01543 100644
--- a/python/mesa_pd/mpi/__init__.py
+++ b/python/mesa_pd/mpi/__init__.py
@@ -2,11 +2,13 @@
 
 from .BroadcastProperty import BroadcastProperty
 from .ClearNextNeighborSync import ClearNextNeighborSync
+from .Notifications import Notifications
 from .ReduceContactHistory import ReduceContactHistory
 from .ReduceProperty import ReduceProperty
 from .ShapePackUnpack import ShapePackUnpack
 from .SyncGhostOwners import SyncGhostOwners
 from .SyncNextNeighbors import SyncNextNeighbors
+from .SyncNextNeighborsNoGhosts import SyncNextNeighborsNoGhosts
 
 __all__ = ['BroadcastProperty',
            'ClearNextNeighborSync',
@@ -15,4 +17,5 @@ __all__ = ['BroadcastProperty',
            'ShapePackUnpack',
            'SyncGhostOwners',
            'SyncNextNeighbors',
+           'SyncNextNeighborsNoGhosts'
            ]
diff --git a/python/mesa_pd/templates/data/ShapeStorage.templ.h b/python/mesa_pd/templates/data/ShapeStorage.templ.h
index 8cede09926bd5df9ac44706aaf14fe2f22f0d0f8..230f6a923ab392b90448961744e825a1b6905c8f 100644
--- a/python/mesa_pd/templates/data/ShapeStorage.templ.h
+++ b/python/mesa_pd/templates/data/ShapeStorage.templ.h
@@ -29,7 +29,7 @@
 #include <mesa_pd/data/DataTypes.h>
 #include <mesa_pd/data/ParticleStorage.h>
 #include <mesa_pd/data/shape/BaseShape.h>
-{%- for shape in shapes %}
+{%- for shape in particle.shapes %}
 #include <mesa_pd/data/shape/{{shape}}.h>
 {%- endfor %}
 
@@ -58,8 +58,8 @@ struct ShapeStorage
    ReturnType doubleDispatch( ParticleStorage& ps, size_t idx, size_t idy, func& f );
 };
 //Make sure that no two different shapes have the same unique identifier!
-{%- for shape in shapes[1:] %}
-static_assert( {{shapes[0]}}::SHAPE_TYPE != {{shape}}::SHAPE_TYPE, "Shape types have to be different!" );
+{%- for shape in particle.shapes[1:] %}
+static_assert( {{particle.shapes[0]}}::SHAPE_TYPE != {{shape}}::SHAPE_TYPE, "Shape types have to be different!" );
 {%- endfor %}
 
 template <typename ShapeT, typename... Args>
@@ -76,7 +76,7 @@ ReturnType ShapeStorage::singleDispatch( ParticleStorage& ps, size_t idx, func&
 
    switch (shapes[ps.getShapeID(idx)]->getShapeType())
    {
-      {%- for shape in shapes %}
+      {%- for shape in particle.shapes %}
       case {{shape}}::SHAPE_TYPE : return f(ps, idx, *static_cast<{{shape}}*>(shapes[ps.getShapeID(idx)].get()));
       {%- endfor %}
       default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idx)]->getShapeType() << ") could not be determined!");
@@ -91,11 +91,11 @@ ReturnType ShapeStorage::doubleDispatch( ParticleStorage& ps, size_t idx, size_t
 
    switch (shapes[ps.getShapeID(idx)]->getShapeType())
    {
-      {%- for shape1 in shapes %}
+      {%- for shape1 in particle.shapes %}
       case {{shape1}}::SHAPE_TYPE :
          switch (shapes[ps.getShapeID(idy)]->getShapeType())
          {
-            {%- for shape2 in shapes %}
+            {%- for shape2 in particle.shapes %}
             case {{shape2}}::SHAPE_TYPE : return f(ps,
                                                    idx,
                                                    *static_cast<{{shape1}}*>(shapes[ps.getShapeID(idx)].get()),
diff --git a/python/mesa_pd/templates/kernel/DoubleCast.templ.h b/python/mesa_pd/templates/kernel/DoubleCast.templ.h
index 68cc2b611eb77aa1078f6826d9a05dcc7890fedd..adc3caf3cba7d2f588d04afb07cd7f924d8a7e41 100644
--- a/python/mesa_pd/templates/kernel/DoubleCast.templ.h
+++ b/python/mesa_pd/templates/kernel/DoubleCast.templ.h
@@ -29,7 +29,7 @@
 #include <mesa_pd/data/DataTypes.h>
 #include <mesa_pd/data/IAccessor.h>
 #include <mesa_pd/data/shape/BaseShape.h>
-{%- for shape in shapes %}
+{%- for shape in particle.shapes %}
 #include <mesa_pd/data/shape/{{shape}}.h>
 {%- endfor %}
 
@@ -74,11 +74,11 @@ auto DoubleCast::operator()( size_t idx, size_t idy, Accessor& ac, func& f, Args
 
    switch (ac.getShape(idx)->getShapeType())
    {
-      {%- for shape1 in shapes %}
+      {%- for shape1 in particle.shapes %}
       case {{shape1}}::SHAPE_TYPE :
          switch (ac.getShape(idy)->getShapeType())
          {
-            {%- for shape2 in shapes %}
+            {%- for shape2 in particle.shapes %}
             case {{shape2}}::SHAPE_TYPE : return f(idx,
                                                    idy,
                                                    *static_cast<{{shape1}}*>(ac.getShape(idx)),
diff --git a/python/mesa_pd/templates/kernel/SingleCast.templ.h b/python/mesa_pd/templates/kernel/SingleCast.templ.h
index d9d771fd99172748aadea4d92130644946b1b43a..eda737d630ae76a19dd8fb660c3de7330e408f00 100644
--- a/python/mesa_pd/templates/kernel/SingleCast.templ.h
+++ b/python/mesa_pd/templates/kernel/SingleCast.templ.h
@@ -29,7 +29,7 @@
 #include <mesa_pd/data/DataTypes.h>
 #include <mesa_pd/data/IAccessor.h>
 #include <mesa_pd/data/shape/BaseShape.h>
-{%- for shape in shapes %}
+{%- for shape in particle.shapes %}
 #include <mesa_pd/data/shape/{{shape}}.h>
 {%- endfor %}
 
@@ -73,7 +73,7 @@ auto SingleCast::operator()( size_t idx, Accessor& ac, func& f, Args&&... args )
    using namespace mesa_pd::data;
    switch (ac.getShape(idx)->getShapeType())
    {
-      {%- for shape in shapes %}
+      {%- for shape in particle.shapes %}
       case {{shape}}::SHAPE_TYPE : return f(idx, *static_cast<{{shape}}*>(ac.getShape(idx)), std::forward<Args>(args)...);
       {%- endfor %}
       default : WALBERLA_ABORT("Shape type (" << ac.getShape(idx)->getShapeType() << ") could not be determined!");
diff --git a/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h b/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
index 47e5bd7fc678b353ed6c363b09903edf1e6306f8..2031f6659bd1eec159b0e60fe321a8c52371d836 100644
--- a/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
+++ b/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
@@ -27,7 +27,7 @@
 #pragma once
 
 #include <mesa_pd/data/shape/BaseShape.h>
-{%- for shape in shapes %}
+{%- for shape in particle.shapes %}
 #include <mesa_pd/data/shape/{{shape}}.h>
 {%- endfor %}
 
@@ -61,7 +61,7 @@ namespace mpi {
       buf >> shapeType;
       switch (shapeType)
       {
-         {%- for shape in shapes %}
+         {%- for shape in particle.shapes %}
          case {{shape}}::SHAPE_TYPE :
             bs = std::make_unique<mesa_pd::data::{{shape}}>();
             bs->unpack(buf);
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bab7acfcc9be2aeabd5c74494db0168dd3df291e
--- /dev/null
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp
@@ -0,0 +1,162 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SyncNextNeighborsNoGhosts.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#include "SyncNextNeighborsNoGhosts.h"
+
+#include <mesa_pd/mpi/RemoveAndNotify.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace mpi {
+
+void SyncNextNeighborsNoGhosts::operator()(data::ParticleStorage& ps,
+                                           const domain::IDomain& domain) const
+{
+   if (numProcesses_ == 1) return;
+
+   neighborRanks_ = domain.getNeighborProcesses();
+   for( uint_t nbProcessRank : neighborRanks_ )
+   {
+      if (bs.sendBuffer(nbProcessRank).isEmpty())
+      {
+         // fill empty buffers with a dummy byte to force transmission
+         bs.sendBuffer(nbProcessRank) << walberla::uint8_c(0);
+      }
+   }
+   generateSynchronizationMessages(ps, domain);
+
+   // size of buffer is unknown and changes with each send
+   bs.setReceiverInfoFromSendBufferState(false, true);
+   bs.sendAll();
+
+   // Receiving the updates for the remote rigid bodies from the connected processes
+   WALBERLA_LOG_DETAIL( "Parsing of particle synchronization response starts..." );
+   ParseMessage parseMessage;
+   for( auto it = bs.begin(); it != bs.end(); ++it )
+   {
+      walberla::uint8_t tmp;
+      it.buffer() >> tmp;
+      while( !it.buffer().isEmpty() )
+      {
+         parseMessage(it.rank(), it.buffer(), ps, domain);
+      }
+   }
+   WALBERLA_LOG_DETAIL( "Parsing of particle synchronization response ended." );
+}
+
+void SyncNextNeighborsNoGhosts::generateSynchronizationMessages(data::ParticleStorage& ps,
+                                                                const domain::IDomain& domain) const
+{
+   const uint_t ownRank = uint_c(rank_);
+
+   WALBERLA_LOG_DETAIL( "Assembling of particle synchronization message starts..." );
+
+   // position update
+   for( auto pIt = ps.begin(); pIt != ps.end(); )
+   {
+      //skip all ghost particles
+      if (data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::GHOST))
+      {
+         ++pIt;
+         continue;
+      }
+
+      //skip all particles that do not communicate (create ghost particles) on other processes
+      if (data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::NON_COMMUNICATING))
+      {
+         ++pIt;
+         continue;
+      }
+
+      //correct position to make sure particle is always inside the domain!
+      //everything is decided by the master particle therefore ghost particles are not touched
+      if (!data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::FIXED) &&
+          !data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::GHOST))
+      {
+         domain.periodicallyMapToDomain( pIt->getPositionRef() );
+      }
+
+      // Note: At this point we know that the particle was locally owned before the position update.
+      WALBERLA_CHECK_EQUAL(pIt->getOwner(), ownRank);
+
+      WALBERLA_LOG_DETAIL( "Processing local particle " << pIt->getUid() );
+
+      //particle has left subdomain?
+      const auto ownerRank = domain.findContainingProcessRank( pIt->getPosition() );
+      if( ownerRank != int_c(ownRank) )
+      {
+         WALBERLA_LOG_DETAIL( "Local particle " << pIt->getUid() << " is no longer on process " << ownRank << " but on process " << ownerRank );
+
+         if( ownerRank < 0 ) {
+            // No owner found: Outflow condition.
+            WALBERLA_LOG_DETAIL( "Sending deletion notifications for particle " << pIt->getUid() << " due to outflow." );
+
+            // remove particle
+            // since there are no ghosts owners no one has to be notified
+            pIt = ps.erase( pIt );
+
+            continue;
+         }
+
+         // create ghost on new owner process
+         auto& buffer( bs.sendBuffer(ownerRank) );
+         WALBERLA_LOG_DETAIL( "Sending ghost copy notification for particle " << pIt->getUid() << " to process " << ownerRank );
+         packNotification(buffer, ParticleGhostCopyNotification( *pIt ));
+
+         WALBERLA_LOG_DETAIL( "Sending migration notification for particle " << pIt->getUid() << " to process " << ownerRank << "." );
+         //WALBERLA_LOG_DETAIL( "Process registration list before migration: " << pIt->getGhostOwners() );
+
+         // Set new owner and transform to ghost particle
+         pIt->setOwner(ownerRank);
+         data::particle_flags::set( pIt->getFlagsRef(), data::particle_flags::GHOST );
+
+         // currently position is mapped to periodically to global domain,
+         // this might not be the correct position for a ghost particle
+         domain.correctParticlePosition( pIt->getPositionRef() );
+
+         // Send migration notification to new owner
+         packNotification(buffer, ParticleMigrationNotification( *pIt ));
+
+         //remove particle from local process
+         pIt = ps.erase( pIt );
+
+         continue;
+
+      } else
+      {
+         // particle still is locally owned after position update.
+         WALBERLA_LOG_DETAIL( "Owner of particle " << pIt->getUid() << " is still process " << pIt->getOwner() );
+      }
+
+      ++pIt;
+   }
+
+   WALBERLA_LOG_DETAIL( "Assembling of particle synchronization message ended." );
+}
+
+}  // namespace mpi
+}  // namespace mesa_pd
+}  // namespace walberla
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ffc5f2b7b915997c0a88b33e0d9fdd8b1b4f860
--- /dev/null
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h
@@ -0,0 +1,81 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SyncNextNeighborsNoGhosts.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/Flags.h>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/domain/IDomain.h>
+#include <mesa_pd/mpi/notifications/PackNotification.h>
+#include <mesa_pd/mpi/notifications/ParseMessage.h>
+#include <mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleMigrationNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleRemovalNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleUpdateNotification.h>
+
+#include <core/mpi/BufferSystem.h>
+#include <core/logging/Logging.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace mpi {
+
+/**
+ * Next neighbor synchronization kernel without ghost particles.
+ *
+ * This kernel conducts particle migrations if they move to another subdomain
+ * but does not generate ghost particles on overlap. This can be useful for
+ * particles without spatial extend like tracer particles.
+ *
+ * \ingroup mesa_pd_mpi
+ */
+class SyncNextNeighborsNoGhosts
+{
+public:
+   void operator()(data::ParticleStorage& ps,
+                   const domain::IDomain& domain) const;
+
+   int64_t getBytesSent() const { return bs.getBytesSent(); }
+   int64_t getBytesReceived() const { return bs.getBytesReceived(); }
+
+   int64_t getNumberOfSends() const { return bs.getNumberOfSends(); }
+   int64_t getNumberOfReceives() const { return bs.getNumberOfReceives(); }
+private:
+   void generateSynchronizationMessages(data::ParticleStorage& ps,
+                                        const domain::IDomain& domain) const;
+   mutable std::vector<uint_t> neighborRanks_; ///cache for neighbor ranks -> will be updated in operator()
+
+   mutable walberla::mpi::BufferSystem bs = walberla::mpi::BufferSystem( walberla::mpi::MPIManager::instance()->comm() );
+
+   int numProcesses_ = walberla::mpi::MPIManager::instance()->numProcesses();
+   int rank_         = walberla::mpi::MPIManager::instance()->rank();
+};
+
+}  // namespace mpi
+}  // namespace mesa_pd
+}  // namespace walberla
diff --git a/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h b/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
index 8da120f08e06ae5301577d9b5e4df03fe892890e..e1dce5b88e07ab9bc112d1812d7ca0fc245c5bf5 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
@@ -113,7 +113,7 @@ void ParseMessage::operator()(int sender,
       WALBERLA_CHECK(data::particle_flags::isSet(pIt->getFlags(), data::particle_flags::GHOST),
                      "Update notification must only concern shadow copies.");
 
-      {%- for prop in properties %}
+      {%- for prop in particle.properties %}
       {%- if prop.syncMode in ["ALWAYS"] %}
       pIt->set{{prop.name | capFirst}}(objparam.{{prop.name}});
       {%- endif %}
@@ -144,7 +144,7 @@ void ParseMessage::operator()(int sender,
 
       pIt->setOwner(receiver_);
       data::particle_flags::unset(pIt->getFlagsRef(), data::particle_flags::GHOST);
-      {%- for prop in properties %}
+      {%- for prop in particle.properties %}
       {%- if prop.syncMode in ["ON_OWNERSHIP_CHANGE"] %}
       pIt->set{{prop.name | capFirst}}(objparam.{{prop.name}}_);
       {%- endif %}
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
index 505dfbccce50dc4cf2c16b044b81451c60275578..e2414008e6359bb5eefa3abe56aae6c1a29fcf79 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
@@ -48,7 +48,7 @@ class ParticleCopyNotification
 public:
    struct Parameters
    {
-      {%- for prop in properties %}
+      {%- for prop in particle.properties %}
       {%- if not prop.syncMode == "NEVER" %}
       {{prop.type}} {{prop.name}} {{'{'}}{{prop.defValue}}{{'}'}};
       {%- endif %}
@@ -64,7 +64,7 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    WALBERLA_ASSERT_EQUAL(ps.find(data.uid), ps.end(), "Particle with same uid already existent!");
 
    auto pIt = ps.create(data.uid);
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if not prop.syncMode == "NEVER" %}
    pIt->set{{prop.name | capFirst}}(data.{{prop.name}});
    {%- endif %}
@@ -95,7 +95,7 @@ template< typename T,    // Element type of SendBuffer
 mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::ParticleCopyNotification& obj )
 {
    buf.addDebugMarker( "cn" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if not prop.syncMode == "NEVER" %}
    buf << obj.particle_.get{{prop.name | capFirst}}();
    {%- endif %}
@@ -107,7 +107,7 @@ template< typename T>    // Element type  of RecvBuffer
 mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::ParticleCopyNotification::Parameters& objparam )
 {
    buf.readDebugMarker( "cn" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if not prop.syncMode == "NEVER" %}
    buf >> objparam.{{prop.name}};
    {%- endif %}
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
index 9fd397201775821331fb425f984fd8f47a47983c..52dd0a7806bcfe2b8194b8b1f636e26ea64c7f29 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
@@ -48,7 +48,7 @@ class ParticleGhostCopyNotification
 public:
    struct Parameters
    {
-      {%- for prop in properties %}
+      {%- for prop in particle.properties %}
       {%- if prop.syncMode in ["ON_GHOST_CREATION", "ALWAYS"] %}
       {{prop.type}} {{prop.name}} {{'{'}}{{prop.defValue}}{{'}'}};
       {%- endif %}
@@ -64,7 +64,7 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    WALBERLA_ASSERT_EQUAL(ps.find(data.uid), ps.end(), "Particle with same uid already existent!");
 
    auto pIt = ps.create(data.uid);
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ON_GHOST_CREATION", "ALWAYS"] %}
    pIt->set{{prop.name | capFirst}}(data.{{prop.name}});
    {%- endif %}
@@ -95,7 +95,7 @@ template< typename T,    // Element type of SendBuffer
 mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::ParticleGhostCopyNotification& obj )
 {
    buf.addDebugMarker( "cn" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ON_GHOST_CREATION", "ALWAYS"] %}
    buf << obj.particle_.get{{prop.name | capFirst}}();
    {%- endif %}
@@ -107,7 +107,7 @@ template< typename T>    // Element type  of RecvBuffer
 mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::ParticleGhostCopyNotification::Parameters& objparam )
 {
    buf.readDebugMarker( "cn" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ON_GHOST_CREATION", "ALWAYS"] %}
    buf >> objparam.{{prop.name}};
    {%- endif %}
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
index 3ca6e41de648bc20658156f348011ebd82a1dad5..f478bcc9c8985b699cb963ab3a01c9f181e994b0 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
@@ -45,7 +45,7 @@ class ParticleMigrationNotification {
 public:
    struct Parameters {
       id_t uid_;
-      {%- for prop in properties %}
+      {%- for prop in particle.properties %}
       {%- if prop.syncMode in ["ON_OWNERSHIP_CHANGE"] %}
       {{prop.type}} {{prop.name}}_ {{'{'}}{{prop.defValue}}{{'}'}};
       {%- endif %}
@@ -80,7 +80,7 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
 {
    buf.addDebugMarker( "mn" );
    buf << obj.particle_.getUid();
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ON_OWNERSHIP_CHANGE"] %}
    buf << obj.particle_.get{{prop.name | capFirst}}();
    {%- endif %}
@@ -93,7 +93,7 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
 {
    buf.readDebugMarker( "mn" );
    buf >> objparam.uid_;
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ON_OWNERSHIP_CHANGE"] %}
    buf >> objparam.{{prop.name}}_;
    {%- endif %}
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
index dd59c8217e1ed1f3974dc86ce647ebba2a73a657..507eeda47fd17ce0eafff79964b5a1274f4aa821 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
@@ -45,7 +45,7 @@ namespace mesa_pd {
 class ParticleUpdateNotification {
 public:
    struct Parameters {
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ALWAYS"] %}
    {{prop.type}} {{prop.name}} {{'{'}}{{prop.defValue}}{{'}'}};
    {%- endif %}
@@ -79,7 +79,7 @@ template< typename T,    // Element type of SendBuffer
 mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::ParticleUpdateNotification& obj )
 {
    buf.addDebugMarker( "un" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ALWAYS"] %}
    buf << obj.particle_.get{{prop.name | capFirst}}();
    {%- endif %}
@@ -91,7 +91,7 @@ template< typename T>    // Element type  of RecvBuffer
 mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::ParticleUpdateNotification::Parameters& objparam )
 {
    buf.readDebugMarker( "un" );
-   {%- for prop in properties %}
+   {%- for prop in particle.properties %}
    {%- if prop.syncMode in ["ALWAYS"] %}
    buf >> objparam.{{prop.name}};
    {%- endif %}
diff --git a/python/mesa_pd/utility.py b/python/mesa_pd/utility.py
index eff810487a3b134e57c0d07fe8fc84e3ca67eedc..b5a86337d29245a4aaff6454b0fb90ae8622ea4d 100644
--- a/python/mesa_pd/utility.py
+++ b/python/mesa_pd/utility.py
@@ -3,42 +3,38 @@
 from jinja2 import Environment, FileSystemLoader
 import os
 
+
 class TerminalColor:
-   DEFAULT = '\033[0m'
-   GREEN   = '\033[92m'
-   YELLOW  = '\033[93m'
-   RED     = '\033[91m'
+    DEFAULT = '\033[0m'
+    GREEN = '\033[92m'
+    YELLOW = '\033[93m'
+    RED = '\033[91m'
+
 
 def find(f, seq):
-   """Return first item in sequence where f(item) == True."""
-   for item in seq:
-      if f(item):
-         return item
-   return None
-
-def capFirst(s):
-   return s[0].capitalize() + s[1:]
-
-def getJinjaEnvironment():
-   dirname = os.path.dirname(__file__)
-   env = Environment(loader=FileSystemLoader(dirname + '/templates'))
-   env.filters['capFirst'] = capFirst
-   return env
-
-def checkInterface(path, template, accessor):
-   with open(path + template, 'r') as myfile:
-      data = myfile.read()
-   for prop in accessor.properties:
-      for func in prop.getFunctionNames():
-         if not (func in data):
-            raise RuntimeError("{} required but not used in kernel ({})".format(func, template))
-
-def generateFile(path, template, context = {}, filename=None):
-   if filename==None:
-      filename = template.replace(".templ", "")
-   env = getJinjaEnvironment()
-   print("generating: " + path + filename)
-   fout = open(path + filename, "wb")
-   content = env.get_template(template).render(context)
-   fout.write(content.encode('utf8'))
-   fout.close()
+    """Return first item in sequence where f(item) == True."""
+    for item in seq:
+        if f(item):
+            return item
+    return None
+
+
+def cap_first(s):
+    return s[0].capitalize() + s[1:]
+
+
+def get_jinja_environment():
+    dirname = os.path.dirname(__file__)
+    env = Environment(loader=FileSystemLoader(dirname + '/templates'))
+    env.filters['capFirst'] = cap_first
+    return env
+
+
+def generate_file(path, template, context={}, filename=None):
+    if filename == None:
+        filename = template.replace(".templ", "")
+    env = get_jinja_environment()
+    print(f"generating: {(path / filename)}")
+    with open(path / filename, "wb") as fout:
+        content = env.get_template(template).render(context)
+        fout.write(content.encode('utf8'))
diff --git a/src/mesa_pd/data/ParticleAccessor.h b/src/mesa_pd/data/ParticleAccessor.h
index ebb81768e28487d7c32bdddcba4a8863a6c79902..59e5753306256ae967be958c297696804fd3a0c7 100644
--- a/src/mesa_pd/data/ParticleAccessor.h
+++ b/src/mesa_pd/data/ParticleAccessor.h
@@ -72,22 +72,6 @@ public:
    std::unordered_set<walberla::mpi::MPIRank>& getGhostOwnersRef(const size_t p_idx) {return ps_->getGhostOwnersRef(p_idx);}
    void setGhostOwners(const size_t p_idx, std::unordered_set<walberla::mpi::MPIRank> const & v) { ps_->setGhostOwners(p_idx, v);}
    
-   size_t const & getShapeID(const size_t p_idx) const {return ps_->getShapeID(p_idx);}
-   size_t& getShapeIDRef(const size_t p_idx) {return ps_->getShapeIDRef(p_idx);}
-   void setShapeID(const size_t p_idx, size_t const & v) { ps_->setShapeID(p_idx, v);}
-   
-   walberla::mesa_pd::Rot3 const & getRotation(const size_t p_idx) const {return ps_->getRotation(p_idx);}
-   walberla::mesa_pd::Rot3& getRotationRef(const size_t p_idx) {return ps_->getRotationRef(p_idx);}
-   void setRotation(const size_t p_idx, walberla::mesa_pd::Rot3 const & v) { ps_->setRotation(p_idx, v);}
-   
-   walberla::mesa_pd::Vec3 const & getAngularVelocity(const size_t p_idx) const {return ps_->getAngularVelocity(p_idx);}
-   walberla::mesa_pd::Vec3& getAngularVelocityRef(const size_t p_idx) {return ps_->getAngularVelocityRef(p_idx);}
-   void setAngularVelocity(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setAngularVelocity(p_idx, v);}
-   
-   walberla::mesa_pd::Vec3 const & getTorque(const size_t p_idx) const {return ps_->getTorque(p_idx);}
-   walberla::mesa_pd::Vec3& getTorqueRef(const size_t p_idx) {return ps_->getTorqueRef(p_idx);}
-   void setTorque(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setTorque(p_idx, v);}
-   
    walberla::mesa_pd::Vec3 const & getLinearVelocity(const size_t p_idx) const {return ps_->getLinearVelocity(p_idx);}
    walberla::mesa_pd::Vec3& getLinearVelocityRef(const size_t p_idx) {return ps_->getLinearVelocityRef(p_idx);}
    void setLinearVelocity(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setLinearVelocity(p_idx, v);}
@@ -104,6 +88,22 @@ public:
    walberla::mesa_pd::Vec3& getOldForceRef(const size_t p_idx) {return ps_->getOldForceRef(p_idx);}
    void setOldForce(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setOldForce(p_idx, v);}
    
+   size_t const & getShapeID(const size_t p_idx) const {return ps_->getShapeID(p_idx);}
+   size_t& getShapeIDRef(const size_t p_idx) {return ps_->getShapeIDRef(p_idx);}
+   void setShapeID(const size_t p_idx, size_t const & v) { ps_->setShapeID(p_idx, v);}
+   
+   walberla::mesa_pd::Rot3 const & getRotation(const size_t p_idx) const {return ps_->getRotation(p_idx);}
+   walberla::mesa_pd::Rot3& getRotationRef(const size_t p_idx) {return ps_->getRotationRef(p_idx);}
+   void setRotation(const size_t p_idx, walberla::mesa_pd::Rot3 const & v) { ps_->setRotation(p_idx, v);}
+   
+   walberla::mesa_pd::Vec3 const & getAngularVelocity(const size_t p_idx) const {return ps_->getAngularVelocity(p_idx);}
+   walberla::mesa_pd::Vec3& getAngularVelocityRef(const size_t p_idx) {return ps_->getAngularVelocityRef(p_idx);}
+   void setAngularVelocity(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setAngularVelocity(p_idx, v);}
+   
+   walberla::mesa_pd::Vec3 const & getTorque(const size_t p_idx) const {return ps_->getTorque(p_idx);}
+   walberla::mesa_pd::Vec3& getTorqueRef(const size_t p_idx) {return ps_->getTorqueRef(p_idx);}
+   void setTorque(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setTorque(p_idx, v);}
+   
    walberla::mesa_pd::Vec3 const & getOldTorque(const size_t p_idx) const {return ps_->getOldTorque(p_idx);}
    walberla::mesa_pd::Vec3& getOldTorqueRef(const size_t p_idx) {return ps_->getOldTorqueRef(p_idx);}
    void setOldTorque(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setOldTorque(p_idx, v);}
@@ -233,22 +233,6 @@ public:
    void setGhostOwners(const size_t /*p_idx*/, std::unordered_set<walberla::mpi::MPIRank> const & v) { ghostOwners_ = v;}
    std::unordered_set<walberla::mpi::MPIRank>& getGhostOwnersRef(const size_t /*p_idx*/) {return ghostOwners_;}
    
-   size_t const & getShapeID(const size_t /*p_idx*/) const {return shapeID_;}
-   void setShapeID(const size_t /*p_idx*/, size_t const & v) { shapeID_ = v;}
-   size_t& getShapeIDRef(const size_t /*p_idx*/) {return shapeID_;}
-   
-   walberla::mesa_pd::Rot3 const & getRotation(const size_t /*p_idx*/) const {return rotation_;}
-   void setRotation(const size_t /*p_idx*/, walberla::mesa_pd::Rot3 const & v) { rotation_ = v;}
-   walberla::mesa_pd::Rot3& getRotationRef(const size_t /*p_idx*/) {return rotation_;}
-   
-   walberla::mesa_pd::Vec3 const & getAngularVelocity(const size_t /*p_idx*/) const {return angularVelocity_;}
-   void setAngularVelocity(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { angularVelocity_ = v;}
-   walberla::mesa_pd::Vec3& getAngularVelocityRef(const size_t /*p_idx*/) {return angularVelocity_;}
-   
-   walberla::mesa_pd::Vec3 const & getTorque(const size_t /*p_idx*/) const {return torque_;}
-   void setTorque(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { torque_ = v;}
-   walberla::mesa_pd::Vec3& getTorqueRef(const size_t /*p_idx*/) {return torque_;}
-   
    walberla::mesa_pd::Vec3 const & getLinearVelocity(const size_t /*p_idx*/) const {return linearVelocity_;}
    void setLinearVelocity(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { linearVelocity_ = v;}
    walberla::mesa_pd::Vec3& getLinearVelocityRef(const size_t /*p_idx*/) {return linearVelocity_;}
@@ -265,6 +249,22 @@ public:
    void setOldForce(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { oldForce_ = v;}
    walberla::mesa_pd::Vec3& getOldForceRef(const size_t /*p_idx*/) {return oldForce_;}
    
+   size_t const & getShapeID(const size_t /*p_idx*/) const {return shapeID_;}
+   void setShapeID(const size_t /*p_idx*/, size_t const & v) { shapeID_ = v;}
+   size_t& getShapeIDRef(const size_t /*p_idx*/) {return shapeID_;}
+   
+   walberla::mesa_pd::Rot3 const & getRotation(const size_t /*p_idx*/) const {return rotation_;}
+   void setRotation(const size_t /*p_idx*/, walberla::mesa_pd::Rot3 const & v) { rotation_ = v;}
+   walberla::mesa_pd::Rot3& getRotationRef(const size_t /*p_idx*/) {return rotation_;}
+   
+   walberla::mesa_pd::Vec3 const & getAngularVelocity(const size_t /*p_idx*/) const {return angularVelocity_;}
+   void setAngularVelocity(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { angularVelocity_ = v;}
+   walberla::mesa_pd::Vec3& getAngularVelocityRef(const size_t /*p_idx*/) {return angularVelocity_;}
+   
+   walberla::mesa_pd::Vec3 const & getTorque(const size_t /*p_idx*/) const {return torque_;}
+   void setTorque(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { torque_ = v;}
+   walberla::mesa_pd::Vec3& getTorqueRef(const size_t /*p_idx*/) {return torque_;}
+   
    walberla::mesa_pd::Vec3 const & getOldTorque(const size_t /*p_idx*/) const {return oldTorque_;}
    void setOldTorque(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { oldTorque_ = v;}
    walberla::mesa_pd::Vec3& getOldTorqueRef(const size_t /*p_idx*/) {return oldTorque_;}
@@ -342,14 +342,14 @@ private:
    walberla::mesa_pd::data::particle_flags::FlagT flags_;
    int owner_;
    std::unordered_set<walberla::mpi::MPIRank> ghostOwners_;
-   size_t shapeID_;
-   walberla::mesa_pd::Rot3 rotation_;
-   walberla::mesa_pd::Vec3 angularVelocity_;
-   walberla::mesa_pd::Vec3 torque_;
    walberla::mesa_pd::Vec3 linearVelocity_;
    walberla::real_t invMass_;
    walberla::mesa_pd::Vec3 force_;
    walberla::mesa_pd::Vec3 oldForce_;
+   size_t shapeID_;
+   walberla::mesa_pd::Rot3 rotation_;
+   walberla::mesa_pd::Vec3 angularVelocity_;
+   walberla::mesa_pd::Vec3 torque_;
    walberla::mesa_pd::Vec3 oldTorque_;
    blockforest::BlockID currentBlock_;
    uint_t type_;
diff --git a/src/mesa_pd/data/ParticleStorage.h b/src/mesa_pd/data/ParticleStorage.h
index 53c157277713dcb3ba503cb85bd9cf893617508f..a22470d675486c78bfa154765b33bd197d0a97b2 100644
--- a/src/mesa_pd/data/ParticleStorage.h
+++ b/src/mesa_pd/data/ParticleStorage.h
@@ -77,14 +77,14 @@ public:
       using flags_type = walberla::mesa_pd::data::particle_flags::FlagT;
       using owner_type = int;
       using ghostOwners_type = std::unordered_set<walberla::mpi::MPIRank>;
-      using shapeID_type = size_t;
-      using rotation_type = walberla::mesa_pd::Rot3;
-      using angularVelocity_type = walberla::mesa_pd::Vec3;
-      using torque_type = walberla::mesa_pd::Vec3;
       using linearVelocity_type = walberla::mesa_pd::Vec3;
       using invMass_type = walberla::real_t;
       using force_type = walberla::mesa_pd::Vec3;
       using oldForce_type = walberla::mesa_pd::Vec3;
+      using shapeID_type = size_t;
+      using rotation_type = walberla::mesa_pd::Rot3;
+      using angularVelocity_type = walberla::mesa_pd::Vec3;
+      using torque_type = walberla::mesa_pd::Vec3;
       using oldTorque_type = walberla::mesa_pd::Vec3;
       using currentBlock_type = blockforest::BlockID;
       using type_type = uint_t;
@@ -126,22 +126,6 @@ public:
       ghostOwners_type& getGhostOwnersRef() {return storage_.getGhostOwnersRef(i_);}
       void setGhostOwners(ghostOwners_type const & v) { storage_.setGhostOwners(i_, v);}
       
-      shapeID_type const & getShapeID() const {return storage_.getShapeID(i_);}
-      shapeID_type& getShapeIDRef() {return storage_.getShapeIDRef(i_);}
-      void setShapeID(shapeID_type const & v) { storage_.setShapeID(i_, v);}
-      
-      rotation_type const & getRotation() const {return storage_.getRotation(i_);}
-      rotation_type& getRotationRef() {return storage_.getRotationRef(i_);}
-      void setRotation(rotation_type const & v) { storage_.setRotation(i_, v);}
-      
-      angularVelocity_type const & getAngularVelocity() const {return storage_.getAngularVelocity(i_);}
-      angularVelocity_type& getAngularVelocityRef() {return storage_.getAngularVelocityRef(i_);}
-      void setAngularVelocity(angularVelocity_type const & v) { storage_.setAngularVelocity(i_, v);}
-      
-      torque_type const & getTorque() const {return storage_.getTorque(i_);}
-      torque_type& getTorqueRef() {return storage_.getTorqueRef(i_);}
-      void setTorque(torque_type const & v) { storage_.setTorque(i_, v);}
-      
       linearVelocity_type const & getLinearVelocity() const {return storage_.getLinearVelocity(i_);}
       linearVelocity_type& getLinearVelocityRef() {return storage_.getLinearVelocityRef(i_);}
       void setLinearVelocity(linearVelocity_type const & v) { storage_.setLinearVelocity(i_, v);}
@@ -158,6 +142,22 @@ public:
       oldForce_type& getOldForceRef() {return storage_.getOldForceRef(i_);}
       void setOldForce(oldForce_type const & v) { storage_.setOldForce(i_, v);}
       
+      shapeID_type const & getShapeID() const {return storage_.getShapeID(i_);}
+      shapeID_type& getShapeIDRef() {return storage_.getShapeIDRef(i_);}
+      void setShapeID(shapeID_type const & v) { storage_.setShapeID(i_, v);}
+      
+      rotation_type const & getRotation() const {return storage_.getRotation(i_);}
+      rotation_type& getRotationRef() {return storage_.getRotationRef(i_);}
+      void setRotation(rotation_type const & v) { storage_.setRotation(i_, v);}
+      
+      angularVelocity_type const & getAngularVelocity() const {return storage_.getAngularVelocity(i_);}
+      angularVelocity_type& getAngularVelocityRef() {return storage_.getAngularVelocityRef(i_);}
+      void setAngularVelocity(angularVelocity_type const & v) { storage_.setAngularVelocity(i_, v);}
+      
+      torque_type const & getTorque() const {return storage_.getTorque(i_);}
+      torque_type& getTorqueRef() {return storage_.getTorqueRef(i_);}
+      void setTorque(torque_type const & v) { storage_.setTorque(i_, v);}
+      
       oldTorque_type const & getOldTorque() const {return storage_.getOldTorque(i_);}
       oldTorque_type& getOldTorqueRef() {return storage_.getOldTorqueRef(i_);}
       void setOldTorque(oldTorque_type const & v) { storage_.setOldTorque(i_, v);}
@@ -284,14 +284,14 @@ public:
    using flags_type = walberla::mesa_pd::data::particle_flags::FlagT;
    using owner_type = int;
    using ghostOwners_type = std::unordered_set<walberla::mpi::MPIRank>;
-   using shapeID_type = size_t;
-   using rotation_type = walberla::mesa_pd::Rot3;
-   using angularVelocity_type = walberla::mesa_pd::Vec3;
-   using torque_type = walberla::mesa_pd::Vec3;
    using linearVelocity_type = walberla::mesa_pd::Vec3;
    using invMass_type = walberla::real_t;
    using force_type = walberla::mesa_pd::Vec3;
    using oldForce_type = walberla::mesa_pd::Vec3;
+   using shapeID_type = size_t;
+   using rotation_type = walberla::mesa_pd::Rot3;
+   using angularVelocity_type = walberla::mesa_pd::Vec3;
+   using torque_type = walberla::mesa_pd::Vec3;
    using oldTorque_type = walberla::mesa_pd::Vec3;
    using currentBlock_type = blockforest::BlockID;
    using type_type = uint_t;
@@ -333,22 +333,6 @@ public:
    ghostOwners_type& getGhostOwnersRef(const size_t idx) {return ghostOwners_[idx];}
    void setGhostOwners(const size_t idx, ghostOwners_type const & v) { ghostOwners_[idx] = v; }
    
-   shapeID_type const & getShapeID(const size_t idx) const {return shapeID_[idx];}
-   shapeID_type& getShapeIDRef(const size_t idx) {return shapeID_[idx];}
-   void setShapeID(const size_t idx, shapeID_type const & v) { shapeID_[idx] = v; }
-   
-   rotation_type const & getRotation(const size_t idx) const {return rotation_[idx];}
-   rotation_type& getRotationRef(const size_t idx) {return rotation_[idx];}
-   void setRotation(const size_t idx, rotation_type const & v) { rotation_[idx] = v; }
-   
-   angularVelocity_type const & getAngularVelocity(const size_t idx) const {return angularVelocity_[idx];}
-   angularVelocity_type& getAngularVelocityRef(const size_t idx) {return angularVelocity_[idx];}
-   void setAngularVelocity(const size_t idx, angularVelocity_type const & v) { angularVelocity_[idx] = v; }
-   
-   torque_type const & getTorque(const size_t idx) const {return torque_[idx];}
-   torque_type& getTorqueRef(const size_t idx) {return torque_[idx];}
-   void setTorque(const size_t idx, torque_type const & v) { torque_[idx] = v; }
-   
    linearVelocity_type const & getLinearVelocity(const size_t idx) const {return linearVelocity_[idx];}
    linearVelocity_type& getLinearVelocityRef(const size_t idx) {return linearVelocity_[idx];}
    void setLinearVelocity(const size_t idx, linearVelocity_type const & v) { linearVelocity_[idx] = v; }
@@ -365,6 +349,22 @@ public:
    oldForce_type& getOldForceRef(const size_t idx) {return oldForce_[idx];}
    void setOldForce(const size_t idx, oldForce_type const & v) { oldForce_[idx] = v; }
    
+   shapeID_type const & getShapeID(const size_t idx) const {return shapeID_[idx];}
+   shapeID_type& getShapeIDRef(const size_t idx) {return shapeID_[idx];}
+   void setShapeID(const size_t idx, shapeID_type const & v) { shapeID_[idx] = v; }
+   
+   rotation_type const & getRotation(const size_t idx) const {return rotation_[idx];}
+   rotation_type& getRotationRef(const size_t idx) {return rotation_[idx];}
+   void setRotation(const size_t idx, rotation_type const & v) { rotation_[idx] = v; }
+   
+   angularVelocity_type const & getAngularVelocity(const size_t idx) const {return angularVelocity_[idx];}
+   angularVelocity_type& getAngularVelocityRef(const size_t idx) {return angularVelocity_[idx];}
+   void setAngularVelocity(const size_t idx, angularVelocity_type const & v) { angularVelocity_[idx] = v; }
+   
+   torque_type const & getTorque(const size_t idx) const {return torque_[idx];}
+   torque_type& getTorqueRef(const size_t idx) {return torque_[idx];}
+   void setTorque(const size_t idx, torque_type const & v) { torque_[idx] = v; }
+   
    oldTorque_type const & getOldTorque(const size_t idx) const {return oldTorque_[idx];}
    oldTorque_type& getOldTorqueRef(const size_t idx) {return oldTorque_[idx];}
    void setOldTorque(const size_t idx, oldTorque_type const & v) { oldTorque_[idx] = v; }
@@ -522,14 +522,14 @@ public:
    std::vector<flags_type> flags_ {};
    std::vector<owner_type> owner_ {};
    std::vector<ghostOwners_type> ghostOwners_ {};
-   std::vector<shapeID_type> shapeID_ {};
-   std::vector<rotation_type> rotation_ {};
-   std::vector<angularVelocity_type> angularVelocity_ {};
-   std::vector<torque_type> torque_ {};
    std::vector<linearVelocity_type> linearVelocity_ {};
    std::vector<invMass_type> invMass_ {};
    std::vector<force_type> force_ {};
    std::vector<oldForce_type> oldForce_ {};
+   std::vector<shapeID_type> shapeID_ {};
+   std::vector<rotation_type> rotation_ {};
+   std::vector<angularVelocity_type> angularVelocity_ {};
+   std::vector<torque_type> torque_ {};
    std::vector<oldTorque_type> oldTorque_ {};
    std::vector<currentBlock_type> currentBlock_ {};
    std::vector<type_type> type_ {};
@@ -560,14 +560,14 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(const ParticleSt
    getFlagsRef() = rhs.getFlags();
    getOwnerRef() = rhs.getOwner();
    getGhostOwnersRef() = rhs.getGhostOwners();
-   getShapeIDRef() = rhs.getShapeID();
-   getRotationRef() = rhs.getRotation();
-   getAngularVelocityRef() = rhs.getAngularVelocity();
-   getTorqueRef() = rhs.getTorque();
    getLinearVelocityRef() = rhs.getLinearVelocity();
    getInvMassRef() = rhs.getInvMass();
    getForceRef() = rhs.getForce();
    getOldForceRef() = rhs.getOldForce();
+   getShapeIDRef() = rhs.getShapeID();
+   getRotationRef() = rhs.getRotation();
+   getAngularVelocityRef() = rhs.getAngularVelocity();
+   getTorqueRef() = rhs.getTorque();
    getOldTorqueRef() = rhs.getOldTorque();
    getCurrentBlockRef() = rhs.getCurrentBlock();
    getTypeRef() = rhs.getType();
@@ -595,14 +595,14 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(ParticleStorage:
    getFlagsRef() = std::move(rhs.getFlagsRef());
    getOwnerRef() = std::move(rhs.getOwnerRef());
    getGhostOwnersRef() = std::move(rhs.getGhostOwnersRef());
-   getShapeIDRef() = std::move(rhs.getShapeIDRef());
-   getRotationRef() = std::move(rhs.getRotationRef());
-   getAngularVelocityRef() = std::move(rhs.getAngularVelocityRef());
-   getTorqueRef() = std::move(rhs.getTorqueRef());
    getLinearVelocityRef() = std::move(rhs.getLinearVelocityRef());
    getInvMassRef() = std::move(rhs.getInvMassRef());
    getForceRef() = std::move(rhs.getForceRef());
    getOldForceRef() = std::move(rhs.getOldForceRef());
+   getShapeIDRef() = std::move(rhs.getShapeIDRef());
+   getRotationRef() = std::move(rhs.getRotationRef());
+   getAngularVelocityRef() = std::move(rhs.getAngularVelocityRef());
+   getTorqueRef() = std::move(rhs.getTorqueRef());
    getOldTorqueRef() = std::move(rhs.getOldTorqueRef());
    getCurrentBlockRef() = std::move(rhs.getCurrentBlockRef());
    getTypeRef() = std::move(rhs.getTypeRef());
@@ -631,14 +631,14 @@ void swap(ParticleStorage::Particle lhs, ParticleStorage::Particle rhs)
    std::swap(lhs.getFlagsRef(), rhs.getFlagsRef());
    std::swap(lhs.getOwnerRef(), rhs.getOwnerRef());
    std::swap(lhs.getGhostOwnersRef(), rhs.getGhostOwnersRef());
-   std::swap(lhs.getShapeIDRef(), rhs.getShapeIDRef());
-   std::swap(lhs.getRotationRef(), rhs.getRotationRef());
-   std::swap(lhs.getAngularVelocityRef(), rhs.getAngularVelocityRef());
-   std::swap(lhs.getTorqueRef(), rhs.getTorqueRef());
    std::swap(lhs.getLinearVelocityRef(), rhs.getLinearVelocityRef());
    std::swap(lhs.getInvMassRef(), rhs.getInvMassRef());
    std::swap(lhs.getForceRef(), rhs.getForceRef());
    std::swap(lhs.getOldForceRef(), rhs.getOldForceRef());
+   std::swap(lhs.getShapeIDRef(), rhs.getShapeIDRef());
+   std::swap(lhs.getRotationRef(), rhs.getRotationRef());
+   std::swap(lhs.getAngularVelocityRef(), rhs.getAngularVelocityRef());
+   std::swap(lhs.getTorqueRef(), rhs.getTorqueRef());
    std::swap(lhs.getOldTorqueRef(), rhs.getOldTorqueRef());
    std::swap(lhs.getCurrentBlockRef(), rhs.getCurrentBlockRef());
    std::swap(lhs.getTypeRef(), rhs.getTypeRef());
@@ -667,14 +667,14 @@ std::ostream& operator<<( std::ostream& os, const ParticleStorage::Particle& p )
          "flags               : " << p.getFlags() << "\n" <<
          "owner               : " << p.getOwner() << "\n" <<
          "ghostOwners         : " << p.getGhostOwners() << "\n" <<
-         "shapeID             : " << p.getShapeID() << "\n" <<
-         "rotation            : " << p.getRotation() << "\n" <<
-         "angularVelocity     : " << p.getAngularVelocity() << "\n" <<
-         "torque              : " << p.getTorque() << "\n" <<
          "linearVelocity      : " << p.getLinearVelocity() << "\n" <<
          "invMass             : " << p.getInvMass() << "\n" <<
          "force               : " << p.getForce() << "\n" <<
          "oldForce            : " << p.getOldForce() << "\n" <<
+         "shapeID             : " << p.getShapeID() << "\n" <<
+         "rotation            : " << p.getRotation() << "\n" <<
+         "angularVelocity     : " << p.getAngularVelocity() << "\n" <<
+         "torque              : " << p.getTorque() << "\n" <<
          "oldTorque           : " << p.getOldTorque() << "\n" <<
          "currentBlock        : " << p.getCurrentBlock() << "\n" <<
          "type                : " << p.getType() << "\n" <<
@@ -773,14 +773,14 @@ inline ParticleStorage::iterator ParticleStorage::create(const id_t& uid)
    flags_.emplace_back();
    owner_.emplace_back(-1);
    ghostOwners_.emplace_back();
-   shapeID_.emplace_back();
-   rotation_.emplace_back();
-   angularVelocity_.emplace_back(real_t(0));
-   torque_.emplace_back(real_t(0));
    linearVelocity_.emplace_back(real_t(0));
    invMass_.emplace_back(real_t(1));
    force_.emplace_back(real_t(0));
    oldForce_.emplace_back(real_t(0));
+   shapeID_.emplace_back();
+   rotation_.emplace_back();
+   angularVelocity_.emplace_back(real_t(0));
+   torque_.emplace_back(real_t(0));
    oldTorque_.emplace_back(real_t(0));
    currentBlock_.emplace_back();
    type_.emplace_back(0);
@@ -834,14 +834,14 @@ inline ParticleStorage::iterator ParticleStorage::erase(iterator& it)
    flags_.pop_back();
    owner_.pop_back();
    ghostOwners_.pop_back();
-   shapeID_.pop_back();
-   rotation_.pop_back();
-   angularVelocity_.pop_back();
-   torque_.pop_back();
    linearVelocity_.pop_back();
    invMass_.pop_back();
    force_.pop_back();
    oldForce_.pop_back();
+   shapeID_.pop_back();
+   rotation_.pop_back();
+   angularVelocity_.pop_back();
+   torque_.pop_back();
    oldTorque_.pop_back();
    currentBlock_.pop_back();
    type_.pop_back();
@@ -882,14 +882,14 @@ inline void ParticleStorage::reserve(const size_t size)
    flags_.reserve(size);
    owner_.reserve(size);
    ghostOwners_.reserve(size);
-   shapeID_.reserve(size);
-   rotation_.reserve(size);
-   angularVelocity_.reserve(size);
-   torque_.reserve(size);
    linearVelocity_.reserve(size);
    invMass_.reserve(size);
    force_.reserve(size);
    oldForce_.reserve(size);
+   shapeID_.reserve(size);
+   rotation_.reserve(size);
+   angularVelocity_.reserve(size);
+   torque_.reserve(size);
    oldTorque_.reserve(size);
    currentBlock_.reserve(size);
    type_.reserve(size);
@@ -915,14 +915,14 @@ inline void ParticleStorage::clear()
    flags_.clear();
    owner_.clear();
    ghostOwners_.clear();
-   shapeID_.clear();
-   rotation_.clear();
-   angularVelocity_.clear();
-   torque_.clear();
    linearVelocity_.clear();
    invMass_.clear();
    force_.clear();
    oldForce_.clear();
+   shapeID_.clear();
+   rotation_.clear();
+   angularVelocity_.clear();
+   torque_.clear();
    oldTorque_.clear();
    currentBlock_.clear();
    type_.clear();
@@ -949,14 +949,14 @@ inline size_t ParticleStorage::size() const
    //WALBERLA_ASSERT_EQUAL( uid_.size(), flags.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), owner.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), ghostOwners.size() );
-   //WALBERLA_ASSERT_EQUAL( uid_.size(), shapeID.size() );
-   //WALBERLA_ASSERT_EQUAL( uid_.size(), rotation.size() );
-   //WALBERLA_ASSERT_EQUAL( uid_.size(), angularVelocity.size() );
-   //WALBERLA_ASSERT_EQUAL( uid_.size(), torque.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), linearVelocity.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), invMass.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), force.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldForce.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), shapeID.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), rotation.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), angularVelocity.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), torque.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldTorque.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), currentBlock.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), type.size() );
@@ -1210,42 +1210,6 @@ public:
    std::unordered_set<walberla::mpi::MPIRank> const & operator()(const data::Particle& p) const {return p.getGhostOwners();}
 };
 ///Predicate that selects a certain property from a Particle
-class SelectParticleShapeID
-{
-public:
-   using return_type = size_t;
-   size_t& operator()(data::Particle& p) const {return p.getShapeIDRef();}
-   size_t& operator()(data::Particle&& p) const {return p.getShapeIDRef();}
-   size_t const & operator()(const data::Particle& p) const {return p.getShapeID();}
-};
-///Predicate that selects a certain property from a Particle
-class SelectParticleRotation
-{
-public:
-   using return_type = walberla::mesa_pd::Rot3;
-   walberla::mesa_pd::Rot3& operator()(data::Particle& p) const {return p.getRotationRef();}
-   walberla::mesa_pd::Rot3& operator()(data::Particle&& p) const {return p.getRotationRef();}
-   walberla::mesa_pd::Rot3 const & operator()(const data::Particle& p) const {return p.getRotation();}
-};
-///Predicate that selects a certain property from a Particle
-class SelectParticleAngularVelocity
-{
-public:
-   using return_type = walberla::mesa_pd::Vec3;
-   walberla::mesa_pd::Vec3& operator()(data::Particle& p) const {return p.getAngularVelocityRef();}
-   walberla::mesa_pd::Vec3& operator()(data::Particle&& p) const {return p.getAngularVelocityRef();}
-   walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getAngularVelocity();}
-};
-///Predicate that selects a certain property from a Particle
-class SelectParticleTorque
-{
-public:
-   using return_type = walberla::mesa_pd::Vec3;
-   walberla::mesa_pd::Vec3& operator()(data::Particle& p) const {return p.getTorqueRef();}
-   walberla::mesa_pd::Vec3& operator()(data::Particle&& p) const {return p.getTorqueRef();}
-   walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getTorque();}
-};
-///Predicate that selects a certain property from a Particle
 class SelectParticleLinearVelocity
 {
 public:
@@ -1282,6 +1246,42 @@ public:
    walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getOldForce();}
 };
 ///Predicate that selects a certain property from a Particle
+class SelectParticleShapeID
+{
+public:
+   using return_type = size_t;
+   size_t& operator()(data::Particle& p) const {return p.getShapeIDRef();}
+   size_t& operator()(data::Particle&& p) const {return p.getShapeIDRef();}
+   size_t const & operator()(const data::Particle& p) const {return p.getShapeID();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleRotation
+{
+public:
+   using return_type = walberla::mesa_pd::Rot3;
+   walberla::mesa_pd::Rot3& operator()(data::Particle& p) const {return p.getRotationRef();}
+   walberla::mesa_pd::Rot3& operator()(data::Particle&& p) const {return p.getRotationRef();}
+   walberla::mesa_pd::Rot3 const & operator()(const data::Particle& p) const {return p.getRotation();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleAngularVelocity
+{
+public:
+   using return_type = walberla::mesa_pd::Vec3;
+   walberla::mesa_pd::Vec3& operator()(data::Particle& p) const {return p.getAngularVelocityRef();}
+   walberla::mesa_pd::Vec3& operator()(data::Particle&& p) const {return p.getAngularVelocityRef();}
+   walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getAngularVelocity();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleTorque
+{
+public:
+   using return_type = walberla::mesa_pd::Vec3;
+   walberla::mesa_pd::Vec3& operator()(data::Particle& p) const {return p.getTorqueRef();}
+   walberla::mesa_pd::Vec3& operator()(data::Particle&& p) const {return p.getTorqueRef();}
+   walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getTorque();}
+};
+///Predicate that selects a certain property from a Particle
 class SelectParticleOldTorque
 {
 public:
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cfd313cefa151826650f8a095f310674902afcdd
--- /dev/null
+++ b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
@@ -0,0 +1,162 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SyncNextNeighborsNoGhosts.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#include "SyncNextNeighborsNoGhosts.h"
+
+#include <mesa_pd/mpi/RemoveAndNotify.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace mpi {
+
+void SyncNextNeighborsNoGhosts::operator()(data::ParticleStorage& ps,
+                                           const domain::IDomain& domain) const
+{
+   if (numProcesses_ == 1) return;
+
+   neighborRanks_ = domain.getNeighborProcesses();
+   for( uint_t nbProcessRank : neighborRanks_ )
+   {
+      if (bs.sendBuffer(nbProcessRank).isEmpty())
+      {
+         // fill empty buffers with a dummy byte to force transmission
+         bs.sendBuffer(nbProcessRank) << walberla::uint8_c(0);
+      }
+   }
+   generateSynchronizationMessages(ps, domain);
+
+   // size of buffer is unknown and changes with each send
+   bs.setReceiverInfoFromSendBufferState(false, true);
+   bs.sendAll();
+
+   // Receiving the updates for the remote rigid bodies from the connected processes
+   WALBERLA_LOG_DETAIL( "Parsing of particle synchronization response starts..." );
+   ParseMessage parseMessage;
+   for( auto it = bs.begin(); it != bs.end(); ++it )
+   {
+      walberla::uint8_t tmp;
+      it.buffer() >> tmp;
+      while( !it.buffer().isEmpty() )
+      {
+         parseMessage(it.rank(), it.buffer(), ps, domain);
+      }
+   }
+   WALBERLA_LOG_DETAIL( "Parsing of particle synchronization response ended." );
+}
+
+void SyncNextNeighborsNoGhosts::generateSynchronizationMessages(data::ParticleStorage& ps,
+                                                                const domain::IDomain& domain) const
+{
+   const uint_t ownRank = uint_c(rank_);
+
+   WALBERLA_LOG_DETAIL( "Assembling of particle synchronization message starts..." );
+
+   // position update
+   for( auto pIt = ps.begin(); pIt != ps.end(); )
+   {
+      //skip all ghost particles
+      if (data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::GHOST))
+      {
+         ++pIt;
+         continue;
+      }
+
+      //skip all particles that do not communicate (create ghost particles) on other processes
+      if (data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::NON_COMMUNICATING))
+      {
+         ++pIt;
+         continue;
+      }
+
+      //correct position to make sure particle is always inside the domain!
+      //everything is decided by the master particle therefore ghost particles are not touched
+      if (!data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::FIXED) &&
+          !data::particle_flags::isSet( pIt->getFlags(), data::particle_flags::GHOST))
+      {
+         domain.periodicallyMapToDomain( pIt->getPositionRef() );
+      }
+
+      // Note: At this point we know that the particle was locally owned before the position update.
+      WALBERLA_CHECK_EQUAL(pIt->getOwner(), ownRank);
+
+      WALBERLA_LOG_DETAIL( "Processing local particle " << pIt->getUid() );
+
+      //particle has left subdomain?
+      const auto ownerRank = domain.findContainingProcessRank( pIt->getPosition() );
+      if( ownerRank != int_c(ownRank) )
+      {
+         WALBERLA_LOG_DETAIL( "Local particle " << pIt->getUid() << " is no longer on process " << ownRank << " but on process " << ownerRank );
+
+         if( ownerRank < 0 ) {
+            // No owner found: Outflow condition.
+            WALBERLA_LOG_DETAIL( "Sending deletion notifications for particle " << pIt->getUid() << " due to outflow." );
+
+            // remove particle
+            // since there are no ghosts owners no one has to be notified
+            pIt = ps.erase( pIt );
+
+            continue;
+         }
+
+         // create ghost on new owner process
+         auto& buffer( bs.sendBuffer(ownerRank) );
+         WALBERLA_LOG_DETAIL( "Sending ghost copy notification for particle " << pIt->getUid() << " to process " << ownerRank );
+         packNotification(buffer, ParticleGhostCopyNotification( *pIt ));
+
+         WALBERLA_LOG_DETAIL( "Sending migration notification for particle " << pIt->getUid() << " to process " << ownerRank << "." );
+         //WALBERLA_LOG_DETAIL( "Process registration list before migration: " << pIt->getGhostOwners() );
+
+         // Set new owner and transform to ghost particle
+         pIt->setOwner(ownerRank);
+         data::particle_flags::set( pIt->getFlagsRef(), data::particle_flags::GHOST );
+
+         // currently position is mapped to periodically to global domain,
+         // this might not be the correct position for a ghost particle
+         domain.correctParticlePosition( pIt->getPositionRef() );
+
+         // Send migration notification to new owner
+         packNotification(buffer, ParticleMigrationNotification( *pIt ));
+
+         //remove particle from local process
+         pIt = ps.erase( pIt );
+
+         continue;
+
+      } else
+      {
+         // particle still is locally owned after position update.
+         WALBERLA_LOG_DETAIL( "Owner of particle " << pIt->getUid() << " is still process " << pIt->getOwner() );
+      }
+
+      ++pIt;
+   }
+
+   WALBERLA_LOG_DETAIL( "Assembling of particle synchronization message ended." );
+}
+
+}  // namespace mpi
+}  // namespace mesa_pd
+}  // namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h
new file mode 100644
index 0000000000000000000000000000000000000000..a802fe4d631a5b5499b07a24560984f3b98f86bf
--- /dev/null
+++ b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h
@@ -0,0 +1,81 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SyncNextNeighborsNoGhosts.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/Flags.h>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/domain/IDomain.h>
+#include <mesa_pd/mpi/notifications/PackNotification.h>
+#include <mesa_pd/mpi/notifications/ParseMessage.h>
+#include <mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleMigrationNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleRemovalNotification.h>
+#include <mesa_pd/mpi/notifications/ParticleUpdateNotification.h>
+
+#include <core/mpi/BufferSystem.h>
+#include <core/logging/Logging.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace mpi {
+
+/**
+ * Next neighbor synchronization kernel without ghost particles.
+ *
+ * This kernel conducts particle migrations if they move to another subdomain
+ * but does not generate ghost particles on overlap. This can be useful for
+ * particles without spatial extend like tracer particles.
+ *
+ * \ingroup mesa_pd_mpi
+ */
+class SyncNextNeighborsNoGhosts
+{
+public:
+   void operator()(data::ParticleStorage& ps,
+                   const domain::IDomain& domain) const;
+
+   int64_t getBytesSent() const { return bs.getBytesSent(); }
+   int64_t getBytesReceived() const { return bs.getBytesReceived(); }
+
+   int64_t getNumberOfSends() const { return bs.getNumberOfSends(); }
+   int64_t getNumberOfReceives() const { return bs.getNumberOfReceives(); }
+private:
+   void generateSynchronizationMessages(data::ParticleStorage& ps,
+                                        const domain::IDomain& domain) const;
+   mutable std::vector<uint_t> neighborRanks_; ///cache for neighbor ranks -> will be updated in operator()
+
+   mutable walberla::mpi::BufferSystem bs = walberla::mpi::BufferSystem( walberla::mpi::MPIManager::instance()->comm() );
+
+   int numProcesses_ = walberla::mpi::MPIManager::instance()->numProcesses();
+   int rank_         = walberla::mpi::MPIManager::instance()->rank();
+};
+
+}  // namespace mpi
+}  // namespace mesa_pd
+}  // namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/mpi/notifications/ParseMessage.h b/src/mesa_pd/mpi/notifications/ParseMessage.h
index b865c131e322b50f9d747ba5ddb6ba8ac14c87ab..1794965c02c8e01f36be1149c41f61f9d55b7340 100644
--- a/src/mesa_pd/mpi/notifications/ParseMessage.h
+++ b/src/mesa_pd/mpi/notifications/ParseMessage.h
@@ -114,9 +114,9 @@ void ParseMessage::operator()(int sender,
                      "Update notification must only concern shadow copies.");
       pIt->setUid(objparam.uid);
       pIt->setPosition(objparam.position);
+      pIt->setLinearVelocity(objparam.linearVelocity);
       pIt->setRotation(objparam.rotation);
       pIt->setAngularVelocity(objparam.angularVelocity);
-      pIt->setLinearVelocity(objparam.linearVelocity);
       pIt->setOldContactHistory(objparam.oldContactHistory);
       pIt->setTemperature(objparam.temperature);
 
diff --git a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
index aa47b020b7a01aa1b54b0971cad16e2f669f7b07..1d9ca382cc3574306b470fea2cbe4b78b0c90cc8 100644
--- a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
@@ -54,12 +54,12 @@ public:
       walberla::mesa_pd::data::particle_flags::FlagT flags {};
       int owner {-1};
       std::unordered_set<walberla::mpi::MPIRank> ghostOwners {};
-      size_t shapeID {};
-      walberla::mesa_pd::Rot3 rotation {};
-      walberla::mesa_pd::Vec3 angularVelocity {real_t(0)};
       walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
       walberla::real_t invMass {real_t(1)};
       walberla::mesa_pd::Vec3 oldForce {real_t(0)};
+      size_t shapeID {};
+      walberla::mesa_pd::Rot3 rotation {};
+      walberla::mesa_pd::Vec3 angularVelocity {real_t(0)};
       walberla::mesa_pd::Vec3 oldTorque {real_t(0)};
       uint_t type {0};
       std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory> oldContactHistory {};
@@ -81,12 +81,12 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setFlags(data.flags);
    pIt->setOwner(data.owner);
    pIt->setGhostOwners(data.ghostOwners);
-   pIt->setShapeID(data.shapeID);
-   pIt->setRotation(data.rotation);
-   pIt->setAngularVelocity(data.angularVelocity);
    pIt->setLinearVelocity(data.linearVelocity);
    pIt->setInvMass(data.invMass);
    pIt->setOldForce(data.oldForce);
+   pIt->setShapeID(data.shapeID);
+   pIt->setRotation(data.rotation);
+   pIt->setAngularVelocity(data.angularVelocity);
    pIt->setOldTorque(data.oldTorque);
    pIt->setType(data.type);
    pIt->setOldContactHistory(data.oldContactHistory);
@@ -123,12 +123,12 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getFlags();
    buf << obj.particle_.getOwner();
    buf << obj.particle_.getGhostOwners();
-   buf << obj.particle_.getShapeID();
-   buf << obj.particle_.getRotation();
-   buf << obj.particle_.getAngularVelocity();
    buf << obj.particle_.getLinearVelocity();
    buf << obj.particle_.getInvMass();
    buf << obj.particle_.getOldForce();
+   buf << obj.particle_.getShapeID();
+   buf << obj.particle_.getRotation();
+   buf << obj.particle_.getAngularVelocity();
    buf << obj.particle_.getOldTorque();
    buf << obj.particle_.getType();
    buf << obj.particle_.getOldContactHistory();
@@ -146,12 +146,12 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.flags;
    buf >> objparam.owner;
    buf >> objparam.ghostOwners;
-   buf >> objparam.shapeID;
-   buf >> objparam.rotation;
-   buf >> objparam.angularVelocity;
    buf >> objparam.linearVelocity;
    buf >> objparam.invMass;
    buf >> objparam.oldForce;
+   buf >> objparam.shapeID;
+   buf >> objparam.rotation;
+   buf >> objparam.angularVelocity;
    buf >> objparam.oldTorque;
    buf >> objparam.type;
    buf >> objparam.oldContactHistory;
diff --git a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
index 0b8265c6f1b3f9b0b7286dfdbc499016a23cc206..ca769b85c986a66b3193b74570a80f1de32ce1c9 100644
--- a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
@@ -53,11 +53,11 @@ public:
       walberla::real_t interactionRadius {real_t(0)};
       walberla::mesa_pd::data::particle_flags::FlagT flags {};
       int owner {-1};
+      walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
+      walberla::real_t invMass {real_t(1)};
       size_t shapeID {};
       walberla::mesa_pd::Rot3 rotation {};
       walberla::mesa_pd::Vec3 angularVelocity {real_t(0)};
-      walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
-      walberla::real_t invMass {real_t(1)};
       uint_t type {0};
       std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory> oldContactHistory {};
       walberla::real_t temperature {real_t(0)};
@@ -77,11 +77,11 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setInteractionRadius(data.interactionRadius);
    pIt->setFlags(data.flags);
    pIt->setOwner(data.owner);
+   pIt->setLinearVelocity(data.linearVelocity);
+   pIt->setInvMass(data.invMass);
    pIt->setShapeID(data.shapeID);
    pIt->setRotation(data.rotation);
    pIt->setAngularVelocity(data.angularVelocity);
-   pIt->setLinearVelocity(data.linearVelocity);
-   pIt->setInvMass(data.invMass);
    pIt->setType(data.type);
    pIt->setOldContactHistory(data.oldContactHistory);
    pIt->setTemperature(data.temperature);
@@ -116,11 +116,11 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getInteractionRadius();
    buf << obj.particle_.getFlags();
    buf << obj.particle_.getOwner();
+   buf << obj.particle_.getLinearVelocity();
+   buf << obj.particle_.getInvMass();
    buf << obj.particle_.getShapeID();
    buf << obj.particle_.getRotation();
    buf << obj.particle_.getAngularVelocity();
-   buf << obj.particle_.getLinearVelocity();
-   buf << obj.particle_.getInvMass();
    buf << obj.particle_.getType();
    buf << obj.particle_.getOldContactHistory();
    buf << obj.particle_.getTemperature();
@@ -136,11 +136,11 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.interactionRadius;
    buf >> objparam.flags;
    buf >> objparam.owner;
+   buf >> objparam.linearVelocity;
+   buf >> objparam.invMass;
    buf >> objparam.shapeID;
    buf >> objparam.rotation;
    buf >> objparam.angularVelocity;
-   buf >> objparam.linearVelocity;
-   buf >> objparam.invMass;
    buf >> objparam.type;
    buf >> objparam.oldContactHistory;
    buf >> objparam.temperature;
diff --git a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
index 5fdd625c1018d3c1561bf5f60ac67d642a68000d..62d0c6da38b6d57cee8279b2297d4979824b8d41 100644
--- a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
@@ -47,9 +47,9 @@ public:
    struct Parameters {
    walberla::id_t uid {UniqueID<data::Particle>::invalidID()};
    walberla::mesa_pd::Vec3 position {real_t(0)};
+   walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
    walberla::mesa_pd::Rot3 rotation {};
    walberla::mesa_pd::Vec3 angularVelocity {real_t(0)};
-   walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
    std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory> oldContactHistory {};
    walberla::real_t temperature {real_t(0)};
    };
@@ -83,9 +83,9 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf.addDebugMarker( "un" );
    buf << obj.particle_.getUid();
    buf << obj.particle_.getPosition();
+   buf << obj.particle_.getLinearVelocity();
    buf << obj.particle_.getRotation();
    buf << obj.particle_.getAngularVelocity();
-   buf << obj.particle_.getLinearVelocity();
    buf << obj.particle_.getOldContactHistory();
    buf << obj.particle_.getTemperature();
    return buf;
@@ -97,9 +97,9 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf.readDebugMarker( "un" );
    buf >> objparam.uid;
    buf >> objparam.position;
+   buf >> objparam.linearVelocity;
    buf >> objparam.rotation;
    buf >> objparam.angularVelocity;
-   buf >> objparam.linearVelocity;
    buf >> objparam.oldContactHistory;
    buf >> objparam.temperature;
    return buf;
diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt
index efb7340505f21de934d657d521b833b203fcc261..5cd319e21cc690fa3b3dd97a7a6811a52ee25341 100644
--- a/tests/mesa_pd/CMakeLists.txt
+++ b/tests/mesa_pd/CMakeLists.txt
@@ -173,6 +173,9 @@ waLBerla_execute_test( NAME   MESA_PD_MPI_ReduceProperty PROCESSES 8 )
 waLBerla_compile_test( NAME   MESA_PD_MPI_ShapePackUnpack FILES mpi/ShapePackUnpack.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_MPI_ShapePackUnpack )
 
+waLBerla_compile_test( NAME   MESA_PD_MPI_SyncNextNeighborsNoGhosts FILES mpi/SyncNextNeighborsNoGhosts.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_MPI_SyncNextNeighborsNoGhosts PROCESSES 2 )
+
 waLBerla_compile_test( NAME   MESA_PD_MPI_VelocityCorrectionNotification FILES mpi/VelocityCorrectionNotification.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_MPI_VelocityCorrectionNotification PROCESSES 8)
 
diff --git a/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp b/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1dae83425ab8a034c9d46117ed0487aedfec99e3
--- /dev/null
+++ b/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
@@ -0,0 +1,90 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   SyncNextNeighborsNoGhosts.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/domain/BlockForestDomain.h>
+#include <mesa_pd/mpi/SyncNextNeighborsNoGhosts.h>
+
+#include <blockforest/Initialization.h>
+#include <core/Environment.h>
+#include <core/mpi/MPIManager.h>
+
+namespace walberla {
+namespace mesa_pd {
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   //init domain partitioning
+   auto forest = blockforest::createBlockForest( math::AABB(0,0,0,10,10,10), // simulation domain
+                                                 Vector3<uint_t>(2,1,1), // blocks in each direction
+                                                 Vector3<bool>(false, false, false) // periodicity
+                                                 );
+   domain::BlockForestDomain domain(forest);
+
+   data::ParticleStorage ps(100);
+
+   Vec3 pt(2.5, 2.5, 2.5);
+   if (forest->begin()->getAABB().contains(pt))
+   {
+      auto pIt = ps.create();
+      pIt->setPosition(pt);
+      pIt->setInteractionRadius(real_t(0));
+      pIt->setOwner(walberla::mpi::MPIManager::instance()->rank());
+   }
+
+   if (forest->begin()->getAABB().contains(pt))
+   {
+      WALBERLA_CHECK_EQUAL(ps.size(), 1);
+   } else
+   {
+      WALBERLA_CHECK_EQUAL(ps.size(), 0);
+   }
+
+   for (auto p : ps)
+   {
+      p.setPosition(Vec3(7.5, 2.5, 2.5));
+   }
+
+   mpi::SyncNextNeighborsNoGhosts SNN;
+
+   SNN(ps, domain);
+
+   if (forest->begin()->getAABB().contains(pt))
+   {
+      WALBERLA_CHECK_EQUAL(ps.size(), 0);
+   } else
+   {
+      WALBERLA_CHECK_EQUAL(ps.size(), 1);
+   }
+
+   return 0;
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::mesa_pd::main(argc, argv);
+}