From ad1f1d69c4ba88bc483fdc2a3b1e003adeed50dc Mon Sep 17 00:00:00 2001
From: Christoph Rettinger <christoph.rettinger@fau.de>
Date: Fri, 3 Nov 2017 15:30:13 +0100
Subject: [PATCH] pe_coupling: New functionality, adapted test case

---
 ...ntainer.h => BodiesForceTorqueContainer.h} |  39 +++-
 src/pe_coupling/utility/ForceOnBodiesAdder.h  |  66 ++++++
 .../utility/ForceTorqueOnBodiesResetter.h     |  60 +++++
 .../utility/ForceTorqueOnBodiesScaler.h       |  72 ++++++
 src/pe_coupling/utility/TimeStep.h            |   1 +
 src/pe_coupling/utility/all.h                 |   5 +-
 .../DragForceSphereMEM.cpp                    |  27 +--
 .../DragForceSphereMEMRefinement.cpp          |  27 +--
 .../SegreSilberbergMEM.cpp                    | 217 +++++++-----------
 .../TorqueSphereMEM.cpp                       |  27 +--
 10 files changed, 329 insertions(+), 212 deletions(-)
 rename src/pe_coupling/utility/{BodiesForceAndTorqueContainer.h => BodiesForceTorqueContainer.h} (74%)
 create mode 100644 src/pe_coupling/utility/ForceOnBodiesAdder.h
 create mode 100644 src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h
 create mode 100644 src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h

diff --git a/src/pe_coupling/utility/BodiesForceAndTorqueContainer.h b/src/pe_coupling/utility/BodiesForceTorqueContainer.h
similarity index 74%
rename from src/pe_coupling/utility/BodiesForceAndTorqueContainer.h
rename to src/pe_coupling/utility/BodiesForceTorqueContainer.h
index 4482382e4..a32dc589d 100644
--- a/src/pe_coupling/utility/BodiesForceAndTorqueContainer.h
+++ b/src/pe_coupling/utility/BodiesForceTorqueContainer.h
@@ -13,7 +13,7 @@
 //  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 BodiesForceAndTorqueContainer.h
