diff --git a/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp b/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1d65e398765707869220e565cd283dde393a8dc5
--- /dev/null
+++ b/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp
@@ -0,0 +1,1622 @@
+//======================================================================================================================
+//
+//  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 BiDisperseFluidizedBedDPM.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "core/all.h"
+#include "boundary/all.h"
+#include "lbm/all.h"
+#include "pe_coupling/all.h"
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/all.h"
+
+#include "core/grid_generator/SCIterator.h"
+#include "core/logging/all.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/PackInfo.h"
+#include "field/distributors/all.h"
+#include "field/interpolators/all.h"
+
+#include "pe/basic.h"
+#include "pe/vtk/SphereVtkOutput.h"
+#include "pe/Types.h"
+#include "pe/utility/DestroyBody.h"
+
+#include "stencil/D3Q27.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/VTKOutput.h"
+
+#include <vector>
+#include <iomanip>
+#include <iostream>
+#include <random>
+
+namespace bidisperse_fluidized_bed_dpm
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+
+///////////////
+// CONSTANTS //
+///////////////
+
+const uint_t FieldGhostLayers( 1 );
+
+//////////////
+// TYPEDEFS //
+//////////////
+
+//#define OutletBC
+
+// PDF field, flag field & body field
+typedef GhostLayerField< Matrix3<real_t>, 1 >                          TensorField_T;
+typedef GhostLayerField< Vector3<real_t>, 1 >                          Vec3Field_T;
+typedef GhostLayerField< real_t, 1 >                                   ScalarField_T;
+typedef lbm::force_model::GuoField< Vec3Field_T >                      ForceModel_T;
+
+typedef lbm::D3Q19< lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T >   LatticeModel_T;
+typedef LatticeModel_T::Stencil                                        Stencil_T;
+typedef lbm::PdfField< LatticeModel_T >                                PdfField_T;
+
+typedef walberla::uint8_t                                              flag_t;
+typedef FlagField< flag_t >                                            FlagField_T;
+
+// boundary handling
+typedef lbm::NoSlip< LatticeModel_T, flag_t >                          NoSlip_T;
+typedef lbm::SimpleUBB< LatticeModel_T, flag_t >                       Inflow_T;
+
+#ifdef OutletBC
+typedef lbm::Outlet< LatticeModel_T, FlagField_T >                     Outflow_T;
+#else
+typedef lbm::SimplePressure< LatticeModel_T, flag_t >                  Outflow_T;
+#endif
+
+typedef boost::tuples::tuple< NoSlip_T, Inflow_T, Outflow_T >          BoundaryConditions_T;
+typedef BoundaryHandling<FlagField_T, Stencil_T, BoundaryConditions_T> BoundaryHandling_T;
+
+typedef boost::tuple<pe::Plane, pe::Sphere> BodyTypeTuple ;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag   ( "fluid" );
+const FlagUID NoSlip_Flag  ( "no slip flag" );
+const FlagUID Inflow_Flag  ( "inflow flag" );
+const FlagUID Outflow_Flag ( "outflow flag" );
+
+// coupling methods
+enum DPMethod { GNS };
+
+// interpolation (for PDFs, velocity and/or solid volume fraction)
+enum Interpolation { INearestNeighbor, IKernel, ITrilinear };
+
+// force distribution
+enum Distribution { DNearestNeighbor, DKernel };
+
+//drag correlation
+enum DragCorrelation { ErgunWenYu, Tang, Stokes, Felice, Tenneti, NoDrag };
+
+//lift correlation
+enum LiftCorrelation { NoLift, Saffman };
+
+//added mass correlation
+enum AddedMassCorrelation { NoAM, Finn };
+
+//effective viscosity
+enum EffectiveViscosity { None, Rescaled, Eilers };
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+class MyBoundaryHandling
+{
+public:
+
+   MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID, real_t uInflow ) :
+      flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ), uInflow_( uInflow ) {}
+
+   BoundaryHandling_T * operator()( IBlock* const block, const StructuredBlockStorage* const storage ) const;
+
+private:
+
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+   real_t uInflow_;
+
+}; // class MyBoundaryHandling
+
+
+BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block, const StructuredBlockStorage * const storage ) const
+{
+   WALBERLA_ASSERT_NOT_NULLPTR( block );
+
+   FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
+   PdfField_T *  pdfField  = block->getData< PdfField_T > ( pdfFieldID_ );
+
+   const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+#ifdef OutletBC
+   BoundaryHandling_T * handling = new BoundaryHandling_T( "Boundary handling", flagField, fluid,
+         boost::tuples::make_tuple( NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ),
+                                    Inflow_T( "Inflow", Inflow_Flag, pdfField, Vector3<real_t>(real_t(0),real_t(0),uInflow_) ),
+                                    Outflow_T( "Outflow", Outflow_Flag, pdfField, flagField, fluid ) ) );
+#else
+   BoundaryHandling_T * handling = new BoundaryHandling_T( "Boundary handling", flagField, fluid,
+         boost::tuples::make_tuple( NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ),
+                                    Inflow_T( "Inflow", Inflow_Flag, pdfField, Vector3<real_t>(real_t(0),real_t(0),uInflow_) ),
+                                    Outflow_T( "Outflow", Outflow_Flag, pdfField, real_t(1) ) ) );
+#endif
+
+   const auto noslip  = flagField->getFlag( NoSlip_Flag );
+   const auto inflow  = flagField->getFlag( Inflow_Flag );
+   const auto outflow = flagField->getFlag( Outflow_Flag );
+
+   CellInterval domainBB = storage->getDomainCellBB();
+
+   domainBB.zMin() -= cell_idx_c( FieldGhostLayers ); // 0
+   domainBB.zMax() += cell_idx_c( FieldGhostLayers ); // cellsZ+1
+
+   // BOTTOM
+   CellInterval bottom( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMin() );
+   storage->transformGlobalToBlockLocalCellInterval( bottom, *block );
+   handling->forceBoundary( inflow, bottom );
+
+   // TOP
+   CellInterval top( domainBB.xMin(), domainBB.yMin(), domainBB.zMax(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( top, *block );
+   handling->forceBoundary( outflow, top );
+
+   domainBB.xMin() -= cell_idx_c( FieldGhostLayers ); // -1
+   domainBB.xMax() += cell_idx_c( FieldGhostLayers ); // cellsX
+
+   // LEFT
+   CellInterval left( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMin(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( left, *block );
+   handling->forceBoundary( noslip, left );
+
+   // RIGHT
+   CellInterval right( domainBB.xMax(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( right, *block );
+   handling->forceBoundary( noslip, right );
+
+   domainBB.yMin() -= cell_idx_c( FieldGhostLayers ); // 0
+   domainBB.yMax() += cell_idx_c( FieldGhostLayers ); // cellsY+1
+
+   // FRONT
+   CellInterval front( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMin(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( front, *block );
+   handling->forceBoundary( noslip, front );
+
+   // BACK
+   CellInterval back( domainBB.xMin(), domainBB.yMax(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( back, *block );
+   handling->forceBoundary( noslip, back );
+
+
+
+   handling->fillWithDomain( FieldGhostLayers );
+
+   return handling;
+}
+//*******************************************************************************************************************
+
+
+// VTK Output
+shared_ptr<vtk::VTKOutput> createFluidFieldVTKWriter( shared_ptr< StructuredBlockForest > & blocks,
+                                                      const BlockDataID & pdfFieldID, const BlockDataID & flagFieldID, uint_t writeFrequency,
+                                                      const std::string & vtkBaseFolder )
+{
+    auto pdfFieldVTKWriter = vtk::createVTKOutput_BlockData( blocks, "fluid_field", writeFrequency, uint_t(1), false, vtkBaseFolder );
+
+    blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfGhostLayerSync( blocks );
+    pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+    pdfFieldVTKWriter->addBeforeFunction( pdfGhostLayerSync );
+
+    field::FlagFieldCellFilter< FlagField_T > fluidFilter( flagFieldID );
+    fluidFilter.addFlag( Fluid_Flag );
+    pdfFieldVTKWriter->addCellInclusionFilter( fluidFilter );
+
+    auto velocityWriter = make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "Velocity (Lattice)" );
+    auto  densityWriter = make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "Density (Lattice)" );
+    pdfFieldVTKWriter->addCellDataWriter( velocityWriter );
+    pdfFieldVTKWriter->addCellDataWriter( densityWriter );
+
+    return pdfFieldVTKWriter;
+}
+
+DPMethod to_DPMethod( const std::string& s )
+{
+   if( s == "GNS"  ) return DPMethod::GNS;
+   throw std::runtime_error("invalid conversion from " + s + " to DPMethod");
+}
+
+Interpolation to_interpolation( const std::string& s )
+{
+   if( s == "nearest" ) return Interpolation::INearestNeighbor;
+   if( s == "kernel"  ) return Interpolation::IKernel;
+   if( s == "trilinear"  ) return Interpolation::ITrilinear;
+   throw std::runtime_error("invalid conversion from " + s + " to Interpolation");
+}
+
+Distribution to_distribution( const std::string& s )
+{
+   if( s == "nearest" ) return Distribution::DNearestNeighbor;
+   if( s == "kernel"  ) return Distribution::DKernel;
+   throw std::runtime_error("invalid conversion from " + s + " to Distribution");
+}
+
+DragCorrelation to_dragCorrelation( const std::string& s )
+{
+   if( s == "ergun" ) return DragCorrelation::ErgunWenYu;
+   if( s == "tang" ) return DragCorrelation::Tang;
+   if( s == "stokes" ) return DragCorrelation::Stokes;
+   if( s == "felice" ) return DragCorrelation::Felice;
+   if( s == "tenneti" ) return DragCorrelation::Tenneti;
+   if( s == "none" ) return DragCorrelation::NoDrag;
+   throw std::runtime_error("invalid conversion from " + s + " to DragCorrelation");
+}
+
+LiftCorrelation to_liftCorrelation( const std::string& s )
+{
+   if( s == "none" ) return LiftCorrelation::NoLift;
+   if( s == "saffman" ) return LiftCorrelation::Saffman;
+   throw std::runtime_error("invalid conversion from " + s + " to LiftCorrelation");
+}
+
+AddedMassCorrelation to_addedMassCorrelation( const std::string& s )
+{
+   if( s == "none" ) return AddedMassCorrelation::NoAM;
+   if( s == "finn" ) return AddedMassCorrelation::Finn;
+   throw std::runtime_error("invalid conversion from " + s + " to AddedMassCorrelation");
+}
+
+EffectiveViscosity to_effvisc( const std::string& s )
+{
+   if( s == "none" ) return EffectiveViscosity::None;
+   if( s == "rescaled"  ) return EffectiveViscosity::Rescaled;
+   if( s == "eilers"  ) return EffectiveViscosity::Eilers;
+   throw std::runtime_error("invalid conversion from " + s + " to effective viscosity");
+}
+
+std::string dpm_to_string( const DPMethod& dpm )
+{
+   if( dpm == DPMethod::GNS ) return "GNS";
+   throw std::runtime_error("invalid conversion from DPMethod to string");
+}
+
+uint_t createSpheresRandomly( StructuredBlockForest & forest, pe::BodyStorage & globalBodyStorage,
+                              const BlockDataID & bodyStorageID, const AABB & generationDomain,
+                              real_t diameter1, real_t diameter2, real_t diameterAvg,
+                              real_t solidVolumeFraction, pe::MaterialID & material )
+{
+   real_t domainVolume = generationDomain.volume();
+   real_t totalSphereVolume = domainVolume * solidVolumeFraction;
+
+   real_t percentageOfSpecies1 = real_t(1);
+   if( diameter1 < diameter2 || diameter1 > diameter2 )
+   {
+      real_t effectiveMass1 = diameter1 * diameter1 * diameter1;
+      real_t effectiveMass2 = diameter2 * diameter2 * diameter2;
+      real_t effectiveMassAvg = diameterAvg * diameterAvg * diameterAvg;
+
+      percentageOfSpecies1 = (effectiveMassAvg - effectiveMass2) / (effectiveMass1 - effectiveMass2);
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("Creating "<< percentageOfSpecies1 * real_t(100) << "% of sphere type 1 with diameter = " << diameter1 <<
+                             " and " << (real_t(1) - percentageOfSpecies1) * real_t(100) << "% of sphere type 2 with diameter = " << diameter2);
+
+   real_t xParticle = real_t(0);
+   real_t yParticle = real_t(0);
+   real_t zParticle = real_t(0);
+   real_t creationDiameter = real_t(0);
+
+   real_t currentSphereVolume = real_t(0);
+   uint_t numberOfSpheres = uint_t(0);
+
+   while( currentSphereVolume < totalSphereVolume )
+   {
+
+      WALBERLA_ROOT_SECTION()
+      {
+         xParticle = math::realRandom<real_t>(generationDomain.xMin(), generationDomain.xMax());
+         yParticle = math::realRandom<real_t>(generationDomain.yMin(), generationDomain.yMax());
+         zParticle = math::realRandom<real_t>(generationDomain.zMin(), generationDomain.zMax());
+
+         real_t speciesDecider = math::realRandom<real_t>(real_t(0), real_t(1));
+         // if decider is in [0,...,percentageOfSpecies1], then species 1 is created, else species 2
+         if( percentageOfSpecies1 > speciesDecider )
+         {
+            creationDiameter = diameter1;
+         }
+         else
+         {
+            creationDiameter = diameter2;
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::broadcastObject( xParticle );
+         mpi::broadcastObject( yParticle );
+         mpi::broadcastObject( zParticle );
+         mpi::broadcastObject( creationDiameter );
+      }
+
+      pe::createSphere( globalBodyStorage, forest.getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( xParticle, yParticle, zParticle ), creationDiameter * real_t(0.5), material );
+
+      currentSphereVolume += math::M_PI / real_t(6) * creationDiameter * creationDiameter * creationDiameter;
+
+      ++numberOfSpheres;
+   }
+
+   return numberOfSpheres;
+}
+
+class ForceOnBodiesAdder
+{
+public:
+
+   ForceOnBodiesAdder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID,
+                       const Vector3<real_t> & forcePerVolume )
+         : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), forcePerVolume_( forcePerVolume )
+   { }
+
+   // set a force on all (only local, to avoid force duplication) bodies
+   void operator()()
+   {
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+         {
+            real_t volume = bodyIt->getVolume();
+            bodyIt->addForce ( forcePerVolume_ * volume );
+         }
+      }
+   }
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   Vector3<real_t> forcePerVolume_;
+};
+
+class BodiesQuantityEvaluator
+{
+public:
+
+   BodiesQuantityEvaluator( SweepTimeloop* timeloop, const shared_ptr<StructuredBlockStorage> & blockStorage,
+                            const BlockDataID & bodyStorageID, const std::string & fileName, uint_t numSpheres, real_t totalMass ) :
+         timeloop_( timeloop ), blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), fileName_( fileName ),
+         numSpheres_( numSpheres ), totalMass_( totalMass )
+   {
+      std::ofstream file;
+      file.open( fileName_.c_str() );
+      file << "#t \tfX \tfY \tfZ \tvelX \tvelY \tvelZ \tCoMX \tCoMY \tCoMZ\n";
+      file.close();
+   }
+
+   void operator()()
+   {
+      // get mean particle velocity
+      Vector3<real_t> particleVelocity = getMeanParticleVelocity();
+      // get force on particles
+      Vector3<real_t> particleForce = getParticleForce();
+      // get center of mass (average position weighted by mass proportion)
+      Vector3<real_t> centerOfMass = getCenterOfMass();
+
+      WALBERLA_ROOT_SECTION()
+      {
+         writeToFile( particleForce,  particleVelocity, centerOfMass );
+      }
+   }
+
+private:
+
+   Vector3<real_t> getMeanParticleVelocity()
+   {
+      Vector3<real_t> velocity(0.);
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            auto bodyVelocity = bodyIt->getLinearVel();
+            velocity[0] += std::fabs(bodyVelocity[0]);
+            velocity[1] += std::fabs(bodyVelocity[1]);
+            velocity[2] += std::fabs(bodyVelocity[2]);
+         }
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( velocity[0], mpi::SUM );
+         mpi::allReduceInplace( velocity[1], mpi::SUM );
+         mpi::allReduceInplace( velocity[2], mpi::SUM );
+      }
+      return velocity / real_c(numSpheres_);
+   }
+
+   Vector3<real_t> getParticleForce()
+   {
+      Vector3<real_t> force(0.);
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            force += bodyIt->getForce();
+         }
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+      }
+      return force;
+   }
+
+   Vector3<real_t> getCenterOfMass()
+   {
+      Vector3<real_t> centerOfMass(0.);
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            auto bodyMass = bodyIt->getMass();
+            auto bodyPosition = bodyIt->getPosition();
+            centerOfMass += ( bodyMass / totalMass_ ) * bodyPosition;
+         }
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( centerOfMass[0], mpi::SUM );
+         mpi::allReduceInplace( centerOfMass[1], mpi::SUM );
+         mpi::allReduceInplace( centerOfMass[2], mpi::SUM );
+      }
+      return centerOfMass;
+   }
+
+   void writeToFile( const Vector3<real_t> & particleForce, const Vector3<real_t> & meanParticleVel, const Vector3<real_t> & centerOfMass )
+   {
+      std::ofstream file;
+      file.open( fileName_.c_str(), std::ofstream::app );
+      file.precision(8);
+
+      file << timeloop_->getCurrentTimeStep() << "\t "
+           << particleForce[0] << "\t " << particleForce[1] << "\t " << particleForce[2] << "\t "
+           << meanParticleVel[0] << "\t " << meanParticleVel[1] << "\t " << meanParticleVel[2] << "\t"
+           << centerOfMass[0] << "\t " << centerOfMass[1] << "\t " << centerOfMass[2] << "\n";
+      file.close();
+   }
+
+   SweepTimeloop* timeloop_;
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   std::string fileName_;
+   const uint_t numSpheres_;
+   const real_t totalMass_;
+};
+
+class CollisionPropertiesEvaluator
+{
+public:
+   CollisionPropertiesEvaluator( pe::cr::ICR & collisionResponse ) : collisionResponse_( collisionResponse ), maximumPenetration_(real_t(0))
+   {}
+
+   void operator()()
+   {
+      real_t maxPen = collisionResponse_.getMaximumPenetration();
+      maximumPenetration_ = std::max( maximumPenetration_, maxPen );
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( maximumPenetration_, mpi::MAX );
+      }
+   }
+   real_t getMaximumPenetrationInSimulation()
+   {
+      return maximumPenetration_;
+   }
+   void resetMaximumPenetration()
+   {
+      maximumPenetration_ = real_t(0);
+   }
+private:
+   pe::cr::ICR & collisionResponse_;
+   real_t maximumPenetration_;
+};
+
+class SphereCounter
+{
+public:
+   SphereCounter( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID, uint_t numSpheres)
+         : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), numSpheres_( numSpheres )
+   {}
+   void operator()()
+   {
+      uint_t curNumSpheres = getNumSpheres();
+      if( curNumSpheres != numSpheres_)
+      {
+         WALBERLA_LOG_INFO_ON_ROOT("Warning: Number of spheres has changed: " << numSpheres_ << " -> " << curNumSpheres );
+         numSpheres_ = curNumSpheres;
+      }
+   }
+
+   void updateNumSpheres()
+   {
+      numSpheres_ = getNumSpheres();
+   }
+
+   uint_t getNumSpheres()
+   {
+      uint_t curNumSpheres = uint_t(0);
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+         {
+            ++curNumSpheres;
+         }
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( curNumSpheres, mpi::SUM );
+      }
+      return curNumSpheres;
+   }
+
+private:
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   uint_t numSpheres_;
+};
+
+void setBodyVelocities(const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID, Vector3<real_t> velocity )
+{
+   for( auto blockIt = blockStorage->begin(); blockIt != blockStorage->end(); ++blockIt )
+   {
+      for( auto bodyIt = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+      {
+         bodyIt->setLinearVel(velocity);
+      }
+   }
+
+}
+
+class DummySweep
+{
+public:
+   DummySweep( )
+   {}
+
+   void operator()(IBlock * const /*block*/)
+   {}
+};
+
+void emptyFunction(){}
+
+//*******************************************************************************************************************
+/*!\brief Simulation of a bidisperse fluidized bed with the discrete particle method.
+ *
+ * The simulation features a fluidized bed with spherical particles inside a rectangular column.
+ * The domain size is [32 x 16 x 256] * d_avg
+ * Since a bidisperse setup is used, i.e. the spheres have either diameter d1 or d2, one can specify d1, d2, and d_avg.
+ * The volume-average diameter d_avg determines the fractional distribution of spheres with d1 and d2.
+ * A monodisperse setting is obtained if d1 = d2 = d_avg.
+ *
+ * The fluidization is driven by a constant and uniform inflow from the bottom plane in positive z-direction.
+ * A plane, not visible to the fluid, is placed above this inlet to prevent particles from directly interfering
+ * with the inlet.
+ * The Reynolds number and the Galileo number characterize the setup.
+ *
+ * Command line customization allows to switch on/off different parts of the DPS (discrete particle simulation)
+ * algorithm.
+ *
+ * In a first step, the spherical particles are created with either d1 or d2 at random positions in the lower part of
+ * the domain. Possible overlaps are resolved, artificially bounded by a horizontal plane at the top of the generation
+ * domain, by carrying out PE-only steps.
+ *
+ * Afterwards, an initial, fluid-only, simulation is possible to equilibrate the fluid- and solid phase.
+ * Since this is not needed for the default case, it is not used by default (numberOfInitialTimeSteps=0).
+ *
+ * Finally, the fully coupled simulation is carried out with the option to write vtk files.
+ *
+ * The algorithm, as well as the setup and the outcome are described in detail in
+ * Rettinger, Ruede - "A Coupled Lattice Boltzmann Method and Discrete Element Method for Discrete Particle Simulations
+ * of Particulate Flows" to be published
+ *
+ */
+//*******************************************************************************************************************
+int main( int argc, char **argv ) {
+   debug::enterTestMode();
+
+   mpi::Environment env(argc, argv);
+
+
+   /////////////////////////////////////////////
+   //                                         //
+   //   Command Line Argument Customization   //
+   //                                         //
+   /////////////////////////////////////////////
+
+   bool funcTest = false;
+   bool vtkIO = false;
+   uint_t vtkWriteFrequency = uint_t(0);
+   uint_t numberOfTimeSteps = uint_t(50000);
+   uint_t numberOfInitialTimeSteps = uint_t(0);
+   std::string vtkBaseFolder = "vtk_out_BiDisperseFluidizedBedDPM";
+
+   real_t densityRatio = real_t(2500) / real_t(1000);
+   real_t ReynoldsNumber = real_t(10); // = rho_f * diameterAvg * uInflow / visc_f
+   real_t GalileoNumber = real_t(28); // = sqrt((densityRatio - 1) * g * diameterAvg ) * diameterAvg / visc_f
+
+   real_t diameter1 = real_t(0.7); // diameter of species 1
+   real_t diameter2 = real_t(0.8); // diameter of species 2
+   real_t diameterAvg = real_t(0.75); // (mass) average diameter of all particles
+
+   uint_t interactionSubCycles = uint_t(2); // number of subcycles that involve evaluation of the interaction force
+   uint_t peSubSteps = uint_t(10); // number of pe only calls in each subcycle
+   real_t solidVolumeFraction = real_t(0.2); // in the generation domain (fraction of the whole domain)
+
+   DPMethod dpm = DPMethod::GNS;
+   Interpolation interpol = Interpolation::IKernel;
+   Distribution dist = Distribution::DKernel;
+   DragCorrelation dragCorr = DragCorrelation::Tenneti;
+   LiftCorrelation liftCorr = LiftCorrelation::Saffman;
+   AddedMassCorrelation addedMassCorr = AddedMassCorrelation::Finn;
+   EffectiveViscosity effVisc = EffectiveViscosity::Eilers;
+
+   bool useTurbulenceModel = true;
+   const real_t smagorinskyConstant = real_t(0.1); //for turbulence model
+
+   real_t lubricationCutOffDistance = diameterAvg; //0 switches it off
+
+   for (int i = 1; i < argc; ++i) {
+      if (std::strcmp(argv[i], "--funcTest") == 0) funcTest = true;
+      else if (std::strcmp(argv[i], "--vtkIOFreq") == 0) vtkWriteFrequency = uint_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--numberOfTimeSteps") == 0) numberOfTimeSteps = uint_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--numberOfInitialTimeSteps") == 0) numberOfInitialTimeSteps = uint_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--densityRatio") == 0) densityRatio = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--Ga") == 0) GalileoNumber = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--Re") == 0) ReynoldsNumber = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--diameter1") == 0) diameter1 = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--diameter2") == 0) diameter2 = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--diameterAvg") == 0) diameterAvg = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--solidVolumeFraction") == 0) solidVolumeFraction = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--interactionSubCycles") == 0) interactionSubCycles = uint_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--peSubSteps") == 0) peSubSteps = uint_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--DPMethod") == 0) dpm = to_DPMethod(argv[++i]);
+      else if (std::strcmp(argv[i], "--interpolation") == 0) interpol = to_interpolation(argv[++i]);
+      else if (std::strcmp(argv[i], "--distribution") == 0) dist = to_distribution(argv[++i]);
+      else if (std::strcmp(argv[i], "--dragCorrelation") == 0) dragCorr = to_dragCorrelation(argv[++i]);
+      else if (std::strcmp(argv[i], "--liftCorrelation") == 0) liftCorr = to_liftCorrelation(argv[++i]);
+      else if (std::strcmp(argv[i], "--addedMassCorrelation") == 0) addedMassCorr = to_addedMassCorrelation(argv[++i]);
+      else if (std::strcmp(argv[i], "--effectiveViscosity") == 0) effVisc = to_effvisc(argv[++i]);
+      else if (std::strcmp(argv[i], "--disableTurbulenceModel") == 0) useTurbulenceModel = false;
+      else if (std::strcmp(argv[i], "--lubricationCutOff") == 0) lubricationCutOffDistance = real_c(std::atof(argv[++i]));
+      else if (std::strcmp(argv[i], "--vtkBaseFolder") == 0) vtkBaseFolder = argv[++i];
+      else WALBERLA_ABORT("Found invalid command line argument \"" << argv[i] << "\" - aborting...");
+   }
+
+   if (vtkWriteFrequency > 0) vtkIO = true;
+
+   WALBERLA_CHECK(diameter1 <= real_t(1), "Diameter is not allowed to be > 1!");
+   WALBERLA_CHECK(diameter2 <= real_t(1), "Diameter is not allowed to be > 1!");
+   WALBERLA_CHECK((diameter1 <= diameterAvg && diameterAvg <= diameter2) ||
+                  (diameter2 <= diameterAvg && diameterAvg <= diameter1),
+                  "Average diameter has to be between diameter 1 and 2!");
+   WALBERLA_CHECK(solidVolumeFraction > real_t(0), "Solid volume fraction has to be > 0!");
+   WALBERLA_CHECK(solidVolumeFraction <= real_t(0.65), "Solid volume fraction is not allowed to be > 0.65!");
+   WALBERLA_CHECK(interactionSubCycles > uint_t(0), "Number of interaction sub cycles has to be at least 1!");
+   WALBERLA_CHECK(peSubSteps > uint_t(0), "Number of pe sub steps has to be at least 1!");
+   WALBERLA_CHECK(lubricationCutOffDistance >= real_t(0), "Lubrication cut off distance has to be non-negative!");
+
+   if (funcTest) {
+      walberla::logging::Logging::instance()->setLogLevel(logging::Logging::LogLevel::WARNING);
+   }
+   if( !funcTest )
+   {
+      // create base directory if it does not yet exist
+      boost::filesystem::path tpath( vtkBaseFolder );
+      if( !boost::filesystem::exists( tpath ) )
+         boost::filesystem::create_directory( tpath );
+   }
+
+
+   ///////////////////////////////
+   //                           //
+   //   SIMULATION PROPERTIES   //
+   //                           //
+   ///////////////////////////////
+
+   const uint_t xlength = uint_c(real_t(32) * diameterAvg);
+   const uint_t ylength = uint_c(real_t(16) * diameterAvg);
+   const uint_t zlength = uint_c(real_t(256) * diameterAvg);
+
+   const real_t uInflow = real_t(0.01);
+   const real_t viscosity = diameterAvg * uInflow / ReynoldsNumber;
+
+   const real_t ug = GalileoNumber * viscosity / diameterAvg;
+   const real_t gravitationalAcc = ug * ug / ((densityRatio - real_t(1)) * diameterAvg);
+
+   const real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
+   const real_t tau = real_t(1) / omega;
+
+   const real_t dx = real_t(1);
+
+   const real_t dt_DEM = real_t(1) / real_c(interactionSubCycles * peSubSteps);
+
+   const real_t dt = real_t(1);
+   const real_t dtInteractionSubCycle = dt / real_c(interactionSubCycles);
+   const real_t dtBodyVelocityTimeDerivativeEvaluation = dtInteractionSubCycle;
+
+
+   const uint_t timesteps = (funcTest) ? uint_t(3)
+                                       : numberOfTimeSteps; // total number of time steps for the whole simulation
+   const uint_t initialTimesteps = (funcTest) ? uint_t(0)
+                                              : numberOfInitialTimeSteps; // total number of time steps for the initial simulation
+
+   const Vector3<real_t> initialFluidVelocity(real_t(0), real_t(0), uInflow);
+
+
+   if (!funcTest) {
+      WALBERLA_LOG_INFO_ON_ROOT("Lx x Ly x Lz = " << xlength << " x " << ylength << " x " << zlength);
+      WALBERLA_LOG_INFO_ON_ROOT("diameter1 = " << diameter1 << ", diameter2 = " << diameter2 << ", diameterAvg = " << diameterAvg);
+      WALBERLA_LOG_INFO_ON_ROOT("Re = " << ReynoldsNumber << ", Ga = " << GalileoNumber);
+      WALBERLA_LOG_INFO_ON_ROOT("tau = " << tau << ", omega = " << omega);
+      WALBERLA_LOG_INFO_ON_ROOT("viscosity = " << viscosity);
+      WALBERLA_LOG_INFO_ON_ROOT("gravity = " << gravitationalAcc);
+      WALBERLA_LOG_INFO_ON_ROOT("input solid volume fraction = " << solidVolumeFraction);
+      WALBERLA_LOG_INFO_ON_ROOT("discrete particle method = " << dpm_to_string(dpm));
+      WALBERLA_LOG_INFO_ON_ROOT("interpolator = " << interpol);
+      WALBERLA_LOG_INFO_ON_ROOT("distribution = " << dist);
+      WALBERLA_LOG_INFO_ON_ROOT("dragCorrelation = " << dragCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("addedMassCorrelation = " << addedMassCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("liftCorrelation = " << liftCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("effective viscosity = " << effVisc);
+      WALBERLA_LOG_INFO_ON_ROOT("turbulence model = " << (useTurbulenceModel ? "yes" : "no"));
+      WALBERLA_LOG_INFO_ON_ROOT("interaction sub cycles = " << interactionSubCycles);
+      WALBERLA_LOG_INFO_ON_ROOT("pe sub steps = " << peSubSteps);
+      WALBERLA_LOG_INFO_ON_ROOT("lubrication cut off distance = " << lubricationCutOffDistance);
+      WALBERLA_LOG_INFO_ON_ROOT("dt_DEM = " << dt_DEM);
+   }
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const uint_t XBlocks = uint_t(4);
+   const uint_t YBlocks = uint_t(1);
+   const uint_t ZBlocks = uint_t(8);
+
+   const uint_t XCells = xlength / XBlocks;
+   const uint_t YCells = ylength / YBlocks;
+   const uint_t ZCells = zlength / ZBlocks;
+
+   if (XBlocks * XCells != xlength ||
+       YBlocks * YCells != ylength ||
+       ZBlocks * ZCells != zlength) WALBERLA_ABORT("Domain decomposition failed!");
+
+   auto blocks = blockforest::createUniformBlockGrid(XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells,
+                                                     dx, 0, false, false,
+                                                     false, false, false,
+                                                     false);
+
+
+   //write domain decomposition to file
+   if( vtkIO )
+   {
+      vtk::writeDomainDecomposition( blocks, "domain_decomposition", vtkBaseFolder );
+   }
+
+   ////////
+   // PE //
+   ////////
+
+   shared_ptr<pe::BodyStorage> globalBodyStorage = make_shared<pe::BodyStorage>();
+   pe::SetBodyTypeIDs<BodyTypeTuple>::execute();
+   auto bodyStorageID = blocks->addBlockData(pe::createStorageDataHandling<BodyTypeTuple>(), "pe Body Storage");
+   auto ccdID = blocks->addBlockData(pe::ccd::createHashGridsDataHandling(globalBodyStorage, bodyStorageID), "CCD");
+   auto fcdID = blocks->addBlockData(
+         pe::fcd::createGenericFCDDataHandling<BodyTypeTuple, pe::fcd::AnalyticCollideFunctor>(), "FCD");
+
+   pe::cr::ICR *cr;
+   pe::cr::DEM cr_dem(globalBodyStorage, blocks->getBlockStoragePointer(), bodyStorageID, ccdID, fcdID);
+   cr = &cr_dem;
+
+   const real_t restitutionCoeff = real_t(0.88);
+   const real_t frictionCoeff = real_t(0.25);
+
+   real_t sphereVolume = diameterAvg * diameterAvg * diameterAvg * math::M_PI / real_t(6); // based on avg. diameter
+   const real_t particleMass = densityRatio * sphereVolume;
+   const real_t Mij = particleMass * particleMass / (real_t(2) * particleMass);
+   const real_t lnDryResCoeff = std::log(restitutionCoeff);
+   const real_t collisionTime = real_t(0.5);
+   const real_t stiffnessCoeff = math::M_PI * math::M_PI * Mij / (collisionTime * collisionTime *
+                                 (real_t(1) - lnDryResCoeff * lnDryResCoeff / (math::M_PI * math::M_PI + lnDryResCoeff * lnDryResCoeff)));
+   const real_t dampingCoeff = -real_t(2) * std::sqrt(Mij * stiffnessCoeff) *
+                               (std::log(restitutionCoeff) / std::sqrt(math::M_PI * math::M_PI + (std::log(restitutionCoeff) * std::log(restitutionCoeff))));
+   const real_t contactDuration = real_t(2) * math::M_PI * Mij / (std::sqrt(real_t(4) * Mij * stiffnessCoeff - dampingCoeff * dampingCoeff)); //formula from Uhlman
+
+   WALBERLA_LOG_INFO_ON_ROOT("Created particle material with:\n"
+                                   << " - coefficient of restitution = " << restitutionCoeff << "\n"
+                                   << " - coefficient of friction = " << frictionCoeff << "\n"
+                                   << " - stiffness coefficient kn = " << stiffnessCoeff << "\n"
+                                   << " - damping coefficient cdn = " << dampingCoeff << "\n"
+                                   << " - contact time Tc = " << contactDuration);
+
+   auto peMaterial = pe::createMaterial("particleMat", densityRatio, restitutionCoeff, frictionCoeff, frictionCoeff,
+                                        real_t(0), real_t(200), stiffnessCoeff, dampingCoeff, dampingCoeff);
+
+   // create bounding planes
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(1, 0, 0), Vector3<real_t>(0, 0, 0), peMaterial);
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(-1, 0, 0), Vector3<real_t>(real_c(xlength), 0, 0), peMaterial);
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(0, 1, 0), Vector3<real_t>(0, 0, 0), peMaterial);
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(0, -1, 0), Vector3<real_t>(0, real_c(ylength), 0), peMaterial);
+
+   // set the planes in z-direction slightly inwards the simulation domain to avoid direct interaction of the spheres with the in- and outflow BC
+   real_t zOffset = real_t(4);
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(0, 0, 1), Vector3<real_t>(0, 0, zOffset), peMaterial);
+   pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(0, 0, -1), Vector3<real_t>(0, 0, real_c(zlength) - zOffset), peMaterial);
+
+
+   /////////////////
+   // PE COUPLING //
+   /////////////////
+
+   // connect to pe
+   const real_t overlap = real_t(1.5) * dx;
+   auto syncCall = boost::bind(pe::syncNextNeighbors<BodyTypeTuple>, boost::ref(blocks->getBlockForest()),
+                               bodyStorageID, static_cast<WcTimingTree *>(NULL), overlap, false);
+   shared_ptr<CollisionPropertiesEvaluator> collisionPropertiesEvaluator = walberla::make_shared<CollisionPropertiesEvaluator>(*cr);
+
+   // create the spheres
+   uint_t numSpheres = 0;
+   {
+      real_t radiusMax = real_t(0.5) * std::max(diameter1, diameter2);
+      AABB generationDomain(radiusMax, radiusMax, radiusMax + zOffset,
+                            real_c(xlength) - radiusMax, real_c(ylength) - radiusMax, real_t(64) * diameterAvg + zOffset);
+      numSpheres = createSpheresRandomly(*blocks, *globalBodyStorage, bodyStorageID, generationDomain,
+                                         diameter1, diameter2, diameterAvg, solidVolumeFraction, peMaterial);
+      syncCall();
+
+      const uint_t initialPeSteps = uint_t(50000);
+      const real_t dt_DEM_init = collisionTime / real_t(uint_t(10) * peSubSteps);
+      const real_t overlapLimit = real_t(0.05) * diameterAvg;
+
+      WALBERLA_LOG_INFO_ON_ROOT("Sphere creation done -- " << numSpheres << " spheres created");
+      WALBERLA_LOG_INFO_ON_ROOT(
+            "resolving overlaps with goal all < " << overlapLimit / diameterAvg * real_t(100) << "%");
+
+      auto boundingPlaneForInit = pe::createPlane(*globalBodyStorage, 0, Vector3<real_t>(0, 0, -1),
+                                                  Vector3<real_t>(0, 0, generationDomain.zMax() + radiusMax),
+                                                  peMaterial);
+
+      for (uint_t pet = uint_t(1); pet <= initialPeSteps; ++pet) {
+         cr->timestep(dt_DEM_init);
+         syncCall();
+         (*collisionPropertiesEvaluator)();
+         real_t maxPen = collisionPropertiesEvaluator->getMaximumPenetrationInSimulation();
+         if (maxPen < overlapLimit) {
+            WALBERLA_LOG_INFO_ON_ROOT("Carried out " << pet << " DEM-only time steps to resolve initial overlaps");
+            break;
+         } else {
+            if (pet % uint_t(200) == uint_t(0)) {
+               WALBERLA_LOG_INFO_ON_ROOT(
+                     pet << " - current max overlap = " << maxPen / diameterAvg * real_t(100) << "%");
+            }
+         }
+         collisionPropertiesEvaluator->resetMaximumPenetration();
+      }
+      pe::destroyBodyBySID(*globalBodyStorage, blocks->getBlockStorage(), bodyStorageID,
+                           boundingPlaneForInit->getSystemID());
+
+      // set body velocities to zero
+      setBodyVelocities(blocks, bodyStorageID, Vector3<real_t>(real_t(0)));
+
+      // some spheres might have gone lost due to unfortunate random generation
+      SphereCounter sphereNumberChecker(blocks, bodyStorageID, numSpheres);
+      sphereNumberChecker.updateNumSpheres();
+      numSpheres = sphereNumberChecker.getNumSpheres();
+
+   }
+
+
+
+   // log setup to file
+   if (!funcTest && vtkIO) {
+      WALBERLA_ROOT_SECTION() {
+         std::string fileName = vtkBaseFolder + "/setup.txt";
+         std::ofstream file;
+         file.open(fileName.c_str());
+         file.precision(8);
+
+         file << "Lx x Ly x Lz = " << xlength << " x " << ylength << " x " << zlength << "\n"
+              << "diameter1 = " << diameter1 << ", diameter2 = " << diameter2 << ", diameterAvg = " << diameterAvg << "\n"
+              << "Re = " << ReynoldsNumber << ", Ga = " << GalileoNumber << "\n"
+              << "tau = " << tau << ", omega = " << omega << "\n"
+              << "viscosity = " << viscosity << "\n"
+              << "gravity = " << gravitationalAcc << "\n"
+              << "input solid volume fraction = " << solidVolumeFraction << "\n"
+              << "discrete particle method = " << dpm_to_string(dpm) << "\n"
+              << "interpolator = " << interpol << "\n"
+              << "distribution = " << dist << "\n"
+              << "dragCorrelation = " << dragCorr << "\n"
+              << "addedMassCorrelation = " << addedMassCorr << "\n"
+              << "liftCorrelation = " << liftCorr << "\n"
+              << "effective viscosity = " << effVisc << "\n"
+              << "turbulence model = " << (useTurbulenceModel ? "yes" : "no") << "\n"
+              << "interaction sub cycles = " << interactionSubCycles << "\n"
+              << "pe sub steps = " << peSubSteps << "\n"
+              << "lubrication cut off distance = " << lubricationCutOffDistance << "\n"
+              << "dt_DEM = " << dt_DEM << "\n"
+              << "Material:\n"
+              << " - coefficient of restitution = " << restitutionCoeff << "\n"
+              << " - coefficient of friction = " << frictionCoeff << "\n"
+              << " - stiffness coefficient kn = " << stiffnessCoeff << "\n"
+              << " - damping coefficient cdn = " << dampingCoeff << "\n"
+              << " - contact time Tc = " << contactDuration << "\n"
+              << "number of spheres = " << numSpheres << "\n";
+      }
+   }
+
+
+   //////////////////////
+   //                  //
+   //    BLOCK DATA    //
+   //                  //
+   //////////////////////
+
+   // create force field
+   BlockDataID forceFieldID = field::addToStorage< Vec3Field_T >( blocks, "force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   BlockDataID dragForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "drag force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID amForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "am force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID liftForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "lift force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   // create omega field
+   BlockDataID omegaFieldID = field::addToStorage< ScalarField_T >( blocks, "omega field", omega, field::zyxf, FieldGhostLayers );
+
+   // create the lattice model
+   LatticeModel_T latticeModel = LatticeModel_T( omegaFieldID, ForceModel_T( forceFieldID ) );
+
+   // add PDF field
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage( blocks, "pdf field (zyxf)", latticeModel, initialFluidVelocity, real_t(1), FieldGhostLayers, field::zyxf );
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" );
+
+   // add boundary handling & initialize outer domain boundaries
+   BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( MyBoundaryHandling( flagFieldID, pdfFieldID, uInflow ), "boundary handling" );
+
+   // field to store fluid velolcity
+   BlockDataID velocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+   BlockDataID oldVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+   BlockDataID swappedOldVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "swapped old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+
+   // create pressure field
+   BlockDataID pressureFieldID = field::addToStorage< ScalarField_T >( blocks, "pressure field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // create solid volume fraction field
+   BlockDataID svfFieldID = field::addToStorage< ScalarField_T >( blocks, "svf field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // field to store pressure gradient
+   BlockDataID pressureGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "pressure gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store curl of fluid velocity
+   BlockDataID velocityCurlFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity curl field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store velocity gradient
+   BlockDataID velocityGradientFieldID = field::addToStorage< TensorField_T >( blocks, "velocity gradient field", Matrix3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store gradient of stress tensor
+   BlockDataID stressTensorGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "stress tensor gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store time derivative of fluid velocity
+   BlockDataID timeDerivativeVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "time derivative velocity field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // communication schemes
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> forceComm( blocks, forceFieldID );
+
+   blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfScheme( blocks );
+   pdfScheme.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+
+   // setup of the communication for synchronizing the velocity field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityFieldID ) );
+
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > oldVelocityCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   oldVelocityCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( oldVelocityFieldID ) );
+
+   // setup of the communication for synchronizing the solid volume fraction field between neighboring blocks which takes into account the svf values inside the ghost layers
+   shared_ptr<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >svfCommunicationScheme = make_shared<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >( blocks, svfFieldID );
+
+   // setup of the communication for synchronizing the pressure field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<ScalarField_T> >( pressureFieldID ) );
+
+   // setup of the communication for synchronizing the pressure gradient field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( pressureGradientFieldID ) );
+
+   // setup of the communication for synchronizing the velocity curl field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCurlCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCurlCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityCurlFieldID ) );
+
+   // communication for synchronizing the velocity gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<TensorField_T> >( velocityGradientFieldID ) );
+
+   // communication for synchronizing the stress tensor gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > stressTensorGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   stressTensorGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( stressTensorGradientFieldID ) );
+
+   // communication for synchronizing the interaction force field
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> dragForceComm( blocks, dragForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> amForceComm( blocks, amForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> liftForceComm( blocks, liftForceFieldID );
+
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> dragForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, dragForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> amForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, amForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> liftForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, liftForceFieldID, uint_t(1) );
+
+   /////////////////////////////////
+   //                             //
+   //    CORRELATION FUNCTIONS    //
+   //                             //
+   /////////////////////////////////
+
+   // drag correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t>&, const Vector3<real_t> &, real_t, real_t, real_t, real_t)> dragCorrelationFunction;
+   if( dragCorr == DragCorrelation::ErgunWenYu )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceErgunWenYu;
+   }
+   else if( dragCorr == DragCorrelation::Tang )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTang;
+   }
+   else if( dragCorr == DragCorrelation::Stokes )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceStokes;
+   }
+   else if( dragCorr == DragCorrelation::Felice )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceFelice;
+   }
+   else if( dragCorr == DragCorrelation::Tenneti )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTenneti;
+   }
+   else if( dragCorr == DragCorrelation::NoDrag )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::noDragForce;
+   }
+   else
+   {
+      WALBERLA_ABORT("Drag correlation not yet implemented!");
+   }
+
+   // lift correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )> liftCorrelationFunction;
+   if( liftCorr == LiftCorrelation::NoLift )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::noLiftForce;
+   }
+   else if( liftCorr == LiftCorrelation::Saffman )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::liftForceSaffman;
+   }
+   else
+   {
+      WALBERLA_ABORT("Lift correlation not yet implemented!");
+   }
+
+   // added mass correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t )> addedMassCorrelationFunction;
+   if( addedMassCorr == AddedMassCorrelation::NoAM )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::noAddedMassForce;
+   }
+   else if( addedMassCorr == AddedMassCorrelation::Finn )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::addedMassForceFinn;
+   }
+   else
+   {
+      WALBERLA_ABORT("Added mass correlation not yet implemented!");
+   }
+
+   // set up effective viscosity calculation
+   boost::function<real_t ( real_t, real_t)> effectiveViscosityFunction;
+   if( effVisc == EffectiveViscosity::None )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateUnchangedEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Rescaled )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateRescaledEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Eilers )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateEilersEffectiveViscosity;
+   }
+   else
+   {
+      WALBERLA_ABORT("Effective viscosity not yet implemented!");
+   }
+   shared_ptr<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator> effectiveViscosityEvaluator =
+         walberla::make_shared<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>(omegaFieldID, svfFieldID, viscosity, effectiveViscosityFunction );
+
+
+   ////////////////////////////////
+   //                            //
+   //    EVALUATION FUNCTIONS    //
+   //                            //
+   ////////////////////////////////
+
+   // evaluator for bodies' velocity time derivative
+   shared_ptr<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator> bodyVelocityTimeDerivativeEvaluator = make_shared<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( blocks, bodyStorageID, dtBodyVelocityTimeDerivativeEvaluation );
+   (*bodyVelocityTimeDerivativeEvaluator)();
+
+   // function used to evaluate the interaction force between fluid and particles
+   boost::function<void(void)> dragAndPressureForceEvaluationFunction;
+   if( dpm == DPMethod::GNS ) {
+      if (interpol == Interpolation::INearestNeighbor) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::IKernel) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::ITrilinear) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      }
+   }
+   else
+   {
+      WALBERLA_ABORT("Discrete particle method not yet implemented!");
+   }
+
+
+   // function to evaluate the lift force contribution
+   boost::function<void(void)> liftForceEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+   // function to evaluate the added mass contribution
+   boost::function<void(void)> addedMassEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+   // function to evaluate lubrication forces
+   boost::function<void(void)> lubricationEvaluationFunction;
+   if( lubricationCutOffDistance > real_t(0) )
+   {
+      typedef pe_coupling::discrete_particle_methods::LubricationForceEvaluator LE_T;
+      shared_ptr<LE_T> lubEval = make_shared<LE_T>( blocks, globalBodyStorage, bodyStorageID, viscosity, lubricationCutOffDistance );
+      lubricationEvaluationFunction = boost::bind(&LE_T::operator(), lubEval);
+   }
+   else
+   {
+      lubricationEvaluationFunction = emptyFunction;
+   }
+
+
+   //////////////////////////////
+   //                          //
+   //    INITIAL SIMULATION    //
+   //                          //
+   //////////////////////////////
+
+   {
+      // init solid volume fraction field
+      pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+
+      (*svfCommunicationScheme)();
+
+      pe_coupling::discrete_particle_methods::ForceFieldResetter forceFieldResetter( forceFieldID );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  forceFieldResetter( &(*blockIt) );
+   }
+
+   if( initialTimesteps > uint_t(0) )
+   {
+
+      SweepTimeloop timeloop( blocks->getBlockStorage(), initialTimesteps );
+
+      ///////// begin of GNS timeloop ///////////////////////
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Pressure Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureCommunicationScheme), "Pressure Field Communication" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( pressureGradientFieldID, pressureFieldID, boundaryHandlingID ), "Pressure Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureGradientCommunicationScheme), "Pressure Gradient Field Communication" );
+
+
+      // subcycling loop begin
+      for( uint_t subcycle = 1; subcycle <= interactionSubCycles; ++subcycle )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+
+         // evaluate Fdrag
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( dragForceFieldID ), "Drag Force Field Reset" )
+                        << AfterFunction( dragAndPressureForceEvaluationFunction, "Fluid-Particle Interaction Force Evaluation" )
+                        << AfterFunction( dragForceComm, "Drag Force Field Communication" );
+
+         // ext forces on bodies
+         timeloop.add() << Sweep( DummySweep(), "Dummy Sweep ")
+                        << AfterFunction( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID  ), "Force on Bodies Reset" );
+
+      }
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+      timeloop.add() << Sweep( makeSharedSweep<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>( effectiveViscosityEvaluator ), "Effective Viscosity Evaluation");
+      if( useTurbulenceModel ) timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSSmagorinskyLESField<LatticeModel_T>(blocks, omegaFieldID, pdfFieldID, svfFieldID, smagorinskyConstant), "Turbulence Model" );
+
+      // execute GNS-LBM sweep, boundary handling, PDF communication
+      auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag );
+
+      timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "GNS-LBM sweep (collide)" );
+
+      timeloop.add() << BeforeFunction( pdfScheme, "LBM Communication" )
+                     << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+      timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "GNS-LBM sweep (stream)" );
+
+
+      timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+      WALBERLA_LOG_INFO_ON_ROOT("Starting initial (GNS-LBM only) simulation with " << initialTimesteps << " time steps.")
+      timeloop.run();
+
+   }
+
+
+   /////////////////////////////
+   //                         //
+   //    ACTUAL SIMULATION    //
+   //                         //
+   /////////////////////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
+
+   {
+      pe_coupling::discrete_particle_methods::ForceFieldResetter forceFieldResetter( forceFieldID );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  forceFieldResetter( &(*blockIt) );
+
+      // evaluate fluid velocity field
+      pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> velocityEvaluator( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  velocityEvaluator( &(*blockIt) );
+
+   }
+
+   // configure vtk output
+   if( vtkIO )
+   {
+      shared_ptr<vtk::VTKOutput> pdfFieldVTKWriter = createFluidFieldVTKWriter( blocks, pdfFieldID, flagFieldID, vtkWriteFrequency, vtkBaseFolder );
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( pdfFieldVTKWriter ), "VTK (fluid field data)" );
+
+      auto bodyVtkOutput = make_shared<pe::SphereVtkOutput>( bodyStorageID, blocks->getBlockStorage() );
+      auto bodyVTK = vtk::createVTKOutput_PointData( bodyVtkOutput, "bodies", vtkWriteFrequency, vtkBaseFolder );
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( bodyVTK ), "VTK (sphere data)" );
+
+   }
+
+
+   ///////// begin of GNS timeloop ///////////////////////
+
+   if( addedMassCorr != AddedMassCorrelation::NoAM )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::FieldDataSwapper<Vec3Field_T>( velocityFieldID, swappedOldVelocityFieldID ), "Velocity Field Swap" );
+   }
+
+   if( addedMassCorr != AddedMassCorrelation::NoAM )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( oldVelocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Old Velocity Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(oldVelocityCommunicationScheme), "Old Velocity Field Communication" );
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityGradientFieldID, oldVelocityFieldID, boundaryHandlingID ), "Velocity Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityGradientCommunicationScheme), "Velocity Gradient Field Communication" );
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityTotalTimeDerivativeFieldEvaluator( timeDerivativeVelocityFieldID, oldVelocityFieldID, swappedOldVelocityFieldID, velocityGradientFieldID, dt ), "Velocity Time Derivative Field Evaluation" );
+   }
+
+   // subcycling loop begin
+   for( uint_t subcycle = 1; subcycle <= interactionSubCycles; ++subcycle )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityCurlFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityCurlFieldID, velocityFieldID, boundaryHandlingID ), "Velocity Curl Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCurlCommunicationScheme), "Velocity Curl Field Communication" );
+      }
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Pressure Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureCommunicationScheme), "Pressure Field Communication" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( pressureGradientFieldID, pressureFieldID, boundaryHandlingID ), "Pressure Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureGradientCommunicationScheme), "Pressure Gradient Field Communication" );
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         // evaluate Flift
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( liftForceFieldID ), "Lift Force Field Reset" )
+                        << AfterFunction( liftForceEvaluationFunction, "Lift Force Evaluation")
+                        << AfterFunction( liftForceComm, "Lift Force Field Communication" );
+      }
+
+      if( addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         // evaluate Fam
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( amForceFieldID ), "AM Force Field Reset" )
+                        << AfterFunction( addedMassEvaluationFunction, "Added Mass Force Evaluation")
+                        << AfterFunction( amForceComm, "Force Field Communication" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( bodyVelocityTimeDerivativeEvaluator ), "Body Velocity Time Derivative Evaluation" );
+      }
+
+      if( liftCorr != LiftCorrelation::NoLift || addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+      }
+
+      // evaluate Fdrag
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( dragForceFieldID ), "Drag Force Field Reset" )
+                     << AfterFunction( dragAndPressureForceEvaluationFunction, "Fluid-Particle Interaction Force Evaluation" )
+                     << AfterFunction( dragForceComm, "Drag Force Field Communication" );
+
+      // ext forces on bodies and PE step(s)
+      timeloop.add() << Sweep( DummySweep(), "Dummy Sweep ")
+                     << AfterFunction( BodiesQuantityEvaluator(&timeloop, blocks, bodyStorageID, vtkBaseFolder+"/quantityLogging.txt", numSpheres, real_t(numSpheres) * particleMass ), "Body Quantity Evaluator")
+                     << AfterFunction( ForceOnBodiesAdder( blocks, bodyStorageID, Vector3<real_t>(0,0,- gravitationalAcc * ( densityRatio - real_t(1) ) )  ), "Gravitational and Buoyancy Force Add" )
+                     << AfterFunction( pe_coupling::discrete_particle_methods::SubgridTimeStep( blocks, bodyStorageID, *cr, syncCall, lubricationEvaluationFunction, dtInteractionSubCycle, peSubSteps ), "Pe Time Step" );
+
+      // update solid volume fraction field
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> ( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag ), "Solid Volume Fraction Field Evaluation" )
+                     << AfterFunction( SharedFunctor< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >(svfCommunicationScheme), "Solid Volume Fraction Field Communication" );
+
+   }
+
+   timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+   timeloop.add() << Sweep( makeSharedSweep<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>( effectiveViscosityEvaluator ), "Effective Viscosity Evaluation");
+   if( useTurbulenceModel ) timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSSmagorinskyLESField<LatticeModel_T>(blocks, omegaFieldID, pdfFieldID, svfFieldID, smagorinskyConstant), "Turbulence Model" );
+
+   // execute GNS-LBM sweep, boundary handling, PDF communication
+   auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag );
+
+   timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "GNS-LBM sweep (collide)" );
+
+   timeloop.add() << BeforeFunction( pdfScheme, "LBM Communication" )
+                  << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+   timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "GNS-LBM sweep (stream)" );
+
+   timeloop.addFuncAfterTimeStep( SphereCounter(blocks, bodyStorageID, numSpheres ), "Sphere Counter" );
+
+   timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+
+   WALBERLA_LOG_INFO_ON_ROOT("Starting actual (fully coupled) simulation with " << timesteps << " time steps.")
+
+   // execute simulation
+   WcTimingPool timeloopTiming;
+
+   for( uint_t t = 0; t < timesteps; ++t )
+   {
+      timeloop.singleStep( timeloopTiming );
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   return 0;
+}
+
+} //namespace bidisperse_fluidized_bed_dpm
+
+int main( int argc, char **argv ){
+   bidisperse_fluidized_bed_dpm::main(argc, argv);
+}
+
diff --git a/apps/showcases/BidisperseFluidizedBed/CMakeLists.txt b/apps/showcases/BidisperseFluidizedBed/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..51318ee88fdac51ecb1c046cf45c0ac379046149
--- /dev/null
+++ b/apps/showcases/BidisperseFluidizedBed/CMakeLists.txt
@@ -0,0 +1,2 @@
+waLBerla_add_executable ( NAME BidisperseFluidizedBedDPM
+                          DEPENDS blockforest boundary core domain_decomposition field lbm pe pe_coupling postprocessing timeloop vtk )
\ No newline at end of file
diff --git a/apps/showcases/CMakeLists.txt b/apps/showcases/CMakeLists.txt
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9220ea4c9e116aa42a7a40b73f0548ed5462d048 100644
--- a/apps/showcases/CMakeLists.txt
+++ b/apps/showcases/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory( BidisperseFluidizedBed )
\ No newline at end of file
diff --git a/src/pe_coupling/all.h b/src/pe_coupling/all.h
index 39c4362a37287e58b2eb5c469ba53c0c62624c8b..2dfc25e1272ad3e361b9d8bd094aa0f09fb4ce8d 100644
--- a/src/pe_coupling/all.h
+++ b/src/pe_coupling/all.h
@@ -24,6 +24,7 @@
 
 #pragma once
 
