diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt
index 47f77911653f7c8eec3d68550711810a8606e153..a81420b475e6378a3f453ebd4ae16c2c0c895e8d 100644
--- a/apps/benchmarks/CMakeLists.txt
+++ b/apps/benchmarks/CMakeLists.txt
@@ -4,6 +4,7 @@ add_subdirectory( CouetteFlow )
 add_subdirectory( ForcesOnSphereNearPlaneInShearFlow )
 add_subdirectory( NonUniformGrid )
 add_subdirectory( MotionSingleHeavySphere )
+add_subdirectory( PeriodicGranularGas )
 add_subdirectory( PoiseuilleChannel )
 add_subdirectory( SchaeferTurek )
-add_subdirectory( UniformGrid )
\ No newline at end of file
+add_subdirectory( UniformGrid )
diff --git a/apps/benchmarks/PeriodicGranularGas/CMakeLists.txt b/apps/benchmarks/PeriodicGranularGas/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d26f6314fd3d73bbac01b460da7240949a45350a
--- /dev/null
+++ b/apps/benchmarks/PeriodicGranularGas/CMakeLists.txt
@@ -0,0 +1,6 @@
+waLBerla_link_files_to_builddir( *.cfg )
+waLBerla_link_files_to_builddir( *.py )
+
+waLBerla_add_executable ( NAME PeriodicGranularGas
+                          FILES PeriodicGranularGas.cpp
+                          DEPENDS blockforest core pe )
diff --git a/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cfg b/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e281e9b0ecd0abe2ee48313040696d09d86d9db1
--- /dev/null
+++ b/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cfg
@@ -0,0 +1,22 @@
+
+PeriodicGranularGas
+{
+   simulationCorner < 0, 0, 0 >;
+   simulationDomain < 30, 30, 30 >;
+   blocks < 2, 2, 2 >;
+   isPeriodic < 1, 1, 1 >;
+
+   radius  0.5;
+   spacing 1.0;
+   vMax    0.0;
+
+   dt                0.1;
+   simulationSteps   1000;
+   visSpacing         100;
+
+   HCSITSmaxIterations 10;
+   HCSITSRelaxationParameter 0.7;
+   HCSITSErrorReductionParameter 0.8;
+   HCSITSRelaxationModelStr ApproximateInelasticCoulombContactByDecoupling;
+   globalLinearAcceleration < 0, 0, 0 >;
+}
diff --git a/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cpp b/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7efaeb660c56c92b7467a83b17312125fa403d19
--- /dev/null
+++ b/apps/benchmarks/PeriodicGranularGas/PeriodicGranularGas.cpp
@@ -0,0 +1,250 @@
+//======================================================================================================================
+//
+//  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   PeriodicGranularGas.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <pe/basic.h>
+#include <pe/vtk/SphereVtkOutput.h>
+
+#include <core/Abort.h>
+#include <core/Environment.h>
+#include <core/grid_generator/SCIterator.h>
+#include <core/logging/Logging.h>
+#include <core/waLBerlaBuildInfo.h>
+#include <vtk/VTKOutput.h>
+
+#include <functional>
+#include <memory>
+
+using namespace walberla;
+using namespace walberla::pe;
+using namespace walberla::timing;
+
+typedef boost::tuple<Sphere> BodyTuple ;
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+
+   logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO);
+   logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO);
+
+   WALBERLA_LOG_INFO_ON_ROOT( "config file: " << argv[1] )
+   WALBERLA_LOG_INFO_ON_ROOT( "waLBerla Revision: " << WALBERLA_GIT_SHA1 );
+
+   math::seedRandomGenerator( static_cast<unsigned int>(1337 * mpi::MPIManager::instance()->worldRank()) );
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** READING COMMANDLINE ARGUMENTS ***");
+   bool bDEM = false;
+   bool bHCSITS = false;
+
+   bool bNN = false;
+   bool bSO = false;
+
+   bool bInelasticFrictionlessContact = false;
+   bool bApproximateInelasticCoulombContactByDecoupling = false;
+   bool bInelasticCoulombContactByDecoupling = false;
+   bool bInelasticGeneralizedMaximumDissipationContact = false;
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--DEM" ) == 0 ) bDEM = true;
+      if( std::strcmp( argv[i], "--HCSITS" ) == 0 ) bHCSITS = true;
+
+      if( std::strcmp( argv[i], "--syncNextNeighbor" ) == 0 ) bNN = true;
+      if( std::strcmp( argv[i], "--syncShadowOwners" ) == 0 ) bSO = true;
+
+      if( std::strcmp( argv[i], "--InelasticFrictionlessContact" ) == 0 ) bInelasticFrictionlessContact = true;
+      if( std::strcmp( argv[i], "--ApproximateInelasticCoulombContactByDecoupling" ) == 0 ) bApproximateInelasticCoulombContactByDecoupling = true;
+      if( std::strcmp( argv[i], "--InelasticCoulombContactByDecoupling" ) == 0 ) bInelasticCoulombContactByDecoupling = true;
+      if( std::strcmp( argv[i], "--InelasticGeneralizedMaximumDissipationContact" ) == 0 ) bInelasticGeneralizedMaximumDissipationContact = true;
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** READING CONFIG FILE ***");
+   auto cfg = env.config();
+   if (cfg == NULL) WALBERLA_ABORT("No config specified!");
+   const Config::BlockHandle mainConf  = cfg->getBlock( "PeriodicGranularGas" );
+
+   int simulationSteps = mainConf.getParameter<int>("simulationSteps", 10 );
+   WALBERLA_LOG_INFO_ON_ROOT("simulationSteps: " << simulationSteps);
+
+   real_t dt = mainConf.getParameter<real_t>("dt", real_c(0.01) );
+   WALBERLA_LOG_INFO_ON_ROOT("dt: " << dt);
+
+   const int visSpacing = mainConf.getParameter<int>("visSpacing",  1000 );
+   WALBERLA_LOG_INFO_ON_ROOT("visSpacing: " << visSpacing);
+   const std::string path = mainConf.getParameter<std::string>("path",  "vtk_out" );
+   WALBERLA_LOG_INFO_ON_ROOT("path: " << path);
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** GLOBALBODYSTORAGE ***");
+   shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>();
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** BLOCKFOREST ***");
+   // create forest
+   shared_ptr< BlockForest > forest = createBlockForestFromConfig( mainConf );
+   if (!forest)
+   {
+      WALBERLA_LOG_INFO_ON_ROOT( "No BlockForest created ... exiting!");
+      return EXIT_SUCCESS;
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("simulationDomain: " << forest->getDomain());
+
+   WALBERLA_LOG_INFO_ON_ROOT("blocks: " << Vector3<uint_t>(forest->getXSize(), forest->getYSize(), forest->getZSize()) );
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** BODYTUPLE ***");
+   // initialize body type ids
+   SetBodyTypeIDs<BodyTuple>::execute();
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** STORAGEDATAHANDLING ***");
+   // add block data
+   auto storageID           = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage");
+   auto ccdID               = forest->addBlockData(ccd::createHashGridsDataHandling( globalBodyStorage, storageID ), "CCD");
+   auto fcdID               = forest->addBlockData(fcd::createGenericFCDDataHandling<BodyTuple, fcd::AnalyticCollideFunctor>(), "FCD");
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** INTEGRATOR ***");
+   std::unique_ptr<cr::ICR> cr;
+   if (bDEM)
+   {
+      cr = std::make_unique<cr::DEM>(globalBodyStorage, forest, storageID, ccdID, fcdID);
+      WALBERLA_LOG_INFO_ON_ROOT("Using DEM!");
+   } else if (bHCSITS)
+   {
+      cr = std::make_unique<cr::HCSITS>(globalBodyStorage, forest, storageID, ccdID, fcdID);
+      configure(mainConf, *static_cast<cr::HCSITS*>(cr.get()));
+      WALBERLA_LOG_INFO_ON_ROOT("Using HCSITS!");
+
+      cr::HCSITS* hcsits = static_cast<cr::HCSITS*>(cr.get());
+
+      if (bInelasticFrictionlessContact)
+      {
+         hcsits->setRelaxationModel(cr::HCSITS::InelasticFrictionlessContact);
+         WALBERLA_LOG_INFO_ON_ROOT("Using InelasticFrictionlessContact!");
+      } else if (bApproximateInelasticCoulombContactByDecoupling)
+      {
+         hcsits->setRelaxationModel(cr::HCSITS::ApproximateInelasticCoulombContactByDecoupling);
+         WALBERLA_LOG_INFO_ON_ROOT("Using ApproximateInelasticCoulombContactByDecoupling!");
+      } else if (bInelasticCoulombContactByDecoupling)
+      {
+         hcsits->setRelaxationModel(cr::HCSITS::InelasticCoulombContactByDecoupling);
+         WALBERLA_LOG_INFO_ON_ROOT("Using InelasticCoulombContactByDecoupling!");
+      } else if (bInelasticGeneralizedMaximumDissipationContact)
+      {
+         hcsits->setRelaxationModel(cr::HCSITS::InelasticGeneralizedMaximumDissipationContact);
+         WALBERLA_LOG_INFO_ON_ROOT("Using InelasticGeneralizedMaximumDissipationContact!");
+      } else
+      {
+         WALBERLA_ABORT("Friction model could not be determined!");
+      }
+   } else
+   {
+      WALBERLA_ABORT("Model could not be determined!");
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SYNCCALL ***");
+   std::function<void(void)> syncCallWithoutTT;
+   if (bNN)
+   {
+      syncCallWithoutTT = std::bind( pe::syncNextNeighbors<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree*>(NULL), real_c(0.0), false );
+      WALBERLA_LOG_INFO_ON_ROOT("Using NextNeighbor sync!");
+   } else if (bSO)
+   {
+      syncCallWithoutTT = std::bind( pe::syncShadowOwners<BodyTuple>, boost::ref(*forest), storageID, static_cast<WcTimingTree*>(NULL), real_c(0.0), false );
+      WALBERLA_LOG_INFO_ON_ROOT("Using ShadowOwner sync!");
+   } else
+   {
+      WALBERLA_ABORT("Synchronization method could not be determined!");
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** VTK ***");
+   auto vtkDomainOutput = vtk::createVTKOutput_DomainDecomposition( forest, "domain_decomposition", 1, "vtk_out", "simulation_step" );
+   auto vtkSphereHelper = make_shared<SphereVtkOutput>(storageID, *forest) ;
+   auto vtkSphereOutput = vtk::createVTKOutput_PointData(vtkSphereHelper, "Bodies", 1, "vtk_out", "simulation_step", false, false);
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - START ***");
+   const real_t   static_cof  ( real_c(0.1) / 2 );   // Coefficient of static friction. Note: pe doubles the input coefficient of friction for material-material contacts.
+   const real_t   dynamic_cof ( static_cof ); // Coefficient of dynamic friction. Similar to static friction for low speed friction.
+   MaterialID     material = createMaterial( "granular", real_t( 1.0 ), 0, static_cof, dynamic_cof, real_t( 0.5 ), 1, 1, 0, 0 );
+
+   auto simulationDomain = forest->getDomain();
+   auto generationDomain = simulationDomain; // simulationDomain.getExtended(-real_c(0.5) * spacing);
+
+   const real_t spacing(1.0);
+   const real_t radius(0.5);
+   uint_t numParticles = uint_c(0);
+
+   for (auto& currentBlock : *forest)
+   {
+      for (auto it = grid_generator::SCIterator(currentBlock.getAABB().getIntersection(generationDomain), Vector3<real_t>(spacing, spacing, spacing) * real_c(0.5), spacing); it != grid_generator::SCIterator(); ++it)
+      {
+         SphereID sp = pe::createSphere( *globalBodyStorage, *forest, storageID, 0, *it, radius, material);
+         if (sp != NULL) ++numParticles;
+      }
+   }
+   mpi::reduceInplace(numParticles, mpi::SUM);
+   WALBERLA_LOG_INFO_ON_ROOT("#particles created: " << numParticles);
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - END ***");
+
+   // synchronize particles
+   syncCallWithoutTT();
+   syncCallWithoutTT();
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - START ***");
+   WALBERLA_MPI_BARRIER();
+   WcTimer timer;
+   for (int i=0; i < simulationSteps; ++i)
+   {
+      if( i % 200 == 0 )
+      {
+         WALBERLA_LOG_DEVEL_ON_ROOT( "Timestep " << i << " / " << simulationSteps );
+      }
+
+      cr->timestep( real_c(dt) );
+      syncCallWithoutTT();
+
+//      if( i % visSpacing == 0 )
+//      {
+//         vtkDomainOutput->write( );
+//         vtkSphereOutput->write( );
+//      }
+   }
+   WALBERLA_MPI_BARRIER();
+   timer.end();
+   WALBERLA_LOG_INFO_ON_ROOT("runtime: " << timer.average());
+   WALBERLA_LOG_INFO_ON_ROOT("*** SIMULATION - END ***");
+
+   WALBERLA_LOG_INFO_ON_ROOT("*** CHECKING RESULT - START ***");
+   for (auto& currentBlock : *forest)
+   {
+      Storage * storage = currentBlock.getData< Storage >( storageID );
+      BodyStorage& localStorage = (*storage)[0];
+
+      auto bodyIt = localStorage.begin();
+      for (auto it = grid_generator::SCIterator(currentBlock.getAABB().getIntersection(generationDomain), Vector3<real_t>(spacing, spacing, spacing) * real_c(0.5), spacing);
+           it != grid_generator::SCIterator();
+           ++it, ++bodyIt)
+      {
+         WALBERLA_CHECK_UNEQUAL(bodyIt, localStorage.end());
+         WALBERLA_CHECK_FLOAT_EQUAL(bodyIt->getPosition(), *it);
+      }
+   }
+   WALBERLA_LOG_INFO_ON_ROOT("*** CHECKING RESULT - END ***");
+
+   return EXIT_SUCCESS;
+}