+//! \file BodiesForceTorqueContainer.h
 //! \ingroup pe_coupling
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
@@ -32,11 +32,11 @@
 namespace walberla {
 namespace pe_coupling {
 
-class BodiesForceAndTorqueContainer
+class BodiesForceTorqueContainer
 {  
 public:
 
-   BodiesForceAndTorqueContainer( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID )
+   BodiesForceTorqueContainer( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID )
    : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID )
      { }
 
@@ -60,7 +60,7 @@ public:
       {
          for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
          {
-            auto & f = bodyForceAndTorqueMap_[ bodyIt->getSystemID() ];
+            auto & f = bodyForceTorqueMap_[ bodyIt->getSystemID() ];
 
             // only add if body has not been added already before (from another block)
             if( f.empty() )
@@ -94,7 +94,8 @@ public:
       {
          for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
          {
-            const auto &f = bodyForceAndTorqueMap_[bodyIt->getSystemID()];
+            const auto &f = bodyForceTorqueMap_[bodyIt->getSystemID()];
+            WALBERLA_ASSERT( !f.empty(), "When attempting to set force/torque on local body " << bodyIt->getSystemID() << " at position " << bodyIt->getPosition() << ", body was not found in map!");
             bodyIt->addForce ( f[0], f[1], f[2] );
             bodyIt->addTorque( f[3], f[4], f[5] );
          }
@@ -103,14 +104,38 @@ public:
 
    void clear()
    {
-      bodyForceAndTorqueMap_.clear();
+      bodyForceTorqueMap_.clear();
+   }
+
+   void swap( BodiesForceTorqueContainer & other )
+   {
+      std::swap( this->bodyForceTorqueMap_, other.bodyForceTorqueMap_ );
    }
 
 private:
 
    shared_ptr<StructuredBlockStorage> blockStorage_;
    const BlockDataID bodyStorageID_;
-   std::map< walberla::id_t, std::vector<real_t> > bodyForceAndTorqueMap_;
+   std::map< walberla::id_t, std::vector<real_t> > bodyForceTorqueMap_;
+};
+
+
+class BodyContainerSwapper
+{
+public:
+   BodyContainerSwapper( const shared_ptr<BodiesForceTorqueContainer> & cont1,
+                         const shared_ptr<BodiesForceTorqueContainer> & cont2 )
+   : cont1_( cont1 ), cont2_( cont2 )
+   { }
+
+   void operator()()
+   {
+      cont1_->swap( *cont2_ );
+   }
+
+private:
+   shared_ptr<BodiesForceTorqueContainer> cont1_;
+   shared_ptr<BodiesForceTorqueContainer> cont2_;
 };
 
 } // namespace pe_coupling
diff --git a/src/pe_coupling/utility/ForceOnBodiesAdder.h b/src/pe_coupling/utility/ForceOnBodiesAdder.h
new file mode 100644
index 000000000..2d6e3f4ea
--- /dev/null
+++ b/src/pe_coupling/utility/ForceOnBodiesAdder.h
@@ -0,0 +1,66 @@
+//======================================================================================================================
+//
+//  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 ForceOnBodiesAdder.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "domain_decomposition/StructuredBlockStorage.h"
+#include "pe/rigidbody/BodyIterators.h"
+
+
+namespace walberla {
+namespace pe_coupling {
+
+class ForceOnBodiesAdder
+{  
+public:
+
+   ForceOnBodiesAdder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID,
+                       const Vector3<real_t> & force )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), force_( force )
+     { }
+
+   // set a constant 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 )
+         {
+            bodyIt->addForce ( force_ );
+         }
+      }
+   }
+
+   void updateForce( const Vector3<real_t> & newForce )
+   {
+      force_ = newForce;
+   }
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   Vector3<real_t> force_;
+};
+
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h
new file mode 100644
index 000000000..c42e96b0c
--- /dev/null
+++ b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h
@@ -0,0 +1,60 @@
+//======================================================================================================================
+//
+//  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 ForceTorqueOnBodiesResetter.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "domain_decomposition/StructuredBlockStorage.h"
+#include "pe/rigidbody/BodyIterators.h"
+
+
+namespace walberla {
+namespace pe_coupling {
+
+class ForceTorqueOnBodiesResetter
+{  
+public:
+
+   ForceTorqueOnBodiesResetter( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID )
+     { }
+
+   // resets forces and torques on all (local and remote) bodies
+   void operator()()
+   {
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
+         {
+            bodyIt->resetForceAndTorque();
+         }
+      }
+   }
+
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+};
+
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h
new file mode 100644
index 000000000..5f07300f3
--- /dev/null
+++ b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.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 ForceTorqueOnBodiesScaler.h
+//! \ingroup pe_coupling
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/math/Vector3.h"
+#include "domain_decomposition/StructuredBlockStorage.h"
+#include "pe/rigidbody/BodyIterators.h"
+
+
+namespace walberla {
+namespace pe_coupling {
+
+// scales force/torquew on all bodies (local and remote) by a constant scalar value
+// can e.g. be used to average the force/torque over two time steps
+class ForceTorqueOnBodiesScaler
+{  
+public:
+
+   ForceTorqueOnBodiesScaler( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID,
+                              const real_t & scalingFactor )
+   : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), scalingFactor_( scalingFactor )
+     { }
+
+   // resets forces and torques on all (local and remote) bodies
+   void operator()()
+   {
+      Vector3<real_t> force(real_t(0));
+      Vector3<real_t> torque(real_t(0));
+      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
+      {
+         for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
+         {
+            force  = scalingFactor_ * bodyIt->getForce();
+            torque = scalingFactor_ * bodyIt->getTorque();
+
+            bodyIt->resetForceAndTorque();
+
+            bodyIt->setForce ( force );
+            bodyIt->setTorque( torque );
+         }
+      }
+   }
+
+
+private:
+
+   shared_ptr<StructuredBlockStorage> blockStorage_;
+   const BlockDataID bodyStorageID_;
+   const real_t scalingFactor_;
+};
+
+} // namespace pe_coupling
+} // namespace walberla
diff --git a/src/pe_coupling/utility/TimeStep.h b/src/pe_coupling/utility/TimeStep.h
index 3fae74e48..791274c23 100644
--- a/src/pe_coupling/utility/TimeStep.h
+++ b/src/pe_coupling/utility/TimeStep.h
@@ -124,6 +124,7 @@ public:
                   for( auto bodyIt = pe::LocalBodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
                   {
                      const auto & f = forceMap[ bodyIt->getSystemID() ];
+                     WALBERLA_ASSERT( !f.empty(), "When attempting to set force/torque on local body " << bodyIt->getSystemID() << " at position " << bodyIt->getPosition() << ", body was not found in map!");
                      bodyIt->addForce ( f[0], f[1], f[2] );
                      bodyIt->addTorque( f[3], f[4], f[5] );
                   }
diff --git a/src/pe_coupling/utility/all.h b/src/pe_coupling/utility/all.h
index f7da33dc9..9bb040405 100644
--- a/src/pe_coupling/utility/all.h
+++ b/src/pe_coupling/utility/all.h
@@ -22,6 +22,9 @@
 
 #pragma once
 
-#include "BodiesForceAndTorqueContainer.h"
+#include "BodiesForceTorqueContainer.h"
+#include "ForceOnBodiesAdder.h"
+#include "ForceTorqueOnBodiesResetter.h"
+#include "ForceTorqueOnBodiesScaler.h"
 #include "LubricationCorrection.h"
 #include "TimeStep.h"
diff --git a/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEM.cpp b/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEM.cpp
index faa683155..095fed5f1 100644
--- a/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEM.cpp
+++ b/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEM.cpp
@@ -58,6 +58,7 @@
 
 #include "pe_coupling/mapping/all.h"
 #include "pe_coupling/momentum_exchange_method/all.h"
+#include "pe_coupling/utility/all.h"
 
 #include <vector>
 #include <iomanip>
@@ -331,30 +332,6 @@ private:
 
 };
 
-
-class ResetForce
-{
-public:
-   ResetForce( const shared_ptr< StructuredBlockStorage > & blocks,
-               const BlockDataID & bodyStorageID )
-   : blocks_( blocks ), bodyStorageID_( bodyStorageID )
-   { }
-
-   void operator()()
-   {
-      for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
-      {
-         for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
-         {
-            bodyIt->resetForceAndTorque();
-         }
-      }
-   }
-private:
-   shared_ptr< StructuredBlockStorage > blocks_;
-   const BlockDataID bodyStorageID_;
-};
-
 class PDFFieldCopy
 {
 public:
@@ -583,7 +560,7 @@ int main( int argc, char **argv )
                   << AfterFunction( SharedFunctor< DragForceEvaluator >( forceEval ), "drag force evaluation" );
 
    // resetting force
-   timeloop.addFuncAfterTimeStep( ResetForce( blocks, bodyStorageID ), "reset force on sphere");
+   timeloop.addFuncAfterTimeStep( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID ), "reset force on sphere");
 
    timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
 
diff --git a/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEMRefinement.cpp b/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEMRefinement.cpp
index 2772c305e..40fb413f6 100644
--- a/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEMRefinement.cpp
+++ b/tests/pe_coupling/momentum_exchange_method/DragForceSphereMEMRefinement.cpp
@@ -61,6 +61,7 @@
 
 #include "pe_coupling/mapping/all.h"
 #include "pe_coupling/momentum_exchange_method/all.h"
+#include "pe_coupling/utility/all.h"
 
 #include "vtk/all.h"
 #include "field/vtk/all.h"
@@ -405,30 +406,6 @@ class ForceEval
 };
 
 