+#include "discrete_particle_methods/all.h"
 #include "mapping/all.h"
 #include "geometry/all.h"
 #include "partially_saturated_cells_method/all.h"
diff --git a/src/pe_coupling/discrete_particle_methods/all.h b/src/pe_coupling/discrete_particle_methods/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..c00728f8247ffab4bf67a227af0ebe02d55ee8f2
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/all.h
@@ -0,0 +1,30 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#include "waLBerlaDefinitions.h"
+
+#include "correlations/all.h"
+#include "evaluators/all.h"
+#include "gns_lbm/all.h"
+#include "utility/all.h"
diff --git a/src/pe_coupling/discrete_particle_methods/correlations/AddedMassForceCorrelations.h b/src/pe_coupling/discrete_particle_methods/correlations/AddedMassForceCorrelations.h
new file mode 100644
index 0000000000000000000000000000000000000000..4574cbc19d02d50b964cb402ba2e8595f5fb36f1
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/correlations/AddedMassForceCorrelations.h
@@ -0,0 +1,54 @@
+//======================================================================================================================
+//
+//  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 AddedMassForceCorrelations.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+
+#include <cmath>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Correlation functions for the added mass force.
+ *
+ * To be compatible with the interface of AddedMassForceEvaluator, all functions use the following signature:
+ * Vector3<real_t> ( const Vector3<real_t> & timeDerivativeFluidVel, const Vector3<real_t> & timeDerivativeBodyVel,
+ *                   const real_t & bodyVolume, const real_t & fluidDensity )
+ */
+
+Vector3<real_t> addedMassForceFinn( const Vector3<real_t> & timeDerivativeFluidVel, const Vector3<real_t> & timeDerivativeBodyVel,
+                                    const real_t & bodyVolume, const real_t & fluidDensity )
+{
+   // formula from Finn et al(2016)
+   const real_t Coeffam = real_t(0.5);
+   return bodyVolume * fluidDensity * Coeffam * ( timeDerivativeFluidVel - timeDerivativeBodyVel );
+}
+
+Vector3<real_t> noAddedMassForce( const Vector3<real_t> &, const Vector3<real_t> &, const real_t &, const real_t & )
+{
+   return Vector3<real_t>(real_t(0));
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/correlations/DragForceCorrelations.h b/src/pe_coupling/discrete_particle_methods/correlations/DragForceCorrelations.h
new file mode 100644
index 0000000000000000000000000000000000000000..91ae0da40a0a12448f30e0c996924f6dc76ae6e1
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/correlations/DragForceCorrelations.h
@@ -0,0 +1,222 @@
+//======================================================================================================================
+//
+//  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 DragForceCorrelations.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Constants.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+
+/*!\brief Various correlation functions for the drag force.
+ *
+ * These functions calculate the drag force for fluid-particle interactions based on different empirical correlations
+ * from literature.
+ * Always be aware that those empirical formulas were obtained by different setups and are thus not generally applicable!
+ *
+ * To be compatible with the interface of InteractionForceEvaluator, all functions use the following signature:
+ * Vector3<real_t> ( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel, real_t solidVolumeFraction,
+ *                   real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+ *
+ */
+
+
+// helper functions -> often used in more advanced drag correlations
+
+// equation to calculate the drag coefficient on isolated spherical particle
+// Schiller, L., Naumann, A., 1935. A drag coefficient correlation. Vdi Zeitung 77, 318-320.
+real_t dragCoeffSchillerNaumann( real_t reynoldsNumber )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( reynoldsNumber, real_t(0) );
+
+   return ( reynoldsNumber < real_t(1000) ) ? real_t(24) * ( real_t(1) + real_t(0.15) * std::pow(reynoldsNumber, real_t(0.687) ) ) / reynoldsNumber
+                                            : real_t(0.44);
+}
+
+// Coefficient from Stokes' law for drag, only valid for Stokes regime (low Reynolds numbers)
+// = 3 * PI * mu * D * fluidVolumeFraction
+real_t dragCoeffStokes ( real_t fluidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity )
+{
+   return real_t(3) * math::M_PI * diameter * fluidDynamicViscosity * fluidVolumeFraction;
+}
+
+// threshold value for absolute relative velocity
+// if it is below this value, a drag force of 0 is set, to avoid instabilities stemming from divisions by this small value
+const real_t thresholdAbsoluteVelocityDifference = real_t(1e-10);
+
+
+//////////////////////
+//                  //
+//   CORRELATIONS   //
+//                  //
+//////////////////////
+
+// Stokes drag law
+Vector3<real_t> dragForceStokes( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel,
+                                 real_t solidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity, real_t /*fluidDensity*/ )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFraction, real_t(0) );
+   WALBERLA_ASSERT_LESS_EQUAL( solidVolumeFraction, real_t(1) );
+
+   Vector3<real_t> velDiff = fluidVel - particleVel;
+   real_t absVelDiff = velDiff.length();
+
+   if( absVelDiff < thresholdAbsoluteVelocityDifference ) return Vector3<real_t>(real_t(0));
+
+   real_t fluidVolumeFraction = real_t(1) - solidVolumeFraction;
+
+   return dragCoeffStokes( fluidVolumeFraction, diameter, fluidDynamicViscosity ) * velDiff;
+}
+
+
+// S. Ergun, Fluid flow through packed columns. Chemical Engineering Progress 48 (1952), 89-94.
+// Y. C. Wen, Y.H. Yu, Mechanics of fluidization. Chemical Engineering Progress Symposium Series 62 (1966), 100-111.
+// see also Beetstra, van der Hoef, Kuipers, "Drag Force of Intermediate Reynolds Number Flow Past Mono- and Bidisperse Arrays of Spheres" (2007)
+Vector3<real_t>  dragForceErgunWenYu( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel,
+                                      real_t solidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFraction, real_t(0) );
+   WALBERLA_ASSERT_LESS_EQUAL( solidVolumeFraction, real_t(1) );
+
+   Vector3<real_t> velDiff = fluidVel - particleVel;
+   real_t absVelDiff = velDiff.length();
+
+   if( absVelDiff < thresholdAbsoluteVelocityDifference ) return Vector3<real_t>(real_t(0));
+
+   real_t fluidVolumeFraction = real_t(1) - solidVolumeFraction;
+
+   if( fluidVolumeFraction < real_t(0.8) )
+   {
+      // Ergun relation
+      real_t reynoldsNumber = fluidVolumeFraction * fluidDensity * absVelDiff * diameter / fluidDynamicViscosity;
+      real_t fDrag = real_t(150) * solidVolumeFraction / ( real_t(18) * fluidVolumeFraction * fluidVolumeFraction ) +
+                     real_t(1.75) / ( real_t(18) * fluidVolumeFraction * fluidVolumeFraction ) * reynoldsNumber;
+      return fDrag * dragCoeffStokes( fluidVolumeFraction, diameter, fluidDynamicViscosity ) * velDiff;
+   } else
+   {
+      // Wen & Yu correlation
+      real_t reynoldsNumber = fluidVolumeFraction * fluidDensity * absVelDiff * diameter / fluidDynamicViscosity;
+      real_t fDrag = dragCoeffSchillerNaumann( reynoldsNumber ) * reynoldsNumber / real_t(24) * std::pow( fluidVolumeFraction, real_t(-3.7) );
+      return fDrag * dragCoeffStokes( fluidVolumeFraction, diameter, fluidDynamicViscosity ) * velDiff;
+   }
+}
+
+// drag correlation proposed by Tang et al. - "A New Drag Correlation from Fully Resolved Simulations of Flow Past
+// Monodisperse Static Arrays of Spheres", AiChE, 2014
+Vector3<real_t> dragForceTang( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel,
+                               real_t solidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFraction, real_t(0) );
+   WALBERLA_ASSERT_LESS_EQUAL( solidVolumeFraction, real_t(1) );
+
+   Vector3<real_t> velDiff = fluidVel - particleVel;
+   real_t absVelDiff = velDiff.length();
+
+   if( absVelDiff < thresholdAbsoluteVelocityDifference ) return Vector3<real_t>(real_t(0));
+
+   real_t fluidVolumeFraction = real_t(1) - solidVolumeFraction;
+   real_t fluidVolumeFractionP2 = fluidVolumeFraction * fluidVolumeFraction;
+   real_t inv_fluidVolumeFractionP4 = real_t(1) / (fluidVolumeFractionP2 * fluidVolumeFractionP2);
+   real_t reynoldsNumber = fluidVolumeFraction * fluidDensity * absVelDiff * diameter / fluidDynamicViscosity;
+
+   // Eq.21 from the paper
+   real_t fDrag = real_t(10) * solidVolumeFraction / fluidVolumeFractionP2 + fluidVolumeFractionP2 * ( real_t(1) + real_t(1.5) * std::sqrt(solidVolumeFraction) )
+                + ( real_t(0.11) * solidVolumeFraction * ( real_t(1) + solidVolumeFraction ) - real_t(0.00456) * inv_fluidVolumeFractionP4
+                + ( real_t(0.169) * fluidVolumeFraction + real_t(0.0644) * inv_fluidVolumeFractionP4 ) * std::pow( reynoldsNumber, -real_t(0.343) ) ) * reynoldsNumber;
+
+   return fDrag * dragCoeffStokes( fluidVolumeFraction, diameter, fluidDynamicViscosity ) * velDiff;
+
+}
+
+
+// drag correlation based on findings from Felice (1994)
+// used e.g. in Kafui et al (2002)
+Vector3<real_t> dragForceFelice( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel,
+                                 real_t solidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFraction, real_t(0) );
+   WALBERLA_ASSERT_LESS_EQUAL( solidVolumeFraction, real_t(1) );
+
+   Vector3<real_t> velDiff = fluidVel - particleVel;
+   real_t absVelDiff = velDiff.length();
+
+   if( absVelDiff < thresholdAbsoluteVelocityDifference ) return Vector3<real_t>(real_t(0));
+
+   real_t fluidVolumeFraction = real_t(1) - solidVolumeFraction;
+
+   real_t reynoldsNumber = fluidVolumeFraction * fluidDensity * absVelDiff * diameter / fluidDynamicViscosity;
+
+   real_t temp1 = ( real_t(0.63) + real_t(4.8) / std::sqrt( reynoldsNumber ) );
+   real_t dragCoeff = temp1 * temp1;
+
+   real_t temp2 = real_t(1.5) - std::log10( reynoldsNumber );
+   real_t chi = real_t(3.7) - std::pow( real_t(0.65), (- real_t(0.5) * temp2 * temp2 ) );
+
+   return real_t(0.125) * dragCoeff * fluidDensity * math::M_PI * diameter * diameter * absVelDiff *
+          std::pow( fluidVolumeFraction, real_t(2) - chi) * velDiff;
+
+}
+
+// drag correlation based on findings from Tenneti, Garg, Subramaniam (2011)
+// used e.g. in Finn, Li, Apte - Particle based modelling and simulation of natural sand dynamics in the wave bottom boundary layer (2016)
+// could be generalized also for non-spherical particles, see Finn et al (2016)
+Vector3<real_t> dragForceTenneti( const Vector3<real_t> & fluidVel, const Vector3<real_t> & particleVel,
+                                  real_t solidVolumeFraction, real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+{
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFraction, real_t(0) );
+   WALBERLA_ASSERT_LESS_EQUAL( solidVolumeFraction, real_t(1) );
+
+   Vector3<real_t> velDiff = fluidVel - particleVel;
+   const real_t absVelDiff = velDiff.length();
+
+   if( absVelDiff < thresholdAbsoluteVelocityDifference ) return Vector3<real_t>(real_t(0));
+
+   const real_t fluidVolumeFraction = real_t(1) - solidVolumeFraction;
+
+   const real_t reynoldsNumber = fluidVolumeFraction * fluidDensity * absVelDiff * diameter / fluidDynamicViscosity;
+
+   const real_t fvfCubed = fluidVolumeFraction * fluidVolumeFraction * fluidVolumeFraction;
+   const real_t A = real_t(5.81) * solidVolumeFraction / fvfCubed + real_t(0.48) * std::cbrt( solidVolumeFraction ) / ( fvfCubed * fluidVolumeFraction );
+
+   const real_t svfCubed = solidVolumeFraction * solidVolumeFraction * solidVolumeFraction;
+   const real_t B = svfCubed * reynoldsNumber * ( real_t(0.95) + real_t(0.61) * svfCubed / ( fluidVolumeFraction * fluidVolumeFraction ) );
+
+   // version from Finn et al.
+   const real_t CdRe0Sphere = real_t(1) + real_t(0.15) *  std::pow( reynoldsNumber, real_t(0.687) );
+
+   const real_t CdRe = fluidVolumeFraction * ( CdRe0Sphere / fvfCubed + A + B );
+
+   return real_t(3) * math::M_PI * diameter * fluidDynamicViscosity * fluidVolumeFraction * CdRe * velDiff;
+
+}
+
+
+Vector3<real_t> noDragForce( const Vector3<real_t> & /*fluidVel*/, const Vector3<real_t> & /*particleVel*/,
+                             real_t /*solidVolumeFraction*/, real_t /*diameter*/, real_t /*fluidDynamicViscosity*/, real_t /*fluidDensity*/ )
+{
+   return Vector3<real_t>(real_t(0));
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/correlations/LiftForceCorrelations.h b/src/pe_coupling/discrete_particle_methods/correlations/LiftForceCorrelations.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e66b93dab441da4fc7e95bd03f915dac930392a
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/correlations/LiftForceCorrelations.h
@@ -0,0 +1,63 @@
+//======================================================================================================================
+//
+//  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 LiftForceCorrelations.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+
+#include <cmath>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Correlation functions for the lift force.
+ *
+ * To be compatible with the interface of LiftForceEvaluator, all functions use the following signature:
+ * Vector3<real_t> ( const Vector3<real_t> & fluidVel, const Vector3<real_t> & curlFluidVel, const Vector3<real_t> & particleVel,
+ *                   real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+ */
+
+// Saffman lift force
+Vector3<real_t> liftForceSaffman ( const Vector3<real_t> & fluidVel, const Vector3<real_t> & curlFluidVel, const Vector3<real_t> & particleVel,
+                                   real_t diameter, real_t fluidDynamicViscosity, real_t fluidDensity )
+{
+   const real_t absCurlVel = curlFluidVel.length();
+   if( absCurlVel < real_t(1e-10) ) return Vector3<real_t>(real_t(0));
+
+   // Finn et al (2016) for spheres
+   const real_t Cl = real_t(1.61) * std::sqrt( ( fluidDynamicViscosity * fluidDensity) / absCurlVel );
+   return Cl * diameter * diameter * ( ( fluidVel - particleVel ) % curlFluidVel );
+
+   // Sun, Xiao (2016)
+   //const real_t Cl = real_t(1.6);
+   //return Cl * fluidDensity * std::sqrt( fluidDynamicViscosity / fluidDensity ) * diameter * diameter * ( ( fluidVel - particleVel ) % curlFluidVel );
+
+}
+
+Vector3<real_t> noLiftForce ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )
+{
+   return Vector3<real_t>(real_t(0));
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/correlations/all.h b/src/pe_coupling/discrete_particle_methods/correlations/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..472e1d9b66e13a3156d623d2e1bfe8a883008b5b
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/correlations/all.h
@@ -0,0 +1,29 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#include "waLBerlaDefinitions.h"
+
+#include "AddedMassForceCorrelations.h"
+#include "DragForceCorrelations.h"
+#include "LiftForceCorrelations.h"
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..4daed731d9671f39df0137cc850217afb3146240
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h
@@ -0,0 +1,145 @@
+//======================================================================================================================
+//
+//  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 AddedMassForceEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/debug/Debug.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/interpolators/all.h"
+#include "field/distributors/all.h"
+#include "field/GhostLayerField.h"
+
+#include "lbm/field/MacroscopicValueCalculation.h"
+#include "lbm/field/PdfField.h"
+#include "lbm/lattice_model/ForceModel.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/rigidbody/RigidBody.h"
+#include "pe/Types.h"
+#include "pe/Materials.h"
+
+#include "BodyVelocityTimeDerivativeEvaluator.h"
+
+#include "stencil/Directions.h"
+
+#include <boost/function.hpp>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluator of the added (virtual) mass force based on the given added mass correlation.
+ *
+ * The added mass force is evaluated for each body (requires interpolation of several quantities from the fluid fields)
+ * and then applied onto them.
+ * The corresponding reaction force is added to the fluid via the given distributor.
+ *
+ * Note that the fluid velocity (and its derivatives) has to be the fluid-phase velocity (and not the volume-averaged velocity).
+ *
+ * For more infos on interpolators, see field interpolators in src/field/interpolators.
+ * For more infos on distributors, see src/field/distributors.
+ */
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+class AddedMassForceEvaluator
+{  
+
+public:
+
+   typedef GhostLayerField< Vector3<real_t>, 1>             Vec3Field_T;
+   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T>    Vec3FieldInterpolator_T;
+   typedef Distributor_T<Vec3Field_T, FlagField_T>          ForceDistributor_T;
+
+   AddedMassForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                            const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID,
+                            const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
+                            const BlockDataID & velocityTimeDerivativeFieldID,
+                            const boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t )> & addedMassForceCorrelationFunction,
+                            const shared_ptr< BodyVelocityTimeDerivativeEvaluator > & bodyVelocityTimeDerivativeEvaluator )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ),
+     addedMassForceCorrelationFunction_( addedMassForceCorrelationFunction ), bodyVelocityTimeDerivativeEvaluator_( bodyVelocityTimeDerivativeEvaluator )
+   {
+      velocityTimeDerivativeFieldInterpolatorID_ = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, velocityTimeDerivativeFieldID, flagFieldID, domainFlags );
+      forceDistributorID_ = field::addDistributor< ForceDistributor_T, FlagField_T >( blockStorage, forceFieldID, flagFieldID, domainFlags );
+   }
+
+   void operator()();
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t )> addedMassForceCorrelationFunction_;
+
+   shared_ptr< BodyVelocityTimeDerivativeEvaluator > bodyVelocityTimeDerivativeEvaluator_;
+
+   BlockDataID velocityTimeDerivativeFieldInterpolatorID_;
+   BlockDataID forceDistributorID_;
+
+};
+
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+void AddedMassForceEvaluator< FlagField_T, FieldInterpolator_T, Distributor_T >
+::operator()()
+{
+
+   for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+   {
+
+      Vec3FieldInterpolator_T * velocityTimeDerivativeInterpolator = blockIt->getData< Vec3FieldInterpolator_T >( velocityTimeDerivativeFieldInterpolatorID_ );
+      ForceDistributor_T * forceDistributor                        = blockIt->getData< ForceDistributor_T >( forceDistributorID_ );
+
+      for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+      {
+         //TODO check if body should be treated by DPM
+
+         Vector3<real_t> forceOnFluid( real_t(0) );
+
+         Vector3<real_t> bodyPosition = bodyIt->getPosition();
+         Vector3<real_t> bodyVelocity = bodyIt->getLinearVel();
+         real_t bodyVolume = bodyIt->getVolume();
+         real_t fluidDensity( real_t(1) );
+
+         // evaluate added (virtual) mass force
+         Vector3<real_t> timeDerivativeFluidVelocity( real_t(0) );
+         Vector3<real_t> timeDerivativeBodyVelocity( real_t(0) );
+         bodyVelocityTimeDerivativeEvaluator_->get(timeDerivativeBodyVelocity, bodyVelocity, bodyIt->getSystemID() );
+         velocityTimeDerivativeInterpolator->get( bodyPosition, &timeDerivativeFluidVelocity );
+         Vector3<real_t> addedMassForce = addedMassForceCorrelationFunction_( timeDerivativeFluidVelocity, timeDerivativeBodyVelocity, bodyVolume, fluidDensity );
+         WALBERLA_ASSERT( !math::isnan(addedMassForce[0]) && !math::isnan(addedMassForce[1]) && !math::isnan(addedMassForce[2]),
+                          "NaN found in added mass force for body at position " << bodyPosition );
+
+         bodyIt->addForce( addedMassForce );
+         forceOnFluid += ( -addedMassForce );
+
+         // set/distribute force on fluid
+         forceDistributor->distribute( bodyPosition, &forceOnFluid );
+      }
+   }
+
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/BodyVelocityTimeDerivativeEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/BodyVelocityTimeDerivativeEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9eadc43ca14ea8b4f9bf93057b34fb29e15d2bd
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/BodyVelocityTimeDerivativeEvaluator.h
@@ -0,0 +1,88 @@
+//======================================================================================================================
+//
+//  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 BodyVelocityTimeDerivativeEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+
+#include <map>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluates the time derivative of the body velocity
+ *
+ * Using the operator()(), an internal map is filled with pairs of the body ID and its current velocity,
+ * after clearing the old map.
+ * Calling get(..), the body's former velocity is fetched from the map and used to calculate a simple approximation of the
+ * body velocity time derivative (du/dt = ( u_new - u_old ) / deltaT )
+ *
+ */
+class BodyVelocityTimeDerivativeEvaluator
+{  
+public:
+
+   BodyVelocityTimeDerivativeEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                                        const BlockDataID & bodyStorageID, const real_t & deltaT = real_t(1) )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), deltaTinv_( real_t(1) / deltaT )
+     { }
+
+   void operator()()
+   {
+      // rebuild velocity map for all known bodies (process local)
+      bodyVelocityMap_.clear();
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
+         {
+            bodyVelocityMap_.insert( std::pair<walberla::id_t, Vector3< real_t > >( bodyIt->getSystemID(), bodyIt->getLinearVel() ) );
+         }
+      }
+   }
+
+   void resetDeltaT( const real_t & deltaT )
+   {
+      deltaTinv_ = real_t(1) / deltaT;
+   }
+
+   void get( Vector3<real_t> & particleVelocityTimeDerivative, const Vector3<real_t> currentParticleVelocity, const walberla::id_t & bodySystemID )
+   {
+      auto it = bodyVelocityMap_.find( bodySystemID );
+      WALBERLA_ASSERT( it != bodyVelocityMap_.end(), "body with ID " << bodySystemID << " not found in body velocity map!" );
+      particleVelocityTimeDerivative = ( currentParticleVelocity - it->second ) * deltaTinv_;
+   }
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   std::map< walberla::id_t, Vector3< real_t > > bodyVelocityMap_;
+   real_t deltaTinv_;
+
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..c052b6059bad2ac9a54ce5bee15a8f8ae6c10033
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h
@@ -0,0 +1,106 @@
+//======================================================================================================================
+//
+//  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 EffectiveViscosityFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "lbm/lattice_model/CollisionModel.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+// correlations for the viscosity
+
+// no change in viscosity
+real_t calculateUnchangedEffectiveViscosity( real_t fluidViscosity, real_t /*porosity*/ )
+{
+   return fluidViscosity;
+}
+
+// see: Fattahi, Waluga, Wohlmuth - "Large scale lattice Boltzmann simulation for the coupling of free and porous media flow"
+real_t calculateRescaledEffectiveViscosity( real_t fluidViscosity, real_t porosity )
+{
+   return fluidViscosity / porosity;
+}
+
+// see: J. R. Finn, M. Li, S. V. Apte - "Particle based modelling and simulation of natural sand dynamics in the wave
+// bottom boundary layer", Journal of Fluid Mechanics 796 (2016) 340–385. doi:10.1017/jfm.2016.246.
+real_t calculateEilersEffectiveViscosity( real_t fluidViscosity, real_t porosity )
+{
+   const real_t closePackingFraction = real_t(0.64);
+   const real_t intrinsicViscosity = real_t(2.5); //for monosized spheres
+   const real_t temp = real_t(1) + real_t(0.5) * intrinsicViscosity * ( real_t(1) - porosity ) / ( porosity  / closePackingFraction );
+   return fluidViscosity * temp * temp;
+}
+
+/*!\brief Evaluates the (local) effective viscosity.
+ *
+ * The effective viscosity (using the provided correlation) is evaluated for each cell and stored,
+ * as the respective omega value, in a field, that can be given to a suitable LBM sweep.
+ *
+ */
+class EffectiveViscosityFieldEvaluator
+{
+public:
+   typedef GhostLayerField< real_t, 1 >  ScalarField_T;
+
+   EffectiveViscosityFieldEvaluator( const BlockDataID & omegaFieldID, const ConstBlockDataID & solidVolumeFractionFieldID,
+                                     const real_t & fluidViscosity,
+                                     const boost::function<real_t (real_t, real_t)> & effectiveViscosityFunc )
+      : omegaFieldID_( omegaFieldID ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), fluidViscosity_( fluidViscosity ),
+        effectiveViscosityFunc_( effectiveViscosityFunc )
+   {
+   }
+
+   void operator()( IBlock * const block )
+   {
+      ScalarField_T* omegaField = block->getData<ScalarField_T>(omegaFieldID_);
+      const ScalarField_T* svfField   = block->getData<ScalarField_T>(solidVolumeFractionFieldID_);
+
+      WALBERLA_FOR_ALL_CELLS_XYZ(omegaField,
+          const real_t porosity = real_t(1) - svfField->get(x,y,z);
+          WALBERLA_ASSERT_FLOAT_UNEQUAL(porosity, real_t(0));
+
+          real_t effectiveViscosity = effectiveViscosityFunc_(fluidViscosity_, porosity);
+
+          //TODO: add level dependence for omega calculation
+          omegaField->get(x,y,z) = lbm::collision_model::omegaFromViscosity(effectiveViscosity);
+      )
+   }
+
+   void resetFluidViscosity( real_t newFluidViscosity )
+   {
+      fluidViscosity_ = newFluidViscosity;
+   }
+
+private:
+   const BlockDataID omegaFieldID_;
+   const ConstBlockDataID solidVolumeFractionFieldID_;
+   real_t fluidViscosity_;
+   boost::function<real_t (real_t, real_t)> effectiveViscosityFunc_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..c913952e3eeddaf1a507fc7edd687dfa3022d1f6
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h
@@ -0,0 +1,180 @@
+//======================================================================================================================
+//
+//  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 InteractionForceEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/debug/Debug.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/interpolators/all.h"
+#include "field/distributors/all.h"
+#include "field/GhostLayerField.h"
+
+#include "lbm/field/MacroscopicValueCalculation.h"
+#include "lbm/field/PdfField.h"
+#include "lbm/lattice_model/ForceModel.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/rigidbody/RigidBody.h"
+#include "pe/rigidbody/Sphere.h"
+#include "pe/Types.h"
+#include "pe/Materials.h"
+
+#include "pe_coupling/geometry/SphereEquivalentDiameter.h"
+
+#include "stencil/Directions.h"
+
+#include <boost/function.hpp>
+
+#include <vector>
+
+#include <cmath>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluator of the two most important interaction forces: drag and pressure gradient
+ *
+ * The evaluation of the drag force is based on the given drag correlation.
+ * The pressure gradient force requires a valid pressure gradient field.
+ * These two forces are evaluated for each body (requires interpolation of several quantities from the fluid fields)
+ * and then applied onto them.
+ * However, only the drag force (with negative sign) is applied (i.e. distributed) onto the fluid since it is assumed that
+ * the pressure force is already implicitly included in the formulation of the equations that describe the fluid flow.
+ *
+ * Note that the fluid velocity, contained in the velocityField, has to be the fluid-phase velocity (and not the volume-averaged velocity).
+ *
+ * For more infos on interpolators, see field interpolators in src/field/interpolators.
+ * For more infos on distributors, see src/field/distributors.
+ */
+
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+class InteractionForceEvaluator
+{  
+
+public:
+
+   typedef GhostLayerField< Vector3<real_t>, 1>             Vec3Field_T;
+   typedef GhostLayerField< real_t, 1>                      ScalarField_T;
+   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T>    Vec3FieldInterpolator_T;
+   typedef FieldInterpolator_T<ScalarField_T, FlagField_T>  ScalarFieldInterpolator_T;
+   typedef Distributor_T<Vec3Field_T, FlagField_T>          ForceDistributor_T;
+
+   InteractionForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                              const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID,
+                              const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
+                              const BlockDataID & velocityFieldID, const BlockDataID & solidVolumeFractionFieldID, const BlockDataID & pressureGradientFieldID,
+                              const boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t, real_t )> & dragForceCorrelationFunction,
+                              real_t fluidDynamicViscosity )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ),
+     dragForceCorrelationFunction_( dragForceCorrelationFunction ), fluidDynamicViscosity_( fluidDynamicViscosity )
+   {
+      velocityFieldInterpolatorID_            = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, velocityFieldID, flagFieldID, domainFlags );
+      solidVolumeFractionFieldInterpolatorID_ = field::addFieldInterpolator< ScalarFieldInterpolator_T, FlagField_T >( blockStorage, solidVolumeFractionFieldID, flagFieldID, domainFlags );
+      pressureGradientFieldInterpolatorID_    = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, pressureGradientFieldID, flagFieldID, domainFlags );
+      forceDistributorID_                     = field::addDistributor< ForceDistributor_T, FlagField_T >( blockStorage, forceFieldID, flagFieldID, domainFlags );
+   }
+
+   void operator()();
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   const BlockDataID pdfFieldID_;
+
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t, real_t )> dragForceCorrelationFunction_;
+
+   BlockDataID velocityFieldInterpolatorID_;
+   BlockDataID solidVolumeFractionFieldInterpolatorID_;
+   BlockDataID pressureGradientFieldInterpolatorID_;
+   BlockDataID forceDistributorID_;
+
+   real_t fluidDynamicViscosity_;
+};
+
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+void InteractionForceEvaluator< FlagField_T, FieldInterpolator_T, Distributor_T >
+::operator()()
+{
+
+   for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+   {
+
+      Vec3FieldInterpolator_T * velocityInterpolator               = blockIt->getData< Vec3FieldInterpolator_T >( velocityFieldInterpolatorID_ );
+      ScalarFieldInterpolator_T * solidVolumeFractionInterpolator  = blockIt->getData< ScalarFieldInterpolator_T >( solidVolumeFractionFieldInterpolatorID_ );
+      Vec3FieldInterpolator_T * pressureGradientInterpolator       = blockIt->getData< Vec3FieldInterpolator_T >( pressureGradientFieldInterpolatorID_ );
+      ForceDistributor_T * forceDistributor                        = blockIt->getData< ForceDistributor_T >( forceDistributorID_ );
+
+      for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+      {
+         //TODO check if body should be treated by DPM
+
+         Vector3<real_t> forceOnFluid( real_t(0) );
+
+         Vector3<real_t> bodyPosition = bodyIt->getPosition();
+
+         // interpolate fluid velocity to body position
+         Vector3<real_t> fluidVelocity( real_t(0) );
+         velocityInterpolator->get( bodyPosition, &fluidVelocity );
+
+         // interpolate solid volume fraction to body position
+         real_t solidVolumeFraction( real_t(0) );
+         solidVolumeFractionInterpolator->get( bodyPosition, &solidVolumeFraction );
+
+         WALBERLA_ASSERT_GREATER( solidVolumeFraction, real_t(0) );
+
+         // evaluate drag force
+         Vector3<real_t> bodyVelocity = bodyIt->getLinearVel();
+         real_t bodyDiameter = getSphereEquivalentDiameter( *(*bodyIt) );
+         real_t bodyVolume = bodyIt->getVolume();
+         real_t fluidDensity( real_t(1) );
+
+         Vector3<real_t> dragForce = dragForceCorrelationFunction_( fluidVelocity, bodyVelocity, solidVolumeFraction, bodyDiameter, fluidDynamicViscosity_, fluidDensity );
+
+         WALBERLA_ASSERT( !math::isnan(dragForce[0]) && !math::isnan(dragForce[1]) && !math::isnan(dragForce[2]),
+                          "NaN found in drag force " << dragForce << " for body at position " << bodyPosition );
+
+         bodyIt->addForce( dragForce );
+
+         forceOnFluid += ( -dragForce );
+
+         // evaluate pressure gradient force = - V * grad(p)
+         Vector3<real_t> pressureGradient( real_t(0) );
+         pressureGradientInterpolator->get( bodyPosition, &pressureGradient );
+         Vector3<real_t> pressureGradientForce = -bodyVolume * pressureGradient;
+         WALBERLA_ASSERT( !math::isnan(pressureGradientForce[0]) && !math::isnan(pressureGradientForce[1]) && !math::isnan(pressureGradientForce[2]),
+                          "NaN found in pressure gradient force " << pressureGradientForce << " for body at position " << bodyPosition );
+         bodyIt->addForce( pressureGradientForce );
+
+         // set/distribute force on fluid
+         forceDistributor->distribute( bodyPosition, &forceOnFluid );
+      }
+   }
+
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ad9c96596064dfe066dda7236e7c7f950fa0996
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h
@@ -0,0 +1,145 @@
+//======================================================================================================================
+//
+//  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 LiftForceEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/debug/Debug.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/interpolators/all.h"
+#include "field/distributors/all.h"
+#include "field/GhostLayerField.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/rigidbody/RigidBody.h"
+#include "pe/Types.h"
+#include "pe/Materials.h"
+
+#include "pe_coupling/geometry/SphereEquivalentDiameter.h"
+
+#include <boost/function.hpp>
+
+#include <cmath>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluator of the lift force based on the given lift correlation.
+ *
+ * The lift force is evaluated for each body (requires interpolation of several quantities from the fluid fields)
+ * and then applied onto them.
+ * The corresponding reaction force is added to the fluid via the given distributor.
+ *
+ * Note that the fluid velocity, contained in the velocityField, has to be the fluid-phase velocity (and not the volume-averaged velocity).
+ *
+ * For more infos on interpolators, see field interpolators in src/field/interpolators.
+ * For more infos on distributors, see src/field/distributors.
+ */
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+class LiftForceEvaluator
+{  
+
+public:
+
+   typedef GhostLayerField< Vector3<real_t>, 1>          Vec3Field_T;
+   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T;
+   typedef Distributor_T<Vec3Field_T, FlagField_T>       ForceDistributor_T;
+
+   LiftForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                       const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID, const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
+                       const BlockDataID & velocityFieldID, const BlockDataID & velocityCurlFieldID,
+                       const boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )> & liftForceCorrelationFunction,
+                       real_t fluidDynamicViscosity )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), liftForceCorrelationFunction_( liftForceCorrelationFunction ), fluidDynamicViscosity_( fluidDynamicViscosity )
+   {
+      velocityFieldInterpolatorID_     = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, velocityFieldID, flagFieldID, domainFlags );
+      velocityCurlFieldInterpolatorID_ = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, velocityCurlFieldID, flagFieldID, domainFlags );
+      forceDistributorID_              = field::addDistributor< ForceDistributor_T, FlagField_T >( blockStorage, forceFieldID, flagFieldID, domainFlags );
+   }
+
+   void operator()();
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   BlockDataID bodyStorageID_;
+
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )> liftForceCorrelationFunction_;
+
+   real_t fluidDynamicViscosity_;
+
+   BlockDataID velocityFieldInterpolatorID_;
+   BlockDataID velocityCurlFieldInterpolatorID_;
+   BlockDataID forceDistributorID_;
+};
+
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T, template<typename,typename> class Distributor_T >
+void LiftForceEvaluator< FlagField_T, FieldInterpolator_T, Distributor_T >
+::operator()()
+{
+
+   for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+   {
+
+      Vec3FieldInterpolator_T * velocityInterpolator     = blockIt->getData< Vec3FieldInterpolator_T >( velocityFieldInterpolatorID_ );
+      Vec3FieldInterpolator_T * velocityCurlInterpolator = blockIt->getData< Vec3FieldInterpolator_T >( velocityCurlFieldInterpolatorID_ );
+      ForceDistributor_T * forceDistributor              = blockIt->getData< ForceDistributor_T >( forceDistributorID_ );
+
+      for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+      {
+         //TODO check if body should be treated by DPM
+
+         Vector3<real_t> forceOnFluid( real_t(0) );
+
+         Vector3<real_t> bodyPosition = bodyIt->getPosition();
+         Vector3<real_t> bodyVelocity = bodyIt->getLinearVel();
+
+         real_t fluidDensity( real_t(1) );
+         real_t bodyDiameter = getSphereEquivalentDiameter( *(*bodyIt) );
+
+         // interpolate fluid velocity and fluid curl to body position
+         Vector3<real_t> fluidVelocity( real_t(0) );
+         velocityInterpolator->get( bodyPosition, &fluidVelocity );
+
+         Vector3<real_t> velocityCurl( real_t(0) );
+         velocityCurlInterpolator->get( bodyPosition, &velocityCurl );
+
+         // evaluate lift force according to empirical model
+         Vector3<real_t> liftForce = liftForceCorrelationFunction_(fluidVelocity, velocityCurl, bodyVelocity, bodyDiameter, fluidDynamicViscosity_, fluidDensity );
+         WALBERLA_ASSERT( !math::isnan(liftForce[0]) && !math::isnan(liftForce[1]) && !math::isnan(liftForce[2]),
+                          "NaN found in lift force for body at position " << bodyPosition );
+         bodyIt->addForce( liftForce );
+         forceOnFluid += ( -liftForce );
+
+         // set/distribute force on fluid
+         forceDistributor->distribute( bodyPosition, &forceOnFluid );
+      }
+   }
+
+}
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/LubricationForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/LubricationForceEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..d69046b3ec98814ed75c21226396f1b757478a4a
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/LubricationForceEvaluator.h
@@ -0,0 +1,326 @@
+//======================================================================================================================
+//
+//  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 LubricationForceEvaluator.h
+//! \ingroup pe_coupling
+//! \author Dominik Bartuschat
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/logging/all.h"
+#include "core/debug/Debug.h"
+#include "domain_decomposition/BlockStorage.h"
+#include "lbm/field/PdfField.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/rigidbody/Plane.h"
+#include "pe/rigidbody/Sphere.h"
+#include "pe/utility/Distance.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluates the lubrication force between for a sphere-sphere or a sphere-plane pair.
+ *
+ * The force is calculated as given in:
+ *  R. Ball, J. Melrose, A simulation technique for many spheres in quasi–static motion under frame–invariant pair drag
+ *  and Brownian forces, Physica A: Statistical Mechanics and its Applications 247 (1) (1997) 444–472.
+ *  doi:10.1016/S0378-4371(97)00412-3.
+ *
+ * However, currently only the normal component is included.
+ *
+ * These formulas contain more components than the version from "pe_coupling/utility/LubricationCorrection.h"
+ *
+ */
+class LubricationForceEvaluator
+{
+public:
+
+   LubricationForceEvaluator ( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                               const shared_ptr<pe::BodyStorage> & globalBodyStorage, const BlockDataID & bodyStorageID,
+                               real_t dynamicViscosity, real_t cutOffDistance = real_t(2) / real_t(3) )
+      : blockStorage_ ( blockStorage )
+      , globalBodyStorage_( globalBodyStorage )
+      , bodyStorageID_( bodyStorageID )
+      , dynamicViscosity_( dynamicViscosity )
+      , cutOffDistance_( cutOffDistance )
+   { }
+
+   void operator()( );
+
+private:
+
+   // helper functions
+   void treatLubricationSphrSphr ( real_t nu_Lattice, real_t cutOff, real_t minimalGap, const pe::SphereID sphereI, const pe::SphereID sphereJ, const math::AABB & blockAABB );
+   void treatLubricationSphrPlane( real_t nu_Lattice, real_t cutOff, real_t minimalGap, const pe::SphereID sphereI, const pe::ConstPlaneID planeJ );
+
+   pe::Vec3 compLubricationSphrSphr ( real_t nu_Lattice, real_t gap, real_t cutOff, const pe::SphereID sphereI, const pe::SphereID sphereJ ) const;
+   pe::Vec3 compLubricationSphrPlane( real_t nu_Lattice, real_t gap, real_t cutOff, const pe::SphereID sphereI, const pe::ConstPlaneID planeJ) const;
+
+   // member variables
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   shared_ptr<pe::BodyStorage> globalBodyStorage_;
+   const BlockDataID pdfFieldID_;
+   const BlockDataID bodyStorageID_;
+
+   real_t dynamicViscosity_;
+   real_t cutOffDistance_;
+
+}; // class LubricationForceEvaluator
+
+
+void LubricationForceEvaluator::operator ()( )
+{
+   WALBERLA_LOG_PROGRESS( "Calculating Lubrication Force" );
+
+   // cut off distance for lubrication force
+   const real_t cutOff = cutOffDistance_;
+
+   for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+   {
+      // minimal gap used to limit the lubrication force
+      const real_t minimalGap = real_t(1e-5) * blockStorage_->dx( blockStorage_->getLevel( *blockIt ) );
+
+      real_t nu_L = dynamicViscosity_;
+
+      // loop over all rigid bodies
+      for( auto body1It = pe::BodyIterator::begin( *blockIt, bodyStorageID_ ); body1It != pe::BodyIterator::end(); ++body1It )
+      {
+         // lubrication forces for spheres
+         if ( body1It->getTypeID() == pe::Sphere::getStaticTypeID() )
+         {
+            pe::SphereID sphereI = static_cast<pe::SphereID> ( *body1It );
+
+            // only moving spheres are considered
+            if ( sphereI->isFixed() )
+               continue;
+
+            auto copyBody1It = body1It;
+            // loop over all rigid bodies after current body1 to avoid double forces
+            for( auto body2It = (++copyBody1It); body2It != pe::BodyIterator::end(); ++body2It )
+            {
+               // sphere-sphere lubrication
+               if ( body2It->getTypeID() == pe::Sphere::getStaticTypeID() )
+               {
+                  pe::SphereID sphereJ = static_cast<pe::SphereID>( *body2It );
+                  treatLubricationSphrSphr( nu_L, cutOff, minimalGap, sphereI, sphereJ, blockIt->getAABB() );
+               }
+            }
+         }
+      }
+
+      // lubrication correction for local bodies with global bodies (for example sphere-plane)
+      for( auto body1It = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID_ ); body1It != pe::LocalBodyIterator::end(); ++body1It )
+      {
+         if ( body1It->getTypeID() == pe::Sphere::getStaticTypeID() )
+         {
+            pe::SphereID sphereI = static_cast<pe::SphereID> ( *body1It );
+
+            // only moving spheres are considered
+            if ( sphereI->isFixed() )
+               continue;
+
+            for (auto body2It = globalBodyStorage_->begin(); body2It != globalBodyStorage_->end(); ++body2It)
+            {
+               if ( body2It->getTypeID() == pe::Plane::getStaticTypeID() )
+               {
+                  // sphere-plane lubrication
+                  pe::PlaneID planeJ = static_cast<pe::PlaneID>( *body2It );
+                  treatLubricationSphrPlane( nu_L, cutOff, minimalGap, sphereI, planeJ );
+               } else if ( body2It->getTypeID() == pe::Sphere::getStaticTypeID() )
+               {
+                  // sphere-sphere lubrication
+                  pe::SphereID sphereJ = static_cast<pe::SphereID>( *body2It );
+                  treatLubricationSphrSphr( nu_L, cutOff, minimalGap, sphereI, sphereJ, blockIt->getAABB() );
+               }
+            }
+         }
+      }
+   }
+}
+
+void LubricationForceEvaluator::treatLubricationSphrSphr( real_t nu_Lattice, real_t cutOff, real_t minimalGap,
+                                                          const pe::SphereID sphereI, const pe::SphereID sphereJ, const math::AABB & blockAABB )
+{
+
+   WALBERLA_ASSERT_UNEQUAL( sphereI->getSystemID(), sphereJ->getSystemID() );
+
+   real_t gap = pe::getSurfaceDistance( sphereI, sphereJ );
+
+   if ( gap > cutOff || gap < real_t(0) )
+   {
+      WALBERLA_LOG_DETAIL("gap " << gap << " larger than cutOff - continue");
+      return;
+   }
+
+   if ( gap < minimalGap )
+   {
+      WALBERLA_LOG_DETAIL("gap " << gap << " smaller than minimal gap " << minimalGap << " - using minimal gap");
+      gap = minimalGap;
+   }
+
+   const pe::Vec3 &posSphereI = sphereI->getPosition();
+   const pe::Vec3 &posSphereJ = sphereJ->getPosition();
+   pe::Vec3 fLub(0);
+
+   // compute (global) coordinate between spheres' centers of gravity
+   pe::Vec3 midPoint( (posSphereI + posSphereJ ) * real_c(0.5) );
+
+   // Let process on which midPoint lies do the lubrication correction
+   // or the local process of sphereI if sphereJ is global
+   if ( blockAABB.contains(midPoint) || sphereJ->isGlobal() )
+   {
+      fLub = compLubricationSphrSphr(nu_Lattice, gap, cutOff, sphereI, sphereJ);
+      sphereI->addForce(fLub);
+      if( !sphereJ->isFixed() )
+         sphereJ->addForce( -fLub);
+
+      WALBERLA_LOG_DETAIL( "Lubrication force on sphere " << sphereI->getID() << " from sphere " << sphereJ->getID() << " is:" << fLub);
+      WALBERLA_LOG_DETAIL( "Lubrication force on sphere " << sphereJ->getID() << " from sphere " << sphereI->getID() << " is:" << -fLub << "\n");
+   }
+
+}
+
+void LubricationForceEvaluator::treatLubricationSphrPlane( real_t nu_Lattice, real_t cutOff, real_t minimalGap,
+                                                           const pe::SphereID sphereI, const pe::ConstPlaneID planeJ )
+{
+
+   real_t gap = pe::getSurfaceDistance( sphereI, planeJ );
+
+   if ( gap > cutOff || gap < real_t(0) )
+   {
+      WALBERLA_LOG_DETAIL("gap " << gap << " larger than cutOff - continue");
+      return;
+   }
+
+   if ( gap < minimalGap )
+   {
+      WALBERLA_LOG_DETAIL("gap " << gap << " smaller than minimal gap " << minimalGap << " - using minimal gap");
+      gap = minimalGap;
+   }
+
+   pe::Vec3 fLub = compLubricationSphrPlane( nu_Lattice, gap, cutOff, sphereI, planeJ);
+
+   WALBERLA_LOG_DETAIL( "Lubrication force on sphere " << sphereI->getID() << " to plane with id " << planeJ->getID() << " is:" << fLub << std::endl );
+   sphereI->addForce( fLub );
+
+}
+
+
+pe::Vec3 LubricationForceEvaluator::compLubricationSphrSphr( real_t nu_Lattice, real_t gap, real_t cutOff,
+                                                             const pe::SphereID sphereI, const pe::SphereID sphereJ) const
+{
+   const pe::Vec3 &posSphereI = sphereI->getPosition();
+   const pe::Vec3 &posSphereJ = sphereJ->getPosition();
+
+   const pe::Vec3 &tmpVec = posSphereJ - posSphereI;
+   const pe::Vec3 &rIJ    = tmpVec.getNormalized();
+
+   real_t diameterSphereI = real_t(2) * sphereI->getRadius();
+   real_t diameterSphereJ = real_t(2) * sphereJ->getRadius();
+
+   pe::Vec3 velDiff(sphereI->getLinearVel() - sphereJ->getLinearVel());
+
+   real_t length = velDiff * rIJ;
+
+   real_t d = real_t(2) * diameterSphereI * diameterSphereJ / ( diameterSphereI + diameterSphereJ );
+   real_t h = gap;
+   real_t r = d + h;
+   real_t a_sq = ( real_t(3) * nu_Lattice * walberla::math::PI * d / real_t(2) ) * ( d / ( real_t(4) * h ) + ( real_t(18) / real_t(40) ) * std::log( d / ( real_t(2) * h ) ) +
+                                                                                     ( real_t(9)/real_t(84) ) * ( h / d ) * std::log( d/( real_t(2)*h ) ) );
+   real_t a_sh = ( nu_Lattice * walberla::math::PI * d / real_t(2) ) * std::log( d / ( real_t(2) * h ) ) * ( d + h ) * ( d + h ) / real_t(4);
+   pe::Vec3 fLub( - a_sq * length * rIJ - a_sh * ( real_t(2) / r ) * ( real_t(2) / r ) * ( velDiff - length * rIJ ) );
+
+   WALBERLA_LOG_DETAIL_SECTION()
+   {
+      std::stringstream ss;
+      ss << "Sphere I: \n uid:" << sphereI->getID() << "\n";
+      ss << "vel: "  << sphereI->getLinearVel() << "\n";
+      ss << "rad: "  << diameterSphereI * real_t(0.5) << "\n";
+      ss << "pos: "  << posSphereI << "\n\n";
+
+      ss << "Sphere J: \n uid:" << sphereJ->getID() << "\n";
+      ss << "vel: "  << sphereJ->getLinearVel() << "\n";
+      ss << "rad: "  << diameterSphereJ * real_t(0.5) << "\n";
+      ss << "pos: "  << posSphereJ << "\n\n";
+
+      real_t distance = gap + diameterSphereI * real_t(0.5) + diameterSphereJ * real_t(0.5);
+      ss << "distance: " << distance << "\n";
+      ss << "nu: "     << nu_Lattice << "\n";
+
+      ss << "gap: "     << gap << "\n";
+      ss << "cutOff: "  << cutOff << "\n";
+      ss << "velDiff "  << velDiff << "\n";
+      ss << "rIJ "      << rIJ << "\n\n";
+
+      ss << "Resulting lubrication force: " << fLub << "\n";
+
+      WALBERLA_LOG_DETAIL( ss.str() );
+   }
+
+   return fLub;
+}
+
+pe::Vec3 LubricationForceEvaluator::compLubricationSphrPlane( real_t nu_Lattice, real_t gap, real_t cutOff,
+                                                              const pe::SphereID sphereI, const pe::ConstPlaneID planeJ) const
+{
+   const pe::Vec3 &posSphereI( sphereI->getPosition() );
+   real_t radiusSphereI = sphereI->getRadius();
+
+   const pe::Vec3 &planeNormal( planeJ->getNormal() ); // took negative of normal from sphere to plane (plane's normal) - sign cancels out anyway
+   pe::Vec3 rIJ( planeNormal.getNormalized() );        // for safety reasons, normalize normal
+
+   real_t length = sphereI->getLinearVel() *  rIJ;
+
+   pe::Vec3 v1 = sphereI->getLinearVel();
+   real_t d = real_t(4) * radiusSphereI;
+   real_t h = gap;
+   real_t r = d + h;
+   real_t a_sq = ( real_t(3) * nu_Lattice * walberla::math::PI * d / real_t(2) ) * ( d / ( real_t(4) * h ) + ( real_t(18) / real_t(40) ) * std::log( d / ( real_t(2) * h ) ) +
+                                                                                     ( real_t(9)/real_t(84) ) * ( h / d ) * std::log( d/( real_t(2)*h ) ) );
+   real_t a_sh = ( nu_Lattice * walberla::math::PI * d / real_t(2) ) * std::log( d / ( real_t(2) * h ) ) * ( d + h ) * ( d + h ) / real_t(4);
+   pe::Vec3 fLub( - a_sq * length * rIJ - a_sh * ( real_t(2) / r ) * ( real_t(2) / r ) * ( v1 - length * rIJ ) );
+
+   WALBERLA_LOG_DETAIL_SECTION() {
+      std::stringstream ss;
+      ss << "Sphere I: \n uid:" << sphereI->getID() << "\n";
+      ss << "vel: "  << sphereI->getLinearVel() << "\n";
+      ss << "rad: "  << radiusSphereI << "\n";
+      ss << "pos: "  << posSphereI << "\n\n";
+
+      real_t distance = gap + radiusSphereI;
+      ss << "distance: " << distance << "\n";
+      ss << "nu: "       << nu_Lattice << "\n";
+
+      ss << "gap: "     << gap << "\n";
+      ss << "cutOff: "  << cutOff << "\n";
+      ss << "velDiff "  << sphereI->getLinearVel() << "\n";
+      ss << "rIJ "      << -rIJ << "\n\n";
+
+      ss << "Resulting lubrication force: " << fLub << "\n";
+
+      WALBERLA_LOG_DETAIL( ss.str() );
+   }
+
+   return fLub;
+}
+
+} // discrete_particle_methods
+} // pe_coupling
+} // walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..61998ae6dc80918604a101a9eed28a5d6f6cf8df
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h
@@ -0,0 +1,75 @@
+//======================================================================================================================
+//
+//  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 PressureFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator of the pressure field, given a PDF field.
+ *
+ * Uses p = rho / 3 = sum_q f_q / 3, to compute the pressure field from a given PDF field.
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class PressureFieldEvaluator
+{
+public:
+   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
+   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+
+   PressureFieldEvaluator( const BlockDataID & pressureFieldID, const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & boundaryHandlingID )
+      : pressureFieldID_( pressureFieldID ), pdfFieldID_( pdfFieldID ), boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const PdfField_T* pdfField                   = block->getData< PdfField_T >( pdfFieldID_ );
+      ScalarField_T* pressureField                 = block->getData< ScalarField_T >( pressureFieldID_ );
+      const BoundaryHandling_T * boundaryHandling  = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      const real_t c_s_sqr = real_t(1)/real_t(3);
+      WALBERLA_FOR_ALL_CELLS_XYZ( pdfField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            real_t density = pdfField->getDensity(x,y,z);
+            WALBERLA_ASSERT( !math::isnan(density), "NaN found when evaluating density in cell " << Cell(x,y,z) );
+            pressureField->get(x,y,z) = c_s_sqr * density;
+         }
+      );
+   }
+
+private:
+   const BlockDataID pressureFieldID_;
+   const ConstBlockDataID pdfFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..2e8511fca1720e12df06cf04bde7c267fba6bb0e
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h
@@ -0,0 +1,120 @@
+//======================================================================================================================
+//
+//  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 PressureGradientFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "stencil/Directions.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator of the pressure gradient field, given a pressure field.
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class PressureGradientFieldEvaluator
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
+   typedef GhostLayerField< real_t, 1>            ScalarField_T;
+   typedef typename LatticeModel_T::Stencil       Stencil_T;
+
+   PressureGradientFieldEvaluator( const BlockDataID & pressureGradientFieldID, const ConstBlockDataID & pressureFieldID,
+                                   const ConstBlockDataID & boundaryHandlingID )
+      : pressureGradientFieldID_( pressureGradientFieldID ), pressureFieldID_( pressureFieldID ),
+        boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      VectorField_T* pressureGradientField    = block->getData< VectorField_T >( pressureGradientFieldID_ );
+      const ScalarField_T* pressureField            = block->getData< ScalarField_T >( pressureFieldID_ );
+      const BoundaryHandling_T * boundaryHandling   = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( pressureGradientField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            pressureGradientField->get(x,y,z) = getPressureGradient( Cell(x,y,z), pressureField, boundaryHandling );
+         }
+      );
+   }
+
+private:
+
+   Vector3<real_t> getPressureGradient( const Cell & cell, const ScalarField_T * pressureField, const BoundaryHandling_T * boundaryHandling )
+   {
+
+      // temporarily store pressure values of surrounding cells
+      std::vector<real_t> pressureValues( Stencil_T::Size, real_t(0) );
+
+      // store pressure value in center cell to potentially apply Neumann like boundary conditions
+      real_t pressureInCenterCell = pressureField->get( cell );
+
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         // check if boundary treatment is necessary
+         if( !boundaryHandling->isDomain( cell + *dir ) )
+         {
+            // copy from center cell
+            pressureValues[ *dir ] = pressureInCenterCell;
+         } else 
+         {
+            pressureValues[ *dir ] = pressureField->get( cell + *dir );
+
+         }
+      }
+
+      // apply gradient formula
+      // uses LBM weighting to determine gradient
+      // See: Ramadugu et al - "Lattice differential operators for computational physics" (2013)
+      // with T = c_s**2
+      const real_t inv_c_s_sqr = real_c(3);
+      Vector3<real_t> gradient( real_c(0) );
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         real_t pressure = pressureValues[ *dir ];
+         gradient[0] += LatticeModel_T::w[ dir.toIdx() ] * real_c(dir.cx()) * pressure;
+         gradient[1] += LatticeModel_T::w[ dir.toIdx() ] * real_c(dir.cy()) * pressure;
+         gradient[2] += LatticeModel_T::w[ dir.toIdx() ] * real_c(dir.cz()) * pressure;
+      }
+      gradient *= inv_c_s_sqr;
+
+      return gradient;
+   }
+
+   const BlockDataID pressureGradientFieldID_;
+   const ConstBlockDataID pressureFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..776aba2298313a10387c1dd6f781b7ef86f43d66
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h
@@ -0,0 +1,102 @@
+//======================================================================================================================
+//
+//  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 SolidVolumeFractionFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/distributors/all.h"
+#include "field/GhostLayerField.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evalutor of the solid volume fraction field.
+ *
+ * Updates the solid volume fraction field. Includes firstly removing all old entries from the field, then remapping
+ * the local bodies' volumes to the cells.
+ * Potentially writes to the ghost layer of the solid volume fraction field which thus requires a special
+ * PullReducion communication afterwards.
+ *
+ * Depending on the used Distributor_T, the resulting solid volume fraction field will vary:
+ * - NearestNeighborDistributor:
+ *       Corresponds to simply assigning the total body volume to the cell containing the body center.
+ * - KernelDistributor:
+ *       The body's volume is not directly assigned to one cell only but spread over the neighboring cells as well.
+ *       See Finn, Li, Apte - "Particle based modelling and simulation of natural sand dynamics in the wave bottom boundary layer" (2016)
+ *       for the application, even though different kernel was used there.
+ *
+ * For more infos on distributors, see src/field/distributors.
+ */
+template< typename FlagField_T, template <typename,typename> class Distributor_T >
+class SolidVolumeFractionFieldEvaluator
+{
+public:
+
+   typedef GhostLayerField< real_t, 1 >              ScalarField_T;
+   typedef Distributor_T<ScalarField_T, FlagField_T> ScalarDistributor_T;
+
+   SolidVolumeFractionFieldEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                                      const BlockDataID & solidVolumeFractionFieldID, const BlockDataID & bodyStorageID,
+                                      const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags )
+   : blockStorage_( blockStorage ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), bodyStorageID_( bodyStorageID )
+   {
+      scalarDistributorID_ = field::addDistributor< ScalarDistributor_T, FlagField_T >( blockStorage, solidVolumeFractionFieldID, flagFieldID, domainFlags );
+   }
+
+   void operator()( IBlock * const block )
+   {
+      ScalarField_T* svfField = block->getData< ScalarField_T >( solidVolumeFractionFieldID_ );
+
+      // reset field
+      WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( svfField,
+         svfField->get(x,y,z) = real_c(0);
+      )
+
+      ScalarDistributor_T * scalarDistributor = block->getData< ScalarDistributor_T >( scalarDistributorID_ );
+
+      // assign the local bodies' volume to the cell, depending on the chosen Distributor_T
+      for( auto bodyIt = pe::LocalBodyIterator::begin(*block, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+      {
+         real_t bodyVolume = bodyIt->getVolume();
+         const Vector3<real_t> bodyPosition = bodyIt->getPosition();
+
+         scalarDistributor->distribute( bodyPosition, &bodyVolume );
+      }
+   }
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   BlockDataID solidVolumeFractionFieldID_;
+   BlockDataID bodyStorageID_;
+   BlockDataID scalarDistributorID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..29dce0a61dbaccc8245379b21784b271017c8dbe
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h
@@ -0,0 +1,137 @@
+//======================================================================================================================
+//
+//  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 StressTensorGradientFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "core/math/Matrix3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "stencil/Directions.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator of the stress tensor gradient field, given a velocity field.
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class StressTensorGradientFieldEvaluator
+{
+public:
+   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
+   typedef typename LatticeModel_T::Stencil       Stencil_T;
+
+   StressTensorGradientFieldEvaluator( const BlockDataID & stressTensorGradientFieldID,
+                                       const ConstBlockDataID & velocityGradientFieldID,
+                                       const ConstBlockDataID & boundaryHandlingID,
+                                       const real_t & dynamicFluidViscosity )
+      : stressTensorGradientFieldID_( stressTensorGradientFieldID ),
+        velocityGradientFieldID_( velocityGradientFieldID ),
+        boundaryHandlingID_( boundaryHandlingID ),
+        dynamicFluidViscosity_( dynamicFluidViscosity )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      VectorField_T* stressTensorGradientField = block->getData< VectorField_T >( stressTensorGradientFieldID_ );
+      const TensorField_T* velocityGradientField     = block->getData< TensorField_T >( velocityGradientFieldID_ );
+      const BoundaryHandling_T * boundaryHandling    = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( stressTensorGradientField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            stressTensorGradientField->get(x,y,z) = getStressTensorGradient( Cell(x,y,z), velocityGradientField, boundaryHandling );
+         }
+      );
+   }
+
+   void resetViscosity( real_t newDynamicFluidViscosity )
+   {
+      dynamicFluidViscosity_ = newDynamicFluidViscosity;
+   }
+
+private:
+
+   // calculates grad( nu * ( ( grad(u) ) + (grad(u))**T ) ), i.e. the gradient of the stress tensor
+   Vector3<real_t> getStressTensorGradient( const Cell & cell, const TensorField_T* velocityGradientField, const BoundaryHandling_T * boundaryHandling )
+   {
+
+      std::vector< Matrix3< real_t > > stressTensorValues( Stencil_T::Size, Matrix3< real_t >(real_t(0)) );
+
+
+      Matrix3< real_t > velGradientInCenterCell = velocityGradientField->get( cell );
+      Matrix3< real_t > stressTensorInCenterCell = ( velGradientInCenterCell + velGradientInCenterCell.getTranspose() ) * dynamicFluidViscosity_;
+
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         // check if boundary treatment is necessary
+         if( !boundaryHandling->isDomain( cell + *dir ) )
+         {
+            // copy from center cell
+            stressTensorValues[ *dir ] = stressTensorInCenterCell;
+         } else {
+            Matrix3< real_t > velGradient = velocityGradientField->get( cell + *dir );
+            stressTensorValues[ *dir ] = ( velGradient + velGradient.getTranspose() ) * dynamicFluidViscosity_;
+         }
+      }
+
+      // obtain the gradient of the tensor using the gradient formula
+      // See: Ramadugu et al - "Lattice differential operators for computational physics" (2013)
+      // with T = c_s**2
+      const real_t inv_c_s_sqr = real_t(3);
+      Vector3<real_t> gradStressTensor( real_t(0) );
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         Vector3<real_t> latticeVel( real_c(dir.cx()), real_c(dir.cy()), real_c(dir.cz()) );
+         Matrix3<real_t> & tempTau = stressTensorValues[ *dir ];
+         Vector3<real_t> tau1( tempTau[0], tempTau[3], tempTau[6] );
+         Vector3<real_t> tau2( tempTau[1], tempTau[4], tempTau[7] );
+         Vector3<real_t> tau3( tempTau[2], tempTau[5], tempTau[8] );
+
+         gradStressTensor[0] += LatticeModel_T::w[ dir.toIdx() ] * latticeVel * tau1;
+         gradStressTensor[1] += LatticeModel_T::w[ dir.toIdx() ] * latticeVel * tau2;
+         gradStressTensor[2] += LatticeModel_T::w[ dir.toIdx() ] * latticeVel * tau3;
+
+      }
+      gradStressTensor *= inv_c_s_sqr;
+
+      return gradStressTensor;
+
+   }
+
+   const BlockDataID stressTensorGradientFieldID_;
+   const ConstBlockDataID velocityGradientFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+   real_t dynamicFluidViscosity_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..0442814dbaa2881004ca675da1fa8af03895bbc3
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h
@@ -0,0 +1,112 @@
+//======================================================================================================================
+//
+//  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 VelocityCurlFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "stencil/Directions.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator of the curl of a given velocity field.
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class VelocityCurlFieldEvaluator
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
+   typedef typename LatticeModel_T::Stencil       Stencil_T;
+
+   VelocityCurlFieldEvaluator( const BlockDataID & velocityCurlFieldID, const ConstBlockDataID & velocityFieldID,
+                               const ConstBlockDataID & boundaryHandlingID )
+      : velocityCurlFieldID_( velocityCurlFieldID ), velocityFieldID_( velocityFieldID ),
+        boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const VectorField_T* velocityField            = block->getData< VectorField_T >( velocityFieldID_ );
+      VectorField_T* velocityCurlField        = block->getData< VectorField_T >( velocityCurlFieldID_ );
+      const BoundaryHandling_T * boundaryHandling   = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( velocityCurlField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            velocityCurlField->get(x,y,z) = getVelocityCurl( Cell(x,y,z), velocityField, boundaryHandling );
+         }
+      );
+   }
+
+private:
+
+   Vector3<real_t> getVelocityCurl( const Cell & cell, const VectorField_T * velocityField, const BoundaryHandling_T * boundaryHandling )
+   {
+
+      std::vector< Vector3<real_t> > velocityValues( Stencil_T::Size, Vector3<real_t>(real_t(0)) );
+
+      Vector3<real_t> velocityInCenterCell = velocityField->get( cell );
+
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         // check if boundary treatment is necessary
+         if( !boundaryHandling->isDomain( cell + *dir ) )
+         {
+            // copy from center cell
+            velocityValues[ *dir ] = velocityInCenterCell;
+         } else {
+            velocityValues[ *dir ] = velocityField->get( cell + *dir );
+         }
+      }
+
+      // apply curl formula
+      // See: Ramadugu et al - Lattice differential operators for computational physics (2013)
+      // with T = c_s**2
+      const real_t inv_c_s_sqr = real_t(3);
+      Vector3<real_t> curl( real_t(0) );
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         Vector3<real_t> latticeVel( real_c(dir.cx()), real_c(dir.cy()), real_c(dir.cz()) );
+         curl += LatticeModel_T::w[ dir.toIdx() ] * ( latticeVel % velocityValues[ *dir ] );
+      }
+      curl *= inv_c_s_sqr;
+
+      return curl;
+   }
+
+   const BlockDataID velocityCurlFieldID_;
+   const ConstBlockDataID velocityFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..70d62853b3fce1c5a88defc46d8c9a0fcd5c48d8
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.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 VelocityFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator of the velocity field, given a PDF field.
+ *
+ * Calculates the velocity in each domain cell, given a PDF field.
+ * Since in LBM, the macroscopic velocity depends on the local fluid forcing, one has to pay attention to the currently
+ * set forces in the corresponding force field before evaluating the velocity.
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class VelocityFieldEvaluator
+{
+public:
+   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+
+   VelocityFieldEvaluator( const BlockDataID & velocityFieldID,
+                           const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & boundaryHandlingID )
+      : velocityFieldID_( velocityFieldID ), pdfFieldID_( pdfFieldID ), boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const PdfField_T* pdfField                   = block->getData< PdfField_T >( pdfFieldID_ );
+      VelocityField_T* velocityField         = block->getData< VelocityField_T >( velocityFieldID_ );
+      const BoundaryHandling_T * boundaryHandling  = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( pdfField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            WALBERLA_ASSERT( !math::isnan(pdfField->getVelocity(x,y,z)[0]) &&
+                             !math::isnan(pdfField->getVelocity(x,y,z)[1]) &&
+                             !math::isnan(pdfField->getVelocity(x,y,z)[2]), "NaN found when evaluating velocity in cell " << Cell(x,y,z) );
+            velocityField->get(x,y,z) = pdfField->getVelocity(x,y,z);
+         }
+      );
+   }
+
+private:
+   const BlockDataID velocityFieldID_;
+   const ConstBlockDataID pdfFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..9e0397a5ab328b563e3e909801c7857b99ae07b1
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h
@@ -0,0 +1,144 @@
+//======================================================================================================================
+//
+//  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 VelocityGradientFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "core/math/Matrix3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "stencil/Directions.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+
+/*!\brief Evaluator of the velocity gradient field, given a velocity field.
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class VelocityGradientFieldEvaluator
+{
+public:
+   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
+   typedef typename LatticeModel_T::Stencil       Stencil_T;
+
+   VelocityGradientFieldEvaluator( const BlockDataID & velocityGradientFieldID,
+                                   const ConstBlockDataID & velocityFieldID,
+                                   const ConstBlockDataID & boundaryHandlingID )
+      : velocityGradientFieldID_( velocityGradientFieldID ),
+        velocityFieldID_( velocityFieldID ),
+        boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const VectorField_T* velocityField = block->getData< VectorField_T >( velocityFieldID_ );
+      TensorField_T* velocityGradientField = block->getData< TensorField_T >( velocityGradientFieldID_ );
+      const BoundaryHandling_T * boundaryHandling = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      Matrix3<real_t> velocityGradient( real_t(0) );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( velocityGradientField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            getVelocityGradient( Cell(x,y,z), velocityField, boundaryHandling, velocityGradient );
+            velocityGradientField->get(x,y,z) = velocityGradient;
+         }
+      );
+   }
+
+private:
+
+   // calculates grad(u)
+   // grad(u) =
+   // | du1/dx1 du2/dx1 du3/dx1 |   | 0 1 2 |   | 0,0  0,1  0,2 |
+   // | du1/dx2 du2/dx2 du3/dx2 | = | 3 4 5 | = | 1,0  1,1  1,2 |
+   // | du1/dx3 du2/dx3 du3/dx3 |   | 6 7 8 |   | 2,0  2,1  2,2 |
+   void getVelocityGradient( const Cell & cell, const VectorField_T * velocityField, const BoundaryHandling_T * boundaryHandling, Matrix3<real_t> & velocityGradient )
+   {
+
+      std::vector< Vector3<real_t> > velocityValues( Stencil_T::Size, Vector3<real_t>(real_t(0)) );
+
+      Vector3<real_t> velocityInCenterCell = velocityField->get( cell );
+
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         // check if boundary treatment is necessary
+         if( !boundaryHandling->isDomain( cell + *dir ) )
+         {
+            // copy from center cell
+            velocityValues[ *dir ] = velocityInCenterCell;
+         } else {
+            velocityValues[ *dir ] = velocityField->get( cell + *dir );
+         }
+      }
+
+      // obtain the matrix grad(u) with the help of the gradient formula from
+      // See: Ramadugu et al - Lattice differential operators for computational physics (2013)
+      // with T = c_s**2
+      const real_t inv_c_s_sqr = real_t(3);
+      velocityGradient = real_t(0);
+      for( auto dir = Stencil_T::beginNoCenter(); dir != Stencil_T::end(); ++dir)
+      {
+         real_t cx = real_c(dir.cx());
+         real_t cy = real_c(dir.cy());
+         real_t cz = real_c(dir.cz());
+
+         // grad(ux)
+         real_t ux = velocityValues[ *dir ][0];
+         velocityGradient[ 0 ] += LatticeModel_T::w[ dir.toIdx() ] * cx * ux;
+         velocityGradient[ 3 ] += LatticeModel_T::w[ dir.toIdx() ] * cy * ux;
+         velocityGradient[ 6 ] += LatticeModel_T::w[ dir.toIdx() ] * cz * ux;
+
+         // grad(uy)
+         real_t uy = velocityValues[ *dir ][1];
+         velocityGradient[ 1 ] += LatticeModel_T::w[ dir.toIdx() ] * cx * uy;
+         velocityGradient[ 4 ] += LatticeModel_T::w[ dir.toIdx() ] * cy * uy;
+         velocityGradient[ 7 ] += LatticeModel_T::w[ dir.toIdx() ] * cz * uy;
+
+         // grad(uz)
+         real_t uz = velocityValues[ *dir ][2];
+         velocityGradient[ 2 ] += LatticeModel_T::w[ dir.toIdx() ] * cx * uz;
+         velocityGradient[ 5 ] += LatticeModel_T::w[ dir.toIdx() ] * cy * uz;
+         velocityGradient[ 8 ] += LatticeModel_T::w[ dir.toIdx() ] * cz * uz;
+
+      }
+      velocityGradient *= inv_c_s_sqr;
+
+   }
+
+   const BlockDataID velocityGradientFieldID_;
+   const ConstBlockDataID velocityFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..62e70f9736d34333d272aa91288bfe4bdced4e11
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h
@@ -0,0 +1,103 @@
+//======================================================================================================================
+//
+//  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 VelocityTotalTimeDerivativeFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "core/math/Matrix3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+
+/*!\brief Evaluator of the total derivative of the fluid velocity.
+ *
+ * This evaluates Du/Dt = du/dt + u * grad(u).
+ * The gradient of the velocity, grad(u), has to be provided by a field, e.g. evaluated with VelocityGradientFieldEvaluator.h.
+ * The time derivative, du/dt, is approximated by a simple backward difference: du/dt = (u_new - u_old ) / deltaT.
+ * This requires two velocity field.
+ *
+ */
+class VelocityTotalTimeDerivativeFieldEvaluator
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
+
+   VelocityTotalTimeDerivativeFieldEvaluator( const BlockDataID & totalTimeDerivativeVelocityFieldID,
+                                              const ConstBlockDataID & currentVelocityFieldID,
+                                              const ConstBlockDataID & formerVelocityFieldID,
+                                              const ConstBlockDataID & velocityGradientFieldID,
+                                              const real_t & deltaT = real_t(1) )
+      : totalTimeDerivativeVelocityFieldID_( totalTimeDerivativeVelocityFieldID ), currentVelocityFieldID_( currentVelocityFieldID ),
+        formerVelocityFieldID_( formerVelocityFieldID ), velocityGradientFieldID_( velocityGradientFieldID ), deltaTinv_( real_t(1) / deltaT )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      VelocityField_T* totalTimeDerivativeVelocityField = block->getData< VelocityField_T >( totalTimeDerivativeVelocityFieldID_ );
+      const VelocityField_T* currentVelocityField       = block->getData< VelocityField_T >( currentVelocityFieldID_ );
+      const VelocityField_T* formerVelocityField        = block->getData< VelocityField_T >( formerVelocityFieldID_ );
+      const TensorField_T*   velocityGradientField      = block->getData< TensorField_T >( velocityGradientFieldID_ );
+
+      // simple backward difference approximation of Du/Dt
+      WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( totalTimeDerivativeVelocityField,
+         Vector3<real_t> vel = currentVelocityField->get(x,y,z);
+
+         // grad(u) =
+         // | du1/dx1 du2/dx1 du3/dx1 |   | 0 1 2 |   | 0,0  0,1  0,2 |
+         // | du1/dx2 du2/dx2 du3/dx2 | = | 3 4 5 | = | 1,0  1,1  1,2 |
+         // | du1/dx3 du2/dx3 du3/dx3 |   | 6 7 8 |   | 2,0  2,1  2,2 |
+         Matrix3<real_t> gradVel = velocityGradientField->get(x,y,z);
+
+         // evaluate u * grad(u) = u_i (d u_j / d x_i)
+         Vector3<real_t> velGradVel( gradVel(0,0) * vel[0] + gradVel(1,0) * vel[1] + gradVel(2,0) * vel[2],
+                                     gradVel(0,1) * vel[0] + gradVel(1,1) * vel[1] + gradVel(2,1) * vel[2],
+                                     gradVel(0,2) * vel[0] + gradVel(1,2) * vel[1] + gradVel(2,2) * vel[2] );
+
+         totalTimeDerivativeVelocityField->get(x,y,z) = ( vel - formerVelocityField->get(x,y,z) ) * deltaTinv_ + velGradVel;
+      );
+   }
+
+   void resetDeltaT( const real_t & deltaT )
+   {
+      deltaTinv_ = real_t(1) / deltaT;
+   }
+
+private:
+   const BlockDataID totalTimeDerivativeVelocityFieldID_;
+   const ConstBlockDataID currentVelocityFieldID_;
+   const ConstBlockDataID formerVelocityFieldID_;
+   const ConstBlockDataID velocityGradientFieldID_;
+   real_t deltaTinv_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/all.h b/src/pe_coupling/discrete_particle_methods/evaluators/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..308ab5754297d2596aa3386a5e73f2e37190f026
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/all.h
@@ -0,0 +1,39 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "waLBerlaDefinitions.h"
+
+#include "AddedMassForceEvaluator.h"
+#include "BodyVelocityTimeDerivativeEvaluator.h"
+#include "EffectiveViscosityFieldEvaluator.h"
+#include "InteractionForceEvaluator.h"
+#include "LiftForceEvaluator.h"
+#include "LubricationForceEvaluator.h"
+#include "PressureFieldEvaluator.h"
+#include "PressureGradientFieldEvaluator.h"
+#include "SolidVolumeFractionFieldEvaluator.h"
+#include "StressTensorGradientFieldEvaluator.h"
+#include "VelocityCurlFieldEvaluator.h"
+#include "VelocityFieldEvaluator.h"
+#include "VelocityGradientFieldEvaluator.h"
+#include "VelocityTotalTimeDerivativeFieldEvaluator.h"
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h
new file mode 100644
index 0000000000000000000000000000000000000000..72649668d966772116e30af5f1d8ae2e8a33937d
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h
@@ -0,0 +1,381 @@
+//======================================================================================================================
+//
+//  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 GNSSweep.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "field/GhostLayerField.h"
+
+#include "lbm/sweeps/StreamPull.h"
+#include "lbm/sweeps/SweepBase.h"
+#include "lbm/lattice_model/all.h"
+
+#include "pe/Types.h"
+
+#include "stencil/Directions.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+
+/*!\brief Sweep for the LBM formulation of the generalized Navier Stokes equations
+ *
+ * Method is taken from:
+ * Z. Guo, T. S. Zhao - "Lattice Boltzmann model for incompressible flows through porous media", Phys. Rev. E 66 (2002)036304. doi:10.1103/PhysRevE.66.036304.
+ * Note: when an external forcing is present, use the GNSExternalForceToForceFieldAdder class to apply the correct external force
+ * (i.e. fluid-volume-fraction * external-force )
+ * Note: the calculated velocity is the volume averaged one!
+ *
+ * A LBM-forcing model that supports spatially (and temporally) varying forces has to be used, e.g. GuoField.
+ *
+ */
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+class GNSSweep
+   : public lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >
+{
+public:
+
+   typedef typename lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >::PdfField_T  PdfField_T;
+   typedef typename LatticeModel_T::Stencil Stencil_T;
+   typedef GhostLayerField< real_t, 1> ScalarField_T;
+
+   static_assert( LatticeModel_T::ForceModel::constant == false, "Only works with non-constant force models!" );
+   static_assert( LatticeModel_T::compressible == false,         "Only works with incompressible models!" );
+
+   GNSSweep( const BlockDataID & pdfFieldID,
+             const BlockDataID & solidVolumeFractionFieldID,
+             const Filter_T & _filter = walberla::field::DefaultEvaluationFilter(),
+             const DensityVelocityIn_T & _densityVelocityIn = lbm::DefaultDensityEquilibriumVelocityCalculation(),
+             const DensityVelocityOut_T & _densityVelocityOut = lbm::DefaultDensityVelocityCallback() ) :
+      lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >( pdfFieldID, _filter, _densityVelocityIn, _densityVelocityOut ),
+      solidVolumeFractionFieldID_( solidVolumeFractionFieldID ) {}
+
+   GNSSweep( const BlockDataID & src, const BlockDataID & dst,
+             const BlockDataID & solidVolumeFractionFieldID,
+             const Filter_T & _filter = walberla::field::DefaultEvaluationFilter(),
+             const DensityVelocityIn_T & _densityVelocityIn = lbm::DefaultDensityEquilibriumVelocityCalculation(),
+             const DensityVelocityOut_T & _densityVelocityOut = lbm::DefaultDensityVelocityCallback() ) :
+      lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >( src, dst, _filter, _densityVelocityIn, _densityVelocityOut ),
+      solidVolumeFractionFieldID_( solidVolumeFractionFieldID ) {}
+
+   void operator()( IBlock * const block, const uint_t numberOfGhostLayersToInclude = uint_t(0) )
+   {
+      streamCollide( block, numberOfGhostLayersToInclude );
+   }
+
+   void streamCollide( IBlock * const block, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
+
+   void stream ( IBlock * const block, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
+   void collide( IBlock * const block, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
+
+   inline ScalarField_T * getSolidVolumeFractionField( IBlock * const block ) const
+   {
+      WALBERLA_ASSERT_NOT_NULLPTR( block );
+      ScalarField_T * solidVolumeFractionField = block->getData<ScalarField_T>( solidVolumeFractionFieldID_ );
+      WALBERLA_ASSERT_NOT_NULLPTR( solidVolumeFractionField );
+      return solidVolumeFractionField;
+   }
+
+   inline void getFields( IBlock * const block, PdfField_T * & src, PdfField_T * & dst, ScalarField_T * & solidVolumeFractionField )
+   {
+      WALBERLA_ASSERT_NOT_NULLPTR( block );
+
+      src = this->getSrcField( block );
+      dst = this->getDstField( block, src );
+      solidVolumeFractionField = getSolidVolumeFractionField( block );
+
+      WALBERLA_ASSERT_NOT_NULLPTR( src );
+      WALBERLA_ASSERT_NOT_NULLPTR( dst );
+      WALBERLA_ASSERT_NOT_NULLPTR( solidVolumeFractionField );
+
+      WALBERLA_ASSERT_EQUAL( src->xyzSize(), dst->xyzSize() );
+      WALBERLA_ASSERT_EQUAL( src->xyzSize(), solidVolumeFractionField->xyzSize() );
+   }
+
+private:
+   const BlockDataID solidVolumeFractionFieldID_;
+};
+
+
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+void GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T
+>::streamCollide( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
+{
+   PdfField_T * src( NULL );
+   PdfField_T * dst( NULL );
+   ScalarField_T * solidVolumeFractionField( NULL );
+
+   getFields( block, src, dst, solidVolumeFractionField );
+
+   WALBERLA_ASSERT_GREATER( src->nrOfGhostLayers(), numberOfGhostLayersToInclude );
+   WALBERLA_ASSERT_GREATER_EQUAL( dst->nrOfGhostLayers(), numberOfGhostLayersToInclude );
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFractionField->nrOfGhostLayers(), numberOfGhostLayersToInclude );
+
+   const auto & lm = src->latticeModel();
+   dst->resetLatticeModel( lm ); /* required so that member functions for getting density and equilibrium velocity can be called for dst! */
+
+   this->filter( *block );
+   this->densityVelocityIn( *block );
+   this->densityVelocityOut( *block );
+
+   WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( src, numberOfGhostLayersToInclude,
+
+      if( this->filter(x,y,z) )
+      {
+         const real_t fluidVolumeFraction = real_t(1) - solidVolumeFractionField->get(x,y,z); //i.e. porosity
+         const real_t invFluidVolumeFraction = real_t(1)/fluidVolumeFraction;
+
+         // stream pull
+         for( auto d = Stencil_T::begin(); d != Stencil_T::end(); ++d )
+            dst->get( x, y, z, d.toIdx() ) = src->get( x-d.cx(), y-d.cy(), z-d.cz(), d.toIdx() );
+
+         Vector3<real_t> velocity;
+         real_t rho = this->densityVelocityIn( velocity, dst, x, y, z );
+
+         this->densityVelocityOut( x, y, z, lm, velocity, rho );
+
+         velocity /= rho;
+
+         const real_t omega = lm.collisionModel().omega( x, y, z, velocity, rho );
+
+         const Vector3<real_t> extForce = lm.forceModel().force(x,y,z);
+
+         // collide
+         for( auto d = Stencil_T::begin(); d != Stencil_T::end(); ++d )
+         {
+            const real_t wq = LatticeModel_T::w[ d.toIdx() ];
+            const Vector3<real_t> c( real_c(d.cx()), real_c(d.cy()), real_c(d.cz()) );
+            const real_t forceTerm = real_t(3.0) * wq * ( real_t(1) - real_t(0.5) * omega ) *
+                     ( ( c - invFluidVolumeFraction * velocity + ( real_t(3) * invFluidVolumeFraction * ( c * velocity ) * c ) ) * extForce ); // modified Guo forcing, multiplication by rho is wrong because of units
+
+            const real_t vel = c * velocity;
+            real_t feq = wq * rho * ( real_t(1) - real_t(1.5) * invFluidVolumeFraction * velocity.sqrLength() +
+                                      real_t(4.5) * invFluidVolumeFraction * vel * vel + real_t(3.0) * vel ); // modified feq
+            feq -= wq; // center PDFs around 0
+
+            dst->get( x, y, z, d.toIdx() ) = ( real_t(1.0) - omega ) * dst->get( x, y, z, d.toIdx() ) +
+                                             omega * feq +
+                                             forceTerm;
+         }
+      }
+
+   ) // WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ
+
+   src->swapDataPointers( dst );
+
+}
+
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+void GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T
+>::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
+{
+   PdfField_T * src( NULL );
+   PdfField_T * dst( NULL );
+   lbm::SweepBase<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>::getFields( block, src, dst );
+   lbm::StreamPull< LatticeModel_T >::execute( src, dst, block, this->filter_, numberOfGhostLayersToInclude );
+}
+
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+void GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T
+>::collide( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
+{
+   PdfField_T * src = this->getSrcField( block );
+   ScalarField_T * solidVolumeFractionField = getSolidVolumeFractionField( block );
+
+   WALBERLA_ASSERT_GREATER( src->nrOfGhostLayers(), numberOfGhostLayersToInclude );
+   WALBERLA_ASSERT_GREATER_EQUAL( solidVolumeFractionField->nrOfGhostLayers(), numberOfGhostLayersToInclude );
+
+   const auto & lm = src->latticeModel();
+
+   this->filter( *block );
+   this->densityVelocityIn( *block );
+   this->densityVelocityOut( *block );
+
+   WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( src, numberOfGhostLayersToInclude,
+   if( this->filter(x,y,z) )
+   {
+      const real_t fluidVolumeFraction = real_t(1) - solidVolumeFractionField->get(x,y,z); //i.e. porosity
+      const real_t invFluidVolumeFraction = real_t(1)/fluidVolumeFraction;
+
+      Vector3<real_t> velocity;
+      real_t rho = this->densityVelocityIn( velocity, src, x, y, z );
+
+      this->densityVelocityOut( x, y, z, lm, velocity, rho );
+
+      velocity /= rho;
+
+      const real_t omega = lm.collisionModel().omega( x, y, z, velocity, rho );
+
+      const Vector3<real_t> extForce = lm.forceModel().force(x,y,z);
+
+      // collide
+      for( auto d = Stencil_T::begin(); d != Stencil_T::end(); ++d )
+      {
+         const real_t wq = LatticeModel_T::w[ d.toIdx() ];
+         const Vector3<real_t> c( real_c(d.cx()), real_c(d.cy()), real_c(d.cz()) );
+         const real_t forceTerm = real_t(3.0) * wq * ( real_t(1) - real_t(0.5) * omega ) *
+                  ( ( c - invFluidVolumeFraction * velocity + ( real_t(3) * invFluidVolumeFraction * ( c * velocity ) * c ) ) * extForce ); // modified Guo forcing
+
+         const real_t vel = c * velocity;
+         real_t feq = wq * rho * ( real_t(1) - real_t(1.5) * invFluidVolumeFraction * velocity.sqrLength() +
+                                   real_t(4.5) * invFluidVolumeFraction * vel * vel + real_t(3.0) * vel ); // modified feq
+         feq -= wq; // center PDFs around 0
+
+         src->get( x, y, z, d.toIdx() ) = ( real_t(1.0) - omega ) * src->get( x, y, z, d.toIdx() ) +
+                                          omega * feq +
+                                          forceTerm;
+      }
+   }
+   ) // WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ
+
+}
+
+/////////////////////////////
+// makeGNSSweep FUNCTIONS //
+////////////////////////////
+
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+shared_ptr< GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > >
+makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID, const Filter_T & filter,
+               const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
+{
+   typedef GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > Sweep_T;
+   return shared_ptr< Sweep_T >( new Sweep_T( pdfFieldID, solidVolumeFractionFieldID, filter, densityVelocityIn, densityVelocityOut ) );
+}
+
+template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn_T, typename DensityVelocityOut_T >
+shared_ptr< GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > >
+makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID, const Filter_T & filter,
+               const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
+{
+   typedef GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > Sweep_T;
+   return shared_ptr< Sweep_T >( new Sweep_T( srcID, dstID, solidVolumeFractionFieldID, filter, densityVelocityIn, densityVelocityOut ) );
+}
+
+// only block data IDs of PDF data
+
+template< typename LatticeModel_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::DefaultEvaluationFilter, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                       lbm::DefaultDensityVelocityCallback > >
+makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::DefaultEvaluationFilter, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DefaultDensityVelocityCallback >
+                       ( pdfFieldID, solidVolumeFractionFieldID,walberla::field::DefaultEvaluationFilter(),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::DefaultDensityVelocityCallback() );
+}
+
+template< typename LatticeModel_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::DefaultEvaluationFilter, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                       lbm::DefaultDensityVelocityCallback > >
+makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::DefaultEvaluationFilter, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DefaultDensityVelocityCallback >
+                       ( srcID, dstID, solidVolumeFractionFieldID, walberla::field::DefaultEvaluationFilter(),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::DefaultDensityVelocityCallback() );
+}
+
+// block data IDs of PDF data + flag field as filter
+
+template< typename LatticeModel_T, typename FlagField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                       lbm::DefaultDensityVelocityCallback > >
+makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DefaultDensityVelocityCallback >
+                       ( pdfFieldID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::DefaultDensityVelocityCallback() );
+}
+
+template< typename LatticeModel_T, typename FlagField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                       lbm::DefaultDensityVelocityCallback > >
+makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DefaultDensityVelocityCallback >
+                       ( srcID, dstID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::DefaultDensityVelocityCallback() );
+}
+
+// block data IDs of PDF data + flag field as filter + block data ID of velocity field (out)
+
+template< typename LatticeModel_T, typename FlagField_T, typename VelocityField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>,
+                       lbm::DefaultDensityEquilibriumVelocityCalculation, lbm::VelocityCallback<VelocityField_T> > >
+makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate, const BlockDataID & velocityFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::VelocityCallback<VelocityField_T> >
+                       ( pdfFieldID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::VelocityCallback<VelocityField_T>( velocityFieldID ) );
+}
+
+template< typename LatticeModel_T, typename FlagField_T, typename VelocityField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>,
+                       lbm::DefaultDensityEquilibriumVelocityCalculation, lbm::VelocityCallback<VelocityField_T> > >
+makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate, const BlockDataID & velocityFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::VelocityCallback<VelocityField_T> >
+                       ( srcID, dstID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(), lbm::VelocityCallback<VelocityField_T>( velocityFieldID ) );
+}
+
+// block data IDs of PDF data + flag field as filter + block data IDs of velocity and density field (out)
+
+template< typename LatticeModel_T, typename FlagField_T, typename VelocityField_T, typename DensityField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>,
+                       lbm::DefaultDensityEquilibriumVelocityCalculation, lbm::DensityVelocityCallback<VelocityField_T,DensityField_T> > >
+makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate,
+               const BlockDataID & velocityFieldID, const BlockDataID & densityFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DensityVelocityCallback<VelocityField_T,DensityField_T> >
+                       ( pdfFieldID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(),
+                         lbm::DensityVelocityCallback<VelocityField_T,DensityField_T>( velocityFieldID, densityFieldID ) );
+}
+
+template< typename LatticeModel_T, typename FlagField_T, typename VelocityField_T, typename DensityField_T >
+shared_ptr< GNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>,
+                       lbm::DefaultDensityEquilibriumVelocityCalculation, lbm::DensityVelocityCallback<VelocityField_T,DensityField_T> > >
+makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID,
+               const ConstBlockDataID & flagFieldID, const Set< FlagUID > & cellsToEvaluate,
+               const BlockDataID & velocityFieldID, const BlockDataID & densityFieldID )
+{
+   return makeGNSSweep< LatticeModel_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, lbm::DefaultDensityEquilibriumVelocityCalculation,
+                         lbm::DensityVelocityCallback<VelocityField_T,DensityField_T> >
+                       ( srcID, dstID, solidVolumeFractionFieldID, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldID, cellsToEvaluate ),
+                         lbm::DefaultDensityEquilibriumVelocityCalculation(),
+                         lbm::DensityVelocityCallback<VelocityField_T,DensityField_T>( velocityFieldID, densityFieldID ) );
+}
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/all.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..9452674f19425aae1104eedfa5ac036488bbdac9
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/all.h
@@ -0,0 +1,30 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#include "waLBerlaDefinitions.h"
+
+#include "utility/all.h"
+
+#include "GNSSweep.h"
+
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h
new file mode 100644
index 0000000000000000000000000000000000000000..73fc6f5474635b7307cecbf184988a8e31acc236
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h
@@ -0,0 +1,77 @@
+//======================================================================================================================
+//
+//  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 GNSExternalForceToForceFieldAdder.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Applies a given constant external force to a force field.
+ *
+ * Utility function when using external forces with methods that couple over the force field.
+ * GNS speciality: the external force is additionally multiplied by the fluid volume fraction before it is added.
+ */
+class GNSExternalForceToForceFieldAdder
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+   typedef GhostLayerField< real_t, 1 >  ScalarField_T;
+
+   GNSExternalForceToForceFieldAdder( const BlockDataID & forceFieldID, const ConstBlockDataID & solidVolumeFractionFieldID, const Vector3<real_t> & externalForce )
+      : forceFieldID_( forceFieldID ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), externalForce_( externalForce )
+   {
+   }
+
+   void operator()( IBlock * const block )
+   {
+      ForceField_T* forceField = block->getData< ForceField_T >( forceFieldID_ );
+      const ScalarField_T* solidVolumeFractionField = block->getData< ScalarField_T >( solidVolumeFractionFieldID_ );
+
+      //TODO include dx force scaling
+      WALBERLA_FOR_ALL_CELLS_XYZ( forceField,
+         forceField->get(x,y,z) += ( real_t(1) - solidVolumeFractionField->get(x,y,z) ) * externalForce_;
+      );
+   }
+
+   void reset( const Vector3<real_t> & newExternalForce )
+   {
+      externalForce_ = newExternalForce;
+   }
+
+private:
+   const BlockDataID forceFieldID_;
+   const ConstBlockDataID solidVolumeFractionFieldID_;
+   Vector3<real_t> externalForce_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe2_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..c284db5fd20767aedd66df9b3bad60a73e8fd7bf
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.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 GNSPressureFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Evaluates the pressure field based on the given (GNS) LBM field..
+ *
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class GNSPressureFieldEvaluator
+{
+public:
+   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
+   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+
+   GNSPressureFieldEvaluator( const BlockDataID & pressureFieldID, const ConstBlockDataID & pdfFieldID,
+                              const ConstBlockDataID & solidVolumeFractionFieldID, const ConstBlockDataID & boundaryHandlingID )
+      : pressureFieldID_( pressureFieldID ), pdfFieldID_( pdfFieldID ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ),
+        boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const PdfField_T* pdfField                    = block->getData< PdfField_T >( pdfFieldID_ );
+      ScalarField_T* pressureField                  = block->getData< ScalarField_T >( pressureFieldID_ );
+      //const ScalarField_T* solidVolumeFractionField = block->getData< ScalarField_T >( solidVolumeFractionFieldID_ );
+      const BoundaryHandling_T * boundaryHandling   = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      const real_t c_s_sqr = real_t(1)/real_t(3);
+      WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( pdfField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            real_t fluidDensity = pdfField->getDensity(x,y,z);
+            //real_t fluidVolumeFraction = real_t(1) - solidVolumeFractionField->get(x,y,z);
+
+            // according to Guo (2002): p = rho * eps / 3
+            // However, this would result in pressure gradients in the presence of solid volume fraction gradients.
+            // As a result, no quiescent steady state solution would exist for a resting fluid above and inside a porous media.
+            // Therefore, a "generalized" pressure is used, which is simply P = rho / 3 (i.e. the standard LBM pressure calculation).
+            pressureField->get(x,y,z) = c_s_sqr * fluidDensity; // / fluidVolumeFraction;
+         }
+      );
+   }
+
+private:
+   const BlockDataID pressureFieldID_;
+   const ConstBlockDataID pdfFieldID_;
+   const ConstBlockDataID solidVolumeFractionFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ae1a67dfa9d6b6cb5ccbc048da7907e3262893a
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h
@@ -0,0 +1,189 @@
+//======================================================================================================================
+//
+//  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 GNSSmagorinskyLESField.h
+//! \ingroup lbm
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/BlockDataID.h"
+#include "domain_decomposition/IBlock.h"
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/iterators/IteratorMacros.h"
+#include "field/EvaluationFilter.h"
+#include "field/GhostLayerField.h"
+
+#include "lbm/field/PdfField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Adjusts locally the fluid viscosity based on a LES-Smagorinsky turbulence model when using GNS-LBM
+ *
+ * The original LES model is discussed in:
+ * - S. Hou, J. Sterling, S. Chen, G. Doolen, A lattice Boltzmann subgrid model for high reynolds number flows, in: A. T.
+ *   Lawniczak, R. Kapral (Eds.), Pattern formation and lattice gas automata, Vol. 6 of Fields Institute Communications,
+ *   American Mathematical Society, Providence, RI, 1996, p. 151–166.
+ * - H. Yu, S. S. Girimaji, L.-S. Luo, DNS and LES of decaying isotropic turbulence with and without frame rotation using
+ *   lattice Boltzmann method, Journal of Computational Physics 209 (2) (2005) 599–616. doi:10.1016/j.jcp.2005.03.022.
+ *
+ * The second reference suggests to use a Smagorinsky constant of 0.1.
+ *
+ * An implementation of if for standard LBM is found in "lbm/lattice_model/SmagorinskyLES.h"
+ *
+ * This version is adapted to use the special GNS-LBM equilibrium distribution functions to calculate the
+ * non-equilibrium PDFs. These are given in Z. Guo, T. S. Zhao - "Lattice Boltzmann model for incompressible flows
+ * through porous media", Phys. Rev. E 66 (2002)036304. doi:10.1103/PhysRevE.66.036304.
+ *
+ * Combining LES with GNS-LBM was proposed in
+ * - C. Rettinger, U. Ruede - "A Coupled Lattice Boltzmann Method and Discrete Element Method for Discrete Particle
+ *   Simulations of Particulate Flows". arXiv preprint arXiv:1711.00336 (2017)
+ *
+ * Uses the value from the omegaField as tau0 and adds a calculated tauTurbulent to it.
+ * Can be used together with the collision model "SRTField"
+ */
+template< typename LatticeModel_T, typename Filter_T = field::DefaultEvaluationFilter >
+class GNSSmagorinskyLESField
+{
+public:
+
+   typedef lbm::PdfField< LatticeModel_T >   PdfField_T;
+   typedef GhostLayerField< real_t, 1 >      ScalarField_T;
+   typedef typename LatticeModel_T::Stencil  Stencil_T;
+
+   static_assert( LatticeModel_T::CollisionModel::constant == false, "Only works with non-constant relaxation time fields!" );
+   static_assert( LatticeModel_T::compressible == false,             "Only works with incompressible models!" );
+
+   GNSSmagorinskyLESField( const shared_ptr< StructuredBlockStorage > & blocks,
+                           const BlockDataID & omegaFieldID, const ConstBlockDataID & pdfFieldID,
+                           const ConstBlockDataID & solidVolumeFractionFieldID,
+                           const real_t & smagorinskyConstant,
+                           const Set<SUID> & requiredSelectors     = Set<SUID>::emptySet(),
+                           const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
+
+   : blocks_( blocks ), filter_( Filter_T() ), omegaFieldID_( omegaFieldID ), pdfFieldID_( pdfFieldID ),
+     solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), smagorinskyConstant_ ( smagorinskyConstant ),
+     requiredSelectors_(requiredSelectors), incompatibleSelectors_( incompatibleSelectors )
+   {}
+
+   GNSSmagorinskyLESField( const shared_ptr< StructuredBlockStorage > & blocks, const Filter_T & filter,
+                           const BlockDataID & omegaFieldID, const ConstBlockDataID & pdfFieldID,
+                           const ConstBlockDataID & solidVolumeFractionFieldID,
+                           const real_t & smagorinskyConstant,
+                           const Set<SUID> & requiredSelectors     = Set<SUID>::emptySet(),
+                           const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
+
+   : blocks_( blocks ), filter_( filter ), omegaFieldID_( omegaFieldID ), pdfFieldID_( pdfFieldID ),
+     solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), smagorinskyConstant_ ( smagorinskyConstant ),
+     requiredSelectors_(requiredSelectors), incompatibleSelectors_( incompatibleSelectors )
+   {}
+
+   void operator()( IBlock * const block );
+
+private:
+
+   shared_ptr< StructuredBlockStorage > blocks_;
+
+   Filter_T filter_;
+
+   BlockDataID omegaFieldID_;
+   ConstBlockDataID pdfFieldID_;
+   ConstBlockDataID solidVolumeFractionFieldID_;
+
+   real_t smagorinskyConstant_;
+
+   Set<SUID> requiredSelectors_;
+   Set<SUID> incompatibleSelectors_;
+
+}; // class GNSSmagorinskyLESField
+
+
+
+template< typename LatticeModel_T, typename Filter_T >
+void GNSSmagorinskyLESField< LatticeModel_T, Filter_T >::operator()( IBlock * const block )
+{
+         ScalarField_T * omegaField = block->getData< ScalarField_T >( omegaFieldID_ );
+   const PdfField_T *   pdfField    = block->getData< PdfField_T >( pdfFieldID_ );
+   const ScalarField_T * svfField   = block->getData< ScalarField_T >( solidVolumeFractionFieldID_ );
+
+   WALBERLA_ASSERT_NOT_NULLPTR( pdfField );
+   WALBERLA_ASSERT_NOT_NULLPTR( omegaField );
+
+
+   WALBERLA_ASSERT_EQUAL( pdfField->xyzSizeWithGhostLayer(), omegaField->xyzSizeWithGhostLayer() );
+
+   const real_t factor = real_t(18) * std::sqrt( real_t(2) ) * smagorinskyConstant_ * smagorinskyConstant_;
+
+   filter_( *block );
+
+   WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ( omegaField,
+
+      if( filter_(x,y,z) )
+      {
+         const real_t tau0 = real_t(1) / omegaField->get(x,y,z);
+
+         Vector3< real_t > momentumDensity;
+         const real_t rho = pdfField->getDensityAndEquilibriumMomentumDensity( momentumDensity, x, y, z );
+
+         Vector3< real_t > velocity = momentumDensity / rho;
+
+         const real_t porosity = real_t(1) - svfField->get(x,y,z);
+         const real_t invPorosity = real_t(1) / porosity;
+
+         // use special GNS equilibrium
+         std::vector< real_t > equilibrium( Stencil_T::Size );
+         for( auto d = Stencil_T::begin(); d != Stencil_T::end(); ++d )
+         {
+            const real_t wq = LatticeModel_T::w[ d.toIdx() ];
+            const real_t vel = real_c(d.cx()) * velocity[0] + real_c(d.cy()) * velocity[1] + real_c(d.cz()) * velocity[2];
+            real_t feq = wq * rho * ( real_t(1) - real_t(1.5) * invPorosity * velocity.sqrLength() +
+                                      real_t(4.5) * invPorosity * vel * vel + real_t(3.0) * vel ); // modified feq
+            feq -= wq; // walberla: center around 0 in incompressible case, attention if always needed!
+            equilibrium[d.toIdx()] = feq;
+         }
+
+         std::vector< real_t > nonEquilibrium( Stencil_T::Size );
+         for( uint_t i = 0; i != Stencil_T::Size; ++i )
+            nonEquilibrium[i] = pdfField->get(x,y,z,i) - equilibrium[i];
+
+         real_t filteredMeanMomentum = real_t(0);
+         for( uint_t alpha = 0; alpha < 3; ++alpha ) {
+            for( uint_t beta = 0; beta < 3; ++beta )
+            {
+               real_t qij = real_t(0);
+               for( auto d = Stencil_T::begin(); d != Stencil_T::end(); ++d )
+                  qij += nonEquilibrium[ d.toIdx() ] * real_c(stencil::c[alpha][*d]) * real_c(stencil::c[beta][*d]);
+               filteredMeanMomentum += qij * qij;
+            }
+         }
+
+         const real_t tauTurbulent = LatticeModel_T::compressible ? ( real_t(0.5) * ( std::sqrt( tau0 * tau0 + (factor / rho) * std::sqrt( real_t(2) * filteredMeanMomentum ) ) - tau0 ) ) :
+                                                                    ( real_t(0.5) * ( std::sqrt( tau0 * tau0 + factor * std::sqrt( real_t(2) * filteredMeanMomentum ) ) - tau0 ) );
+
+         omegaField->get(x,y,z) = real_t(1) / ( tau0 + tauTurbulent );
+      }
+   )
+}
+
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h
new file mode 100644
index 0000000000000000000000000000000000000000..157c812b7788617cfe659c72a4ba966aa3083dfa
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h
@@ -0,0 +1,88 @@
+//======================================================================================================================
+//
+//  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 GNSVelocityFieldEvaluator.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Evaluator for the fluid-phase velocity, given a (GNS) PDf field.
+ *
+ * Evaluator for the fluid-phase velocity when using GNS-LBM (in contrast to the volume-averaged velocity that is
+ * obtained by calculating the first order moment of the PDFs).
+ *
+ * See: Z. Guo, T. S. Zhao - "Lattice Boltzmann model for incompressible flows through porous media",
+ * Phys. Rev. E 66 (2002)036304. doi:10.1103/PhysRevE.66.036304.
+ *
+ * Since in LBM, the macroscopic velocity depends on the local fluid forcing, one has to pay attention to the currently
+ * set forces in the corresponding force field before evaluating the velocity.
+ */
+template <typename LatticeModel_T, typename BoundaryHandling_T>
+class GNSVelocityFieldEvaluator
+{
+public:
+   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
+   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+
+   GNSVelocityFieldEvaluator( const BlockDataID & velocityFieldID, const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & solidVolumeFractionFieldID,
+                              const ConstBlockDataID & boundaryHandlingID )
+      : velocityFieldID_( velocityFieldID ), pdfFieldID_( pdfFieldID ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ),
+        boundaryHandlingID_( boundaryHandlingID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      const PdfField_T* pdfField                  = block->getData< PdfField_T >( pdfFieldID_ );
+      VelocityField_T* velocityField              = block->getData< VelocityField_T >( velocityFieldID_ );
+      const ScalarField_T* svfField               = block->getData< ScalarField_T >( solidVolumeFractionFieldID_ );
+      const BoundaryHandling_T* boundaryHandling  = block->getData< BoundaryHandling_T >( boundaryHandlingID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( pdfField,
+         if( boundaryHandling->isDomain(x,y,z) )
+         {
+            real_t fluidVolumeFraction = real_t(1) - svfField->get(x,y,z);
+            Vector3<real_t> volumeAverageVelocity = pdfField->getVelocity(x,y,z);
+            velocityField->get(x,y,z) = volumeAverageVelocity / fluidVolumeFraction;
+         }
+      );
+   }
+
+private:
+   const BlockDataID velocityFieldID_;
+   const ConstBlockDataID pdfFieldID_;
+   const ConstBlockDataID solidVolumeFractionFieldID_;
+   const ConstBlockDataID boundaryHandlingID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/all.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..25f944954114f98807cba1e6ee44873abd5281a1
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/all.h
@@ -0,0 +1,29 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "waLBerlaDefinitions.h"
+
+#include "GNSExternalForceToForceFieldAdder.h"
+#include "GNSPressureFieldEvaluator.h"
+#include "GNSSmagorinskyLESField.h"
+#include "GNSVelocityFieldEvaluator.h"
diff --git a/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h
new file mode 100644
index 0000000000000000000000000000000000000000..bffee15731a24711405afab296683ba4d5ed29c5
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h
@@ -0,0 +1,100 @@
+//======================================================================================================================
+//
+//  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 AveragedInteractionForceFieldToForceFieldAdder.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Adds an accumulated force field value to another force field, applying continuous averaging.
+ *
+ * The force field contains all external force, etc acting on the fluid, except the fluid-solid interaction forces
+ * from the DPS coupling.
+ * The interaction force field only contains the interaction forces, simply added up over several subcycling timesteps,
+ * with the maximum number given by maximumAveragingSteps.
+ * This class will average the interaction force over the currently carried out sub cycles (counted via an internal counter)
+ * and add this averaged quantity to the force field, to get rid of possible oscillations in the interaction force
+ * and have a valid force field in each sub cycling iteration.
+ *
+ * Can also be used to simply add the current interaction force field to the force field if maximumAveragingSteps = 1.
+ */
+class AveragedInteractionForceFieldToForceFieldAdder
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+
+   AveragedInteractionForceFieldToForceFieldAdder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & forceFieldID,
+                                                   const ConstBlockDataID & interactionForceFieldID, uint_t maximumAveragingSteps )
+      : blockStorage_( blockStorage), forceFieldID_( forceFieldID ), interactionForceFieldID_( interactionForceFieldID ),
+        maximumAveragingSteps_( maximumAveragingSteps ), averagingCounter_( uint_t(0) )
+   {
+   }
+
+   void operator()()
+   {
+      ++averagingCounter_;
+
+      const real_t averagingFactor = real_t(1)/real_c(averagingCounter_);
+
+
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         ForceField_T* forceField = blockIt->getData< ForceField_T >( forceFieldID_ );
+         const ForceField_T* interactionForceField = blockIt->getData< ForceField_T >( interactionForceFieldID_ );
+
+         WALBERLA_FOR_ALL_CELLS_XYZ( forceField,
+            forceField->get(x,y,z) += averagingFactor * interactionForceField->get(x,y,z);
+         );
+      }
+
+      if( averagingCounter_ >= maximumAveragingSteps_ )
+      {
+         resetAveragingCounter();
+      }
+
+   }
+
+   void resetAveragingCounter( )
+   {
+      averagingCounter_ = uint_t(0);
+   }
+
+private:
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID forceFieldID_;
+   const ConstBlockDataID interactionForceFieldID_;
+   uint_t maximumAveragingSteps_;
+   uint_t averagingCounter_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h b/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e9a475eb7a261821c78f1ab4e802fd1580a3330
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h
@@ -0,0 +1,110 @@
+//======================================================================================================================
+//
+//  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 BodyVelocityInitializer.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/debug/Debug.h"
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/interpolators/all.h"
+#include "field/distributors/all.h"
+#include "field/GhostLayerField.h"
+
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/rigidbody/RigidBody.h"
+#include "pe/Types.h"
+#include "pe/Materials.h"
+
+#include "pe_coupling/geometry/SphereEquivalentDiameter.h"
+
+#include <boost/function.hpp>
+
+#include <cmath>
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Initializes the bodies with the velocity of the surrounding fluid.
+ *
+ * The class uses an interpolator to obtain the approximate value of the fluid velocity at the bodies' position,
+ * and then sets the bodies' velocity accordingly.
+ *
+ * For more infos on interpolators, see field interpolators in src/field/interpolators.
+ */
+
+template< typename FlagField_T, template<typename,typename> class FieldInterpolator_T >
+class BodyVelocityInitializer
+{  
+
+public:
+
+   typedef GhostLayerField< Vector3<real_t>, 1>          Vec3Field_T;
+   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T;
+
+   BodyVelocityInitializer( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                            const BlockDataID & bodyStorageID, const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
+                            const BlockDataID & velocityFieldID )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID )
+   {
+      velocityFieldInterpolatorID_ = field::addFieldInterpolator< Vec3FieldInterpolator_T, FlagField_T >( blockStorage, velocityFieldID, flagFieldID, domainFlags );
+   }
+
+   void operator()()
+   {
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+
+         Vec3FieldInterpolator_T * velocityInterpolator = blockIt->getData< Vec3FieldInterpolator_T >( velocityFieldInterpolatorID_ );
+
+         for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
+         {
+            //TODO check if body should be treated by DPM
+
+            Vector3<real_t> forceOnFluid( real_t(0) );
+
+            Vector3<real_t> bodyPosition = bodyIt->getPosition();
+
+            // interpolate fluid velocity to body position
+            Vector3<real_t> fluidVelocity( real_t(0) );
+            velocityInterpolator->get( bodyPosition, &fluidVelocity );
+
+            WALBERLA_ASSERT( !math::isnan(fluidVelocity[0]) && !math::isnan(fluidVelocity[1]) && !math::isnan(fluidVelocity[2]),
+                             "NaN found in interpolated fluid velocity at position " << bodyPosition );
+
+            bodyIt->setLinearVel( fluidVelocity );
+         }
+      }
+   }
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   BlockDataID bodyStorageID_;
+   BlockDataID velocityFieldInterpolatorID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/CombinedReductionFieldCommunication.h b/src/pe_coupling/discrete_particle_methods/utility/CombinedReductionFieldCommunication.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d5345775cd62998e5e3075de9c9dff2c79a7a8d
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/CombinedReductionFieldCommunication.h
@@ -0,0 +1,75 @@
+//======================================================================================================================
+//
+//  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 CombinedReductionFieldCommunication.h
+//! \ingroup pe_coupling
+//! \author Tobias Schruff <schruff@iww.rwth-aachen.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "blockforest/communication/UniformBufferedScheme.h"
+#include "blockforest/StructuredBlockForest.h"
+
+#include "field/communication/PackInfo.h"
+#include "field/communication/UniformPullReductionPackInfo.h"
+
+#include "stencil/D3Q27.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+/*!\brief Special communication routine to += the values in the ghost layers and update the ghost layers.
+ *
+ * Reduces the field with += to obtain the values from the block neighbors' ghost layers and add them onto the corresponding values inside the domain.
+ * This is followed by a regular ghost layer communication to synchronize the ghost layer values.
+ *
+ * This functionality is typically used to synchronize the solid volume fraction field and the interaction force fields,
+ * after they have been filled by a distributor.
+
+ * For more infos on distributors, see src/field/distributors.
+ *
+ */
+template< typename GhostLayerField_T >
+class CombinedReductionFieldCommunication
+{
+public:
+
+   CombinedReductionFieldCommunication( const shared_ptr<StructuredBlockForest> & bf, const BlockDataID & glFieldID )
+      : pullReductionScheme_( bf ), commScheme_( bf )
+   {
+      pullReductionScheme_.addPackInfo( make_shared< field::communication::UniformPullReductionPackInfo<std::plus, GhostLayerField_T> >( glFieldID ) );
+      commScheme_.addPackInfo( make_shared< field::communication::PackInfo<GhostLayerField_T> >( glFieldID ) );
+      
+   }
+
+   void operator()()
+   {
+      pullReductionScheme_();
+      commScheme_();
+   }
+
+private:
+   blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pullReductionScheme_;
+   blockforest::communication::UniformBufferedScheme<stencil::D3Q27> commScheme_; 
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5e27bc315afe6fd349eb52c1438772b4c7e496b
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h
@@ -0,0 +1,72 @@
+//======================================================================================================================
+//
+//  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 ExternalForceToForceFieldAdder.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Applies a given constant external force to a force field.
+ *
+ * Utility function when using external forces with methods that couple over the force field
+ * (all discrete particle methods, e.g.) since the force field is typically reset in each iteration.
+ */
+class ExternalForceToForceFieldAdder
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+
+   ExternalForceToForceFieldAdder( const BlockDataID & forceFieldID, const Vector3<real_t> & externalForce )
+      : forceFieldID_( forceFieldID ), externalForce_( externalForce )
+   {
+   }
+
+   void operator()( IBlock * const block )
+   {
+      ForceField_T* forceField = block->getData< ForceField_T >( forceFieldID_ );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ( forceField,
+         //TODO include level dependent dx force scaling
+         forceField->get(x,y,z) += externalForce_;
+      );
+   }
+
+   void reset( const Vector3<real_t> & newExternalForce )
+   {
+      externalForce_ = newExternalForce;
+   }
+
+private:
+   const BlockDataID forceFieldID_;
+   Vector3<real_t> externalForce_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/FieldDataSwapper.h b/src/pe_coupling/discrete_particle_methods/utility/FieldDataSwapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..de5bce4d0ef13fb2e50ee6b8b7aa3a983a0018ec
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/FieldDataSwapper.h
@@ -0,0 +1,63 @@
+//======================================================================================================================
+//
+//  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 FieldDataSwapper.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Swaps the data of two identical fields.
+ *
+ * This functionality is e.g. used to store a former version of the velocity field to calculate the time derivative
+ * of the velocity numerically.
+ */
+template< typename Field_T >
+class FieldDataSwapper
+{
+public:
+
+   FieldDataSwapper( const BlockDataID & srcFieldID, const BlockDataID & dstFieldID )
+      : srcFieldID_( srcFieldID ), dstFieldID_( dstFieldID )
+   { }
+
+   void operator()(IBlock * const block)
+   {
+      Field_T* srcField = block->getData< Field_T >( srcFieldID_ );
+      Field_T* dstField = block->getData< Field_T >( dstFieldID_ );
+      srcField->swapDataPointers(dstField);
+   }
+
+private:
+   const BlockDataID srcFieldID_;
+   const BlockDataID dstFieldID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h b/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h
new file mode 100644
index 0000000000000000000000000000000000000000..d58e30d51e2abe08298ba3b688b4cc909c124a28
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h
@@ -0,0 +1,61 @@
+//======================================================================================================================
+//
+//  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 ForceFieldResetter.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+
+#include "field/GhostLayerField.h"
+
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+using math::Vector3;
+
+/*!\brief Resets the values currently stored in the force field to (0,0,0).
+ */
+class ForceFieldResetter
+{
+public:
+   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+
+   ForceFieldResetter( const BlockDataID & forceFieldID )
+      : forceFieldID_( forceFieldID )
+   {
+   }
+
+   void operator()( IBlock * const block )
+   {
+      ForceField_T* forceField = block->getData<ForceField_T>(forceFieldID_);
+      WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ(forceField,
+                                                       forceField->get(x,y,z).reset();
+      )
+   }
+
+private:
+   const BlockDataID forceFieldID_;
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/SubgridTimeStep.h b/src/pe_coupling/discrete_particle_methods/utility/SubgridTimeStep.h
new file mode 100644
index 0000000000000000000000000000000000000000..130a4829fa5a6bd87960c937f7fa12621cddc525
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/SubgridTimeStep.h
@@ -0,0 +1,164 @@
+//======================================================================================================================
+//
+//  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 SubgridTimeStep.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/debug/Debug.h"
+#include "core/timing/TimingTree.h"
+
+#include "domain_decomposition/BlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "pe/cr/ICR.h"
+#include "pe/rigidbody/BodyIterators.h"
+#include "pe/synchronization/SyncForces.h"
+
+#include <boost/function.hpp>
+
+#include <map>
+
+/*!\brief Carries out the the PE time steps, including sub iteration functionality.
+ *
+ * This class is similar to src/pe_coupling/utility/TimeStep.
+ *
+ * It executes 'intermediateSteps' PE steps within one timestep of size 'timeStepSize'.
+ *
+ * These PE sub iterations require, that the current external (e.g. hydrodynamic, gravitational,..) forces and torques
+ * acting on each particle remains unchanged. Thus, a map is set up internally that stores and re-sets these forces
+ * and torques in each PE sub iteration
+ * .
+ * Additionally, a function 'forceEvaluationFunc' can be given that allows to evaluate different forces before the PE
+ * step is carried out. An example are particle-particle lubrication forces that have to be updated in each sub iteration.
+ *
+ */
+namespace walberla {
+namespace pe_coupling {
+namespace discrete_particle_methods {
+
+class SubgridTimeStep
+{
+public:
+
+   typedef field::GhostLayerField< Vector3<real_t>, 1 > ForceField_T;
+
+   explicit SubgridTimeStep( const shared_ptr<StructuredBlockStorage> & blockStorage,
+                             const BlockDataID & bodyStorageID,
+                             pe::cr::ICR & collisionResponse,
+                             const boost::function<void (void)> & synchronizeFunc,
+                             const boost::function<void ()> & forceEvaluationFunc,
+                             const real_t timeStepSize = real_t(1), const uint_t intermediateSteps = uint_t(1) )
+   : timeStepSize_( timeStepSize )
+   , intermediateSteps_( ( intermediateSteps == 0 ) ? uint_t(1) : intermediateSteps )
+   , blockStorage_( blockStorage )
+   , bodyStorageID_(bodyStorageID)
+   , collisionResponse_(collisionResponse)
+   , synchronizeFunc_(synchronizeFunc)
+   , forceEvaluationFunc_(forceEvaluationFunc)
+   {}
+
+   void operator()()
+   {
+      if( intermediateSteps_ == 1 )
+      {
+         forceEvaluationFunc_();
+
+         collisionResponse_.timestep( timeStepSize_ );
+         synchronizeFunc_();
+      }
+      else
+      {
+         // sum up all forces from shadow copies on local body (owner)
+         pe::reduceForces(blockStorage_->getBlockStorage(), bodyStorageID_);
+         // send total forces to shadow owners
+         pe::distributeForces(blockStorage_->getBlockStorage(), bodyStorageID_);
+
+         // store force/torques. Those should only contain external forces (gravity, buoyancy,..)
+         // generate map from all known bodies (process local) to total forces
+         std::map< walberla::id_t, std::vector< real_t > > forceMap;
+         for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+         {
+            for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
+            {
+               auto & f = forceMap[ bodyIt->getSystemID() ];
+
+               const auto & force = bodyIt->getForce();
+               f.push_back( force[0] );
+               f.push_back( force[1] );
+               f.push_back( force[2] );
+
+               const auto & torque = bodyIt->getTorque();
+               f.push_back( torque[0] );
+               f.push_back( torque[1] );
+               f.push_back( torque[2] );
+
+               if ( bodyIt->isRemote() )
+               {
+                  bodyIt->resetForceAndTorque();
+               }
+            }
+         }
+
+         // perform pe sub time steps
+         const real_t subTimeStepSize = timeStepSize_ / real_c( intermediateSteps_ );
+         for( uint_t i = 0; i != intermediateSteps_; ++i )
+         {
+            // evaluate forces (e.g. lubrication forces)
+            forceEvaluationFunc_();
+
+            // in the first set forces on local bodies are already set by force synchronization
+            if( i != 0 ) {
+               for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+               {
+                  for( auto body = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); body != pe::LocalBodyIterator::end(); ++body )
+                  {
+                     const auto & f = forceMap[ body->getSystemID() ];
+                     body->addForce ( f[0], f[1], f[2] );
+                     body->addTorque( f[3], f[4], f[5] );
+                  }
+               }
+            }
+
+            collisionResponse_.timestep( subTimeStepSize );
+            synchronizeFunc_();
+         }
+      }
+   }
+
+protected:
+
+   const real_t timeStepSize_;
+
+   const uint_t intermediateSteps_;
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID &  bodyStorageID_;
+
+   pe::cr::ICR & collisionResponse_;
+   boost::function<void (void)> synchronizeFunc_;
+   boost::function<void ()> forceEvaluationFunc_;
+
+};
+
+
+} // namespace discrete_particle_methods
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/discrete_particle_methods/utility/all.h b/src/pe_coupling/discrete_particle_methods/utility/all.h
new file mode 100644
index 0000000000000000000000000000000000000000..6c471171429b3dd2887e13ab4933fcfecc0839f1
--- /dev/null
+++ b/src/pe_coupling/discrete_particle_methods/utility/all.h
@@ -0,0 +1,32 @@
+//======================================================================================================================
+//
+//  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 all.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "waLBerlaDefinitions.h"
+
+#include "AveragedInteractionForceFieldToForceFieldAdder.h"
+#include "BodyVelocityInitializer.h"
+#include "CombinedReductionFieldCommunication.h"
+#include "ExternalForceToForceFieldAdder.h"
+#include "ForceFieldResetter.h"
+#include "SubgridTimeStep.h"
+#include "FieldDataSwapper.h"
diff --git a/src/pe_coupling/geometry/SphereEquivalentDiameter.h b/src/pe_coupling/geometry/SphereEquivalentDiameter.h
new file mode 100644
index 0000000000000000000000000000000000000000..3e1bfbc7b417e2e397a5548efa90512333a346d4
--- /dev/null
+++ b/src/pe_coupling/geometry/SphereEquivalentDiameter.h
@@ -0,0 +1,47 @@
+//======================================================================================================================
+//
+//  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 SphereEquivalentDiameter.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+
+#include "pe/rigidbody/RigidBody.h"
+#include "pe/rigidbody/Sphere.h"
+
+namespace walberla {
+namespace pe_coupling {
+
+
+// calculates sphere-equivalent diameter (diameter of a sphere with same volume as given body)
+real_t getSphereEquivalentDiameter( pe::RigidBody & body )
+{
+   if( body.getTypeID() == pe::Sphere::getStaticTypeID() )
+   {
+      pe::Sphere & sphere = static_cast<pe::Sphere &>( body );
+      real_t radius = sphere.getRadius();
+      return real_t(2) * radius;
+   } else {
+      const real_t preFac = real_t(6) / math::M_PI;
+      return std::cbrt( body.getVolume() * preFac );
+   }
+}
+
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/geometry/all.h b/src/pe_coupling/geometry/all.h
index 20c149c33762b30b4bc14d0d0de4725e3a49e79a..559cd2f3e464378953e64810e25bc792ee3619d1 100644
--- a/src/pe_coupling/geometry/all.h
+++ b/src/pe_coupling/geometry/all.h
@@ -25,3 +25,4 @@
 #include "PeBodyOverlapFunctions.h"
 #include "PeIntersectionRatio.h"
 #include "PeOverlapFraction.h"
+#include "SphereEquivalentDiameter.h"
diff --git a/tests/pe_coupling/CMakeLists.txt b/tests/pe_coupling/CMakeLists.txt
index 8e9fc4f570a189ca3b94e4c2bea54eb2527c7529..25caff061daee5159475793ddbf91d64d62250b3 100644
--- a/tests/pe_coupling/CMakeLists.txt
+++ b/tests/pe_coupling/CMakeLists.txt
@@ -131,3 +131,13 @@ waLBerla_execute_test( NAME TorqueSphereMEMMRParallelTest  COMMAND $<TARGET_FILE
 waLBerla_compile_test( FILES momentum_exchange_method/SquirmerTest.cpp DEPENDS blockforest pe timeloop )
 waLBerla_execute_test( NAME SquirmerShortTest COMMAND $<TARGET_FILE:SquirmerTest> --shortrun PROCESSES 1 )
 waLBerla_execute_test( NAME SquirmerTest COMMAND $<TARGET_FILE:SquirmerTest> PROCESSES 1 CONFIGURATIONS Release RelWithDbgInfo  )
+
+###################################################################################################
+# Discrete particle methods tests
+###################################################################################################
+
+waLBerla_compile_test( FILES discrete_particle_methods/SphereWallCollisionBehaviorDPM.cpp DEPENDS blockforest pe timeloop )
+waLBerla_execute_test( NAME SphereWallCollisionBehaviorDPMFuncTest COMMAND $<TARGET_FILE:SphereWallCollisionBehaviorDPM> --funcTest PROCESSES 1 )
+
+waLBerla_compile_test( FILES discrete_particle_methods/HinderedSettlingDynamicsDPM.cpp DEPENDS blockforest pe timeloop )
+waLBerla_execute_test( NAME HinderedSettlingDynamicsDPMFuncTest COMMAND $<TARGET_FILE:HinderedSettlingDynamicsDPM> --funcTest PROCESSES 4 LABELS longrun CONFIGURATIONS RelWithDbgInfo )
diff --git a/tests/pe_coupling/discrete_particle_methods/HinderedSettlingDynamicsDPM.cpp b/tests/pe_coupling/discrete_particle_methods/HinderedSettlingDynamicsDPM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8786074ddbe78cea3ca1a560f4f6b8ff2d03fda6
--- /dev/null
+++ b/tests/pe_coupling/discrete_particle_methods/HinderedSettlingDynamicsDPM.cpp
@@ -0,0 +1,1771 @@
+//======================================================================================================================
+//
+//  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 HinderedSettlingDynamicsDPM.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "core/all.h"
+#include "boundary/all.h"
+#include "lbm/all.h"
+#include "pe_coupling/all.h"
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/all.h"
+
+#include "core/grid_generator/SCIterator.h"
+#include "core/logging/all.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/PackInfo.h"
+#include "field/distributors/all.h"
+#include "field/interpolators/all.h"
+
+#include "pe/basic.h"
+#include "pe/vtk/SphereVtkOutput.h"
+#include "pe/Types.h"
+
+#include "stencil/D3Q27.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/VTKOutput.h"
+
+#include <vector>
+#include <iomanip>
+#include <iostream>
+#include <random>
+
+namespace hindered_settling_dynamics_dpm
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+
+///////////////
+// CONSTANTS //
+///////////////
+
+const uint_t FieldGhostLayers( 1 );
+
+//////////////
+// TYPEDEFS //
+//////////////
+
+// PDF field, flag field & body field
+typedef GhostLayerField< Matrix3<real_t>, 1 >                          TensorField_T;
+typedef GhostLayerField< Vector3<real_t>, 1 >                          Vec3Field_T;
+typedef GhostLayerField< real_t, 1 >                                   ScalarField_T;
+typedef lbm::force_model::GuoField< Vec3Field_T >                      ForceModel_T;
+
+typedef lbm::D3Q19< lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T >   LatticeModel_T;
+typedef LatticeModel_T::Stencil                                        Stencil_T;
+typedef lbm::PdfField< LatticeModel_T >                                PdfField_T;
+
+typedef walberla::uint8_t                                              flag_t;
+typedef FlagField< flag_t >                                            FlagField_T;
+
+// boundary handling
+typedef lbm::NoSlip< LatticeModel_T, flag_t >                          NoSlip_T;
+
+typedef boost::tuples::tuple< NoSlip_T >                               BoundaryConditions_T;
+typedef BoundaryHandling<FlagField_T, Stencil_T, BoundaryConditions_T> BoundaryHandling_T;
+
+typedef boost::tuple<pe::Plane, pe::Sphere> BodyTypeTuple ;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag  ( "fluid" );
+const FlagUID NoSlip_Flag ( "no slip flag" );
+
+// coupling methods
+enum DPMethod { GNS };
+
+// interpolation (for PDFs, velocity and/or solid volume fraction)
+enum Interpolation { INearestNeighbor, IKernel, ITrilinear };
+
+// force distribution
+enum Distribution { DNearestNeighbor, DKernel };
+
+//drag correlation
+enum DragCorrelation { ErgunWenYu, Tang, Stokes, Felice, Tenneti, NoDrag };
+
+//lift correlation
+enum LiftCorrelation { NoLift, Saffman };
+
+//added mass correlation
+enum AddedMassCorrelation { NoAM, Finn };
+
+//effective viscosity
+enum EffectiveViscosity { None, Rescaled, Eilers };
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+class MyBoundaryHandling
+{
+public:
+
+   MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID ) :
+      flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ) {}
+
+   BoundaryHandling_T * operator()( IBlock* const block, const StructuredBlockStorage* const/* storage */ ) const;
+
+private:
+
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+
+}; // class MyBoundaryHandling
+
+
+BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block, const StructuredBlockStorage * const /*storage*/ ) const
+{
+   WALBERLA_ASSERT_NOT_NULLPTR( block );
+
+   FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
+   PdfField_T *  pdfField  = block->getData< PdfField_T > ( pdfFieldID_ );
+
+   const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+   BoundaryHandling_T * handling = new BoundaryHandling_T( "Boundary handling", flagField, fluid,
+         boost::tuples::make_tuple( NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ) ) );
+
+   handling->fillWithDomain( FieldGhostLayers );
+   return handling;
+}
+//*******************************************************************************************************************
+
+
+// VTK Output
+shared_ptr<vtk::VTKOutput> createFluidFieldVTKWriter( shared_ptr< StructuredBlockForest > & blocks,
+                                                      const BlockDataID & pdfFieldID, const BlockDataID & flagFieldID, uint_t writeFrequency,
+                                                      const std::string & vtkBaseFolder )
+{
+    auto pdfFieldVTKWriter = vtk::createVTKOutput_BlockData( blocks, "fluid_field", writeFrequency, uint_t(1), false, vtkBaseFolder );
+
+    blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfGhostLayerSync( blocks );
+    pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+    pdfFieldVTKWriter->addBeforeFunction( pdfGhostLayerSync );
+
+    field::FlagFieldCellFilter< FlagField_T > fluidFilter( flagFieldID );
+    fluidFilter.addFlag( Fluid_Flag );
+    pdfFieldVTKWriter->addCellInclusionFilter( fluidFilter );
+
+    auto velocityWriter = make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "Velocity (Lattice)" );
+    auto  densityWriter = make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "Density (Lattice)" );
+    pdfFieldVTKWriter->addCellDataWriter( velocityWriter );
+    pdfFieldVTKWriter->addCellDataWriter( densityWriter );
+
+    return pdfFieldVTKWriter;
+}
+
+DPMethod to_DPMethod( const std::string& s )
+{
+   if( s == "GNS"  ) return DPMethod::GNS;
+   throw std::runtime_error("invalid conversion from " + s + " to DPMethod");
+}
+
+Interpolation to_interpolation( const std::string& s )
+{
+   if( s == "nearest" ) return Interpolation::INearestNeighbor;
+   if( s == "kernel"  ) return Interpolation::IKernel;
+   if( s == "trilinear"  ) return Interpolation::ITrilinear;
+   throw std::runtime_error("invalid conversion from " + s + " to Interpolation");
+}
+
+Distribution to_distribution( const std::string& s )
+{
+   if( s == "nearest" ) return Distribution::DNearestNeighbor;
+   if( s == "kernel"  ) return Distribution::DKernel;
+   throw std::runtime_error("invalid conversion from " + s + " to Distribution");
+}
+
+DragCorrelation to_dragCorrelation( const std::string& s )
+{
+   if( s == "ergun" ) return DragCorrelation::ErgunWenYu;
+   if( s == "tang" ) return DragCorrelation::Tang;
+   if( s == "stokes" ) return DragCorrelation::Stokes;
+   if( s == "felice" ) return DragCorrelation::Felice;
+   if( s == "tenneti" ) return DragCorrelation::Tenneti;
+   if( s == "none" ) return DragCorrelation::NoDrag;
+   throw std::runtime_error("invalid conversion from " + s + " to DragCorrelation");
+}
+
+LiftCorrelation to_liftCorrelation( const std::string& s )
+{
+   if( s == "none" ) return LiftCorrelation::NoLift;
+   if( s == "saffman" ) return LiftCorrelation::Saffman;
+   throw std::runtime_error("invalid conversion from " + s + " to LiftCorrelation");
+}
+
+AddedMassCorrelation to_addedMassCorrelation( const std::string& s )
+{
+   if( s == "none" ) return AddedMassCorrelation::NoAM;
+   if( s == "finn" ) return AddedMassCorrelation::Finn;
+   throw std::runtime_error("invalid conversion from " + s + " to AddedMassCorrelation");
+}
+
+EffectiveViscosity to_effvisc( const std::string& s )
+{
+   if( s == "none" ) return EffectiveViscosity::None;
+   if( s == "rescaled"  ) return EffectiveViscosity::Rescaled;
+   if( s == "eilers"  ) return EffectiveViscosity::Eilers;
+   throw std::runtime_error("invalid conversion from " + s + " to effective viscosity");
+}
+
+std::string dpm_to_string( const DPMethod& dpm )
+{
+   if( dpm == DPMethod::GNS ) return "GNS";
+   throw std::runtime_error("invalid conversion from DPMethod to string");
+}
+
+uint_t createSpheresRandomly( StructuredBlockForest & forest, pe::BodyStorage & globalBodyStorage,
+                              const BlockDataID & bodyStorageID, const AABB & generationDomain,
+                              real_t diameter, real_t solidVolumeFraction, pe::MaterialID & material, real_t initialZVelocity )
+{
+   real_t domainVolume = generationDomain.volume();
+   real_t totalSphereVolume = domainVolume * solidVolumeFraction;
+   real_t sphereVolume = diameter * diameter * diameter * math::M_PI / real_t(6);
+   uint_t numberOfSpheres = uint_c( totalSphereVolume / sphereVolume );
+
+   real_t xParticle = real_t(0);
+   real_t yParticle = real_t(0);
+   real_t zParticle = real_t(0);
+
+   for( uint_t nSphere = 0; nSphere < numberOfSpheres; ++nSphere )
+   {
+
+      WALBERLA_ROOT_SECTION()
+      {
+         xParticle = math::realRandom<real_t>(generationDomain.xMin(), generationDomain.xMax());
+         yParticle = math::realRandom<real_t>(generationDomain.yMin(), generationDomain.yMax());
+         zParticle = math::realRandom<real_t>(generationDomain.zMin(), generationDomain.zMax());
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::broadcastObject( xParticle );
+         mpi::broadcastObject( yParticle );
+         mpi::broadcastObject( zParticle );
+      }
+
+
+      pe::SphereID sp = pe::createSphere( globalBodyStorage, forest.getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( xParticle, yParticle, zParticle ), diameter * real_t(0.5), material );
+      if( sp != NULL )
+      {
+         sp->setLinearVel(Vector3<real_t>(real_t(0),real_t(0),initialZVelocity));
+      }
+   }
+
+   return numberOfSpheres;
+}
+
+uint_t createSphereLattice( StructuredBlockForest & forest, pe::BodyStorage & globalBodyStorage,
+                            const BlockDataID & bodyStorageID, const AABB & generationDomain,
+                            real_t diameter, real_t solidVolumeFraction, pe::MaterialID & material, real_t initialZVelocity )
+{
+   real_t sphereVolume = math::M_PI * diameter * diameter * diameter / real_t(6);
+   real_t numSpheresDesired = solidVolumeFraction * generationDomain.volume() / sphereVolume;
+   uint_t spheresPerDirection = uint_c(std::cbrt(numSpheresDesired) );
+
+   real_t spacing = generationDomain.xSize() / real_c(spheresPerDirection);
+
+   WALBERLA_ASSERT( spacing >= diameter );
+
+   Vector3<real_t> generationOrigin( generationDomain.xMin() + spacing * real_t(0.5), generationDomain.yMin() + spacing * real_t(0.5), generationDomain.zMin() + spacing * real_t(0.5));
+
+   uint_t numSpheres( 0 );
+
+   for( auto it = grid_generator::SCIterator(generationDomain, generationOrigin, spacing); it != grid_generator::SCIterator(); ++it )
+   {
+      pe::SphereID sp = pe::createSphere( globalBodyStorage, forest.getBlockStorage(), bodyStorageID, 0, *it, diameter * real_t(0.5), material );
+
+      if( sp != NULL )
+      {
+         sp->setLinearVel(Vector3<real_t>(real_t(0),real_t(0),initialZVelocity));
+         ++numSpheres;
+      }
+   }
+
+   WALBERLA_MPI_SECTION()
+   {
+      mpi::allReduceInplace( numSpheres, mpi::SUM );
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("Created spheres in lattice arrangement with a center-to-center spacing of " << spacing << " ( " << real_t(100)*spacing/diameter << "% of diameter )" );
+
+   return numSpheres;
+}
+
+void resetSphereVelocities( const shared_ptr<StructuredBlockForest> & blocks, const BlockDataID & bodyStorageID, Vector3<real_t> vel )
+{
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
+   {
+      for( auto bodyIt = pe::BodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID); bodyIt != pe::BodyIterator::end<pe::Sphere>(); ++bodyIt )
+      {
+         bodyIt->setAngularVel(real_t(0),real_t(0),real_t(0));
+         bodyIt->setLinearVel(vel);
+      }
+   }
+}
+
+class QuantityEvaluator
+{
+public:
+
+   QuantityEvaluator( SweepTimeloop* timeloop,  StructuredBlockStorage & blocks,
+                      const BlockDataID & bodyStorageID, const BlockDataID & forceFieldID, const BlockDataID & pdfFieldID,
+                      const std::string & fileName, bool fileIO, const uint_t & numSpheres, const real_t & velExpected,
+                      const boost::function<Vector3<real_t> ()>  & evaluateMeanFluidVelocity ) :
+      timeloop_( timeloop ), blocks_( blocks ), bodyStorageID_( bodyStorageID ), forceFieldID_( forceFieldID ),
+      pdfFieldID_( pdfFieldID ), fileName_( fileName ), fileIO_( fileIO ),
+      numSpheres_( numSpheres ), velExpected_( velExpected ), evaluateMeanFluidVelocity_( evaluateMeanFluidVelocity )
+   {
+      if( fileIO ) {
+         std::ofstream file;
+         file.open(fileName_.c_str());
+         file
+               << "#t velExpected fX_particle fY_particle fZ_particle fX_fluid fY_fluid fZ_fluid velX_particle velY_particle velZ_particle velX_fluid velY_fluid velZ_fluid velX_rel velY_rel velZ_rel\n";
+         file.close();
+      }
+   }
+
+   void operator()() {
+      // get mean particle velocity
+      Vector3<real_t> particleVelocity = getMeanParticleVelocity();
+      // get force on particles
+      Vector3<real_t> particleForce = getParticleForce();
+      // get force on fluid
+      Vector3<real_t> fluidForce = getFluidForce();
+      //get mean fluid velocity
+      Vector3<real_t> fluidVelocity = evaluateMeanFluidVelocity_();
+
+      WALBERLA_LOG_INFO_ON_ROOT(
+            "mean particle force = " << particleForce[2] << ", mean fluid velocity = " << fluidVelocity[2]
+                                     << ", mean fluid force = " << fluidForce[2]);
+
+      if( fileIO_ )
+      {
+         WALBERLA_ROOT_SECTION()
+         {
+            writeToFile(particleVelocity, particleForce, fluidForce, fluidVelocity);
+         }
+      }
+   }
+
+   real_t getSettlingVelocity()
+   {
+      Vector3<real_t> particleVelocity = getMeanParticleVelocity();
+      Vector3<real_t> fluidVelocity = evaluateMeanFluidVelocity_();
+      return particleVelocity[2] - fluidVelocity[2];
+   }
+
+   Vector3<real_t> getMeanFluidVelocity()
+   {
+      return evaluateMeanFluidVelocity_();
+   }
+
+   Vector3<real_t> getMeanParticleVelocity()
+   {
+      Vector3<real_t> velocity(0.);
+      uint_t counter = 0;
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            velocity += bodyIt->getLinearVel();
+            ++counter;
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( velocity[0], mpi::SUM );
+         mpi::allReduceInplace( velocity[1], mpi::SUM );
+         mpi::allReduceInplace( velocity[2], mpi::SUM );
+         mpi::allReduceInplace( counter, mpi::SUM );
+      }
+
+      WALBERLA_CHECK_EQUAL( counter, numSpheres_, "Total number of spheres in the domain has changed!" );
+
+      return velocity / real_c(counter);
+   }
+
+   Vector3<real_t> getMeanForceOnParticles()
+   {
+      Vector3<real_t> force = getParticleForce();
+      return force;
+   }
+
+private:
+
+   Vector3<real_t> getParticleForce()
+   {
+      Vector3<real_t> force(0.);
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            force += bodyIt->getForce();
+         }
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+      }
+      return force;
+   }
+   Vector3<real_t> getFluidForce()
+   {
+
+      Vector3<real_t> force(0.);
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         Vec3Field_T* forceField = blockIt->getData< Vec3Field_T >( forceFieldID_ );
+         WALBERLA_FOR_ALL_CELLS_XYZ( forceField,
+            force += forceField->get(x,y,z);
+         );
+      }
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+      }
+      return force;
+   }
+
+   void writeToFile( const Vector3<real_t> & meanParticleVel, const Vector3<real_t> & particleForce, const Vector3<real_t> & fluidForce,
+                     const Vector3<real_t> & meanFluidVel )
+   {
+      std::ofstream file;
+      file.open( fileName_.c_str(), std::ofstream::app );
+      file.precision(8);
+
+      file << timeloop_->getCurrentTimeStep() << "\t " << velExpected_ << "\t "
+           << particleForce[0] << "\t " << particleForce[1] << "\t " << particleForce[2] << "\t "
+           << fluidForce[0] << "\t " << fluidForce[1] << "\t " << fluidForce[2] << "\t "
+           << meanParticleVel[0] << "\t " << meanParticleVel[1] << "\t " << meanParticleVel[2] << "\t "
+           << meanFluidVel[0] << "\t " << meanFluidVel[1] << "\t " << meanFluidVel[2] << "\t"
+           << meanParticleVel[0] - meanFluidVel[0] << "\t " << meanParticleVel[1] - meanFluidVel[1] << "\t " << meanParticleVel[2] - meanFluidVel[2] << "\n";
+      file.close();
+   }
+
+   SweepTimeloop* timeloop_;
+   StructuredBlockStorage & blocks_;
+   const BlockDataID bodyStorageID_;
+   const BlockDataID forceFieldID_;
+   const BlockDataID pdfFieldID_;
+   std::string fileName_;
+   bool fileIO_;
+   const uint_t numSpheres_;
+   const real_t velExpected_;
+
+   boost::function<Vector3<real_t> ()> evaluateMeanFluidVelocity_;
+
+
+};
+
+Vector3<real_t> getGNSMeanFluidVelocity( const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID pdfFieldID, BlockDataID svfFieldID, real_t domainVolume )
+{
+   Vector3<real_t> velocity( real_t(0) );
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
+   {
+      PdfField_T* pdfField = blockIt->getData< PdfField_T >( pdfFieldID );
+      ScalarField_T* svfField = blockIt->getData< ScalarField_T >( svfFieldID );
+      WALBERLA_FOR_ALL_CELLS_XYZ( pdfField,
+         real_t svf = svfField->get(x,y,z);
+         velocity += pdfField->getVelocity(x,y,z) / (real_t(1) - svf );
+      );
+   }
+   WALBERLA_MPI_SECTION()
+   {
+      mpi::allReduceInplace( velocity[0], mpi::SUM );
+      mpi::allReduceInplace( velocity[1], mpi::SUM );
+      mpi::allReduceInplace( velocity[2], mpi::SUM );
+   }
+   return velocity / domainVolume;
+}
+
+void logSingleResultToFile( const std::string & fileName, const real_t & solidVolumeFraction, const real_t & settlingVel,
+                            const real_t & expectedVel, const real_t & velUnhindered, const real_t & ReynoldsNumber, const real_t & tau )
+{
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ofstream file;
+      file.open( fileName.c_str(), std::ofstream::app );
+      file.precision(8);
+
+      file << solidVolumeFraction << " \t" << settlingVel << " \t" << expectedVel << " \t"
+           << settlingVel/velUnhindered << " \t" << expectedVel/velUnhindered << " \t" << ReynoldsNumber << " \t" << tau << "\n";
+   }
+}
+
+
+class CollisionPropertiesEvaluator
+{
+public:
+   CollisionPropertiesEvaluator( pe::cr::ICR & collisionResponse ) : collisionResponse_( collisionResponse ), maximumPenetration_(real_t(0))
+   {}
+
+   void operator()()
+   {
+      real_t maxPen = collisionResponse_.getMaximumPenetration();
+      maximumPenetration_ = std::max( maximumPenetration_, maxPen );
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( maximumPenetration_, mpi::MAX );
+      }
+   }
+   real_t getMaximumPenetrationInSimulation()
+   {
+      return maximumPenetration_;
+   }
+   void resetMaximumPenetration()
+   {
+      maximumPenetration_ = real_t(0);
+   }
+private:
+   pe::cr::ICR & collisionResponse_;
+   real_t maximumPenetration_;
+};
+
+class DummySweep
+{
+public:
+   DummySweep( )
+   {}
+
+   void operator()(IBlock * const /*block*/)
+   {}
+};
+
+void emptyFunction(){}
+
+//*******************************************************************************************************************
+/*!\brief Testcase that evaluates the hindered settling velocity of several spheres in a periodic box filled with fluid
+ *
+ * Sphere of radius D are randomly generated inside the domain to obtain a specific global solid volume fraction
+ * and then start to settle under gravity.
+ * The domain size is [32 x 32 x 32] * D and fully periodic.
+ *     _______________
+ *    | o  o   o   o  |
+ *    |   o   o       | |
+ *    | o        o    | | gravity (z-direction)
+ *    |    o   o     o| v
+ *    |o   o    o   o |
+ *    |  o   o    o   |
+ *    |_______o_____o_|
+ *
+ * An external force is applied onto the fluid in opposite gravitational direction to counteract the settling motion and
+ * avoid infinitely large settling velocity (since no walls are present).
+ *
+ *
+ * Many parts of the discrete particle method can be modified via command line arguments:
+ *  - discrete particle method
+ *  - interpolation method
+ *  - distribution method
+ *  - number of interaction subcycles
+ *  - number of pe steps per interaction subcycle
+ *  - correlations for drag, lift, added mass
+ *  - effective viscosity
+ *  - turbulence model
+ *  - lubrication force and its cut off distance
+ *
+ * An extensive description of the different components and this test case is given in Rettinger, Ruede (2017).
+ *
+ *
+ * References:
+ * Experimental:
+ * - J. Richardson, W. Zaki - "The sedimentation of a suspension of uniform spheres under conditions of viscous flow",
+ *   Chemical Engineering Science 3 (2) (1954) 65–73. doi:10.1016/0009-2509(54)85015-9.
+ * - T. Baldock, M. Tomkins, P. Nielsen, M. Hughes - "Settling velocity of sediments at high concentrations",
+ *   Coastal Engineering 51 (1) (2004) 91–100. doi:10.1016/j.coastaleng.2003.12.004.
+ *
+ * Discrete particle simulations:
+ * - C. Rettinger, U. Ruede - "A Coupled Lattice Boltzmann Method and Discrete Element Method for Discrete Particle
+ *   Simulations of Particulate Flows". arXiv preprint arXiv:1711.00336 (2017)
+ * - J. R. Finn, M. Li, S. V. Apte - "Particle based modelling and simulation of natural sand dynamics in the wave bottom
+ *   boundary layer", Journal of Fluid Mechanics 796 (2016) 340–385. doi:10.1017/jfm.2016.246.
+ *
+ */
+//*******************************************************************************************************************
+int main( int argc, char **argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+
+   /////////////////////////////////////////////
+   //                                         //
+   //   Command Line Argument Customization   //
+   //                                         //
+   /////////////////////////////////////////////
+
+   bool funcTest  = false;
+   bool vtkIO     = false;
+   bool fileIO    = false;
+   uint_t vtkWriteFrequency = 0;
+   std::string baseFolder = "vtk_out_HinderedSettlingDPM";
+
+   real_t densityRatio = real_t(2500) / real_t(1000);
+   real_t gravity = real_t(0.002); // has to be small enough to keep settling velocities small
+   real_t diameter = real_t(0.5);
+   uint_t interactionSubCycles = uint_t(1); // number of subcycles that involve evaluation of the interaction force -> around 3 for stability with added mass
+   uint_t peSubSteps = uint_t(1); // number of pe only calls in each subcycle
+   real_t solidVolumeFraction = real_t(0.05);
+
+   DPMethod dpm = DPMethod::GNS;
+   Interpolation interpol = Interpolation::IKernel;
+   Distribution dist = Distribution::DKernel;
+   DragCorrelation dragCorr = DragCorrelation::Tenneti;
+   LiftCorrelation liftCorr = LiftCorrelation ::NoLift;
+   AddedMassCorrelation addedMassCorr = AddedMassCorrelation::NoAM;
+   EffectiveViscosity effVisc = EffectiveViscosity::None;
+
+   bool useTurbulenceModel = false;
+   const real_t smagorinskyConstant = real_t(0.1); //for turbulence model
+
+   real_t lubricationCutOffDistance = real_t(0); //0 switches it off
+   bool useLubricationCorrection = false; // false: use full lubrication force, true: use only correction part
+
+   bool createSpheresInLattice = false;
+   bool initialSimulationToAdjustFluidForcing = false;
+
+   real_t initialSphereVelocityZ( real_t(0) );
+
+   real_t relativeVelDiffLimit( real_t(1e-5) ); //set negative to avoid convergence before 30 dimensionless timesteps
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--funcTest" )                      == 0 ) funcTest = true;
+      else if( std::strcmp( argv[i], "--vtkIOFreq" )                == 0 ) vtkWriteFrequency = uint_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--fileIO" )                   == 0 ) fileIO = true;
+      else if( std::strcmp( argv[i], "--baseFolder" )               == 0 ) baseFolder = argv[++i];
+      else if( std::strcmp( argv[i], "--gravity" )                  == 0 ) gravity = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--diameter" )                 == 0 ) diameter = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--solidVolumeFraction" )      == 0 ) solidVolumeFraction = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--interactionSubCycles" )     == 0 ) interactionSubCycles = uint_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--peSubSteps" )               == 0 ) peSubSteps = uint_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--DPMethod" )                 == 0 ) dpm = to_DPMethod( argv[++i] );
+      else if( std::strcmp( argv[i], "--interpolation" )            == 0 ) interpol = to_interpolation( argv[++i] );
+      else if( std::strcmp( argv[i], "--distribution" )             == 0 ) dist = to_distribution( argv[++i] );
+      else if( std::strcmp( argv[i], "--dragCorrelation" )          == 0 ) dragCorr = to_dragCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--liftCorrelation" )          == 0 ) liftCorr = to_liftCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--addedMassCorrelation" )     == 0 ) addedMassCorr = to_addedMassCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--effectiveViscosity" )       == 0 ) effVisc = to_effvisc( argv[++i] );
+      else if( std::strcmp( argv[i], "--useTurbulenceModel" )       == 0 ) useTurbulenceModel = true;
+      else if( std::strcmp( argv[i], "--useLubricationCorrection" ) == 0 ) useLubricationCorrection = true;
+      else if( std::strcmp( argv[i], "--lubricationCutOff" )        == 0 ) lubricationCutOffDistance = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--createSpheresInLattice" )   == 0 ) createSpheresInLattice = true;
+      else if( std::strcmp( argv[i], "--initialSimulation" )        == 0 ) initialSimulationToAdjustFluidForcing = true;
+      else if( std::strcmp( argv[i], "--convergenceLimit" )         == 0 ) relativeVelDiffLimit = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--initialSphereVelocityZ" )   == 0 ) initialSphereVelocityZ = real_c( std::atof( argv[++i] ) );
+      else WALBERLA_ABORT("Found invalid command line argument \"" << argv[i] << "\" - aborting...");
+   }
+
+   if( vtkWriteFrequency > 0 ) vtkIO = true;
+
+   WALBERLA_CHECK( diameter <= real_t(1), "Diameter is not allowed to be > 1!" );
+   WALBERLA_CHECK( solidVolumeFraction <= real_t(0.65), "Solid volume fraction is not allowed to be > 0.65!" );
+   WALBERLA_CHECK( interactionSubCycles > uint_t(0), "Number of interaction sub cycles has to be at least 1!");
+   WALBERLA_CHECK( peSubSteps > uint_t(0), "Number of pe sub steps has to be at least 1!");
+   WALBERLA_CHECK( lubricationCutOffDistance >= real_t(0), "Lubrication cut off distance has to be non-negative!");
+
+   if( funcTest )
+   {
+      walberla::logging::Logging::instance()->setLogLevel(logging::Logging::LogLevel::WARNING);
+   }
+   if( fileIO )
+   {
+      // create base directory if it does not yet exist
+      boost::filesystem::path tpath( baseFolder );
+      if( !boost::filesystem::exists( tpath ) )
+         boost::filesystem::create_directory( tpath );
+   }
+
+   ///////////////////////////////
+   //                           //
+   //   SIMULATION PROPERTIES   //
+   //                           //
+   ///////////////////////////////
+
+   const uint_t xlength = uint_c( real_t(32) * diameter )  ;
+   const uint_t ylength = xlength;
+   const uint_t zlength = xlength;
+
+   if( solidVolumeFraction < real_t(1e-10) )
+   {
+      // create only a single sphere
+      solidVolumeFraction = math::M_PI * diameter * diameter * diameter / ( real_t(6) * real_c( xlength * ylength * zlength));
+   }
+
+   const real_t diameter_SI = real_t(0.00035); // m, Finn et al, Tab 5
+   const real_t gravity_SI = real_t(9.81); // m/s^2
+
+   const real_t dx_SI = diameter_SI / diameter;
+   const real_t dx = real_t(1);
+   const real_t viscosity_SI = real_t(1e-3); // kg/(ms)
+   const real_t densityFluid_SI = real_t(1e3); // kg/m^3
+
+   const real_t dt_SI = std::sqrt(gravity * dx_SI / gravity_SI);
+   const real_t viscosity = ( viscosity_SI/densityFluid_SI ) * dt_SI / ( dx_SI * dx_SI );
+   const real_t tau = real_t(1) / lbm::collision_model::omegaFromViscosity( viscosity );
+
+   real_t gravitationalForce = - gravity * ( densityRatio - real_t(1) ) * diameter * diameter * diameter * math::PI / real_t(6);
+
+   // unhindered settling velocity of a single sphere in infinite fluid, would come from experiments or DNS, NOT Stokes settling velocity, from Finn et al, Tab 5
+   const real_t velUnhindered_SI = real_t(-0.048); // m/s
+
+   const real_t velStokes = -( densityRatio - real_t(1) ) * diameter * diameter * gravity / ( real_t(18) * viscosity );
+   const real_t velUnhindered = velUnhindered_SI * dt_SI / dx_SI;
+
+   const real_t dt_DEM = real_t(1) / real_c(interactionSubCycles * peSubSteps);
+
+   const real_t dt = real_t(1);
+   const real_t dtInteractionSubCycle = dt / real_c(interactionSubCycles);
+   const real_t dtBodyVelocityTimeDerivativeEvaluation = dtInteractionSubCycle;
+
+   const uint_t tStokes = uint_c(densityRatio * diameter * diameter / ( real_t(18) * viscosity ));
+
+   const uint_t timesteps = (funcTest) ? uint_t(10) : uint_t(30) * tStokes; // total number of time steps for the whole simulation
+
+   const Vector3<real_t> initialFluidVelocity( real_t(0) );
+
+   const std::string fileNameLoggingInit = baseFolder+"/evalHinderedSettlingDynamicsSubgrid_eps"+std::to_string(uint_c(real_t(100) * solidVolumeFraction))+"_d"+std::to_string(uint_c(real_t(100) * diameter))+"_init.txt";
+   const std::string fileNameLogging = baseFolder+"/evalHinderedSettlingDynamicsSubgrid_eps"+std::to_string(uint_c(real_t(100) * solidVolumeFraction))+"_d"+std::to_string(uint_c(real_t(100) * diameter))+".txt";
+
+   if( !funcTest ) {
+      WALBERLA_LOG_INFO_ON_ROOT("Lx x Ly x Lz = " << xlength << " x " << ylength << " x " << zlength);
+      WALBERLA_LOG_INFO_ON_ROOT("tau = " << tau);
+      WALBERLA_LOG_INFO_ON_ROOT("viscosity = " << viscosity);
+      WALBERLA_LOG_INFO_ON_ROOT("gravity = " << gravity);
+      WALBERLA_LOG_INFO_ON_ROOT("total external (gravity & buoyancy) force on single sphere = " << gravitationalForce);
+      WALBERLA_LOG_INFO_ON_ROOT("diameter = " << diameter);
+      WALBERLA_LOG_INFO_ON_ROOT("input solid volume fraction = " << solidVolumeFraction);
+      WALBERLA_LOG_INFO_ON_ROOT("discrete particle method = " << dpm_to_string(dpm));
+      WALBERLA_LOG_INFO_ON_ROOT("interpolator = " << interpol);
+      WALBERLA_LOG_INFO_ON_ROOT("distribution = " << dist);
+      WALBERLA_LOG_INFO_ON_ROOT("dragCorrelation = " << dragCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("addedMassCorrelation = " << addedMassCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("liftCorrelation = " << liftCorr);
+      WALBERLA_LOG_INFO_ON_ROOT("effective viscosity = " << effVisc);
+      WALBERLA_LOG_INFO_ON_ROOT("turbulence model = " << (useTurbulenceModel ? "yes" : "no"));
+      WALBERLA_LOG_INFO_ON_ROOT("interaction sub cycles = " << interactionSubCycles);
+      WALBERLA_LOG_INFO_ON_ROOT("pe sub steps = " << peSubSteps);
+      WALBERLA_LOG_INFO_ON_ROOT("lubrication cut off distance = " << lubricationCutOffDistance);
+      WALBERLA_LOG_INFO_ON_ROOT("use lubrication correction term instead of full formula = " << (useLubricationCorrection ? "yes" : "no"));
+      WALBERLA_LOG_INFO_ON_ROOT("Ga = " << std::sqrt((densityRatio - real_t(1)) * gravity * diameter * diameter * diameter) / viscosity);
+      WALBERLA_LOG_INFO_ON_ROOT("dx_SI = " << dx_SI << ", dt_SI = " << dt_SI);
+      WALBERLA_LOG_INFO_ON_ROOT("dt_DEM = " << dt_DEM);
+      WALBERLA_LOG_INFO_ON_ROOT("t_ref = " << tStokes << " simulation steps");
+      WALBERLA_LOG_INFO_ON_ROOT("logging is written to " + fileNameLogging );
+   }
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const uint_t XBlocks = uint_t(4);
+   const uint_t YBlocks = uint_t(4);
+   const uint_t ZBlocks = uint_t(4);
+
+   const uint_t XCells  = xlength / XBlocks;
+   const uint_t YCells  = ylength / YBlocks;
+   const uint_t ZCells  = zlength / ZBlocks;
+
+   if( XBlocks * XCells != xlength ||
+       YBlocks * YCells != ylength ||
+       ZBlocks * ZCells != zlength ) WALBERLA_ABORT("Domain decomposition failed!");
+
+   auto blocks = blockforest::createUniformBlockGrid( XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells,
+                                                      dx, 0, false, false,
+                                                      true, true, true,
+                                                      false );
+
+
+   ////////
+   // PE //
+   ////////
+
+   shared_ptr<pe::BodyStorage>  globalBodyStorage = make_shared<pe::BodyStorage>();
+   pe::SetBodyTypeIDs<BodyTypeTuple>::execute();
+   auto bodyStorageID = blocks->addBlockData(pe::createStorageDataHandling<BodyTypeTuple>(), "pe Body Storage");
+   auto ccdID         = blocks->addBlockData(pe::ccd::createHashGridsDataHandling( globalBodyStorage, bodyStorageID ), "CCD");
+   auto fcdID         = blocks->addBlockData(pe::fcd::createGenericFCDDataHandling<BodyTypeTuple, pe::fcd::AnalyticCollideFunctor>(), "FCD");
+
+   pe::cr::ICR* cr;
+   pe::cr::DEM cr_dem(globalBodyStorage, blocks->getBlockStoragePointer(), bodyStorageID, ccdID, fcdID );
+   cr = &cr_dem;
+
+   const real_t restitutionCoeff = real_t(0.88);
+   const real_t frictionCoeff = real_t(0.25);
+
+   real_t sphereVolume = diameter * diameter * diameter * math::M_PI / real_t(6);
+   const real_t particleMass = densityRatio * sphereVolume;
+   const real_t Mij = particleMass * particleMass / ( real_t(2) * particleMass );
+   const real_t lnDryResCoeff = std::log(restitutionCoeff);
+   const real_t collisionTime = real_t(0.5);
+   const real_t stiffnessCoeff = math::M_PI * math::M_PI * Mij / ( collisionTime * collisionTime * ( real_t(1) - lnDryResCoeff * lnDryResCoeff / ( math::M_PI * math::M_PI + lnDryResCoeff* lnDryResCoeff ) ) );
+   const real_t dampingCoeff = - real_t(2) * std::sqrt( Mij * stiffnessCoeff ) *
+                               ( std::log(restitutionCoeff) / std::sqrt( math::M_PI * math::M_PI + (std::log(restitutionCoeff) * std::log(restitutionCoeff) ) ) );
+   const real_t contactDuration = real_t(2) * math::M_PI * Mij / ( std::sqrt( real_t(4) * Mij * stiffnessCoeff - dampingCoeff * dampingCoeff )); //formula from Uhlman
+
+   if( !funcTest ) {
+      WALBERLA_LOG_INFO_ON_ROOT("Created sediment material with:\n"
+                                      << " - coefficient of restitution = " << restitutionCoeff << "\n"
+                                      << " - coefficient of friction = " << frictionCoeff << "\n"
+                                      << " - stiffness coefficient kn = " << stiffnessCoeff << "\n"
+                                      << " - damping coefficient cdn = " << dampingCoeff << "\n"
+                                      << " - contact time Tc = " << contactDuration);
+   }
+   auto peMaterial = pe::createMaterial( "sedimentMat", densityRatio, restitutionCoeff, frictionCoeff, frictionCoeff, real_t(0), real_t(200), stiffnessCoeff, dampingCoeff, dampingCoeff );
+
+   /////////////////
+   // PE COUPLING //
+   /////////////////
+
+   // connect to pe
+   const real_t overlap = real_t( 1.5 ) * dx;
+   auto syncCall = boost::bind( pe::syncNextNeighbors<BodyTypeTuple>, boost::ref(blocks->getBlockForest()), bodyStorageID, static_cast<WcTimingTree*>(NULL), overlap, false );
+   shared_ptr<CollisionPropertiesEvaluator> collisionPropertiesEvaluator = walberla::make_shared<CollisionPropertiesEvaluator>( *cr );
+
+   // create the spheres
+   uint_t numSpheres = 0;
+
+   if ( createSpheresInLattice )
+   {
+      numSpheres = createSphereLattice( *blocks, *globalBodyStorage, bodyStorageID, AABB( real_t(0), real_t(0), real_t(0), real_c(xlength), real_c(ylength), real_c(zlength) ),
+                                        diameter, solidVolumeFraction, peMaterial, real_t(0) );
+      syncCall();
+
+   } else {
+      numSpheres = createSpheresRandomly( *blocks, *globalBodyStorage, bodyStorageID, AABB( real_t(0), real_t(0), real_t(0), real_c(xlength), real_c(ylength), real_c(zlength) ),
+                                          diameter, solidVolumeFraction, peMaterial, real_t(0) );
+      syncCall();
+
+      const uint_t initialPeSteps = uint_t(50000);
+      const real_t dt_DEM_init = collisionTime / real_t(10);
+      const real_t overlapLimit = real_t(0.05) * diameter;
+      
+      WALBERLA_LOG_INFO_ON_ROOT("Sphere creation done --- resolving overlaps with goal all < " << overlapLimit / diameter * real_t(100) << "%");
+      
+      for( uint_t pet = uint_t(1); pet <= initialPeSteps; ++pet )
+      {
+         cr->timestep( dt_DEM_init );
+         syncCall();
+         (*collisionPropertiesEvaluator)();
+         real_t maxPen = collisionPropertiesEvaluator->getMaximumPenetrationInSimulation();
+         if( maxPen < overlapLimit )
+         {
+            WALBERLA_LOG_INFO_ON_ROOT("Carried out " << pet << " DEM-only time steps to resolve initial overlaps");
+            break;
+         }else{
+            if( pet % uint_t(200) == uint_t(0) )
+            {
+               WALBERLA_LOG_INFO_ON_ROOT(pet << " - current max overlap = " << maxPen / diameter * real_t(100) << "%");
+            }
+         }
+         collisionPropertiesEvaluator->resetMaximumPenetration();
+      }
+   }
+
+   Vector3<real_t> initialSphereVelocity(real_t(0), real_t(0), initialSphereVelocityZ);
+   WALBERLA_LOG_INFO_ON_ROOT("Resetting sphere velocity to " << initialSphereVelocity );
+   resetSphereVelocities( blocks, bodyStorageID, initialSphereVelocity );
+
+
+   const real_t domainVolume = real_c( xlength * ylength * zlength );
+   real_t actualSolidVolumeFraction = real_c( numSpheres ) * diameter * diameter * diameter * math::M_PI / ( real_t(6) * domainVolume );
+   real_t ReynoldsNumber = std::fabs(velUnhindered) * diameter / viscosity;
+
+   // apply external forcing on fluid to approximately balance the force from the settling particles to avoid too large fluid or particle velocities
+   const real_t extForceZ = actualSolidVolumeFraction * gravity * (densityRatio - real_t(1));
+   Vector3<real_t> extForce = Vector3<real_t>(real_t(0), real_t(0), extForceZ );
+
+   // apply estimate by Richardson & Zaki (1954) for unbounded flow (DomainLength->infty)
+   real_t n = 0;
+   if( ReynoldsNumber < real_t(0.2) )
+   {
+      n = real_t(4.65);
+   } else if ( ReynoldsNumber < real_t(1) )
+   {
+      n = real_t(4.35) * std::pow( ReynoldsNumber, real_t(-0.03) );
+   } else if ( ReynoldsNumber < real_t(500) )
+   {
+      n = real_t(4.45) * std::pow( ReynoldsNumber, real_t(-0.1) );
+   } else
+   {
+      n = real_t(2.39);
+   }
+
+   real_t expectedVelocity = velUnhindered * std::pow( ( real_t(1) - actualSolidVolumeFraction ), n );
+
+   WALBERLA_LOG_INFO_ON_ROOT("solid volume fraction = " << actualSolidVolumeFraction );
+   WALBERLA_LOG_INFO_ON_ROOT("number of spheres = " << numSpheres );
+   WALBERLA_LOG_INFO_ON_ROOT("velocity_Stokes = " << velStokes );
+   WALBERLA_LOG_INFO_ON_ROOT("velocity_Unhindered = " << velUnhindered );
+   WALBERLA_LOG_INFO_ON_ROOT("Re = " << ReynoldsNumber );
+   WALBERLA_LOG_INFO_ON_ROOT("expected settling velocity = " << expectedVelocity <<" = " << expectedVelocity * dx_SI / dt_SI << " m/s" );
+   WALBERLA_LOG_INFO_ON_ROOT("external forcing on fluid = " << extForce );
+   WALBERLA_LOG_INFO_ON_ROOT("total external forcing applied on all fluid cells = " << extForce[2] * (real_t(1) - actualSolidVolumeFraction) * real_c( xlength * ylength * zlength ) );
+   WALBERLA_LOG_INFO_ON_ROOT("total external (gravity & buoyancy) force on all spheres = " << gravitationalForce * real_c(numSpheres) );
+
+   //////////////////////
+   //                  //
+   //    BLOCK DATA    //
+   //                  //
+   //////////////////////
+
+   // create force field
+   BlockDataID forceFieldID = field::addToStorage< Vec3Field_T >( blocks, "force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   BlockDataID dragForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "drag force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID amForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "am force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID liftForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "lift force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   // create omega field
+   BlockDataID omegaFieldID = field::addToStorage< ScalarField_T >( blocks, "omega field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // create the lattice model
+   LatticeModel_T latticeModel = LatticeModel_T( omegaFieldID, ForceModel_T( forceFieldID ) );
+
+   // add PDF field
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage( blocks, "pdf field (zyxf)", latticeModel, initialFluidVelocity, real_t(1), FieldGhostLayers, field::zyxf );
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" );
+
+   // add boundary handling & initialize outer domain boundaries
+   BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( MyBoundaryHandling( flagFieldID, pdfFieldID ), "boundary handling" );
+
+   // field to store fluid velolcity
+   BlockDataID velocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+   BlockDataID oldVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+   BlockDataID swappedOldVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "swapped old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers );
+
+   // create pressure field
+   BlockDataID pressureFieldID = field::addToStorage< ScalarField_T >( blocks, "pressure field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // create solid volume fraction field
+   BlockDataID svfFieldID = field::addToStorage< ScalarField_T >( blocks, "svf field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // field to store pressure gradient
+   BlockDataID pressureGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "pressure gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store curl of fluid velocity
+   BlockDataID velocityCurlFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity curl field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store velocity gradient
+   BlockDataID velocityGradientFieldID = field::addToStorage< TensorField_T >( blocks, "velocity gradient field", Matrix3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store gradient of stress tensor
+   BlockDataID stressTensorGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "stress tensor gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store time derivative of fluid velocity
+   BlockDataID timeDerivativeVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "time derivative velocity field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // communication schemes
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> forceComm( blocks, forceFieldID );
+
+   blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfScheme( blocks );
+   pdfScheme.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+
+   // setup of the communication for synchronizing the velocity field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityFieldID ) );
+
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > oldVelocityCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   oldVelocityCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( oldVelocityFieldID ) );
+
+   // setup of the communication for synchronizing the solid volume fraction field between neighboring blocks which takes into account the svf values inside the ghost layers
+   shared_ptr<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >svfCommunicationScheme = make_shared<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >( blocks, svfFieldID );
+
+   // setup of the communication for synchronizing the pressure field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<ScalarField_T> >( pressureFieldID ) );
+
+   // setup of the communication for synchronizing the pressure gradient field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( pressureGradientFieldID ) );
+
+   // setup of the communication for synchronizing the velocity curl field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCurlCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCurlCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityCurlFieldID ) );
+
+   // communication for synchronizing the velocity gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<TensorField_T> >( velocityGradientFieldID ) );
+
+   // communication for synchronizing the stress tensor gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > stressTensorGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   stressTensorGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( stressTensorGradientFieldID ) );
+
+   // communication for synchronizing the interaction force field
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> dragForceComm( blocks, dragForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> amForceComm( blocks, amForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> liftForceComm( blocks, liftForceFieldID );
+
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> dragForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, dragForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> amForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, amForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> liftForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, liftForceFieldID, uint_t(1) );
+
+   /////////////////////////////////
+   //                             //
+   //    CORRELATION FUNCTIONS    //
+   //                             //
+   /////////////////////////////////
+
+   // drag correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t>&, const Vector3<real_t> &, real_t, real_t, real_t, real_t)> dragCorrelationFunction;
+   if( dragCorr == DragCorrelation::ErgunWenYu )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceErgunWenYu;
+   }
+   else if( dragCorr == DragCorrelation::Tang )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTang;
+   }
+   else if( dragCorr == DragCorrelation::Stokes )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceStokes;
+   }
+   else if( dragCorr == DragCorrelation::Felice )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceFelice;
+   }
+   else if( dragCorr == DragCorrelation::Tenneti )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTenneti;
+   }
+   else if( dragCorr == DragCorrelation::NoDrag )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::noDragForce;
+   }
+   else
+   {
+      WALBERLA_ABORT("Drag correlation not yet implemented!");
+   }
+
+   // lift correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )> liftCorrelationFunction;
+   if( liftCorr == LiftCorrelation::NoLift )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::noLiftForce;
+   }
+   else if( liftCorr == LiftCorrelation::Saffman )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::liftForceSaffman;
+   }
+   else
+   {
+      WALBERLA_ABORT("Lift correlation not yet implemented!");
+   }
+
+   // added mass correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t )> addedMassCorrelationFunction;
+   if( addedMassCorr == AddedMassCorrelation::NoAM )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::noAddedMassForce;
+   }
+   else if( addedMassCorr == AddedMassCorrelation::Finn )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::addedMassForceFinn;
+   }
+   else
+   {
+      WALBERLA_ABORT("Added mass correlation not yet implemented!");
+   }
+
+   // set up effective viscosity calculation
+   boost::function<real_t ( real_t, real_t)> effectiveViscosityFunction;
+   if( effVisc == EffectiveViscosity::None )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateUnchangedEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Rescaled )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateRescaledEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Eilers )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateEilersEffectiveViscosity;
+   }
+   else
+   {
+      WALBERLA_ABORT("Effective viscosity not yet implemented!");
+   }
+   shared_ptr<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator> effectiveViscosityEvaluator =
+         walberla::make_shared<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>(omegaFieldID, svfFieldID, viscosity, effectiveViscosityFunction );
+
+
+   ////////////////////////////////
+   //                            //
+   //    EVALUATION FUNCTIONS    //
+   //                            //
+   ////////////////////////////////
+
+   // evaluator for bodies' velocity time derivative
+   shared_ptr<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator> bodyVelocityTimeDerivativeEvaluator = make_shared<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( blocks, bodyStorageID, dtBodyVelocityTimeDerivativeEvaluation );
+   (*bodyVelocityTimeDerivativeEvaluator)();
+
+   // function used to evaluate the interaction force between fluid and particles
+   boost::function<void(void)> dragAndPressureForceEvaluationFunction;
+   if( dpm == DPMethod::GNS ) {
+      if (interpol == Interpolation::INearestNeighbor) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::IKernel) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::ITrilinear) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      }
+   }
+   else
+   {
+      WALBERLA_ABORT("Discrete particle method not yet implemented!");
+   }
+
+
+   // function to evaluate the lift force contribution
+   boost::function<void(void)> liftForceEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+   // function to evaluate the added mass contribution
+   boost::function<void(void)> addedMassEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+   // function to evaluate lubrication forces
+   boost::function<void(void)> lubricationEvaluationFunction;
+   if( lubricationCutOffDistance > real_t(0) )
+   {
+      if( useLubricationCorrection )
+      {
+         typedef pe_coupling::LubricationCorrection LE_T;
+         shared_ptr<LE_T> lubEval = make_shared<LE_T>( blocks, globalBodyStorage, bodyStorageID, viscosity, lubricationCutOffDistance );
+         lubricationEvaluationFunction = boost::bind(&LE_T::operator(), lubEval);
+      }
+      else
+      {
+         typedef pe_coupling::discrete_particle_methods::LubricationForceEvaluator LE_T;
+         shared_ptr<LE_T> lubEval = make_shared<LE_T>( blocks, globalBodyStorage, bodyStorageID, viscosity, lubricationCutOffDistance );
+         lubricationEvaluationFunction = boost::bind(&LE_T::operator(), lubEval);
+      }
+   } else {
+      lubricationEvaluationFunction = emptyFunction;
+   }
+
+
+   // function to evaluate the mean fluid velocity
+   boost::function<Vector3<real_t> ()> evaluateMeanFluidVelocity = boost::bind(getGNSMeanFluidVelocity, blocks, pdfFieldID, svfFieldID, domainVolume);
+
+
+   //////////////////////////////
+   //                          //
+   //    INITIAL SIMULATION    //
+   //                          //
+   //////////////////////////////
+
+   // initial simulation: keep particles fix, evaluate acting force on particles
+   // if this force is approximately converged, see if it matches the gravitational force
+   // if not, change the external force accordingly
+
+   if( solidVolumeFraction > real_t(0.01) && initialSimulationToAdjustFluidForcing )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("===================================================================================" );
+      WALBERLA_LOG_INFO_ON_ROOT("Starting initial simulation to equilibrate fluid forcing and interaction force");
+
+      // create the timeloop
+      SweepTimeloop timeloopInit( blocks->getBlockStorage(), timesteps );
+
+
+      // evaluation function
+      shared_ptr<QuantityEvaluator> quantityEvaluator = walberla::make_shared< QuantityEvaluator >( &timeloopInit, *blocks, bodyStorageID, forceFieldID, pdfFieldID, fileNameLoggingInit, fileIO, numSpheres, expectedVelocity, evaluateMeanFluidVelocity );
+
+      shared_ptr<pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder> gnsExternalForceOnForceFieldAdder =
+               walberla::make_shared<pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder>( forceFieldID, svfFieldID, extForce );
+
+
+      //initial setup
+      {
+         // init solid volume fraction field
+         if( dist == Distribution::DNearestNeighbor )
+         {
+            pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::NearestNeighborDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+            for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+         } else {
+            pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+            for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+         }
+
+         (*svfCommunicationScheme)();
+
+         pe_coupling::discrete_particle_methods::ForceFieldResetter forceFieldResetter( forceFieldID );
+         for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  forceFieldResetter( &(*blockIt) );
+
+         for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  (*gnsExternalForceOnForceFieldAdder)( &(*blockIt) );
+
+      }
+
+      (*quantityEvaluator)();
+
+      ///////// begin of GNS timeloop ///////////////////////
+
+      timeloopInit.addFuncBeforeTimeStep( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID), "Resetting force on bodies" );
+
+
+      timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Pressure Field Evaluation" )
+                         << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureCommunicationScheme), "Pressure Field Communication" );
+
+      timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( pressureGradientFieldID, pressureFieldID, boundaryHandlingID ), "Pressure Gradient Field Evaluation" )
+                         << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureGradientCommunicationScheme), "Pressure Gradient Field Communication" );
+
+      // subcycling loop begin
+      for( uint_t subcycle = 1; subcycle <= interactionSubCycles; ++subcycle )
+      {
+         timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" );
+         timeloopInit.add() << Sweep( makeSharedSweep(gnsExternalForceOnForceFieldAdder), "Force Field Add" )
+                            << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+         timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                            << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+
+         // evaluate Fdrag
+         timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( dragForceFieldID ), "Drag Force Field Reset" )
+                            << AfterFunction( dragAndPressureForceEvaluationFunction, "Fluid-Particle Interaction Force Evaluation" )
+                            << AfterFunction( dragForceComm, "Drag Force Field Communication" );
+
+         if( subcycle != interactionSubCycles )
+         {
+            timeloopInit.add() << Sweep( DummySweep(), "DummySweep")
+                               << AfterFunction( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID), "Resetting force on bodies" );
+         }
+      }
+
+      timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" );
+      timeloopInit.add() << Sweep( makeSharedSweep(gnsExternalForceOnForceFieldAdder), "Force Field Add" )
+                         << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+      timeloopInit.add() << Sweep( makeSharedSweep<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>( effectiveViscosityEvaluator ), "Effective Viscosity Evaluation");
+      if( useTurbulenceModel ) timeloopInit.add() << Sweep( pe_coupling::discrete_particle_methods::GNSSmagorinskyLESField<LatticeModel_T>(blocks, omegaFieldID, pdfFieldID, svfFieldID, smagorinskyConstant), "Turbulence Model" );
+
+      // execute GNS-LBM sweep, boundary handling, PDF communication
+      auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag );
+
+      timeloopInit.add() << Sweep( lbm::makeCollideSweep( sweep ), "GNS-LBM sweep (collide)" );
+
+      timeloopInit.add() << BeforeFunction( pdfScheme, "LBM Communication" )
+                     << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+      timeloopInit.add() << Sweep( lbm::makeStreamSweep( sweep ), "GNS-LBM sweep (stream)" );
+
+
+      // evaluate the forces / velocities
+      timeloopInit.addFuncAfterTimeStep( SharedFunctor<QuantityEvaluator>( quantityEvaluator ), "Quantity Evaluator" );
+
+      // configure vtk output
+      if( vtkIO )
+      {
+         auto fluidVTKWriter = vtk::createVTKOutput_BlockData( blocks, "fluid_field_init", vtkWriteFrequency, uint_t(1), false, baseFolder );
+         blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfGhostLayerSync( blocks );
+         pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+         fluidVTKWriter->addBeforeFunction( pdfGhostLayerSync );
+         auto velocityWriter = make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "Velocity (Lattice)" );
+         auto  densityWriter = make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "Density (Lattice)" );
+         fluidVTKWriter->addCellDataWriter( velocityWriter );
+         fluidVTKWriter->addCellDataWriter( densityWriter );
+         timeloopInit.addFuncAfterTimeStep( vtk::writeFiles( fluidVTKWriter ), "VTK (fluid field data)" );
+
+         timeloopInit.addFuncAfterTimeStep( field::createVTKOutput<Vec3Field_T, float>( forceFieldID, *blocks, "force_field_init", vtkWriteFrequency, 1, false, baseFolder ), "VTK (force field init)" );
+      }
+
+      // execute simulation
+      WcTimingPool timeloopInitTiming;
+
+      real_t oldInteractionForce( real_t(0) );
+      real_t curInteractionForce( real_t(0) );
+      real_t relativeForceDiffLimit( real_t(1e-4) );
+      real_t relativeForceConvergenceLimit( real_t( 1e-3 ) );
+      for( uint_t t = 0; t <= timesteps; ++t )
+      {
+         timeloopInit.singleStep( timeloopInitTiming );
+         curInteractionForce = quantityEvaluator->getMeanForceOnParticles()[2];
+
+         if( std::isnan(curInteractionForce) ) WALBERLA_ABORT( "Nan found in interaction force!" );
+
+         if( t > tStokes ) // leave enough timesteps to let the velocity field adapt to the new forcing
+         {
+            if( std::fabs((curInteractionForce - oldInteractionForce ) / curInteractionForce) < relativeForceDiffLimit || t == timesteps)
+            {
+               WALBERLA_LOG_INFO_ON_ROOT("initial simulation ended with relative difference of interaction forces of " << relativeForceDiffLimit << " after " << t << " time steps.");
+
+
+               real_t actingExternalForceOnSpheres = real_c(numSpheres) * ( ( - gravity * densityRatio * diameter * diameter * diameter * math::PI / real_t(6)  ) +
+                                                                            ( gravity * real_t(1) * diameter * diameter * diameter * math::PI / real_t(6) ) +
+                                                                            ( extForce[2] * real_t(1) * diameter * diameter * diameter * math::PI / real_t(6) ) );
+               WALBERLA_LOG_INFO_ON_ROOT("f_interaction_z = " << curInteractionForce << ", f_ext_z = " << actingExternalForceOnSpheres );
+               if( std::fabs( ( std::fabs( curInteractionForce ) - std::fabs( actingExternalForceOnSpheres ) )/ std::fabs( curInteractionForce ) ) < relativeForceConvergenceLimit )
+               {
+                  WALBERLA_LOG_INFO_ON_ROOT("initial simulation converged");
+                  break;
+               }
+               else
+               {
+                  t = 0u;
+                  //timeloopInit.setCurrentTimeStepToZero();
+                  extForce[2] = extForce[2] * ( std::fabs( actingExternalForceOnSpheres ) / std::fabs( curInteractionForce ) );
+                  gnsExternalForceOnForceFieldAdder->reset(extForce);
+                  WALBERLA_LOG_INFO_ON_ROOT("restarting initial simulation with new external force = " << extForce[2]);
+                  curInteractionForce = real_t(0);
+               }
+            }
+            oldInteractionForce = curInteractionForce;
+         }
+
+      }
+
+   }
+
+   // reset remaining force on all bodies
+   pe_coupling::ForceTorqueOnBodiesResetter forceResetter( blocks, bodyStorageID);
+   forceResetter();
+
+   WALBERLA_LOG_INFO_ON_ROOT("===================================================================================" );
+   WALBERLA_LOG_INFO_ON_ROOT("Starting simulation with:" );
+   WALBERLA_LOG_INFO_ON_ROOT("external forcing on fluid = " << extForce );
+   WALBERLA_LOG_INFO_ON_ROOT("total external forces on all particles = " << real_c(numSpheres) * ( - gravity * ( densityRatio - real_t(1) ) + extForce[2] ) * diameter * diameter * diameter * math::PI / real_t(6) );
+   WALBERLA_LOG_INFO_ON_ROOT("simulating " << timesteps << " time steps" );
+
+
+   /////////////////////////////
+   //                         //
+   //    ACTUAL SIMULATION    //
+   //                         //
+   /////////////////////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
+
+   // evaluation function
+   shared_ptr<QuantityEvaluator> quantityEvaluator = walberla::make_shared< QuantityEvaluator >( &timeloop, *blocks, bodyStorageID, forceFieldID, pdfFieldID, fileNameLogging, fileIO, numSpheres, expectedVelocity, evaluateMeanFluidVelocity );
+   {
+      (*collisionPropertiesEvaluator)();
+      real_t maxPen = collisionPropertiesEvaluator->getMaximumPenetrationInSimulation();
+      WALBERLA_LOG_INFO_ON_ROOT("maximum penetration before the simulation (maxPen) = " <<  maxPen << ", maxPen / D = " << real_t(100) * maxPen / diameter << "%");
+   }
+   collisionPropertiesEvaluator->resetMaximumPenetration();
+
+   //initial setup if no initial simulation has been carried out earlier
+   if( !initialSimulationToAdjustFluidForcing )
+   {
+      // init solid volume fraction field
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::NearestNeighborDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+         for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+      } else {
+         pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+         for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+      }
+
+      (*svfCommunicationScheme)();
+
+      pe_coupling::discrete_particle_methods::ForceFieldResetter forceFieldResetter( forceFieldID );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  forceFieldResetter( &(*blockIt) );
+
+      pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder gnsExternalForceAdder( forceFieldID, svfFieldID, extForce );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  gnsExternalForceAdder( &(*blockIt) );
+   }
+
+
+   ///////// begin of GNS timeloop ///////////////////////
+
+   if( addedMassCorr != AddedMassCorrelation::NoAM )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::FieldDataSwapper<Vec3Field_T>( velocityFieldID, swappedOldVelocityFieldID ), "Velocity Field Swap" );
+   }
+
+   if( addedMassCorr != AddedMassCorrelation::NoAM )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( oldVelocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Old Velocity Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(oldVelocityCommunicationScheme), "Old Velocity Field Communication" );
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityGradientFieldID, oldVelocityFieldID, boundaryHandlingID ), "Velocity Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityGradientCommunicationScheme), "Velocity Gradient Field Communication" );
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityTotalTimeDerivativeFieldEvaluator( timeDerivativeVelocityFieldID, oldVelocityFieldID, swappedOldVelocityFieldID, velocityGradientFieldID, dt ), "Velocity Time Derivative Field Evaluation" );
+   }
+
+   // subcycling loop begin
+   for( uint_t subcycle = 1; subcycle <= interactionSubCycles; ++subcycle )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" );
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder( forceFieldID, svfFieldID, extForce ), "Force Field Add" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityCurlFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityCurlFieldID, velocityFieldID, boundaryHandlingID ), "Velocity Curl Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCurlCommunicationScheme), "Velocity Curl Field Communication" );
+      }
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Pressure Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureCommunicationScheme), "Pressure Field Communication" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( pressureGradientFieldID, pressureFieldID, boundaryHandlingID ), "Pressure Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureGradientCommunicationScheme), "Pressure Gradient Field Communication" );
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         // evaluate Flift
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( liftForceFieldID ), "Lift Force Field Reset" )
+                        << AfterFunction( liftForceEvaluationFunction, "Lift Force Evaluation")
+                        << AfterFunction( liftForceComm, "Lift Force Field Communication" );
+      }
+
+      if( addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         // evaluate Fam
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( amForceFieldID ), "AM Force Field Reset" )
+                        << AfterFunction( addedMassEvaluationFunction, "Added Mass Force Evaluation")
+                        << AfterFunction( amForceComm, "Force Field Communication" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( bodyVelocityTimeDerivativeEvaluator ), "Body Velocity Time Derivative Evaluation" );
+      }
+
+      if( liftCorr != LiftCorrelation::NoLift || addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" );
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder( forceFieldID, svfFieldID, extForce ), "Force Field Add" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+      }
+
+      // evaluate Fdrag
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( dragForceFieldID ), "Drag Force Field Reset" )
+                     << AfterFunction( dragAndPressureForceEvaluationFunction, "Fluid-Particle Interaction Force Evaluation" )
+                     << AfterFunction( dragForceComm, "Drag Force Field Communication" );
+
+      // ext forces on bodies
+      timeloop.add() << Sweep( DummySweep(), "Dummy Sweep ")
+                     << AfterFunction( pe_coupling::ForceOnBodiesAdder( blocks, bodyStorageID, Vector3<real_t>(0,0,- gravity * densityRatio * diameter * diameter * diameter * math::PI / real_t(6) )  ), "Gravitational Force Add" )
+                     << AfterFunction( pe_coupling::ForceOnBodiesAdder( blocks, bodyStorageID, Vector3<real_t>(0,0,gravity * real_t(1) * diameter * diameter * diameter * math::PI / real_t(6) ) ), "Buoyancy Force (due to gravity) Add" )
+                     << AfterFunction( pe_coupling::ForceOnBodiesAdder( blocks, bodyStorageID, Vector3<real_t>(0,0,extForce[2] * real_t(1) * diameter * diameter * diameter * math::PI / real_t(6) ) ), "Buoyancy Force (due to external fluid force) Add" )
+                     << AfterFunction( pe_coupling::discrete_particle_methods::SubgridTimeStep( blocks, bodyStorageID, *cr, syncCall, lubricationEvaluationFunction, dtInteractionSubCycle, peSubSteps ), "Pe Time Step" );
+
+      timeloop.add() << Sweep( DummySweep(), "Dummy Sweep ")
+                     << AfterFunction( SharedFunctor<CollisionPropertiesEvaluator>( collisionPropertiesEvaluator ), "Collision properties evaluator");
+
+
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::NearestNeighborDistributor> ( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag ), "Solid Volume Fraction Field Evaluation" )
+                        << AfterFunction( SharedFunctor< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >(svfCommunicationScheme), "Solid Volume Fraction Field Communication" );
+      }
+      else
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> ( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag ), "Solid Volume Fraction Field Evaluation" )
+                        << AfterFunction( SharedFunctor< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >(svfCommunicationScheme), "Solid Volume Fraction Field Communication" );
+      }
+   }
+
+   timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" );
+   timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSExternalForceToForceFieldAdder( forceFieldID, svfFieldID, extForce ), "Force Field Add" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+   timeloop.add() << Sweep( makeSharedSweep<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>( effectiveViscosityEvaluator ), "Effective Viscosity Evaluation");
+   if( useTurbulenceModel ) timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSSmagorinskyLESField<LatticeModel_T>(blocks, omegaFieldID, pdfFieldID, svfFieldID, smagorinskyConstant), "Turbulence Model" );
+
+   // execute GNS-LBM sweep, boundary handling, PDF communication
+   auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag );
+
+   timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "GNS-LBM sweep (collide)" );
+
+   timeloop.add() << BeforeFunction( pdfScheme, "LBM Communication" )
+                  << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+   timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "GNS-LBM sweep (stream)" );
+
+
+
+   timeloop.addFuncAfterTimeStep( SharedFunctor<QuantityEvaluator>( quantityEvaluator ), "Quantity Evaluator" );
+
+   timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+   // configure vtk output
+   if( vtkIO )
+   {
+       shared_ptr<vtk::VTKOutput> pdfFieldVTKWriter = createFluidFieldVTKWriter( blocks, pdfFieldID, flagFieldID, vtkWriteFrequency, baseFolder );
+       timeloop.addFuncAfterTimeStep( vtk::writeFiles( pdfFieldVTKWriter ), "VTK (fluid field data)" );
+
+       auto bodyVtkOutput   = make_shared<pe::SphereVtkOutput>( bodyStorageID, blocks->getBlockStorage() );
+       auto bodyVTK   = vtk::createVTKOutput_PointData( bodyVtkOutput, "bodies", vtkWriteFrequency, baseFolder );
+       timeloop.addFuncAfterTimeStep( vtk::writeFiles( bodyVTK ), "VTK (sphere data)" );
+
+       timeloop.addFuncAfterTimeStep( field::createVTKOutput<Vec3Field_T, float>( forceFieldID, *blocks, "force_field", vtkWriteFrequency, uint_t(0), false, baseFolder ), "VTK (force field)" );
+       timeloop.addFuncAfterTimeStep( field::createVTKOutput<ScalarField_T, float>( svfFieldID, *blocks, "svf_field", vtkWriteFrequency, uint_t(0), false, baseFolder ), "VTK (svf field)" );
+       timeloop.addFuncAfterTimeStep( field::createVTKOutput<ScalarField_T, float>( omegaFieldID, *blocks, "omega_field", vtkWriteFrequency, uint_t(0), false, baseFolder ), "VTK (omega field)" );
+
+   }
+
+   // execute simulation
+   WcTimingPool timeloopTiming;
+
+   real_t oldSettlingVel( real_t(0) );
+   real_t curSettlingVel( real_t(0) );
+   for( uint_t t = 0; t < timesteps; ++t )
+   {
+      timeloop.singleStep( timeloopTiming );
+      curSettlingVel = quantityEvaluator->getSettlingVelocity();
+      if( t > tStokes )
+      {
+         if( std::fabs((curSettlingVel - oldSettlingVel ) / curSettlingVel) < relativeVelDiffLimit )
+         {
+            WALBERLA_LOG_INFO_ON_ROOT("simulation terminated with relative difference of settling velocities of " << relativeVelDiffLimit << " after " << t << " time steps.");
+            break;
+         }
+         oldSettlingVel = curSettlingVel;
+      }
+   }
+
+   //log to file
+   if( fileIO ) logSingleResultToFile( baseFolder+"/logSettlingVel.txt", actualSolidVolumeFraction, curSettlingVel, expectedVelocity, velUnhindered, ReynoldsNumber, tau );
+
+   timeloopTiming.logResultOnRoot();
+
+   real_t meanParticleVel = quantityEvaluator->getMeanParticleVelocity()[2];
+   real_t meanFluidVel = quantityEvaluator->getMeanFluidVelocity()[2];
+
+   WALBERLA_LOG_INFO_ON_ROOT("Result for solid volume fraction = " << actualSolidVolumeFraction << " with " << numSpheres << " spheres.");
+   WALBERLA_LOG_INFO_ON_ROOT("diameter = " << diameter << ", tau = " << tau);
+   WALBERLA_LOG_INFO_ON_ROOT(" - simulated settling velocity = " << curSettlingVel << ", us/uT = " << curSettlingVel / velUnhindered);
+   WALBERLA_LOG_INFO_ON_ROOT(" - expected settling velocity = " << expectedVelocity << ", ue/uT = " << expectedVelocity / velUnhindered);
+   WALBERLA_LOG_INFO_ON_ROOT("detailed overview:");
+   WALBERLA_LOG_INFO_ON_ROOT(" - mean particle velocity  = " << meanParticleVel << " = " << meanParticleVel * dx_SI/dt_SI << " m/s ( " << std::fabs(meanParticleVel/curSettlingVel)*real_t(100) << "% of settling vel)");
+   WALBERLA_LOG_INFO_ON_ROOT(" - mean fluid velocity     = " << meanFluidVel << " = " << meanFluidVel * dx_SI/dt_SI << " m/s ( " << std::fabs(meanFluidVel/curSettlingVel)*real_t(100) << "% of settling vel)");
+   WALBERLA_LOG_INFO_ON_ROOT(" - mean relative velocity  = " << curSettlingVel << " = " << curSettlingVel * dx_SI/dt_SI << " m/s");
+   WALBERLA_LOG_INFO_ON_ROOT(" - expected velocity (R&Z) = " << expectedVelocity << " = " << expectedVelocity * dx_SI/dt_SI << " m/s");
+   
+   real_t maxPen = collisionPropertiesEvaluator->getMaximumPenetrationInSimulation();
+   WALBERLA_LOG_INFO_ON_ROOT(" - maximum penetration (maxPen) = " <<  maxPen << ", maxPen / D = " << real_t(100) * maxPen / diameter << "%");
+
+
+   if ( fileIO ) {
+      WALBERLA_ROOT_SECTION() {
+         std::string fileName = baseFolder + "/logSettlingBehavior.txt";
+         std::ofstream file;
+         file.open(fileName.c_str(), std::ofstream::app);
+         file.precision(8);
+
+         file << actualSolidVolumeFraction << " \t" << numSpheres << " \t"
+              << diameter << " \t" << tau << " \t" << extForceZ << " \t" << relativeVelDiffLimit << " \t"
+              << interpol << " \t" << dist << " \t" << addedMassCorr << " \t" << liftCorr << " \t" << effVisc << " \t"
+              << ((useTurbulenceModel) ? 1 : 0) << " \t"
+              << interactionSubCycles << " \t" << peSubSteps << " \t" << lubricationCutOffDistance << " \t"
+              << curSettlingVel << " \t" << expectedVelocity << " \t" << velUnhindered << " \t" << maxPen << " \n";
+      }
+   }
+
+   return 0;
+}
+
+} //namespace hindered_settling_dynamics_dpm
+
+int main( int argc, char **argv ){
+   hindered_settling_dynamics_dpm::main(argc, argv);
+}
+
diff --git a/tests/pe_coupling/discrete_particle_methods/SphereWallCollisionBehaviorDPM.cpp b/tests/pe_coupling/discrete_particle_methods/SphereWallCollisionBehaviorDPM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b4616f72d3d8b3cebae47ebd4894ee6ab26dc08
--- /dev/null
+++ b/tests/pe_coupling/discrete_particle_methods/SphereWallCollisionBehaviorDPM.cpp
@@ -0,0 +1,1401 @@
+//======================================================================================================================
+//
+//  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 SphereWallCollisionBehaviorDPM.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "core/all.h"
+#include "boundary/all.h"
+#include "lbm/all.h"
+#include "pe_coupling/all.h"
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/all.h"
+
+#include "core/grid_generator/SCIterator.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/PackInfo.h"
+#include "field/distributors/all.h"
+#include "field/interpolators/all.h"
+
+#include "pe/basic.h"
+#include "pe/vtk/SphereVtkOutput.h"
+#include "pe/Types.h"
+
+#include "stencil/D3Q27.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/VTKOutput.h"
+
+#include <vector>
+#include <iomanip>
+#include <iostream>
+#include <random>
+
+namespace sphere_wall_collision_behavior_dpm
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+
+///////////////
+// CONSTANTS //
+///////////////
+
+const uint_t FieldGhostLayers( 1 );
+
+//////////////
+// TYPEDEFS //
+//////////////
+
+// PDF field, flag field & body field
+typedef GhostLayerField< Matrix3<real_t>, 1 >                          TensorField_T;
+typedef GhostLayerField< Vector3<real_t>, 1 >                          Vec3Field_T;
+typedef GhostLayerField< real_t, 1 >                                   ScalarField_T;
+typedef lbm::force_model::GuoField< Vec3Field_T >                      ForceModel_T;
+
+typedef lbm::D3Q19< lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T >   LatticeModel_T;
+typedef LatticeModel_T::Stencil                                        Stencil_T;
+typedef lbm::PdfField< LatticeModel_T >                                PdfField_T;
+
+typedef walberla::uint8_t                                              flag_t;
+typedef FlagField< flag_t >                                            FlagField_T;
+
+// boundary handling
+typedef lbm::NoSlip< LatticeModel_T, flag_t >                          NoSlip_T;
+
+typedef boost::tuples::tuple< NoSlip_T >                               BoundaryConditions_T;
+typedef BoundaryHandling<FlagField_T, Stencil_T, BoundaryConditions_T> BoundaryHandling_T;
+
+typedef boost::tuple<pe::Plane, pe::Sphere> BodyTypeTuple ;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag  ( "fluid" );
+const FlagUID NoSlip_Flag ( "no slip flag" );
+
+
+// coupling methods
+enum DPMethod { GNS };
+
+// interpolation (for PDFs, velocity and/or solid volume fraction)
+enum Interpolation { INearestNeighbor, IKernel, ITrilinear };
+
+// force distribution
+enum Distribution { DNearestNeighbor, DKernel };
+
+//drag correlation
+enum DragCorrelation { ErgunWenYu, Tang, Stokes, Felice, Tenneti, NoDrag };
+
+//lift correlation
+enum LiftCorrelation { NoLift, Saffman };
+
+//added mass correlation
+enum AddedMassCorrelation { NoAM, Finn };
+
+//effective viscosity
+enum EffectiveViscosity { None, Rescaled, Eilers };
+
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+class MyBoundaryHandling
+{
+public:
+
+   MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID ) :
+      flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ) {}
+
+   BoundaryHandling_T * operator()( IBlock* const block, const StructuredBlockStorage* const storage ) const;
+
+private:
+
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+
+}; // class MyBoundaryHandling
+
+
+BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block, const StructuredBlockStorage * const storage ) const
+{
+   WALBERLA_ASSERT_NOT_NULLPTR( block );
+
+   FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
+   PdfField_T *  pdfField  = block->getData< PdfField_T > ( pdfFieldID_ );
+
+   const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+   BoundaryHandling_T * handling = new BoundaryHandling_T( "Boundary handling", flagField, fluid,
+         boost::tuples::make_tuple( NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ) ) );
+
+   const auto noslip = flagField->getFlag( NoSlip_Flag );
+
+   CellInterval domainBB = storage->getDomainCellBB();
+
+   domainBB.xMin() -= cell_idx_c( FieldGhostLayers ); // -1
+   domainBB.xMax() += cell_idx_c( FieldGhostLayers ); // cellsX
+
+   // LEFT
+   CellInterval left( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMin(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( left, *block );
+   handling->forceBoundary( noslip, left );
+
+   // RIGHT
+   CellInterval right( domainBB.xMax(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( right, *block );
+   handling->forceBoundary( noslip, right );
+
+   domainBB.yMin() -= cell_idx_c( FieldGhostLayers ); // 0
+   domainBB.yMax() += cell_idx_c( FieldGhostLayers ); // cellsY+1
+
+   // FRONT
+   CellInterval front( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMin(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( front, *block );
+   handling->forceBoundary( noslip, front );
+
+   // BACK
+   CellInterval back( domainBB.xMin(), domainBB.yMax(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( back, *block );
+   handling->forceBoundary( noslip, back );
+
+   domainBB.zMin() -= cell_idx_c( FieldGhostLayers ); // 0
+   domainBB.zMax() += cell_idx_c( FieldGhostLayers ); // cellsZ+1
+
+   // BOTTOM
+   CellInterval bottom( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMin() );
+   storage->transformGlobalToBlockLocalCellInterval( bottom, *block );
+   handling->forceBoundary( noslip, bottom );
+
+   // TOP
+   CellInterval top( domainBB.xMin(), domainBB.yMin(), domainBB.zMax(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+   storage->transformGlobalToBlockLocalCellInterval( top, *block );
+   handling->forceBoundary( noslip, top );
+
+   handling->fillWithDomain( FieldGhostLayers );
+
+   return handling;
+}
+//*******************************************************************************************************************
+
+
+DPMethod to_DPMethod( const std::string& s )
+{
+   if( s == "GNS"  ) return DPMethod::GNS;
+   throw std::runtime_error("invalid conversion from " + s + " to DPMethod");
+}
+
+Interpolation to_interpolation( const std::string& s )
+{
+   if( s == "nearest" ) return Interpolation::INearestNeighbor;
+   if( s == "kernel"  ) return Interpolation::IKernel;
+   if( s == "trilinear"  ) return Interpolation::ITrilinear;
+   throw std::runtime_error("invalid conversion from " + s + " to Interpolation");
+}
+
+Distribution to_distribution( const std::string& s )
+{
+   if( s == "nearest" ) return Distribution::DNearestNeighbor;
+   if( s == "kernel"  ) return Distribution::DKernel;
+   throw std::runtime_error("invalid conversion from " + s + " to Distribution");
+}
+
+DragCorrelation to_dragCorrelation( const std::string& s )
+{
+   if( s == "ergun" ) return DragCorrelation::ErgunWenYu;
+   if( s == "tang" ) return DragCorrelation::Tang;
+   if( s == "stokes" ) return DragCorrelation::Stokes;
+   if( s == "felice" ) return DragCorrelation::Felice;
+   if( s == "tenneti" ) return DragCorrelation::Tenneti;
+   if( s == "none" ) return DragCorrelation::NoDrag;
+   throw std::runtime_error("invalid conversion from " + s + " to DragCorrelation");
+}
+
+LiftCorrelation to_liftCorrelation( const std::string& s )
+{
+   if( s == "none" ) return LiftCorrelation::NoLift;
+   if( s == "saffman" ) return LiftCorrelation::Saffman;
+   throw std::runtime_error("invalid conversion from " + s + " to LiftCorrelation");
+}
+
+AddedMassCorrelation to_addedMassCorrelation( const std::string& s )
+{
+   if( s == "none" ) return AddedMassCorrelation::NoAM;
+   if( s == "finn" ) return AddedMassCorrelation::Finn;
+   throw std::runtime_error("invalid conversion from " + s + " to AddedMassCorrelation");
+}
+
+EffectiveViscosity to_effvisc( const std::string& s )
+{
+   if( s == "none" ) return EffectiveViscosity::None;
+   if( s == "rescaled"  ) return EffectiveViscosity::Rescaled;
+   if( s == "eilers"  ) return EffectiveViscosity::Eilers;
+   throw std::runtime_error("invalid conversion from " + s + " to effective viscosity");
+}
+
+std::string dpm_to_string( const DPMethod& dpm )
+{
+   if( dpm == DPMethod::GNS ) return "GNS";
+   throw std::runtime_error("invalid conversion from DPMethod to string");
+}
+
+class QuantityEvaluator
+{
+public:
+
+   QuantityEvaluator( SweepTimeloop* timeloop,  StructuredBlockStorage & blocks,
+                      const BlockDataID & bodyStorageID, real_t diameter, real_t densityRatio, real_t Galileo,
+                      const std::string & baseFolder, bool writeLogging ) :
+      timeloop_( timeloop ), blocks_( blocks ), bodyStorageID_( bodyStorageID ), diameter_( diameter ),
+      terminalVelocity_(0), evaluateTerminalVelocity_( true ),
+      writeLogging_( writeLogging )
+   {
+      if( writeLogging_ )
+      {
+         fileName_ = baseFolder+"/evalCollisionBehaviorDPM_"+std::to_string(uint_c(real_t(10) * densityRatio))+"_"+std::to_string(uint_c(real_t(10) * Galileo))+"_"+std::to_string(uint_c(real_t(10) * diameter))+".txt";
+         std::ofstream file;
+         file.open( fileName_.c_str() );
+         file << "#t z velz x y velx vely fx fy fz\n";
+         file.close();
+      }
+   }
+
+   void operator()()
+   {
+      // get particle position
+      Vector3<real_t> particlePosition = getParticlePosition();
+      // get particle velocity
+      Vector3<real_t> particleVelocity = getMeanParticleVelocity();
+      // get force on particles
+      Vector3<real_t> particleForce = getParticleForce();
+
+      if( evaluateTerminalVelocity_ ) terminalVelocity_ = std::min(particleVelocity[2], terminalVelocity_);
+
+      WALBERLA_ROOT_SECTION()
+      {
+         positionsOverTime_.push_back(particlePosition[2]);
+         velocitiesOverTime_.push_back(particleVelocity[2]);
+         if( writeLogging_ ) writeToFile( particlePosition, particleVelocity, particleForce );
+      }
+   }
+
+   real_t getVelocity()
+   {
+      return getMeanParticleVelocity()[2];
+   }
+
+   real_t getTerminalVelocity()
+   {
+      return terminalVelocity_;
+   }
+
+   real_t getPosition()
+   {
+      return getParticlePosition()[2];
+   }
+
+   void stopTerminalVelocityEvaluation()
+   {
+      evaluateTerminalVelocity_ = false;
+   }
+
+   void writeScaledOutputToFile(uint_t tImpact, real_t terminalVelocity)
+   {
+      std::ofstream file;
+      file.open( fileName_.c_str() );
+      file.precision(8);
+      file << "# t position velocity\n";
+      for(uint_t t = 0; t < positionsOverTime_.size(); ++t)
+      {
+         file << (real_c(t) - real_c(tImpact)) * terminalVelocity / diameter_ << " " << ( positionsOverTime_[t] - real_t(0.5) * diameter_ ) / diameter_ << " " << velocitiesOverTime_[t] / terminalVelocity << "\n";
+
+      }
+      file.close();
+
+   }
+
+private:
+
+   Vector3<real_t> getParticleForce()
+   {
+      Vector3<real_t> force(0.);
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            force += bodyIt->getForce();
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+
+      }
+      return force;
+   }
+
+   Vector3<real_t> getMeanParticleVelocity()
+   {
+      Vector3<real_t> velocity(0.);
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            velocity += bodyIt->getLinearVel();
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( velocity[0], mpi::SUM );
+         mpi::allReduceInplace( velocity[1], mpi::SUM );
+         mpi::allReduceInplace( velocity[2], mpi::SUM );
+
+      }
+
+      return velocity;
+   }
+
+   Vector3<real_t> getParticlePosition()
+   {
+      Vector3<real_t> position(0.);
+      uint_t counter( 0 );
+      for( auto blockIt = blocks_.begin(); blockIt != blocks_.end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::LocalBodyIterator::begin< pe::Sphere >( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end<pe::Sphere>(); ++bodyIt )
+         {
+            position = bodyIt->getPosition();
+            ++counter;
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( position[0], mpi::SUM );
+         mpi::allReduceInplace( position[1], mpi::SUM );
+         mpi::allReduceInplace( position[2], mpi::SUM );
+         mpi::allReduceInplace( counter, mpi::SUM );
+      }
+
+      WALBERLA_CHECK_EQUAL(counter, uint_t(1), "No more sphere in domain!");
+
+      return position;
+   }
+
+   void writeToFile( const Vector3<real_t> & position, const Vector3<real_t> & vel, const Vector3<real_t> & force )
+   {
+      std::ofstream file;
+      file.open( fileName_.c_str(), std::ofstream::app );
+      file.precision(8);
+
+      file << timeloop_->getCurrentTimeStep() << " \t"  << position[2] << " \t" << vel[2] << " \t "
+           << position[0] << " \t " << position[1] << " \t "
+           << vel[0] << " \t " << vel[1] << " \t "
+           << force[0] << " \t " << force[1] << " \t " << force[2] << "\n";
+      file.close();
+   }
+
+   SweepTimeloop* timeloop_;
+   StructuredBlockStorage & blocks_;
+   const BlockDataID bodyStorageID_;
+   real_t diameter_;
+   real_t terminalVelocity_;
+   std::string fileName_;
+   std::vector<real_t> positionsOverTime_;
+   std::vector<real_t> velocitiesOverTime_;
+   bool evaluateTerminalVelocity_;
+   bool writeLogging_;
+
+};
+
+
+class CollisionPropertiesEvaluator
+{
+public:
+   CollisionPropertiesEvaluator( pe::cr::ICR & collisionResponse ) : collisionResponse_( collisionResponse ), maximumPenetration_(real_t(0))
+   {}
+
+   void operator()()
+   {
+      real_t maxPen = collisionResponse_.getMaximumPenetration();
+      maximumPenetration_ = std::max( maximumPenetration_, maxPen );
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( maximumPenetration_, mpi::MAX );
+      }
+   }
+   real_t getMaximumPenetrationInSimulation()
+   {
+      return maximumPenetration_;
+   }
+private:
+   pe::cr::ICR & collisionResponse_;
+   real_t maximumPenetration_;
+};
+
+void emptyFunction(){}
+
+//*******************************************************************************************************************
+/*!\brief Testcase that evaluates the collision behavior of a settling sphere and a resting wall in a viscous fluid
+ *
+ * A glass sphere of radius D is settling under gravity towards the bottom plane.
+ * The domain size is [32 x 32 x 512] * D, to ensure that the terminal settling velocity is reached before it hits the wall.
+ *     _______________
+ *    |               |
+ *    |       o       | |
+ *    |       |       | | gravity (z-direction)
+ *    |       |       | v
+ *    |       |       |
+ *    |       |       |
+ *    |_______|_______|
+ *
+ * The sphere properties are: dry coeff of restitution = 0.97, coeff of friction = 0.1
+ * Depending on the Stokes number ( St = ( rho_s / rho_f ) * Re / 9 ), the actual coefficient of resitution changes
+ * due to the presence of the viscous fluid.
+ * The Reynolds number is evaluated as Re = u_T * D / visc, with the terminal settling velocity u_T.
+ * Thus, this quantity is not a-priori available.
+ * The main input parameter is the Galileo number, which, together with the density ratio, determines the fluid viscosity.
+ *
+ * Many parts of the discrete particle method can be modified via command line arguments:
+ *  - discrete particle method
+ *  - interpolation method
+ *  - distribution method
+ *  - number of interaction subcycles
+ *  - number of pe steps per interaction subcycle
+ *  - correlations for drag, lift, added mass
+ *  - effective viscosity
+ *  - turbulence model
+ *  - lubrication force and its cut off distance
+ *
+ * An extensive description of the different components and this test case is given in Rettinger, Ruede (2017).
+ *
+ * The actual coeff of restitution can be compared to experimental findings from Joseph et al and Gondret et al.
+ *
+ *
+ * References:
+ * Experimental:
+ * - G. G. Joseph, R. Zenit, M. L. Hunt, A. M. Rosenwinkel - "Particle–wall collisions in a viscous fluid", Journal of Fluid
+ *   Mechanics 433 (2001) 329–346. doi:10.1017/S0022112001003470.
+ * - P. Gondret, M. Lance, L. Petit - "Bouncing motion of spherical particles in fluids", Physics of Fluids
+ *   14 (2) (2002) 643–652. doi:10.1063/1.1427920.
+ *
+ * Fully resolved simulations:
+ * - A. Kidanemariam, M. Uhlmann - "Direct numerical simulation of pattern formation in subaqueous sediment", Journal of
+ *   Fluid Mechanics 750. doi:10.1017/jfm.2014.284.
+ *
+ * Discrete particle simulations:
+ * - C. Rettinger, U. Ruede - "A Coupled Lattice Boltzmann Method and Discrete Element Method for Discrete Particle
+ *   Simulations of Particulate Flows". arXiv preprint arXiv:1711.00336 (2017)
+ * - R. Sun, H. Xiao - "SediFoam: A general–purpose, open–source CFD-–DEM solver for particle–laden flow with emphasis
+ *   on sediment transport", Computers & Geosciences 89 (2016) 207–219. doi:10.1016/j.cageo.2016.01.011.
+ * - J. R. Finn, M. Li, S. V. Apte - "Particle based modelling and simulation of natural sand dynamics in the wave bottom
+ *   boundary layer", Journal of Fluid Mechanics 796 (2016) 340–385. doi:10.1017/jfm.2016.246.
+ *
+ */
+//*******************************************************************************************************************
+int main( int argc, char **argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+   /////////////////////////////////////////////
+   //                                         //
+   //   Command Line Argument Customization   //
+   //                                         //
+   /////////////////////////////////////////////
+
+   bool funcTest = false;
+   bool fileIO   = false;
+   std::string baseFolder = "vtk_out_SphereWallDPM";
+
+   real_t gravity = real_t(1e-4);
+   real_t densityRatio = real_t(2.0);
+   real_t diameter = real_t(0.5);
+   real_t GalileoNumber = real_t(30.9);
+   uint_t interactionSubCycles = uint_t(1); // number of subcycles that involve evaluation of the interaction force
+   uint_t peSubSteps = uint_t(1); // number of pe only calls in each subcycle
+   real_t collisionTime = real_t(1);
+
+   DPMethod dpm = DPMethod::GNS;
+   Interpolation interpol = Interpolation::IKernel;
+   Distribution dist = Distribution::DKernel;
+   DragCorrelation dragCorr = DragCorrelation::Tenneti;
+   LiftCorrelation liftCorr = LiftCorrelation ::NoLift;
+   AddedMassCorrelation addedMassCorr = AddedMassCorrelation::NoAM;
+   EffectiveViscosity effVisc = EffectiveViscosity::None;
+   bool useTurbulenceModel = false;
+   real_t lubricationCutOffDistance = real_t(0); //0 switches it off, should be <= diameter for sphere-wall collision, and <= diameter/2 for sphere-sphere collision
+   bool useLubricationCorrection = false; // false: use full lubrication force, true: use only correction part
+   const real_t smagorinskyConstant = real_t(0.1); //for turbulence model
+
+   uint_t dimlessTimesteps = uint_t(500);
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--funcTest" )                      == 0 ) funcTest = true;
+      else if( std::strcmp( argv[i], "--fileIO" )                   == 0 ) fileIO = true;
+      else if( std::strcmp( argv[i], "--baseFolder" )               == 0 ) baseFolder = argv[++i];
+      else if( std::strcmp( argv[i], "--gravity" )                  == 0 ) gravity = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--Galileo" )                  == 0 ) GalileoNumber = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--densityRatio" )             == 0 ) densityRatio = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--diameter" )                 == 0 ) diameter = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--collisionTime" )            == 0 ) collisionTime = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--interactionSubCycles" )     == 0 ) interactionSubCycles = uint_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--peSubSteps" )               == 0 ) peSubSteps = uint_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--DPMethod" )                 == 0 ) dpm = to_DPMethod( argv[++i] );
+      else if( std::strcmp( argv[i], "--interpolation" )            == 0 ) interpol = to_interpolation( argv[++i] );
+      else if( std::strcmp( argv[i], "--distribution" )             == 0 ) dist = to_distribution( argv[++i] );
+      else if( std::strcmp( argv[i], "--dragCorrelation" )          == 0 ) dragCorr = to_dragCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--liftCorrelation" )          == 0 ) liftCorr = to_liftCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--addedMassCorrelation" )     == 0 ) addedMassCorr = to_addedMassCorrelation( argv[++i] );
+      else if( std::strcmp( argv[i], "--effectiveViscosity" )       == 0 ) effVisc = to_effvisc( argv[++i] );
+      else if( std::strcmp( argv[i], "--useTurbulenceModel" )       == 0 ) useTurbulenceModel = true;
+      else if( std::strcmp( argv[i], "--useLubricationCorrection" ) == 0 ) useLubricationCorrection = true;
+      else if( std::strcmp( argv[i], "--lubricationCutOff" )        == 0 ) lubricationCutOffDistance = real_c( std::atof( argv[++i] ) );
+      else if( std::strcmp( argv[i], "--timesteps" )                == 0 ) dimlessTimesteps = uint_c( std::atof( argv[++i] ) );
+      else WALBERLA_ABORT("Found invalid command line argument: \"" << argv[i] << "\" - aborting...");
+   }
+
+   WALBERLA_CHECK( diameter <= real_t(1), "Diameter is not allowed to be > 1!" );
+   WALBERLA_CHECK( interactionSubCycles > uint_t(0), "Number of interaction sub cycles has to be at least 1!");
+   WALBERLA_CHECK( peSubSteps > uint_t(0), "Number of pe sub steps has to be at least 1!");
+   WALBERLA_CHECK( lubricationCutOffDistance >= real_t(0), "Lubrication cut off distance has to be non-negative!");
+
+   if( funcTest )
+   {
+      walberla::logging::Logging::instance()->setLogLevel(logging::Logging::LogLevel::WARNING);
+   }
+   if( fileIO )
+   {
+      // create base directory if it does not yet exist
+      boost::filesystem::path tpath( baseFolder );
+      if( !boost::filesystem::exists( tpath ) )
+         boost::filesystem::create_directory( tpath );
+   }
+
+   ///////////////////////////////
+   //                           //
+   //   SIMULATION PROPERTIES   //
+   //                           //
+   ///////////////////////////////
+
+   // roughly resembles the experimental setup from Gondret et al (2002)
+   const uint_t xlength = uint_t( real_t(32) * diameter);
+   const uint_t ylength = uint_t( real_t(32) * diameter);
+   const uint_t zlength = uint_t(real_t(512) * diameter);
+   const real_t dx = real_t(1);
+
+   const real_t ug = std::sqrt(( densityRatio - real_t(1)) * gravity * diameter );
+   const real_t viscosity = ug * diameter / GalileoNumber;
+   const real_t tau = real_t(1) / lbm::collision_model::omegaFromViscosity(viscosity);
+
+   const real_t sphereVolume = math::M_PI * diameter * diameter * diameter / real_t(6);
+   Vector3<real_t> gravitationalForce ( real_t(0), real_t(0), ( densityRatio - real_t(1) ) * sphereVolume * gravity );
+
+   if( !funcTest )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("Lx x Ly x Lz = " << xlength << " x " << ylength << " x " << zlength);
+      WALBERLA_LOG_INFO_ON_ROOT("density ratio = " << densityRatio );
+      WALBERLA_LOG_INFO_ON_ROOT("Ga = " << GalileoNumber );
+      WALBERLA_LOG_INFO_ON_ROOT("diameter = " << diameter );
+      WALBERLA_LOG_INFO_ON_ROOT("tau = " << tau );
+      WALBERLA_LOG_INFO_ON_ROOT("viscosity = " << viscosity );
+      WALBERLA_LOG_INFO_ON_ROOT("u_g = " << ug );
+      WALBERLA_LOG_INFO_ON_ROOT("gravity = " << gravity );
+      WALBERLA_LOG_INFO_ON_ROOT("total external (gravity & buoyancy) force on sphere = " << gravitationalForce );
+      WALBERLA_LOG_INFO_ON_ROOT("-------------------------------------------------------------------------------" );
+      WALBERLA_LOG_INFO_ON_ROOT("discrete particle method = " << dpm_to_string(dpm) );
+      WALBERLA_LOG_INFO_ON_ROOT("interpolator = " << interpol );
+      WALBERLA_LOG_INFO_ON_ROOT("distribution = " << dist );
+      WALBERLA_LOG_INFO_ON_ROOT("dragCorrelation = " << dragCorr );
+      WALBERLA_LOG_INFO_ON_ROOT("addedMassCorrelation = " << addedMassCorr );
+      WALBERLA_LOG_INFO_ON_ROOT("liftCorrelation = " << liftCorr );
+      WALBERLA_LOG_INFO_ON_ROOT("effective viscosity = " << effVisc );
+      WALBERLA_LOG_INFO_ON_ROOT("turbulence model = " << ( useTurbulenceModel ? "yes" : "no" ) );
+      WALBERLA_LOG_INFO_ON_ROOT("interaction sub cycles = " << interactionSubCycles );
+      WALBERLA_LOG_INFO_ON_ROOT("pe sub steps = " << peSubSteps );
+      WALBERLA_LOG_INFO_ON_ROOT("lubrication cut off distance = " << lubricationCutOffDistance );
+      WALBERLA_LOG_INFO_ON_ROOT("use lubrication correction term instead of full formula = " << ( useLubricationCorrection ? "yes" : "no" ) );
+      WALBERLA_LOG_INFO_ON_ROOT("dt_DEM = " << real_t(1) / real_c(interactionSubCycles * peSubSteps) );
+   }
+
+
+   const real_t dt = real_t(1);
+   const real_t dtInteractionSubCycle = dt / real_c(interactionSubCycles);
+   const real_t dtBodyVelocityTimeDerivativeEvaluation = dtInteractionSubCycle;
+
+
+   const real_t tStokes = densityRatio * diameter * diameter / ( real_t(18) * viscosity );
+   const uint_t timesteps = (funcTest) ? uint_t(3) : dimlessTimesteps * uint_c(tStokes); // total number of time steps for the whole simulation
+
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const uint_t XBlocks = uint_t(1);
+   const uint_t YBlocks = uint_t(1);
+   const uint_t ZBlocks = uint_t(32);
+
+   const uint_t XCells  = xlength / XBlocks;
+   const uint_t YCells  = ylength / YBlocks;
+   const uint_t ZCells  = zlength / ZBlocks;
+
+   if( XBlocks * XCells != xlength ||
+       YBlocks * YCells != ylength ||
+       ZBlocks * ZCells != zlength ) WALBERLA_ABORT("Domain decomposition failed!");
+
+   auto blocks = blockforest::createUniformBlockGrid( XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells,
+                                                      dx, 0, false, false,
+                                                      false, false, false,
+                                                      false );
+
+
+   ////////
+   // PE //
+   ////////
+
+   shared_ptr<pe::BodyStorage>  globalBodyStorage = make_shared<pe::BodyStorage>();
+   pe::SetBodyTypeIDs<BodyTypeTuple>::execute();
+   auto bodyStorageID = blocks->addBlockData(pe::createStorageDataHandling<BodyTypeTuple>(), "pe Body Storage");
+   auto ccdID         = blocks->addBlockData(pe::ccd::createHashGridsDataHandling( globalBodyStorage, bodyStorageID ), "CCD");
+   auto fcdID         = blocks->addBlockData(pe::fcd::createGenericFCDDataHandling<BodyTypeTuple, pe::fcd::AnalyticCollideFunctor>(), "FCD");
+
+   pe::cr::ICR* cr;
+   pe::cr::DEM cr_dem(globalBodyStorage, blocks->getBlockStoragePointer(), bodyStorageID, ccdID, fcdID );
+   cr = &cr_dem;
+
+   /////////////////
+   // PE COUPLING //
+   /////////////////
+
+   // connect to pe
+   const real_t overlap = real_t( 1.5 ) * dx;
+   auto syncCall = boost::bind( pe::syncNextNeighbors<BodyTypeTuple>, boost::ref(blocks->getBlockForest()), bodyStorageID, static_cast<WcTimingTree*>(NULL), overlap, false );
+
+   // create the sphere
+   const real_t restitutionCoeff = real_t(0.97);
+   const real_t frictionCoeff = real_t(0.1);
+
+   const real_t scaledCollisionTime = collisionTime * diameter / dx; // smaller diameter -> smaller collision time to keep maximum penetration small -> more pe substeps to resolve it
+
+   const real_t particleMass = densityRatio * sphereVolume;
+   const real_t Mij = particleMass; // * particleMass / ( real_t(2) * particleMass ); // Mij = M for sphere-wall collision
+   const real_t lnDryResCoeff = std::log(restitutionCoeff);
+   const real_t stiffnessCoeff = math::M_PI * math::M_PI * Mij / ( scaledCollisionTime * scaledCollisionTime * ( real_t(1) - lnDryResCoeff * lnDryResCoeff / ( math::M_PI * math::M_PI + lnDryResCoeff* lnDryResCoeff ) ) );
+   const real_t normalizedStiffnessCoeff = stiffnessCoeff / ( ( densityRatio - real_t(1) ) * gravity * sphereVolume / diameter );
+
+   const real_t dampingCoeff = - real_t(2) * std::sqrt( Mij * stiffnessCoeff ) *
+                               ( std::log(restitutionCoeff) / std::sqrt( math::M_PI * math::M_PI + (std::log(restitutionCoeff) * std::log(restitutionCoeff) ) ) );
+   const real_t contactDuration = real_t(2) * math::M_PI * Mij / ( std::sqrt( real_t(4) * Mij * stiffnessCoeff - dampingCoeff * dampingCoeff )); //formula from Uhlman
+   const real_t contactDuration2 = std::sqrt(( math::M_PI * math::M_PI + std::log(restitutionCoeff) * std::log(restitutionCoeff)) / ( stiffnessCoeff / Mij)); //formula from Finn
+
+   if( !funcTest )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT( "Created material with:\n"
+                                       << " - coefficient of restitution = " << restitutionCoeff << "\n"
+                                       << " - coefficient of friction = " << frictionCoeff << "\n"
+                                       << " - normalized stiffness coefficient kn* = " << normalizedStiffnessCoeff << "\n"
+                                       << " - stiffness coefficient kn = " << stiffnessCoeff << "\n"
+                                       << " - damping coefficient cdn = " << dampingCoeff << "\n"
+                                       << " - contact time Tc = " << contactDuration << "\n"
+                                       << " - contact time Tc2 = " << contactDuration2);
+   }
+
+   auto peMaterial = pe::createMaterial( "sedimentMat", densityRatio, restitutionCoeff, frictionCoeff, frictionCoeff, real_t(0), real_t(200), stiffnessCoeff, dampingCoeff, dampingCoeff );
+   real_t zPosition = real_c(zlength) - real_t(3) * diameter;
+   pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>(real_c(xlength) * real_t(0.5), real_c(ylength) * real_t(0.5), zPosition), diameter * real_t(0.5), peMaterial );
+   pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>( 0, 0, 1 ), Vector3<real_t>( 0, 0, 0 ), peMaterial );
+
+
+   //////////////////////
+   //                  //
+   //    BLOCK DATA    //
+   //                  //
+   //////////////////////
+
+   // create force field
+   BlockDataID forceFieldID = field::addToStorage< Vec3Field_T >( blocks, "force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   BlockDataID dragForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "drag force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID amForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "am force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+   BlockDataID liftForceFieldID = field::addToStorage< Vec3Field_T >( blocks, "lift force field", Vector3<real_t>(real_t(0)), field::zyxf, FieldGhostLayers );
+
+   // create omega field
+   BlockDataID omegaFieldID = field::addToStorage< ScalarField_T >( blocks, "omega field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // create the lattice model
+   LatticeModel_T latticeModel = LatticeModel_T( omegaFieldID, ForceModel_T( forceFieldID ) );
+
+   // add PDF field
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage( blocks, "pdf field (zyxf)", latticeModel, Vector3<real_t>(0), real_t(1), FieldGhostLayers, field::zyxf );
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" );
+
+   // add boundary handling & initialize outer domain boundaries
+   BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( MyBoundaryHandling( flagFieldID, pdfFieldID ), "boundary handling" );
+
+   // field to store fluid velolcity
+   BlockDataID velocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity field", Vector3<real_t>(0), field::zyxf, FieldGhostLayers );
+   BlockDataID swappedOldVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "swapped old velocity field", Vector3<real_t>(0), field::zyxf, FieldGhostLayers );
+
+   // create pressure field
+   BlockDataID pressureFieldID = field::addToStorage< ScalarField_T >( blocks, "pressure field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // create solid volume fraction field
+   BlockDataID svfFieldID = field::addToStorage< ScalarField_T >( blocks, "svf field", real_t(0), field::zyxf, FieldGhostLayers );
+
+   // field to store pressure gradient
+   BlockDataID pressureGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "pressure gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store curl of fluid velocity
+   BlockDataID velocityCurlFieldID = field::addToStorage< Vec3Field_T >( blocks, "velocity curl field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store velocity gradient
+   BlockDataID velocityGradientFieldID = field::addToStorage< TensorField_T >( blocks, "velocity gradient field", Matrix3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store gradient of stress tensor
+   BlockDataID stressTensorGradientFieldID = field::addToStorage< Vec3Field_T >( blocks, "stress tensor gradient field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // field to store time derivative of fluid velocity
+   BlockDataID timeDerivativeVelocityFieldID = field::addToStorage< Vec3Field_T >( blocks, "time derivative velocity field", Vector3<real_t>(real_c(0)), field::zyxf, FieldGhostLayers );
+
+   // communication schemes
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> forceComm( blocks, forceFieldID );
+
+   blockforest::communication::UniformBufferedScheme<stencil::D3Q27> pdfScheme( blocks );
+   pdfScheme.addPackInfo( make_shared< field::communication::PackInfo<PdfField_T> >( pdfFieldID ) );
+
+   // setup of the communication for synchronizing the velocity field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityFieldID ) );
+
+   // setup of the communication for synchronizing the solid volume fraction field between neighboring blocks which takes into account the svf values inside the ghost layers
+   shared_ptr<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >svfCommunicationScheme = make_shared<pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >( blocks, svfFieldID );
+
+   // setup of the communication for synchronizing the pressure field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<ScalarField_T> >( pressureFieldID ) );
+
+   // setup of the communication for synchronizing the pressure gradient field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > pressureGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   pressureGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( pressureGradientFieldID ) );
+
+   // setup of the communication for synchronizing the velocity curl field between neighboring blocks
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityCurlCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityCurlCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( velocityCurlFieldID ) );
+
+   // communication for synchronizing the velocity gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > velocityGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   velocityGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<TensorField_T> >( velocityGradientFieldID ) );
+
+   // communication for synchronizing the stress tensor gradient values
+   shared_ptr<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> > stressTensorGradientCommunicationScheme = make_shared<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >( blocks );
+   stressTensorGradientCommunicationScheme->addPackInfo( make_shared< field::communication::PackInfo<Vec3Field_T> >( stressTensorGradientFieldID ) );
+
+   // communication for synchronizing the interaction force field
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> dragForceComm( blocks, dragForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> amForceComm( blocks, amForceFieldID );
+   pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<Vec3Field_T> liftForceComm( blocks, liftForceFieldID );
+
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> dragForceFieldToForceFieldAdder =
+            make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, dragForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> amForceFieldToForceFieldAdder =
+         make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, amForceFieldID, uint_t(1) );
+   shared_ptr<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> liftForceFieldToForceFieldAdder =
+         make_shared<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder>(blocks, forceFieldID, liftForceFieldID, uint_t(1) );
+
+   /////////////////////////////////
+   //                             //
+   //    CORRELATION FUNCTIONS    //
+   //                             //
+   /////////////////////////////////
+
+   // drag correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t>&, const Vector3<real_t> &, real_t, real_t, real_t, real_t)> dragCorrelationFunction;
+   if( dragCorr == DragCorrelation::ErgunWenYu )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceErgunWenYu;
+   }
+   else if( dragCorr == DragCorrelation::Tang )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTang;
+   }
+   else if( dragCorr == DragCorrelation::Stokes )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceStokes;
+   }
+   else if( dragCorr == DragCorrelation::Felice )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceFelice;
+   }
+   else if( dragCorr == DragCorrelation::Tenneti )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTenneti;
+   }
+   else if( dragCorr == DragCorrelation::NoDrag )
+   {
+      dragCorrelationFunction = pe_coupling::discrete_particle_methods::noDragForce;
+   }
+   else
+   {
+      WALBERLA_ABORT("Drag correlation not yet implemented!");
+   }
+
+   // lift correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t, real_t )> liftCorrelationFunction;
+   if( liftCorr == LiftCorrelation::NoLift )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::noLiftForce;
+   }
+   else if( liftCorr == LiftCorrelation::Saffman )
+   {
+      liftCorrelationFunction = pe_coupling::discrete_particle_methods::liftForceSaffman;
+   }
+   else
+   {
+      WALBERLA_ABORT("Lift correlation not yet implemented!");
+   }
+
+   // added mass correlation function
+   boost::function<Vector3<real_t> ( const Vector3<real_t> &, const Vector3<real_t> &, real_t, real_t )> addedMassCorrelationFunction;
+   if( addedMassCorr == AddedMassCorrelation::NoAM )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::noAddedMassForce;
+   }
+   else if( addedMassCorr == AddedMassCorrelation::Finn )
+   {
+      addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::addedMassForceFinn;
+   }
+   else
+   {
+      WALBERLA_ABORT("Added mass correlation not yet implemented!");
+   }
+
+   // set up effective viscosity calculation
+   boost::function<real_t ( real_t, real_t)> effectiveViscosityFunction;
+   if( effVisc == EffectiveViscosity::None )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateUnchangedEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Rescaled )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateRescaledEffectiveViscosity;
+   }
+   else if( effVisc == EffectiveViscosity::Eilers )
+   {
+      effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateEilersEffectiveViscosity;
+   }
+   else
+   {
+      WALBERLA_ABORT("Effective viscosity not yet implemented!");
+   }
+   shared_ptr<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator> effectiveViscosityEvaluator =
+         walberla::make_shared<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>(omegaFieldID, svfFieldID, viscosity, effectiveViscosityFunction );
+
+
+   ////////////////////////////////
+   //                            //
+   //    EVALUATION FUNCTIONS    //
+   //                            //
+   ////////////////////////////////
+
+   // evaluator for bodies' velocity time derivative
+   shared_ptr<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator> bodyVelocityTimeDerivativeEvaluator = make_shared<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( blocks, bodyStorageID, dtBodyVelocityTimeDerivativeEvaluation );
+   (*bodyVelocityTimeDerivativeEvaluator)();
+
+   // function used to evaluate the interaction force between fluid and particles
+   boost::function<void(void)> dragAndPressureForceEvaluationFunction;
+   if( dpm == DPMethod::GNS ) {
+      if (interpol == Interpolation::INearestNeighbor) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::IKernel) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      } else if (interpol == Interpolation::ITrilinear) {
+         if (dist == Distribution::DNearestNeighbor) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         } else if (dist == Distribution::DKernel) {
+            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor> IFE_T;
+            shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
+                                                                     flagFieldID, Fluid_Flag,
+                                                                     velocityFieldID, svfFieldID,
+                                                                     pressureGradientFieldID, dragCorrelationFunction,
+                                                                     viscosity);
+            dragAndPressureForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+         }
+      }
+   }
+   else
+   {
+      WALBERLA_ABORT("Discrete particle method not yet implemented!");
+   }
+
+
+   // function to evaluate the lift force contribution
+   boost::function<void(void)> liftForceEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
+                                                                        viscosity );
+         liftForceEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+
+   // function to evaluate the added mass contribution
+   boost::function<void(void)> addedMassEvaluationFunction;
+   if( interpol == Interpolation::INearestNeighbor )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::IKernel )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+   else if( interpol == Interpolation::ITrilinear )
+   {
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+      else if( dist == Distribution::DKernel )
+      {
+         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
+                                                                        timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
+                                                                        bodyVelocityTimeDerivativeEvaluator );
+         addedMassEvaluationFunction = boost::bind(&IFE_T::operator(), forceEvaluatorPtr);
+      }
+   }
+
+   // function to evaluate lubrication forces
+   boost::function<void(void)> lubricationEvaluationFunction;
+   if( lubricationCutOffDistance > real_t(0) )
+   {
+      if( useLubricationCorrection )
+      {
+         typedef pe_coupling::LubricationCorrection LE_T;
+         shared_ptr<LE_T> lubEval = make_shared<LE_T>( blocks, globalBodyStorage, bodyStorageID, viscosity, lubricationCutOffDistance );
+         lubricationEvaluationFunction = boost::bind(&LE_T::operator(), lubEval);
+      }
+      else
+      {
+         typedef pe_coupling::discrete_particle_methods::LubricationForceEvaluator LE_T;
+         shared_ptr<LE_T> lubEval = make_shared<LE_T>( blocks, globalBodyStorage, bodyStorageID, viscosity, lubricationCutOffDistance );
+         lubricationEvaluationFunction = boost::bind(&LE_T::operator(), lubEval);
+      }
+   } else {
+      lubricationEvaluationFunction = emptyFunction;
+   }
+
+
+   ///////////////////////////
+   //                       //
+   //    CREATE TIMELOOP    //
+   //                       //
+   ///////////////////////////
+
+   // initial SVF calculation
+   if( dist == Distribution::DNearestNeighbor )
+   {
+      pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::NearestNeighborDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+   } else {
+      pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> svfEvaluator( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag );
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )  svfEvaluator( &(*blockIt) );
+   }
+   (*svfCommunicationScheme)();
+
+   // create the timeloop
+   SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
+
+   // evaluation functions for simulation
+   shared_ptr<QuantityEvaluator> quantityEvaluator = walberla::make_shared< QuantityEvaluator >( &timeloop, *blocks, bodyStorageID, diameter, densityRatio, GalileoNumber, baseFolder, fileIO );
+   shared_ptr<CollisionPropertiesEvaluator> collisionPropertiesEvaluator = walberla::make_shared<CollisionPropertiesEvaluator>( *cr );
+
+   if( addedMassCorr != AddedMassCorrelation::NoAM )
+   {
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::FieldDataSwapper<Vec3Field_T>( velocityFieldID, swappedOldVelocityFieldID ), "Velocity Field Swap" );
+   }
+
+   // subcycling loop begin
+   for( uint_t subcycle = 1; subcycle <= interactionSubCycles; ++subcycle )
+   {
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                     << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Old Velocity Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Old Velocity Field Communication" );
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityCurlFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityCurlFieldID, velocityFieldID, boundaryHandlingID ), "Velocity Curl Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCurlCommunicationScheme), "Velocity Curl Field Communication" );
+      }
+
+      if( addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( velocityGradientFieldID, velocityFieldID, boundaryHandlingID ), "Velocity Gradient Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityGradientCommunicationScheme), "Velocity Gradient Field Communication" );
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::VelocityTotalTimeDerivativeFieldEvaluator( timeDerivativeVelocityFieldID, velocityFieldID, swappedOldVelocityFieldID, velocityGradientFieldID, dt ), "Velocity Time Derivative Field Evaluation" );
+      }
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Pressure Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureCommunicationScheme), "Pressure Field Communication" );
+
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator<LatticeModel_T, BoundaryHandling_T>( pressureGradientFieldID, pressureFieldID, boundaryHandlingID ), "Pressure Gradient Field Evaluation" )
+                     << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(pressureGradientCommunicationScheme), "Pressure Gradient Field Communication" );
+
+
+      if( liftCorr != LiftCorrelation::NoLift )
+      {
+         // evaluate Flift
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( liftForceFieldID ), "Lift Force Field Reset" )
+                        << AfterFunction( liftForceEvaluationFunction, "Lift Force Evaluation")
+                        << AfterFunction( liftForceComm, "Lift Force Field Communication" );
+      }
+
+      if( addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+
+         // evaluate Fam
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( amForceFieldID ), "AM Force Field Reset" )
+                        << AfterFunction( addedMassEvaluationFunction, "Added Mass Force Evaluation")
+                        << AfterFunction( amForceComm, "Force Field Communication" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator>( bodyVelocityTimeDerivativeEvaluator ), "Body Velocity Time Derivative Evaluation" );
+      }
+
+      if( liftCorr != LiftCorrelation::NoLift || addedMassCorr != AddedMassCorrelation::NoAM )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                        << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator<LatticeModel_T, BoundaryHandling_T> ( velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID ), "Velocity Field Evaluation" )
+                        << AfterFunction ( SharedFunctor<blockforest::communication::UniformBufferedScheme<stencil::D3Q27> >(velocityCommunicationScheme), "Velocity Field Communication" );
+      }
+
+      // evaluate Fdrag
+      timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( dragForceFieldID ), "Drag Force Field Reset" )
+                     << AfterFunction( dragAndPressureForceEvaluationFunction, "Fluid-Particle Interaction Force Evaluation" )
+                     << AfterFunction( dragForceComm, "Drag Force Field Communication" )
+                     << AfterFunction( pe_coupling::ForceOnBodiesAdder( blocks, bodyStorageID, -gravitationalForce  ), "Gravitational and buoyancy force" )
+                     << AfterFunction( pe_coupling::discrete_particle_methods::SubgridTimeStep( blocks, bodyStorageID, *cr, syncCall, lubricationEvaluationFunction, dtInteractionSubCycle, peSubSteps ), "Pe Time Step" )
+                     << AfterFunction( SharedFunctor<CollisionPropertiesEvaluator>( collisionPropertiesEvaluator ), "Collision properties evaluator");
+
+      if( dist == Distribution::DNearestNeighbor )
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::NearestNeighborDistributor> ( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag ), "Solid Volume Fraction Field Evaluation" )
+                        << AfterFunction( SharedFunctor< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >(svfCommunicationScheme), "Solid Volume Fraction Field Communication" );
+      }
+      else
+      {
+         timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator<FlagField_T, field::KernelDistributor> ( blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag ), "Solid Volume Fraction Field Evaluation" )
+                        << AfterFunction( SharedFunctor< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication<ScalarField_T> >(svfCommunicationScheme), "Solid Volume Fraction Field Communication" );
+      }
+
+   }
+
+   timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::ForceFieldResetter( forceFieldID ), "Force Field Reset" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (liftForceFieldToForceFieldAdder ), "Lift Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (amForceFieldToForceFieldAdder ), "AM Force Field To Force Field Adder" )
+                  << AfterFunction( SharedFunctor<pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder> (dragForceFieldToForceFieldAdder ), "Drag Force Field To Force Field Adder" );
+
+   timeloop.add() << Sweep( makeSharedSweep<pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator>( effectiveViscosityEvaluator ), "Effective Viscosity Evaluation");
+   if( useTurbulenceModel ) timeloop.add() << Sweep( pe_coupling::discrete_particle_methods::GNSSmagorinskyLESField<LatticeModel_T>(blocks, omegaFieldID, pdfFieldID, svfFieldID, smagorinskyConstant), "Turbulence Model" );
+
+   // execute GNS-LBM sweep, boundary handling, PDF communication
+   auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag );
+
+   timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "GNS-LBM sweep (collide)" );
+
+   timeloop.add() << BeforeFunction( pdfScheme, "LBM Communication" )
+                  << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+   timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "GNS-LBM sweep (stream)" );
+
+   timeloop.addFuncAfterTimeStep( SharedFunctor<QuantityEvaluator>( quantityEvaluator ), "Quantity Evaluator" );
+
+   timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+
+   //////////////////////
+   //                  //
+   //    SIMULATION    //
+   //                  //
+   //////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   real_t currentVelocity(0);
+   real_t oldVelocity(0);
+   uint_t numberOfBouncesUntilTermination = uint_t(4);
+   uint_t countedBounces = uint_t(0);
+   uint_t tImpact = uint_t(0);
+   real_t terminalVelocity(0);
+   real_t maxPositionAfterBounce = real_t(0);
+
+   // three different evaluation times for the rebound velocity (see Kidanemariam, Uhlmann (2014) )
+   real_t evalOffsetFactorS = real_t(0.05);
+   real_t evalOffsetFactorM = real_t(0.1);
+   real_t evalOffsetFactorL = real_t(0.15);
+   real_t reboundVelocityS( 0. );
+   real_t reboundVelocityM( 0. );
+   real_t reboundVelocityL( 0. );
+   uint_t tEvalOffsetS = 0;
+   uint_t tEvalOffsetM = 0;
+   uint_t tEvalOffsetL = 0;
+
+   for( size_t t = 0; t < timesteps; ++t)
+   {
+      timeloop.singleStep(timeloopTiming);
+      currentVelocity = quantityEvaluator->getVelocity();
+      if( currentVelocity > real_t(0) && oldVelocity < real_t(0) )
+      {
+         ++countedBounces;
+         if( countedBounces == 1 )
+         {
+            tImpact = t;
+            terminalVelocity = std::fabs(quantityEvaluator->getTerminalVelocity());
+            quantityEvaluator->stopTerminalVelocityEvaluation();
+            tEvalOffsetS = uint_c(std::ceil(evalOffsetFactorS * diameter / terminalVelocity));
+            tEvalOffsetM = uint_c(std::ceil(evalOffsetFactorM * diameter / terminalVelocity));
+            tEvalOffsetL = uint_c(std::ceil(evalOffsetFactorL * diameter / terminalVelocity));
+         }
+         WALBERLA_LOG_INFO_ON_ROOT("----------- " << countedBounces << ". bounce detected ----------------")
+      }
+      if( currentVelocity > oldVelocity && oldVelocity > real_t(0) && countedBounces == 1)
+      {
+         // impact was wrongly detected in an intermediate step, but should be the time when the collision is fully resolved and maximal velocity is reached
+         ++tImpact;
+      }
+      if( t == tImpact + tEvalOffsetS ) reboundVelocityS = currentVelocity;
+      if( t == tImpact + tEvalOffsetM ) reboundVelocityM = currentVelocity;
+      if( t == tImpact + tEvalOffsetL ) reboundVelocityL = currentVelocity;
+      if( countedBounces >= numberOfBouncesUntilTermination) break;
+      if( countedBounces >= 1 )
+      {
+         maxPositionAfterBounce = std::max(maxPositionAfterBounce, quantityEvaluator->getPosition());
+      }
+      if( countedBounces >= 1 && ( real_c(t-tImpact) * terminalVelocity / diameter ) > real_t(100) ) break;
+
+      oldVelocity = currentVelocity;
+   }
+
+   maxPositionAfterBounce -= diameter / real_t(2);
+
+   timeloopTiming.logResultOnRoot();
+
+   if( !funcTest )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("v_T = " << terminalVelocity);
+      real_t Re = terminalVelocity * diameter / viscosity;
+      WALBERLA_LOG_INFO_ON_ROOT("Re_T = " << Re);
+      real_t St = densityRatio * Re / real_t(9);
+      WALBERLA_LOG_INFO_ON_ROOT("density ratio = " << densityRatio);
+      WALBERLA_LOG_INFO_ON_ROOT("St = " << St);
+
+
+      WALBERLA_LOG_INFO_ON_ROOT("tI = " << tImpact )
+
+      WALBERLA_LOG_INFO_ON_ROOT(" - tRS = " <<  evalOffsetFactorS * diameter / terminalVelocity << " = "  << tEvalOffsetS  );
+      real_t effectiveCoefficientOfRestitutionS = reboundVelocityS / terminalVelocity;
+      WALBERLA_LOG_INFO_ON_ROOT("   e_eff = " << effectiveCoefficientOfRestitutionS << ", e/e_dry = " << effectiveCoefficientOfRestitutionS / restitutionCoeff);
+
+      WALBERLA_LOG_INFO_ON_ROOT(" - tRM = " <<  evalOffsetFactorM * diameter / terminalVelocity << " = "  << tEvalOffsetM  );
+      real_t effectiveCoefficientOfRestitutionM = reboundVelocityM / terminalVelocity;
+      WALBERLA_LOG_INFO_ON_ROOT("   e_eff = " << effectiveCoefficientOfRestitutionM << ", e/e_dry = " << effectiveCoefficientOfRestitutionM / restitutionCoeff);
+
+      WALBERLA_LOG_INFO_ON_ROOT(" - tRL = " <<  evalOffsetFactorL * diameter / terminalVelocity << " = "  << tEvalOffsetL  );
+      real_t effectiveCoefficientOfRestitutionL = reboundVelocityL / terminalVelocity;
+      WALBERLA_LOG_INFO_ON_ROOT("   e_eff = " << effectiveCoefficientOfRestitutionL << ", e/e_dry = " << effectiveCoefficientOfRestitutionL / restitutionCoeff);
+
+      WALBERLA_LOG_INFO_ON_ROOT("maximum position after first bounce (zMax) = " << maxPositionAfterBounce << ", zMax / D = " << maxPositionAfterBounce / diameter );
+      real_t maxPen = collisionPropertiesEvaluator->getMaximumPenetrationInSimulation();
+      WALBERLA_LOG_INFO_ON_ROOT("maximum penetration (maxPen) = " <<  maxPen << ", maxPen / D = " << real_t(100) * maxPen / diameter << "%");
+
+      if( fileIO ) quantityEvaluator->writeScaledOutputToFile(tImpact, terminalVelocity);
+
+      //log to file
+      if( fileIO ) {
+         WALBERLA_ROOT_SECTION() {
+            std::string fileName = baseFolder + "/logCollisionBehavior.txt";
+            std::ofstream file;
+            file.open(fileName.c_str(), std::ofstream::app);
+            file.precision(8);
+
+            file << densityRatio << " \t" << GalileoNumber << " \t" << diameter << " \t"
+                 << tau << " \t" << gravity << " \t" << normalizedStiffnessCoeff << " \t" << interpol << " \t" << dist << " \t"
+                 << addedMassCorr << " \t" << liftCorr << " \t" << effVisc << " \t" << ((useTurbulenceModel) ? 1 : 0) << " \t"
+                 << interactionSubCycles << " \t" << peSubSteps << " \t" << lubricationCutOffDistance << " \t"
+                 << terminalVelocity << " \t" << Re << " \t" << St << " \t"
+                 << effectiveCoefficientOfRestitutionM << " \t" << restitutionCoeff << " \t"
+                 << maxPositionAfterBounce << " \t " << maxPen << "\n";
+         }
+      }
+
+   }
+
+   return 0;
+}
+
+} //namespace sphere_wall_collision_behavior_dpm
+
+int main( int argc, char **argv ){
+   sphere_wall_collision_behavior_dpm::main(argc, argv);
+}
+