diff --git a/apps/showcases/CMakeLists.txt b/apps/showcases/CMakeLists.txt
index 9220ea4c9e116aa42a7a40b73f0548ed5462d048..5770d193a05532c7285993dba466c5aca7873276 100644
--- a/apps/showcases/CMakeLists.txt
+++ b/apps/showcases/CMakeLists.txt
@@ -1 +1,2 @@
-add_subdirectory( BidisperseFluidizedBed )
\ No newline at end of file
+add_subdirectory( BidisperseFluidizedBed )
+add_subdirectory( Mixer )
diff --git a/apps/showcases/Mixer/CMakeLists.txt b/apps/showcases/Mixer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c355caf515c20ba7afde2493b3b22be15c7e20d7
--- /dev/null
+++ b/apps/showcases/Mixer/CMakeLists.txt
@@ -0,0 +1,5 @@
+waLBerla_link_files_to_builddir( *.cfg )
+
+waLBerla_add_executable( NAME Mixer
+   FILES Mixer.cpp
+   DEPENDS core mesa_pd sqlite vtk )
diff --git a/apps/showcases/Mixer/GenerateModule.py b/apps/showcases/Mixer/GenerateModule.py
new file mode 100755
index 0000000000000000000000000000000000000000..c52f310eb6e9fef4815c6152b88aec28960ca48a
--- /dev/null
+++ b/apps/showcases/Mixer/GenerateModule.py
@@ -0,0 +1,67 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from mesa_pd import Module
+import mesa_pd.data as data
+import mesa_pd.kernel as kernel
+import mesa_pd.mpi as mpi
+
+import argparse
+
+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?')
+    args = parser.parse_args()
+
+    mpd = Module(args.path)
+    ps = mpd.add(data.ParticleStorage())
+
+    ps.set_shapes("Sphere", "HalfSpace", "Box", "CylindricalBoundary")
+
+    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("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("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_include("blockforest/BlockForest.h")
+    ps.add_property("currentBlock",     "blockforest::BlockID",    defValue="",          syncMode="NEVER")
+
+    mpd.add(data.LinkedCells())
+    mpd.add(data.SparseLinkedCells())
+    mpd.add(data.ShapeStorage(ps))
+
+    mpd.add(kernel.DoubleCast(ps))
+    mpd.add(kernel.ExplicitEuler())
+    mpd.add(kernel.ForceLJ())
+    mpd.add(kernel.HeatConduction())
+    mpd.add(kernel.InsertParticleIntoLinkedCells())
+    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.VelocityVerlet())
+
+    mpd.add(mpi.BroadcastProperty())
+    mpd.add(mpi.ClearGhostOwnerSync())
+    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/apps/showcases/Mixer/Mixer.cfg b/apps/showcases/Mixer/Mixer.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4a0ac53146ab4abae61e3ca6e06171a27b1435c7
--- /dev/null
+++ b/apps/showcases/Mixer/Mixer.cfg
@@ -0,0 +1,17 @@
+MIXER
+{
+   simulationCorner < 0, 0, 0 >;
+   simulationDomain < 0.3, 0.3, 0.3 >;
+   blocks < 1,1,1 >;
+   isPeriodic < 0, 0, 0 >;
+
+   radius        0.004;
+   spacing       0.010;
+   vMax          0.1;
+   rotationSpeed -500;
+   gravity < 0.0, 0, -9.81 >;
+
+   dt                 0.000001;
+   simulationSteps    100000;
+   visSpacing         100;
+}
diff --git a/apps/showcases/Mixer/Mixer.cpp b/apps/showcases/Mixer/Mixer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..75016f8bd5dca74e64e898c2145c0739ffe03b0e
--- /dev/null
+++ b/apps/showcases/Mixer/Mixer.cpp
@@ -0,0 +1,516 @@
+//======================================================================================================================
+//
+//  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   Mixer.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+#include <mesa_pd/vtk/ParticleVtkOutput.h>
+
+#include <mesa_pd/collision_detection/BroadPhase.h>
+#include <mesa_pd/collision_detection/AnalyticContactDetection.h>
+
+#include <mesa_pd/data/LinkedCells.h>
+#include <mesa_pd/data/ParticleAccessorWithShape.h>
+#include <mesa_pd/data/ParticleStorage.h>
+
+#include <mesa_pd/data/ShapeStorage.h>
+
+#include <mesa_pd/domain/BlockForestDomain.h>
+
+#include <mesa_pd/kernel/DoubleCast.h>
+#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/InsertParticleIntoLinkedCells.h>
+#include <mesa_pd/kernel/ParticleSelector.h>
+#include <mesa_pd/kernel/SpringDashpot.h>
+
+#include <mesa_pd/mpi/ContactFilter.h>
+#include <mesa_pd/mpi/SyncNextNeighbors.h>
+#include <mesa_pd/mpi/ReduceProperty.h>
+
+#include <mesa_pd/mpi/notifications/ForceTorqueNotification.h>
+
+#include <mesa_pd/sorting/LinearizedCompareFunctor.h>
+
+#include <blockforest/Initialization.h>
+#include <core/Abort.h>
+#include <core/Environment.h>
+#include <core/math/Random.h>
+#include <core/mpi/Reduce.h>
+#include <core/grid_generator/SCIterator.h>
+#include <core/logging/Logging.h>
+#include <core/OpenMP.h>
+#include <core/timing/Timer.h>
+#include <core/timing/TimingPool.h>
+#include <core/waLBerlaBuildInfo.h>
+#include <sqlite/SQLite.h>
+#include <vtk/VTKOutput.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace walberla {
+namespace mesa_pd {
+
+class SelectRank
+{
+public:
+   using return_type = int;
+   int operator()(const data::Particle& /*p*/) const { return rank_; }
+   int operator()(const data::Particle&& /*p*/) const { return rank_; }
+private:
+   int rank_ = walberla::mpi::MPIManager::instance()->rank();
+};
+
+class SelectIdx
+{
+public:
+   using return_type = int;
+   auto operator()(const data::Particle& p) const { return p.getIdx(); }
+   auto operator()(const data::Particle&& p) const { return p.getIdx(); }
+private:
+};
+
+class SelectGhost
+{
+public:
+   using return_type = int;
+   bool operator()(const data::Particle& p) const { return data::particle_flags::isSet(p.getFlags(), data::particle_flags::GHOST); }
+   bool operator()(const data::Particle&& p) const { return data::particle_flags::isSet(p.getFlags(), data::particle_flags::GHOST); }
+};
+
+class SelectRotation
+{
+public:
+   using return_type = Vec3;
+   auto operator()(const data::Particle& p) const { return p.getRotation().getMatrix() * Vec3(1,0,0); }
+   auto operator()(const data::Particle&& p) const { return p.getRotation().getMatrix() * Vec3(1,0,0); }
+};
+
+auto createBoundary( const std::shared_ptr<data::ParticleStorage>& ps,
+                     const std::shared_ptr<data::ShapeStorage>& ss,
+                     const Vec3&  pos,
+                     const real_t radius,
+                     const Vec3&  axis)
+{
+   auto p0              = ps->create(true);
+   p0->setPosition( pos );
+   auto cb = ss->create<data::CylindricalBoundary>( radius, axis );
+   ss->shapes[cb]->updateMassAndInertia(std::numeric_limits<real_t>::infinity());
+   p0->setShapeID( cb );
+   p0->setOwner( walberla::mpi::MPIManager::instance()->rank() );
+   p0->setType( 0 );
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   return p0;
+}
+
+auto createPlane( const std::shared_ptr<data::ParticleStorage>& ps,
+                  const std::shared_ptr<data::ShapeStorage>& ss,
+                  const Vec3& pos,
+                  const Vec3& normal)
+{
+   auto p0              = ps->create(true);
+   p0->setPosition( pos );
+   auto plane = ss->create<data::HalfSpace>( normal );
+   ss->shapes[plane]->updateMassAndInertia(std::numeric_limits<real_t>::infinity());
+   p0->setShapeID( plane );
+   p0->setOwner( walberla::mpi::MPIManager::instance()->rank() );
+   p0->setType( 0 );
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(p0->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   return p0;
+}
+
+int main( int argc, char ** argv )
+{
+   using namespace walberla::timing;
+
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO);
+   logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO);
+
+   WALBERLA_LOG_INFO_ON_ROOT( "config file: " << argv[1] );
+   WALBERLA_LOG_INFO_ON_ROOT( "waLBerla Revision: " << WALBERLA_GIT_SHA1 );
+
+   math::seedRandomGenerator( static_cast<unsigned int>(1337 * walberla::mpi::MPIManager::instance()->worldRank()) );
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** READING CONFIG FILE ***");
+   auto cfg = env.config();
+   if (cfg == nullptr) WALBERLA_ABORT("No config specified!");
+   const Config::BlockHandle mainConf  = cfg->getBlock( "MIXER" );
+
+   const real_t spacing = mainConf.getParameter<real_t>("spacing", real_t(1.0) );
+   WALBERLA_LOG_INFO_ON_ROOT("spacing: " << spacing);
+
+   const real_t radius = mainConf.getParameter<real_t>("radius", real_t(0.5) );
+   WALBERLA_LOG_INFO_ON_ROOT("radius: " << radius);
+
+   const real_t vMax = mainConf.getParameter<real_t>("vMax", real_t(0.5) );
+   WALBERLA_LOG_INFO_ON_ROOT("vMax: " << vMax);
+
+   const real_t rotationSpeed = mainConf.getParameter<real_t>("rotationSpeed", real_t(0.5) );
+   WALBERLA_LOG_INFO_ON_ROOT("rotationSpeed: " << rotationSpeed);
+
+   const Vec3 gravity = mainConf.getParameter<Vec3>("gravity", Vec3(0, 0, real_t(9.81)));
+   WALBERLA_LOG_INFO_ON_ROOT("rotationSpeed: " << rotationSpeed);
+
+   int64_t simulationSteps = mainConf.getParameter<int64_t>("simulationSteps", 1000 );
+   WALBERLA_LOG_INFO_ON_ROOT("simulationSteps: " << simulationSteps);
+
+   real_t dt = mainConf.getParameter<real_t>("dt", real_c(0.01) );
+   WALBERLA_LOG_INFO_ON_ROOT("dt: " << dt);
+
+   real_t cor = mainConf.getParameter<real_t>("cor", real_c(0.95) );
+   WALBERLA_LOG_INFO_ON_ROOT("cor: " << cor);
+
+   real_t ct_in_dt = mainConf.getParameter<real_t>("ct_in_dt", real_c(20) );
+   WALBERLA_LOG_INFO_ON_ROOT("ct_in_dt: " << ct_in_dt);
+
+   real_t density = mainConf.getParameter<real_t>("density", real_c(2707) );
+   WALBERLA_LOG_INFO_ON_ROOT("density: " << density);
+
+   const int visSpacing = mainConf.getParameter<int>("visSpacing",  1000 );
+   WALBERLA_LOG_INFO_ON_ROOT("visSpacing: " << visSpacing);
+   const std::string path = mainConf.getParameter<std::string>("path",  "vtk_out" );
+   WALBERLA_LOG_INFO_ON_ROOT("path: " << path);
+
+   const std::string sqlFile = mainConf.getParameter<std::string>("sqlFile",  "benchmark.sqlite" );
+   WALBERLA_LOG_INFO_ON_ROOT("sqlFile: " << sqlFile);
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** BLOCKFOREST ***");
+   // create forest
+   auto forest = blockforest::createBlockForestFromConfig( mainConf );
+   if (!forest)
+   {
+      WALBERLA_LOG_INFO_ON_ROOT( "No BlockForest created ... exiting!");
+      return EXIT_SUCCESS;
+   }
+   domain::BlockForestDomain domain(forest);
+
+   auto simulationDomain = forest->getDomain();
+   auto localDomain = forest->begin()->getAABB();
+   for (auto& blk : *forest)
+   {
+      localDomain.merge(blk.getAABB());
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - START ***");
+
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(100);
+   auto ss = std::make_shared<data::ShapeStorage>();
+   data::ParticleAccessorWithShape accessor(ps, ss);
+   data::LinkedCells     lc(localDomain.getExtended(spacing), spacing+spacing );
+
+   auto  smallSphere = ss->create<data::Sphere>( radius );
+   ss->shapes[smallSphere]->updateMassAndInertia(density);
+   for (auto& iBlk : *forest)
+   {
+      for (auto pt : grid_generator::SCGrid(iBlk.getAABB(), Vector3<real_t>(spacing, spacing, spacing) * real_c(0.5), spacing))
+      {
+         WALBERLA_CHECK(iBlk.getAABB().contains(pt));
+         auto maxDist = real_t(simulationDomain.xSize()) * real_t(0.5) - spacing;
+         auto dist = (pt - simulationDomain.center());
+         dist[2] = real_t(0);
+         if (dist.sqrLength() > maxDist*maxDist) continue;
+
+         auto p                       = ps->create();
+         p->getPositionRef()          = pt;
+         p->getInteractionRadiusRef() = radius;
+         p->setLinearVelocity( Vec3(math::realRandom(-vMax, vMax),math::realRandom(-vMax, vMax),math::realRandom(-vMax, vMax)) );
+         p->getShapeIDRef()           = smallSphere;
+         p->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+         p->getTypeRef()              = 0;
+      }
+   }
+   int64_t numParticles = int64_c(ps->size());
+   walberla::mpi::reduceInplace(numParticles, walberla::mpi::SUM);
+   WALBERLA_LOG_INFO_ON_ROOT("#particles created: " << numParticles);
+
+   auto boundary = createBoundary(ps,
+                                  ss,
+                                  simulationDomain.center(),
+                                  real_t(simulationDomain.xSize()) * real_t(0.5),
+                                  Vec3(0,0,1));
+   WALBERLA_UNUSED(boundary);
+   //boundary->setAngularVelocity(Vec3(real_t(0), real_t(0), rotationSpeed));
+   createPlane(ps, ss, simulationDomain.minCorner(), Vec3(0,0,1));
+   createPlane(ps, ss, simulationDomain.maxCorner(), Vec3(0,0,-1));
+
+   Rot3 dRot;
+   Vec3 origin = simulationDomain.center();
+   Vec3 dp;
+
+   auto mixingBlade = ss->create<data::Box>( Vec3(0.009,simulationDomain.ySize(),simulationDomain.zSize()) );
+   ss->shapes[mixingBlade]->updateMassAndInertia(std::numeric_limits<real_t>::infinity());
+   auto box0                       = ps->create();
+   box0->getPositionRef()          = Vec3(simulationDomain.xSize() * real_t(0.5),0.0,0.0);
+   box0->setShapeID( mixingBlade );
+   box0->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   box0->getTypeRef()              = 0;
+   data::particle_flags::set(box0->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(box0->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(box0->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(box0->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   box0->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15 );
+//   box0->getRotationRef().rotate(Vec3(0,0,1), -math::pi * 0.15 );
+
+   auto box1                       = ps->create();
+   box1->getPositionRef()          = Vec3(simulationDomain.xSize() * real_t(0.5),0.0,0.0);
+   box1->setShapeID( mixingBlade );
+   box1->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   box1->getTypeRef()              = 0;
+   data::particle_flags::set(box1->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(box1->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(box1->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(box1->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   box1->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15 );
+//   box1->getRotationRef().rotate(Vec3(0,0,1), -math::pi * 0.15 );
+   dp = ( box1->getPosition() - origin );
+   dRot = Rot3(Vec3(real_t(0), real_t(0), math::pi * 0.5));
+   box1->setPosition( origin + dRot.getMatrix() * dp );
+   box1->getRotationRef().rotate(dRot);
+
+   auto box2                       = ps->create();
+   box2->getPositionRef()          = Vec3(simulationDomain.xSize() * real_t(0.5),0.0,0.0);
+   box2->setShapeID( mixingBlade );
+   box2->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   box2->getTypeRef()              = 0;
+   data::particle_flags::set(box2->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(box2->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(box2->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(box2->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   box2->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15 );
+//   box2->getRotationRef().rotate(Vec3(0,0,1), -math::pi * 0.15 );
+   dp = ( box2->getPosition() - origin );
+   dRot = Rot3(Vec3(real_t(0), real_t(0), math::pi));
+   box2->setPosition( origin + dRot.getMatrix() * dp );
+   box2->getRotationRef().rotate(dRot);
+
+   auto box3                       = ps->create();
+   box3->getPositionRef()          = Vec3(simulationDomain.xSize() * real_t(0.5),0.0,0.0);
+   box3->setShapeID( mixingBlade );
+   box3->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   box3->getTypeRef()              = 0;
+   data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::GLOBAL);
+   data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::INFINITE);
+   data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::FIXED);
+   data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
+   box3->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15 );
+//   box3->getRotationRef().rotate(Vec3(0,0,1), -math::pi * 0.15 );
+   dp = ( box3->getPosition() - origin );
+   dRot = Rot3(Vec3(real_t(0), real_t(0), math::pi * 1.5));
+   box3->setPosition( origin + dRot.getMatrix() * dp );
+   box3->getRotationRef().rotate(dRot);
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - END ***");
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** VTK ***");
+   auto vtkDomainOutput = walberla::vtk::createVTKOutput_DomainDecomposition( forest, "domain_decomposition", 1, "vtk", "simulation_step" );
+   auto vtkOutput       = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps) ;
+   auto vtkWriter       = walberla::vtk::createVTKOutput_PointData(vtkOutput, "Bodies", 1, "vtk", "simulation_step", false, false);
+   vtkOutput->addOutput<SelectGhost>("ghost");
+   vtkOutput->addOutput<SelectRank>("rank");
+   vtkOutput->addOutput<SelectIdx>("idx");
+//   vtkOutput->addOutput<SelectRotation>("rot");
+//   auto select_scaling = std::make_shared<vtk::OutputSelector<SelectScaling>>(SelectScaling(ss));
+//   vtkOutput->addOutput("scale", select_scaling);
+   vtkOutput->setParticleSelector([smallSphere](const data::ParticleStorage::iterator& pIt){ return pIt->getShapeID() == smallSphere;});
+   vtkDomainOutput->write();
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - START ***");
+   // Init kernels
+   kernel::ExplicitEuler                 explicitEuler( dt );
+   kernel::InsertParticleIntoLinkedCells ipilc;
+   kernel::SpringDashpot                 dem(1);
+   const auto ct  = dt * ct_in_dt;
+   const auto stiffness = (math::pi*math::pi + std::log(cor)*std::log(cor)) / (ct*ct);
+   const auto damping   = (real_t(2) * std::log(cor)) / (ct);
+   dem.setStiffness(0, 0, real_t(8.11e6));
+   dem.setDampingN (0, 0, real_t(6.86e1));
+   dem.setDampingT (0, 0, real_t(6.86e1));
+   dem.setFriction (0, 0, real_t(0.4));
+
+   collision_detection::AnalyticContactDetection acd;
+   kernel::DoubleCast                 double_cast;
+   mpi::ContactFilter                 contact_filter;
+   mpi::ReduceProperty                RP;
+   mpi::SyncNextNeighbors             SNN;
+
+   // initial sync
+   SNN(*ps, domain);
+
+   std::ofstream fout;
+   WALBERLA_ROOT_SECTION()
+   {
+      fout.open("timings.txt");
+   }
+   dRot = Rot3(Vec3(real_t(0), real_t(0), rotationSpeed) * dt);
+   for (int64_t i=0; i < simulationSteps; ++i)
+   {
+      if (i % visSpacing == 0)
+      {
+//         vtkWriter->write();
+         WALBERLA_ROOT_SECTION()
+         {
+            fout << std::setprecision( 20 ) << timing::WcPolicy::getTimestamp() << "\n";
+         }
+      }
+
+      if (i == 80000)
+      {
+         WcTimer timer;
+         sorting::LinearizedCompareFunctor linear(lc.domain_, lc.numCellsPerDim_);
+         ps->sort(linear);
+         timer.end();
+         WALBERLA_LOG_DEVEL_ON_ROOT("time needed for sorting: " << timer.total());
+      }
+
+      ps->forEachParticle(false,
+                          kernel::SelectLocal(),
+                          accessor,
+                          [&](const size_t idx, auto& ac){ac.setForce(idx, gravity );},
+      accessor);
+
+      lc.clear();
+      ps->forEachParticle(true,
+                          kernel::SelectAll(),
+                          accessor,
+                          ipilc,
+                          accessor,
+                          lc);
+
+      lc.forEachParticlePairHalf(true,
+                                 kernel::SelectAll(),
+                                 accessor,
+                                 [&](const size_t idx1, const size_t idx2, auto& ac)
+      {
+         if ((ac.getShapeID(idx1) != smallSphere) && (ac.getShapeID(idx2) != smallSphere))
+         {
+
+         } else
+         {
+            if (collision_detection::isInInteractionDistance(idx1, idx2, ac))
+            {
+               if (double_cast(idx1, idx2, ac, acd, ac ))
+               {
+                  if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), domain))
+                  {
+                     auto meff = real_t(1) / (ac.getInvMass(idx1) + ac.getInvMass(idx2));
+                     dem.setStiffness(0, 0, stiffness * meff);
+                     dem.setDampingN(0, 0, damping * meff);
+                     dem(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(), acd.getPenetrationDepth());
+                  }
+               }
+            }
+         }
+      },
+      accessor );
+
+      RP.operator()<ForceTorqueNotification>(*ps);
+
+      ps->forEachParticle(true,
+                          kernel::SelectLocal(),
+                          accessor,
+                          explicitEuler,
+                          accessor);
+
+      ps->forEachParticle(true,
+                          kernel::SelectAll(),
+                          accessor,
+                          [&](const size_t idx1, auto& ac)
+      {
+         if (ac.getShapeID(idx1) == mixingBlade)
+         {
+            //rotate
+            dp = ( ac.getPosition(idx1) - origin );
+            ac.setPosition(idx1, origin + dRot.getMatrix() * dp );
+            ac.getRotationRef(idx1).rotate(dRot);
+         }
+      },
+      accessor );
+
+      //SNN(*ps, domain);
+
+      if( i % 1000 == 0 )
+      {
+         WALBERLA_LOG_DEVEL_ON_ROOT( "Timestep " << i << " / " << simulationSteps );
+      }
+   }
+   fout.close();
+   WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - END ***");
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SQL OUTPUT - START ***");
+   numParticles = 0;
+   int64_t numGhostParticles = 0;
+   ps->forEachParticle(false,
+                       kernel::SelectAll(),
+                       accessor,
+                       [&numParticles, &numGhostParticles](const size_t idx, auto& ac)
+   {
+      if (data::particle_flags::isSet( ac.getFlagsRef(idx), data::particle_flags::GHOST))
+      {
+         ++numGhostParticles;
+      } else
+      {
+         ++numParticles;
+      }
+   },
+   accessor);
+   walberla::mpi::reduceInplace(numParticles, walberla::mpi::SUM);
+   walberla::mpi::reduceInplace(numGhostParticles, walberla::mpi::SUM);
+   WALBERLA_ROOT_SECTION()
+   {
+      std::map< std::string, walberla::int64_t > integerProperties;
+      std::map< std::string, double >            realProperties;
+      std::map< std::string, std::string >       stringProperties;
+
+      stringProperties["walberla_git"]         = WALBERLA_GIT_SHA1;
+      stringProperties["tag"]                  = "mesa_pd";
+      integerProperties["mpi_num_processes"]   = walberla::mpi::MPIManager::instance()->numProcesses();
+      integerProperties["omp_max_threads"]     = omp_get_max_threads();
+      integerProperties["simulationSteps"]     = simulationSteps;
+      integerProperties["num_particles"]       = numParticles;
+      integerProperties["num_ghost_particles"] = numGhostParticles;
+      integerProperties["blocks_x"]            = int64_c(forest->getXSize());
+      integerProperties["blocks_y"]            = int64_c(forest->getYSize());
+      integerProperties["blocks_z"]            = int64_c(forest->getZSize());
+      realProperties["domain_x"]               = double_c(forest->getDomain().xSize());
+      realProperties["domain_y"]               = double_c(forest->getDomain().ySize());
+      realProperties["domain_z"]               = double_c(forest->getDomain().zSize());
+
+
+      sqlite::storeRunInSqliteDB( sqlFile, integerProperties, stringProperties, realProperties );
+   }
+   WALBERLA_LOG_INFO_ON_ROOT("*** SQL OUTPUT - END ***");
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace mesa_pd
+} // namespace walberla
+
+int main( int argc, char* argv[] )
+{
+   return walberla::mesa_pd::main( argc, argv );
+}