-class ResetForce
-{
-   public:
-      ResetForce( const shared_ptr< StructuredBlockStorage > & blocks,
-                  const BlockDataID & bodyStorageID )
-      : blocks_( blocks ), bodyStorageID_( bodyStorageID )
-      { }
-
-      void operator()()
-      {
-         for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
-         {
-            for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
-            {
-                bodyIt->resetForceAndTorque();
-            }
-         }
-      }
-private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      const BlockDataID bodyStorageID_;
-};
-
-
 //////////
 // MAIN //
 //////////
@@ -596,7 +573,7 @@ int main( int argc, char **argv )
    timeloop.addFuncAfterTimeStep( makeSharedFunctor( forceEval ), "drag force evaluation" );
 
    // resetting force
-   timeloop.addFuncAfterTimeStep( ResetForce( blocks, bodyStorageID ), "reset force on sphere");
+   timeloop.addFuncAfterTimeStep( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID ), "reset force on sphere");
 
    if( vtkIO )
    {
diff --git a/tests/pe_coupling/momentum_exchange_method/SegreSilberbergMEM.cpp b/tests/pe_coupling/momentum_exchange_method/SegreSilberbergMEM.cpp
index 752d57d30..3bff481c7 100644
--- a/tests/pe_coupling/momentum_exchange_method/SegreSilberbergMEM.cpp
+++ b/tests/pe_coupling/momentum_exchange_method/SegreSilberbergMEM.cpp
@@ -241,11 +241,11 @@ void initPDF(const shared_ptr< StructuredBlockStorage > & blocks, const BlockDat
          // obtain the physical coordinate of the center of the current cell
          const Vector3< real_t > p = blocks->getBlockLocalCellCenter( *block, *cell );
 
-         const real_t rho = real_c(1);
-         const real_t height = real_c(setup.zlength) * real_c(0.5);
+         const real_t rho = real_t(1);
+         const real_t height = real_c(setup.zlength) * real_t(0.5);
 
          // parabolic Poiseuille profile for a given external force
-         Vector3< real_t > velocity ( setup.forcing / ( real_c(2) * setup.nu) * (height * height - ( p[2] - height ) * ( p[2] - height ) ), real_c(0), real_c(0) );
+         Vector3< real_t > velocity ( setup.forcing / ( real_t(2) * setup.nu) * (height * height - ( p[2] - height ) * ( p[2] - height ) ), real_t(0), real_t(0) );
          pdf->setToEquilibrium( *cell, velocity, rho );
       }
    }
@@ -385,72 +385,8 @@ class SteadyStateCheck
       real_t posNew_;
 };
 
-// When using N LBM steps and one (larger) pe step in a single simulation step, the force applied on the pe bodies in each LBM sep has to be scaled
-// by a factor of 1/N before running the pe simulation. This corresponds to an averaging of the force and torque over the N LBM steps and is said to
-// damp oscillations. Usually, N = 2.
-// See Ladd - " Numerical simulations of particulate suspensions via a discretized Boltzmann equation. Part 1. Theoretical foundation", 1994, p. 302
-class AverageForce
-{
-   public:
-      AverageForce( const shared_ptr< StructuredBlockStorage > & blocks, const BlockDataID & bodyStorageID,
-                    const uint_t lbmSubCycles )
-      : blocks_( blocks ), bodyStorageID_( bodyStorageID ), invLbmSubCycles_( real_c(1) / real_c( lbmSubCycles ) ) { }
-
-      void operator()()
-      {
-         pe::Vec3 force  = pe::Vec3(0.0);
-         pe::Vec3 torque = pe::Vec3(0.0);
-
-         for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
-         {
-            for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
-            {
-               force  = invLbmSubCycles_ * bodyIt->getForce();
-               torque = invLbmSubCycles_ * bodyIt->getTorque();
-
-               bodyIt->resetForceAndTorque();
-
-               bodyIt->setForce ( force );
-               bodyIt->setTorque( torque );
-            }
-         }
-      }
-   private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      const BlockDataID bodyStorageID_;
-      real_t invLbmSubCycles_;
-};
 
 
-// The flow in the channel is here driven by a body force and is periodic
-// Thus, the pressure gradient, usually encountered in a channel flow, is not present here
-// The buoyancy force on the body due to this pressure gradient has to be added 'artificially'
-// F_{buoyancy} = - V_{body} * grad ( p ) = V_{body} * \rho_{fluid} *  a
-// ( V_{body} := volume of body,  a := acceleration driving the flow )
-class BuoyancyForce
-{
-   public:
-   BuoyancyForce( const shared_ptr< StructuredBlockStorage > & blocks, const BlockDataID & bodyStorageID,
-                  const Vector3<real_t> pressureForce )
-      : blocks_( blocks ), bodyStorageID_( bodyStorageID ), pressureForce_( pressureForce ) { }
-
-      void operator()()
-      {
-
-         for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
-         {
-            for( auto bodyIt = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt )
-            {
-               bodyIt->addForce ( pressureForce_ );
-            }
-         }
-      }
-   private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      const BlockDataID bodyStorageID_;
-      const Vector3<real_t> pressureForce_;
-};
-
 class PDFFieldCopy
 {
 public:
@@ -523,6 +459,7 @@ int main( int argc, char **argv )
    bool MO_MR            = false; //use multireflection boundary condition
    bool eanReconstructor = false; //use equilibrium and non-equilibrium reconstructor
    bool extReconstructor = false; //use extrapolation reconstructor
+   bool averageForceTorqueOverTwoTimSteps = true;
 
    for( int i = 1; i < argc; ++i )
    {
@@ -535,6 +472,7 @@ int main( int argc, char **argv )
       if( std::strcmp( argv[i], "--MO_MR"  )           == 0 ) { MO_MR            = true; continue; }
       if( std::strcmp( argv[i], "--eanReconstructor" ) == 0 ) { eanReconstructor = true; continue; }
       if( std::strcmp( argv[i], "--extReconstructor" ) == 0 ) { extReconstructor = true; continue; }
+      if( std::strcmp( argv[i], "--noForceAveraging" ) == 0 ) { averageForceTorqueOverTwoTimSteps = false; continue; }
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
@@ -544,28 +482,26 @@ int main( int argc, char **argv )
 
    Setup setup;
 
-   setup.particleDiam =  real_c( 12 );                          // particle diameter
-   setup.rho_p = real_c( 1 );                                   // particle density
-   setup.tau = real_c( 1.4 );                                   // relaxation time
-   setup.nu = (real_c(2) * setup.tau - real_c(1) ) / real_c(6); // viscosity in lattice units
-   setup.timesteps = functest ? 1 : ( shortrun ? uint_c(200) : uint_c( 250000 ) ); // maximum number of time steps
-   setup.zlength = uint_c( 4.0 * setup.particleDiam );          // zlength in lattice cells of the channel
+   setup.particleDiam =  real_t( 12 );                          // particle diameter
+   setup.rho_p = real_t( 1 );                                   // particle density
+   setup.tau = real_t( 1.4 );                                   // relaxation time
+   setup.nu = (real_t(2) * setup.tau - real_t(1) ) / real_t(6); // viscosity in lattice units
+   setup.timesteps = functest ? 1 : ( shortrun ? uint_t(200) : uint_t( 250000 ) ); // maximum number of time steps
+   setup.zlength = uint_c( real_t(4) * setup.particleDiam );          // zlength in lattice cells of the channel
    setup.xlength = setup.zlength;                               // xlength in lattice cells of the channel
    setup.ylength = setup.zlength;                               // ylength in lattice cells of the channel
-   setup.Re = real_c( 13 );                                     // bulk Reynoldsnumber = uMean * width / nu
+   setup.Re = real_t( 13 );                                     // bulk Reynoldsnumber = uMean * width / nu
 
-   const real_t particleRadius = real_c(0.5) * setup.particleDiam; // particle radius
-   const real_t dx = real_c(1);                                    // lattice grid spacing
-   const uint_t numLbmSubCycles = uint_c(2);                       // number of LBM subcycles, i.e. number of LBM steps until one pe step is carried out
-   const real_t dt_pe       = real_c(numLbmSubCycles);             // time step size for PE (LU): here 2 times as large as for LBM
-   const uint_t pe_interval = uint_c(1);                           // subcycling interval for PE: only 1 PE step
-   const real_t omega = real_c(1) / setup.tau;                     // relaxation rate
-   const real_t convergenceLimit = real_c( 1e-5 );                 // tolerance for relative change in position
-   setup.checkFrequency = uint_c( real_c(100) / dt_pe );           // evaluate the position only every checkFrequency LBM time steps
+   const real_t particleRadius = real_t(0.5) * setup.particleDiam; // particle radius
+   const real_t dx = real_t(1);                                    // lattice grid spacing
+   const uint_t numPeSubcycles = uint_c(1);                        // number of pe subcycles per LBM step
+   const real_t omega = real_t(1) / setup.tau;                     // relaxation rate
+   const real_t convergenceLimit = real_t( 1e-5 );                 // tolerance for relative change in position
+   setup.checkFrequency = uint_t( 100 );           // evaluate the position only every checkFrequency LBM time steps
 
    const real_t uMean = setup.Re * setup.nu / real_c( setup.zlength );
-   const real_t uMax = real_c(2) * uMean;
-   setup.forcing = uMax * ( real_c(2) * setup.nu ) / real_c( real_c(3./4.) * real_c(setup.zlength) * real_c(setup.zlength) ); // forcing to drive the flow
+   const real_t uMax = real_t(2) * uMean;
+   setup.forcing = uMax * ( real_t(2) * setup.nu ) / ( real_t(3./4.) * real_c(setup.zlength) * real_c(setup.zlength) ); // forcing to drive the flow
    setup.Re_p = uMean * particleRadius * particleRadius / ( setup.nu * real_c( setup.zlength ) );  // particle Reynolds number = uMean * r * r / ( nu * width )
 
    if( fileIO || vtkIO ){
@@ -581,9 +517,9 @@ int main( int argc, char **argv )
    // BLOCK STRUCTURE SETUP //
    ///////////////////////////
 
-   setup.nBlocks[0] = uint_c(3);
-   setup.nBlocks[1] = uint_c(3);
-   setup.nBlocks[2] = (processes == 18) ? uint_c(2) : uint_c(1);
+   setup.nBlocks[0] = uint_t(3);
+   setup.nBlocks[1] = uint_t(3);
+   setup.nBlocks[2] = (processes == 18) ? uint_t(2) : uint_t(1);
    setup.cellsPerBlock[0] = setup.xlength / setup.nBlocks[0];
    setup.cellsPerBlock[1] = setup.ylength / setup.nBlocks[1];
    setup.cellsPerBlock[2] = setup.zlength / setup.nBlocks[2];
@@ -609,7 +545,7 @@ int main( int argc, char **argv )
    pe::cr::DEM cr(globalBodyStorage, blocks->getBlockStoragePointer(), bodyStorageID, ccdID, fcdID, NULL);
 
    // set up synchronization procedure
-   const real_t overlap = real_c( 1.5 ) * dx;
+   const real_t overlap = real_t( 1.5 ) * dx;
    boost::function<void(void)> syncCall;
    if (!syncShadowOwners)
    {
@@ -622,19 +558,19 @@ int main( int argc, char **argv )
    // create pe bodies
 
    // bounding planes (global)
-   const auto planeMaterial = pe::createMaterial( "myPlaneMat", real_c(8920), real_c(0), real_c(1), real_c(1), real_c(0), real_c(1), real_c(1), real_c(0), real_c(0) );
+   const auto planeMaterial = pe::createMaterial( "myPlaneMat", real_t(8920), real_t(0), real_t(1), real_t(1), real_t(0), real_t(1), real_t(1), real_t(0), real_t(0) );
    pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>(0,0,1), Vector3<real_t>(0,0,0), planeMaterial );
    pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>(0,0,-1), Vector3<real_t>(0,0,real_c(setup.zlength)), planeMaterial );
 
    // add the sphere
-   const auto sphereMaterial = pe::createMaterial( "mySphereMat", setup.rho_p , real_c(0.5), real_c(0.1), real_c(0.1), real_c(0.24), real_c(200), real_c(200), real_c(0), real_c(0) );
-   Vector3<real_t> position( real_c(setup.xlength) * real_c(0.5), real_c(setup.ylength) * real_c(0.5), real_c(setup.zlength) * real_c(0.5) - real_c(1) );
+   const auto sphereMaterial = pe::createMaterial( "mySphereMat", setup.rho_p , real_t(0.5), real_t(0.1), real_t(0.1), real_t(0.24), real_t(200), real_t(200), real_t(0), real_t(0) );
+   Vector3<real_t> position( real_c(setup.xlength) * real_t(0.5), real_c(setup.ylength) * real_t(0.5), real_c(setup.zlength) * real_t(0.5) - real_t(1) );
    auto sphere = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, position, particleRadius, sphereMaterial );
    if( sphere != NULL )
    {
-      real_t height = real_c( 0.5 ) * real_c( setup.zlength );
+      real_t height = real_t( 0.5 ) * real_c( setup.zlength );
       // set sphere velocity to undisturbed fluid velocity at sphere center
-      sphere->setLinearVel( setup.forcing/( real_c(2) * setup.nu) * ( height * height - ( position[2] - height ) * ( position[2] - height ) ), real_c(0), real_c(0) );
+      sphere->setLinearVel( setup.forcing/( real_t(2) * setup.nu) * ( height * height - ( position[2] - height ) * ( position[2] - height ) ), real_t(0), real_t(0) );
    }
 
    syncCall();
@@ -644,16 +580,16 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // create the lattice model
-   LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ), SimpleConstant( Vector3<real_t> ( setup.forcing, real_c(0), real_c(0) ) ) );
+   LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ), SimpleConstant( Vector3<real_t> ( setup.forcing, real_t(0), real_t(0) ) ) );
 
    // add PDF field
    BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, "pdf field (zyxf)", latticeModel,
-                                                                         Vector3< real_t >( real_c(0), real_c(0), real_c(0) ), real_c(1),
+                                                                         Vector3< real_t >( real_t(0) ), real_t(1),
                                                                          uint_t(1), field::zyxf );
 
    // add PDF field, for MR boundary condition only
    BlockDataID pdfFieldPreColID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, " pre collision pdf field", latticeModel,
-                                                                               Vector3< real_t >( real_c(0), real_c(0), real_c(0) ), real_c(1),
+                                                                               Vector3< real_t >( real_t(0) ), real_t(1),
                                                                                uint_t(1), field::zyxf );
 
    // initialize already with the Poiseuille flow profile
@@ -733,48 +669,71 @@ int main( int argc, char **argv )
                       ( blocks, boundaryHandlingID, bodyStorageID, bodyFieldID, reconstructor, FormerMO_Flag, Fluid_Flag  ), "PDF Restore" );
    }
 
-   for( uint_t lbmSubCycle = uint_c(0); lbmSubCycle < numLbmSubCycles; ++lbmSubCycle )
+   shared_ptr<pe_coupling::BodiesForceTorqueContainer> bodiesFTContainer1 = make_shared<pe_coupling::BodiesForceTorqueContainer>(blocks, bodyStorageID);
+   boost::function<void(void)> storeForceTorqueInCont1 = boost::bind(&pe_coupling::BodiesForceTorqueContainer::store, bodiesFTContainer1);
+   shared_ptr<pe_coupling::BodiesForceTorqueContainer> bodiesFTContainer2 = make_shared<pe_coupling::BodiesForceTorqueContainer>(blocks, bodyStorageID);
+   boost::function<void(void)> setForceTorqueOnBodiesFromCont2 = boost::bind(&pe_coupling::BodiesForceTorqueContainer::setOnBodies, bodiesFTContainer2);
+
+   bodiesFTContainer2->store();
+
+   if( MO_MR )
    {
-      if( MO_MR )
-      {
-         // copy pdfs to have the pre collision PDFs available, needed for MR boundary treatment
-         timeloop.add() << Sweep( PDFFieldCopy( pdfFieldID, pdfFieldPreColID ), "pdf field copy" );
+      // copy pdfs to have the pre collision PDFs available, needed for MR boundary treatment
+      timeloop.add() << Sweep( PDFFieldCopy( pdfFieldID, pdfFieldPreColID ), "pdf field copy" );
 
-         auto sweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
+      auto sweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
 
-         // collision sweep
-         timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "cell-wise LB sweep (collide)" );
+      // collision sweep
+      timeloop.add() << Sweep( lbm::makeCollideSweep( sweep ), "cell-wise LB sweep (collide)" );
 
-         // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
-         timeloop.add() << BeforeFunction( commFunction, "LBM Communication" )
-                        << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+      // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+      timeloop.add() << BeforeFunction( commFunction, "LBM Communication" )
+                     << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
 
-         // streaming
-         timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "cell-wise LB sweep (stream)" );
-      }
-      else
-      {
-         // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
-         timeloop.add() << BeforeFunction( commFunction, "LBM Communication" )
-                        << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+      // streaming
+      timeloop.add() << Sweep( lbm::makeStreamSweep( sweep ), "cell-wise LB sweep (stream)" );
+   }
+   else
+   {
+      // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+      timeloop.add() << BeforeFunction( commFunction, "LBM Communication" )
+                     << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
 
-         // stream + collide LBM step
-         timeloop.add()
-            << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag ) ), "cell-wise LB sweep" );
-      }
+      // stream + collide LBM step
+      timeloop.add()
+         << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag ) ), "cell-wise LB sweep" );
    }
 
-   // average the forces acting on the bodies over the number of LBM steps
-   timeloop.addFuncAfterTimeStep( AverageForce( blocks, bodyStorageID, numLbmSubCycles ), "Force averaging for several LBM steps" );
+   // Averaging the force/torque over two time steps is said to damp oscillations of the interaction force/torque.
+   // See Ladd - " Numerical simulations of particulate suspensions via a discretized Boltzmann equation. Part 1. Theoretical foundation", 1994, p. 302
+   if( averageForceTorqueOverTwoTimSteps ) {
+
+      // store force/torque from hydrodynamic interactions in container1
+      timeloop.addFuncAfterTimeStep(storeForceTorqueInCont1, "Force Storing");
+
+      // set force/torque from previous time step (in container2)
+      timeloop.addFuncAfterTimeStep(setForceTorqueOnBodiesFromCont2, "Force setting");
+
+      // average the force/torque by scaling it with factor 1/2
+      timeloop.addFuncAfterTimeStep( pe_coupling::ForceTorqueOnBodiesScaler(blocks, bodyStorageID, real_t(0.5)),  "Force averaging");
+
+      // swap containers
+      timeloop.addFuncAfterTimeStep( pe_coupling::BodyContainerSwapper( bodiesFTContainer1, bodiesFTContainer2 ), "Swap FT container" );
+
+   }
 
    // add pressure force contribution
-   timeloop.addFuncAfterTimeStep( BuoyancyForce( blocks, bodyStorageID,
-                                                 Vector3<real_t>( math::M_PI / real_c(6) * setup.forcing * setup.particleDiam * setup.particleDiam * setup.particleDiam ,
-                                                                  real_c(0), real_c(0) ) ),
-                                  "Buoyancy force" );
+   // The flow in the channel is here driven by a body force and is periodic
+   // Thus, the pressure gradient, usually encountered in a channel flow, is not present here
+   // The buoyancy force on the body due to this pressure gradient has to be added 'artificially'
+   // F_{buoyancy} = - V_{body} * grad ( p ) = V_{body} * \rho_{fluid} *  a
+   // ( V_{body} := volume of body,  a := acceleration driving the flow )
+   Vector3<real_t> buoyancyForce(math::M_PI / real_t(6) * setup.forcing * setup.particleDiam * setup.particleDiam * setup.particleDiam ,
+                                 real_t(0), real_t(0));
+   timeloop.addFuncAfterTimeStep( pe_coupling::ForceOnBodiesAdder( blocks, bodyStorageID, buoyancyForce ), "Buoyancy force" );
 
    // add pe timesteps
-   timeloop.addFuncAfterTimeStep( pe_coupling::TimeStep( blocks, bodyStorageID, cr, syncCall, dt_pe, pe_interval ), "pe Time Step" );
+   timeloop.addFuncAfterTimeStep( pe_coupling::TimeStep( blocks, bodyStorageID, cr, syncCall, real_t(1), numPeSubcycles ), "pe Time Step" );
 
    // check for convergence of the particle position
    shared_ptr< SteadyStateCheck > check = make_shared< SteadyStateCheck >( &timeloop, &setup, blocks, bodyStorageID,
@@ -797,7 +756,7 @@ int main( int argc, char **argv )
 
       // pdf field (ghost layers cannot be written because re-sampling/coarsening is applied)
       auto pdfFieldVTK = vtk::createVTKOutput_BlockData( blocks, "fluid_field", writeFrequency );
-      pdfFieldVTK->setSamplingResolution( real_c(1) );
+      pdfFieldVTK->setSamplingResolution( real_t(1) );
 
       blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > pdfGhostLayerSync( blocks );
       pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo< PdfField_T > >( pdfFieldID ) );
@@ -843,7 +802,7 @@ int main( int argc, char **argv )
    {
       // reference value from Inamuro et al. - "Flow between parallel walls containing the lines of neutrally buoyant circular cylinders" (2000), Table 2
       // note that this reference value provides only a rough estimate
-      real_t referencePos = real_c( 0.2745 ) * real_c( setup.zlength );
+      real_t referencePos = real_t( 0.2745 ) * real_c( setup.zlength );
       real_t relErr = std::fabs( referencePos - check->getPosition() ) / referencePos;
       if ( fileIO )
       {
@@ -855,7 +814,7 @@ int main( int argc, char **argv )
          }
       }
       // the relative error has to be below 10%
-      WALBERLA_CHECK_LESS( relErr, real_c(0.1) );
+      WALBERLA_CHECK_LESS( relErr, real_t(0.1) );
    }
 
    return EXIT_SUCCESS;
diff --git a/tests/pe_coupling/momentum_exchange_method/TorqueSphereMEM.cpp b/tests/pe_coupling/momentum_exchange_method/TorqueSphereMEM.cpp
index 70f2d4150..4e41079db 100644
--- a/tests/pe_coupling/momentum_exchange_method/TorqueSphereMEM.cpp
+++ b/tests/pe_coupling/momentum_exchange_method/TorqueSphereMEM.cpp
@@ -58,6 +58,7 @@
 
 #include "pe_coupling/mapping/all.h"
 #include "pe_coupling/momentum_exchange_method/all.h"
+#include "pe_coupling/utility/all.h"
 
 #include <vector>
 #include <iomanip>
@@ -276,30 +277,6 @@ class TorqueEval
 
 };
 
-
-class ResetForce
-{
-   public:
-      ResetForce( const shared_ptr< StructuredBlockStorage > & blocks,
-                  const BlockDataID & bodyStorageID )
-      : blocks_( blocks ), bodyStorageID_( bodyStorageID )
-      { }
-
-      void operator()()
-      {
-         for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
-         {
-            for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
-            {
-                bodyIt->resetForceAndTorque();
-            }
-         }
-      }
-private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      const BlockDataID bodyStorageID_;
-};
-
 class PDFFieldCopy
 {
 public:
@@ -539,7 +516,7 @@ int main( int argc, char **argv )
                      << AfterFunction( SharedFunctor< TorqueEval >( torqueEval ), "torque evaluation" );
    }
    // resetting force
-   timeloop.addFuncAfterTimeStep( ResetForce( blocks, bodyStorageID ), "reset force on sphere");
+   timeloop.addFuncAfterTimeStep( pe_coupling::ForceTorqueOnBodiesResetter( blocks, bodyStorageID ), "reset force on sphere");
 
    timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
 
-- 
GitLab