void testHelperFunctions( const std::string & meshFile, const uint_t numTotalBlocks )
{
   auto mesh = make_shared<MeshType>();
   mesh::readAndBroadcast( meshFile, *mesh);
   auto triDist = make_shared< mesh::TriangleDistance<MeshType> >( mesh );
   auto distanceOctree = make_shared< DistanceOctree< MeshType > >( triDist );

   const real_t meshVolume  = real_c( computeVolume( *mesh ) );
   const real_t blockVolume = meshVolume / real_c( numTotalBlocks );
   static const real_t cellsPersBlock = real_t(1000);
   const real_t cellVolume = blockVolume / cellsPersBlock;
   const real_t dx = std::pow( cellVolume, real_t(1) / real_t(3) );

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with createStructuredBlockStorageInsideMesh with block size" );
   auto sbf0 = mesh::createStructuredBlockStorageInsideMesh( distanceOctree, dx, numTotalBlocks );
   
   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with createStructuredBlockStorageInsideMesh with block size" );
   Vector3<uint_t> blockSize( sbf0->getNumberOfXCells(), sbf0->getNumberOfYCells(), sbf0->getNumberOfZCells() );
   auto sbf1 = mesh::createStructuredBlockStorageInsideMesh( distanceOctree, dx, blockSize );

   auto exteriorAabb = computeAABB( *mesh ).getScaled( real_t(2) );

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with createStructuredBlockStorageInsideMesh with block size" );
   auto sbf2 = mesh::createStructuredBlockStorageOutsideMesh( exteriorAabb, distanceOctree, dx, numTotalBlocks );

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with createStructuredBlockStorageInsideMesh with block size" );
   blockSize = Vector3<uint_t>( sbf2->getNumberOfXCells(), sbf2->getNumberOfYCells(), sbf2->getNumberOfZCells() );
   auto sbf3 = mesh::createStructuredBlockStorageOutsideMesh( exteriorAabb, distanceOctree, dx, blockSize );
}
Exemple #2
0
// function sweep
void simpleSweep( IBlock * block, BlockDataID fieldBlockDataID )
{
   // retrieve the field from the block
   auto field = block->getData< Field<real_t,1> >( fieldBlockDataID );

   // some bogus "algorithm"
   for( auto iter = field->begin(); iter != field->end(); ++iter )
   {
      if( *iter > real_c(ARBITRARY_VALUE) )
         *iter /= real_c(2);
      else
         *iter *= real_c(2);
   }
}
Exemple #3
0
   void operator()( IBlock * block )
   {
      // the fieldID is now a member! (was set by the constructor)
      auto field = block->getData< Field<real_t,1> >( fieldID_ );

      // some bogus "algorithm"
      for( auto iter = field->begin(); iter != field->end(); ++iter )
      {
         if( *iter > real_c(ARBITRARY_VALUE) )
            *iter /= real_c(2);
         else
            *iter *= real_c(2);
      }
   }
Exemple #4
0
Field<real_t,1> * createFields( IBlock * const block, StructuredBlockStorage * const storage )
{
   return new Field<real_t,1> ( storage->getNumberOfXCells( *block ), // number of cells in x direction for this block
                                storage->getNumberOfYCells( *block ), // number of cells in y direction for this block
                                storage->getNumberOfZCells( *block ), // number of cells in z direction for this block
                                real_c(0) );                          // initial value
}
Exemple #5
0
int main( int argc, char ** argv )
{
   walberla::Environment env( argc, argv );

   // create blocks
   shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid(
            uint_c( 3), uint_c(2), uint_c( 4), // number of blocks in x,y,z direction
            uint_c(10), uint_c(8), uint_c(12), // how many cells per block (x,y,z)
            real_c(0.5),                       // dx: length of one cell in physical coordinates
            false,                             // one block per process? - "false" means all blocks to one process
            false, false, false );             // no periodicity

   // add a field to all blocks - and store the returned block data ID which is needed to access the field
   BlockDataID fieldID = blocks->addStructuredBlockData< Field<real_t,1> >( &createFields, "My Field" );

   // iterate all blocks and initialize with random values
   for( auto blockIterator = blocks->begin(); blockIterator != blocks->end(); ++blockIterator )
   {
      IBlock & currentBlock = *blockIterator;

      // get the field stored on the current block
      Field<real_t,1> * field = currentBlock.getData< Field<real_t,1> >( fieldID );

      // iterate the field and set random values
      for( auto iter = field->begin(); iter != field->end(); ++iter )
         *iter = real_c( rand() % ARBITRARY_VALUE );
   }

   // create time loop
   const uint_t numberOfTimesteps = uint_c(10); // number of time steps for non-gui runs
   SweepTimeloop timeloop( blocks, numberOfTimesteps );

   // registering the function sweep
   auto pointerToTwoArgFunction = & simpleSweep;
   auto pointerToOneArgFunction = std::bind( pointerToTwoArgFunction, std::placeholders::_1, fieldID );
   timeloop.add() << Sweep( pointerToOneArgFunction, "BogusAlgorithm" );

   // registering the class sweep
   timeloop.add() << Sweep( SimpleSweep(fieldID), "BogusAlgorithmButNowAsFunctor" );

   // two sweeps were registered, so both are executed in each time step

   GUI gui( timeloop, blocks, argc, argv );
   gui.run();

   return EXIT_SUCCESS;
}
Exemple #6
0
/*!\brief Returns the squirmer's surface velocity at a given relative position.
 *
 * \param rpos The relative global coordinate.
 * \return The local surface velocity of the squirmer.
 */
const Vec3 Squirmer::getSquirmerVelocity( const Vec3& rpos ) const
{
   const auto rs = rpos.getNormalized();
   const auto e = getQuaternion().rotate(Vec3(0.0,0.0,1.0)).getNormalized();
   const auto v0 = getSquirmerVelocity();
   const auto beta = getSquirmerBeta();
   return ((e * rs) * rs - e) * real_c(1.5) * v0 * (1 + beta * (e * rs));
}
Exemple #7
0
real_t getSphereSphereOverlap(const real_t d, const real_t r1, const real_t r2)
{
   WALBERLA_ASSERT_GREATER(d, r1);
   WALBERLA_ASSERT_GREATER(d, r2);

   //http://math.stackexchange.com/questions/297751/overlapping-spheres
   if (d > r1+r2)
      return 0; else
      return math::M_PI / (real_c(12.0)  * d) * (r1 + r2 - d) * (r1 + r2 - d) * (d*d + 2*d*(r1+r2) - 3*(r1-r2)*(r1-r2));
}
void test(const shared_ptr< DistanceOctree< MeshType > > & distanceOctree, const MeshType & mesh, const AABB & domainAABB, Vector3<uint_t> numBlocks)
{
   Vector3<real_t> blockSize(domainAABB.xSize() / real_c(numBlocks[0]),
      domainAABB.ySize() / real_c(numBlocks[1]),
      domainAABB.zSize() / real_c(numBlocks[2]));

   real_t maxError = blockSize.min() / real_t(10);

   SetupBlockForest setupBlockforest;
   setupBlockforest.addRootBlockExclusionFunction(F(distanceOctree, maxError));
   setupBlockforest.addWorkloadMemorySUIDAssignmentFunction(blockforest::uniformWorkloadAndMemoryAssignment);


   setupBlockforest.init(domainAABB, numBlocks[0], numBlocks[1], numBlocks[2], false, false, false);
   WALBERLA_LOG_DEVEL(setupBlockforest.toString());


   std::vector< Vector3<real_t> > vertexPositions;
   vertexPositions.reserve(mesh.n_vertices());
   for (auto vIt = mesh.vertices_begin(); vIt != mesh.vertices_end(); ++vIt)
   {
      vertexPositions.push_back(toWalberla(mesh.point(*vIt)));
   }

   std::vector< const blockforest::SetupBlock* > setupBlocks;
   setupBlockforest.getBlocks(setupBlocks);

   // Check wether all vertices are located in allocated blocks
   std::vector< Vector3<real_t> > uncoveredVertices(vertexPositions);

   for (auto bIt = setupBlocks.begin(); bIt != setupBlocks.end(); ++bIt)
   {
      const AABB & aabb = (*bIt)->getAABB();

      uncoveredVertices.erase(std::remove_if(uncoveredVertices.begin(), uncoveredVertices.end(), PointInAABB(aabb)), uncoveredVertices.end());
   }

   WALBERLA_CHECK(uncoveredVertices.empty(), "Not all vertices of the mesh are located in allocated blocks!");

   //setupBlockforest.assignAllBlocksToRootProcess();
   //setupBlockforest.writeVTKOutput( "setupblockforest" );
}
ExprSystemInitFunction::ExprSystemInitFunction( const shared_ptr<StructuredBlockForest> & blocks )
   : blocks_( blocks ), cell_(), symbolTable_( new exprtk::symbol_table<real_t>() ), expr_( new std::map<std::string, Expression>() )
{
   // add constants
   symbolTable_->add_constant( "n_x", real_c( blocks_->getNumberOfXCells() ) );
   symbolTable_->add_constant( "n_y", real_c( blocks_->getNumberOfYCells() ) );
   symbolTable_->add_constant( "n_z", real_c( blocks_->getNumberOfZCells() ) );
         
   // add variables
   symbolTable_->add_variable( "x" , cell_[0] );
   symbolTable_->add_variable( "y" , cell_[1] );
   symbolTable_->add_variable( "z" , cell_[2] );

   // add global constants (pi, e, etc.)
   symbolTable_->add_constants();

   // register symbols with expressions
   (*expr_)[ "u_x" ].register_symbol_table( *symbolTable_ );
   (*expr_)[ "u_y" ].register_symbol_table( *symbolTable_ );
   (*expr_)[ "u_z" ].register_symbol_table( *symbolTable_ );
   (*expr_)[ "rho" ].register_symbol_table( *symbolTable_ );
}
Exemple #10
0
   /*! Loops over measurements and updates them if necessary
    *******************************************************************************************************************/
   void PerformanceMeter::updateCellCounts()
   {
      WALBERLA_ASSERT_GREATER( timer_.getCounter(), 0 );

      uint_t ts = timer_.getCounter();

      for( auto measureIt = measurements_.begin(); measureIt != measurements_.end(); ++measureIt )
      {
         if( measureIt->countingFreq > 0 &&  ts % measureIt->countingFreq == 0 )
         {
            uint_t cells = 0;
            for( auto block = blockStorage_.begin(); block != blockStorage_.end(); ++block )
               cells += measureIt->countFunction( *block );

            real_t cellsLastTimeStep = real_c ( cells );

            measureIt->counts += 1;
            real_t rCounts = real_c( measureIt->counts );
            measureIt->avgCellsPerTimeStep = (rCounts - real_t(1.0) ) / rCounts * measureIt->avgCellsPerTimeStep +
                                                        real_t(1.0)   / rCounts * cellsLastTimeStep;
         }
      }
   }
Exemple #11
0
SquirmerID createSquirmer( BodyStorage& globalStorage, BlockStorage& blocks, BlockDataID storageID,
                        id_t uid, const Vec3& gpos, real_t radius,
                        real_t squirmerVelocity, real_t squirmerBeta,
                        MaterialID material,
                        bool global, bool communicating, bool infiniteMass )
{
   WALBERLA_ASSERT_UNEQUAL( Squirmer::getStaticTypeID(), std::numeric_limits<id_t>::max(), "Squirmer TypeID not initalized!");
   // Checking the radius
   if( radius <= real_c(0) )
      throw std::invalid_argument( "Invalid squirmer radius" );

   SquirmerID squirmer = nullptr;

   if (global)
   {
      const id_t sid = UniqueID<RigidBody>::createGlobal();
      WALBERLA_ASSERT_EQUAL(communicating, false);
      WALBERLA_ASSERT_EQUAL(infiniteMass, true);
      SquirmerPtr sq = std::make_unique<Squirmer>(sid, uid, gpos, Vec3(0,0,0), Quat(), radius, squirmerVelocity, squirmerBeta, material, global, false, true);
      squirmer = static_cast<SquirmerID>(&globalStorage.add(std::move(sq)));
   } else
   {
      for (auto& block : blocks){
         if (block.getAABB().contains(gpos))
         {
            const id_t sid( UniqueID<RigidBody>::create() );

            BodyStorage& bs = (*block.getData<Storage>(storageID))[0];
            SquirmerPtr sq = std::make_unique<Squirmer>(sid, uid, gpos, Vec3(0,0,0), Quat(), radius, squirmerVelocity, squirmerBeta, material, global, communicating, infiniteMass);
            sq->MPITrait.setOwner(Owner(MPIManager::instance()->rank(), block.getId().getID()));
            squirmer = static_cast<SquirmerID>(&bs.add( std::move(sq) ));
         }
      }
   }

   if (squirmer != nullptr)
   {
      // Logging the successful creation of the squirmer
      WALBERLA_LOG_DETAIL(
                "Created squirmer " << squirmer->getSystemID() << "\n"
             << "   User-ID         = " << uid << "\n"
             << "   Global position = " << gpos << "\n"
             << "   Radius          = " << radius << "\n"
             << "   LinVel          = " << squirmer->getLinearVel() << "\n"
             << "   Material        = " << Material::getName( material )
               );
   }

   return squirmer;
}
Exemple #12
0
   /*! Prints all added measurements to the given output stream on specified process
    * \param os            Output stream where results are printed
    * \param targetRank    the MPI world rank of the process that should print.
    *                      or a negative value, if all process should print.
    *******************************************************************************************************************/
   void PerformanceMeter::print( std::ostream & os, int targetRank )
   {
      if( measurements_.size() == 0)
         return;

      std::vector<real_t> totalNrCells;
      reduce( totalNrCells, targetRank );
      if ( MPIManager::instance()->worldRank() == targetRank || targetRank < 0 )
      {
         os << "Performance Results: " << std::endl;
         for( uint_t i = 0; i < measurements_.size(); ++i ) {
            real_t res = totalNrCells[i] / real_c( timer_.average() );
            res *= measurements_[i].scaling;
            os << " - ";
            os << std::setw (16) << std::left  << measurements_[i].name ;
            os << std::setw (8)  << std::right << res << std::endl;
         }
      }
   }
Exemple #13
0
void main( int argc, char ** argv )
{
   Environment env(argc, argv);
   WALBERLA_UNUSED(env);
   walberla::mpi::MPIManager::instance()->useWorldComm();

   //init domain partitioning
   auto forest = blockforest::createBlockForest( AABB(0,0,0,20,20,20), // simulation domain
                                                 Vector3<uint_t>(2,2,2), // blocks in each direction
                                                 Vector3<bool>(false, false, false) // periodicity
                                                 );
   domain::BlockForestDomain domain(forest);

   //init data structures
   data::ParticleStorage ps(100);

   //initialize particle
   auto uid = createSphere(ps, domain);
   WALBERLA_LOG_DEVEL_ON_ROOT("uid: " << uid);

   //init kernels
   mpi::ReduceProperty    RP;
   mpi::SyncNextNeighbors SNN;

   //sync
   SNN(ps, domain);

   auto pIt = ps.find(uid);
   if (pIt != ps.end())
   {
      pIt->getForceRef() += Vec3(real_c(walberla::mpi::MPIManager::instance()->rank()));
   }

   RP.operator()<ForceTorqueNotification>(ps);

   if (walberla::mpi::MPIManager::instance()->rank() == 0)
   {
      WALBERLA_CHECK_FLOAT_EQUAL( pIt->getForce(), Vec3(real_t(28)) );
   } else
   {
      WALBERLA_CHECK_FLOAT_EQUAL( pIt->getForce(), Vec3(real_t(walberla::mpi::MPIManager::instance()->rank())) );
   }
}
Exemple #14
0
int main(int argc, char **argv)
{
   walberla::Environment env( argc, argv );

   const uint_t cells [] = { 1,1,1 };
   const uint_t blockCount [] = { 1, 1,1 };
   const uint_t nrOfTimeSteps = 20;

   // Create BlockForest
   auto blocks = blockforest::createUniformBlockGrid(blockCount[0],blockCount[1],blockCount[2],  //blocks
                                                     cells[0],cells[1],cells[2], //cells
                                                     1,                          //dx
                                                     false,                      //one block per process
                                                     true,true,true);            //periodicity


   // In addition to the normal GhostLayerField's  we allocated additionally a field containing the whole global simulation domain for each block
   // we can then check if the GhostLayer communication is correct, by comparing the small field to the corresponding part of the big field
   BlockDataID pdfField     = field::addToStorage<PdfField>( blocks, "Src" );

   // Init src field with some values
   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) // block loop
   {
      // Init PDF field
      PdfField * src = blockIt->getData<PdfField>(pdfField);
      for( auto cellIt = src->beginWithGhostLayerXYZ(); cellIt != src->end(); ++cellIt ) // over all x,y,z,f
      {
         for( auto d = stencil::D3Q19::begin(); d != stencil::D3Q19::end(); ++d )
            cellIt.getF( d.toIdx() ) = real_c( d.toIdx() );
      }

   }

   // Create TimeLoop
   SweepTimeloop timeloop (blocks, nrOfTimeSteps );

   GUI gui (timeloop, blocks, argc, argv);
   gui.run();
   //timeloop.singleStep();
   return EXIT_SUCCESS;
}
Exemple #15
0
/*******************************************************************************************************************//**
 * \brief   Query if n is prime.
 *
 * \param   n  The number to be checked.
 *
 * \return  true if n is prime, false if not.
 **********************************************************************************************************************/
bool isPrime( const uint_t n )
{
   switch(n)
   {
   case 0:
   case 1:
      return false;
   case 2:
      return true;
   default:
      if( n % 2 == 0 )
         return false;
      uint_t sqrtN = uint_c( std::sqrt( real_c(n) ) );
      for( uint_t i = 3; i <= sqrtN; i += 2 )
         if( n % i == 0 )
            return false;
      break;
   }

   return true;
}
Exemple #16
0
   /*! Collects the result of the PerformanceMeter from all MPI_Processes
    *
    * \param targetRank  MPI world rank of the process where the PerformanceMeter is reduced to
    *                    on all other processes a null pointer is returned. If targetRank < 0 all processes
    *                    have a valid result
    *
    * \return  a map of measurement-name to measurement value if (worldRank == targetRank ) || targetRank < 0
    *          and a null pointer otherwise
    *******************************************************************************************************************/
   shared_ptr< std::map<std::string, real_t> > PerformanceMeter::getReduced ( int targetRank )
   {
      typedef std::map<std::string, real_t> ResultMap;
      shared_ptr < ResultMap > res;

      std::vector<real_t> totalNrCells;
      reduce( totalNrCells, targetRank );

      if ( MPIManager::instance()->worldRank() == targetRank || targetRank < 0 )
      {
         res = make_shared< ResultMap > ();

         for( uint_t i = 0; i < measurements_.size(); ++i )
         {
            real_t value = totalNrCells[i] / real_c( timer_.average() );
            value *= measurements_[i].scaling;
            (*res) [ measurements_[i].name ] = value;
         }
      }

      return res;
   }
   void checkSphWallLubricationForce()
   {
      const uint_t timestep (timeloop_->getCurrentTimeStep()+1);

      // variables for output of total force - requires MPI-reduction
      pe::Vec3 forceSphr1(0);

      // temporary variables
      real_t gap        (0);

      for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
      {
         for( auto curSphereIt = pe::BodyIterator::begin<pe::Sphere>( *blockIt, bodyStorageID_); curSphereIt != pe::BodyIterator::end<pe::Sphere>(); ++curSphereIt )
         {
            pe::SphereID sphereI = ( curSphereIt.getBodyID() );
            if ( sphereI->getID() == id1_ )
            {
               for( auto globalBodyIt = globalBodyStorage_->begin(); globalBodyIt != globalBodyStorage_->end(); ++globalBodyIt)
               {
                  if( globalBodyIt->getID() == id2_ )
                  {
                     pe::PlaneID planeJ = static_cast<pe::PlaneID>( globalBodyIt.getBodyID() );
                     gap = pe::getSurfaceDistance(sphereI, planeJ);
                     break;
                  }
               }
               break;
            }
         }
      }

      WALBERLA_MPI_SECTION()
      {
         mpi::reduceInplace( gap, mpi::MAX );
      }
      WALBERLA_ROOT_SECTION()
      {
         if (gap < real_comparison::Epsilon<real_t>::value )
         {
            gap = walberla::math::Limits<real_t>::inf();
            WALBERLA_LOG_INFO_ON_ROOT( "Sphere still too far from wall to calculate gap!" );
         } else {
            WALBERLA_LOG_INFO_ON_ROOT( "Gap between sphere and wall: " << gap );
         }
      }

      // get force on sphere
      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 )
         {
            if( bodyIt->getID() == id1_ )
            {
               forceSphr1 += bodyIt->getForce();
            }
         }
      }

      // MPI reduction of pe forces over all processes
      WALBERLA_MPI_SECTION()
      {
         mpi::reduceInplace( forceSphr1[0], mpi::SUM );
         mpi::reduceInplace( forceSphr1[1], mpi::SUM );
         mpi::reduceInplace( forceSphr1[2], mpi::SUM );
      }
      WALBERLA_LOG_INFO_ON_ROOT("Total force on sphere " << id1_ << " : " << forceSphr1);

      if ( print_ )
      {
         WALBERLA_ROOT_SECTION()
         {
            std::ofstream file1;
            std::string filename1("Gap_LubricationForceBody1.txt");
            file1.open( filename1.c_str(), std::ofstream::app );
            file1.setf( std::ios::unitbuf );
            file1.precision(15);
            file1 << gap << " " << forceSphr1[0] << std::endl;
            file1.close();
         }
      }

      WALBERLA_ROOT_SECTION()
      {
         if ( timestep == uint_t(26399) )
         {
            // according to the formula from Ding & Aidun 2003
            // F = 6 * M_PI * rho_L * nu_L * relative velocity of both bodies=relative velocity of the sphere * r * r * 1/gap
            // the correct analytically calculated value is 339.292006996217
            // in this geometry setup the relative error is 0.183515322065561 %
            real_t analytical = real_c(6.0) * walberla::math::M_PI * real_c(1.0) * nu_L_ * real_c(-vel_[0]) * radius_ * radius_ * real_c(1.0)/gap;
            real_t relErr     = std::fabs( analytical - forceSphr1[0] ) / analytical * real_c(100.0);
            WALBERLA_CHECK_LESS( relErr, real_t(1) );
         }
      }

   }
   void checkSphSphLubricationForce()
   {
      const uint_t timestep (timeloop_->getCurrentTimeStep()+1);

      // variables for output of total force - requires MPI-reduction
      pe::Vec3 forceSphr1(0);
      pe::Vec3 forceSphr2(0);

      // temporary variables
      real_t gap        (0);

      // calculate gap between the two spheres
      for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt )
      {
         for( auto curSphereIt = pe::BodyIterator::begin<pe::Sphere>( *blockIt, bodyStorageID_); curSphereIt != pe::BodyIterator::end<pe::Sphere>(); ++curSphereIt )
         {
            pe::SphereID sphereI = ( curSphereIt.getBodyID() );
            if ( sphereI->getID() == id1_ )
            {
               for( auto blockIt2 = blocks_->begin(); blockIt2 != blocks_->end(); ++blockIt2 )
               {
                  for( auto oSphereIt = pe::BodyIterator::begin<pe::Sphere>( *blockIt2, bodyStorageID_); oSphereIt != pe::BodyIterator::end<pe::Sphere>(); ++oSphereIt )
                  {
                     pe::SphereID sphereJ = ( oSphereIt.getBodyID() );
                     if ( sphereJ->getID() == id2_ )
                     {
                        gap = pe::getSurfaceDistance( sphereI, sphereJ );
                        break;
                     }
                  }
               }
               break;
            }
         }
      }

      WALBERLA_MPI_SECTION()
      {
         mpi::reduceInplace( gap, mpi::MAX );
      }
      WALBERLA_ROOT_SECTION()
      {
         if (gap < real_comparison::Epsilon<real_t>::value )
         {
            // shadow copies have not been synced yet as the spheres are outside the overlap region
            gap = walberla::math::Limits<real_t>::inf();
            WALBERLA_LOG_INFO_ON_ROOT( "Spheres still too far apart to calculate gap!" );
         } else {
            WALBERLA_LOG_INFO_ON_ROOT( "Gap between sphere 1 and 2: " << gap );
         }
      }


      // get force on spheres
      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 )
         {
            if( bodyIt->getID() == id1_ )
            {
               forceSphr1 += bodyIt->getForce();
            } else if( bodyIt->getID() == id2_ )
            {
               forceSphr2 += bodyIt->getForce();
            }
         }
      }

      // MPI reduction of pe forces over all processes
      WALBERLA_MPI_SECTION()
      {
         mpi::reduceInplace( forceSphr1[0], mpi::SUM );
         mpi::reduceInplace( forceSphr1[1], mpi::SUM );
         mpi::reduceInplace( forceSphr1[2], mpi::SUM );
         mpi::reduceInplace( forceSphr2[0], mpi::SUM );
         mpi::reduceInplace( forceSphr2[1], mpi::SUM );
         mpi::reduceInplace( forceSphr2[2], mpi::SUM );
      }
      WALBERLA_LOG_INFO_ON_ROOT("Total force on sphere " << id1_ << " : " << forceSphr1);
      WALBERLA_LOG_INFO_ON_ROOT("Total force on sphere " << id2_ << " : " << forceSphr2);

      if ( print_ )
      {
         WALBERLA_ROOT_SECTION()
         {
            std::ofstream file1;
            std::string filename1("Gap_LubricationForceBody1.txt");
            file1.open( filename1.c_str(), std::ofstream::app );
            file1.setf( std::ios::unitbuf );
            file1.precision(15);
            file1 << gap << " " << forceSphr1[0] << std::endl;
            file1.close();

            std::ofstream file2;
            std::string filename2("Gap_LubricationForceBody2.txt");
            file2.open( filename2.c_str(), std::ofstream::app );
            file2.setf( std::ios::unitbuf );
            file2.precision(15);
            file2 << gap << " " << forceSphr2[0] << std::endl;
            file2.close();
         }
      }


      WALBERLA_ROOT_SECTION()
      {
         if ( timestep == uint_t(1000) )
         {
            // both force x-components should be the same only with inverted signs
            WALBERLA_CHECK_FLOAT_EQUAL ( forceSphr2[0], -forceSphr1[0] );

            // according to the formula from Ding & Aidun 2003
            // F = 3/2 * M_PI * rho_L * nu_L * relative velocity of both spheres * r * r * 1/gap
            // the correct analytically calculated value is 339.2920063998
            // in this geometry setup the relative error is 0.1246489711 %
            real_t analytical = real_c(3.0)/real_c(2.0) * walberla::math::M_PI * real_c(1.0) * nu_L_ * real_c(2.0) * real_c(vel_[0]) * radius_ * radius_ * real_c(1.0)/gap;
            real_t relErr     = std::fabs( analytical - forceSphr2[0] ) / analytical * real_c(100.0);
            WALBERLA_CHECK_LESS( relErr, real_t(1) );
         }
      }
   }
static void test() {

   for( uint_t i = 0; i < 5; ++i ) {

      SetupBlockForest forest;

      forest.addRefinementSelectionFunction( refinementSelectionFunctionAll );

      real_t xmin = math::realRandom( real_c(-100), real_c(100) );
      real_t xmax = math::realRandom( xmin + real_c(10), real_c(120) );
      real_t ymin = math::realRandom( real_c(-100), real_c(100) );
      real_t ymax = math::realRandom( ymin + real_c(10), real_c(120) );
      real_t zmin = math::realRandom( real_c(-100), real_c(100) );
      real_t zmax = math::realRandom( zmin + real_c(10), real_c(120) );

      AABB domain( xmin, ymin, zmin, xmax, ymax, zmax );
      forest.init( domain, math::intRandom( uint_t(5), uint_t(20) ), math::intRandom( uint_t(5), uint_t(20) ), math::intRandom( uint_t(5), uint_t(20) ),
                           math::boolRandom(), math::boolRandom(), math::boolRandom() );

      checkNeighborhoodConsistency( forest );
      checkCollectorConsistency( forest );
   }

   for( uint_t i = 0; i < 5; ++i ) {

      SetupBlockForest forest;

      forest.addRefinementSelectionFunction( refinementSelectionFunctionRandom );

      real_t xmin = math::realRandom( real_c(-100), real_c(100) );
      real_t xmax = math::realRandom( xmin + real_c(10), real_c(120) );
      real_t ymin = math::realRandom( real_c(-100), real_c(100) );
      real_t ymax = math::realRandom( ymin + real_c(10), real_c(120) );
      real_t zmin = math::realRandom( real_c(-100), real_c(100) );
      real_t zmax = math::realRandom( zmin + real_c(10), real_c(120) );

      AABB domain( xmin, ymin, zmin, xmax, ymax, zmax );
      forest.init( domain, math::intRandom( uint_t(5), uint_t(20) ), math::intRandom( uint_t(5), uint_t(20) ), math::intRandom( uint_t(5), uint_t(20) ),
                           math::boolRandom(), math::boolRandom(), math::boolRandom() );

      checkNeighborhoodConsistency( forest );
      checkCollectorConsistency( forest );
   }
}
void test( const std::string & meshFile, const uint_t numProcesses, const uint_t numTotalBlocks )
{
   auto mesh = make_shared<MeshType>();
   mesh::readAndBroadcast( meshFile, *mesh);

   auto aabb = computeAABB( *mesh );

   auto domainAABB = aabb.getScaled( typename MeshType::Scalar(3) );

   auto triDist = make_shared< mesh::TriangleDistance<MeshType> >( mesh );
   auto distanceOctree = make_shared< DistanceOctree< MeshType > >( triDist );

   const real_t meshVolume  = real_c( computeVolume( *mesh ) );
   const real_t blockVolume = meshVolume / real_c( numTotalBlocks );
   static const real_t cellsPersBlock = real_t(1000);
   const real_t cellVolume = blockVolume / cellsPersBlock;
   const Vector3<real_t> cellSize( std::pow( cellVolume, real_t(1) / real_t(3) ) );

   ComplexGeometryStructuredBlockforestCreator bfc( domainAABB, cellSize, makeExcludeMeshInterior( distanceOctree, cellSize.min() ) );
   auto wl = mesh::makeMeshWorkloadMemory( distanceOctree, cellSize );
   wl.setInsideCellWorkload(0);
   wl.setOutsideCellWorkload(1);
   wl.setForceZeroMemoryOnZeroWorkload(true);
   bfc.setWorkloadMemorySUIDAssignmentFunction( wl );
   bfc.setRefinementSelectionFunction( makeRefinementSelection( distanceOctree, 5, cellSize[0], cellSize[0] * real_t(5) ) );

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with StaticLevelwiseCurveBalanceWeighted Partitioner" );
   bfc.setTargetProcessAssignmentFunction( blockforest::StaticLevelwiseCurveBalanceWeighted() );
   auto sbf_default = bfc.createSetupBlockForest( Vector3<uint_t>(64,64,64), numProcesses );
   //sbf_default->writeVTKOutput("sbf_default");
   WALBERLA_LOG_INFO_ON_ROOT( sbf_default->toString() );

   return;
#ifdef WALBERLA_BUILD_WITH_PARMETIS

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with ParMetis (PART_KWAY, no commweights)" );
   bfc.setTargetProcessAssignmentFunction( blockforest::StaticLevelwiseParMetis( blockforest::StaticLevelwiseParMetis::PARMETIS_PART_KWAY ) );
   auto sbf = bfc.createSetupBlockForest( numTotalBlocks, numProcesses );
   //sbf->writeVTKOutput("sbf");
   WALBERLA_LOG_INFO_ON_ROOT( sbf->toString() );


   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with ParMetis (PART_KWAY, commweights)" );
   bfc.setTargetProcessAssignmentFunction( blockforest::StaticLevelwiseParMetis( commInXDirection, blockforest::StaticLevelwiseParMetis::PARMETIS_PART_KWAY ) );
   auto sbf_edge = bfc.createSetupBlockForest( numTotalBlocks, numProcesses );
   //sbf_edge->writeVTKOutput("sbf_edge");
   WALBERLA_LOG_INFO_ON_ROOT( sbf_edge->toString() );

   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with ParMetis (PART_GEOM_KWAY, no commweights)" );
   bfc.setTargetProcessAssignmentFunction( blockforest::StaticLevelwiseParMetis( blockforest::StaticLevelwiseParMetis::PARMETIS_PART_GEOM_KWAY ) );
   auto sbf_geom = bfc.createSetupBlockForest( numTotalBlocks, numProcesses );
   //sbf_geom->writeVTKOutput("sbf_geom");
   WALBERLA_LOG_INFO_ON_ROOT( sbf_geom->toString() );


   WALBERLA_LOG_INFO_ON_ROOT( "Creating SBF with ParMetis (PART_GEOM_KWAY, commweights)" );
   bfc.setTargetProcessAssignmentFunction( blockforest::StaticLevelwiseParMetis( commInXDirection, blockforest::StaticLevelwiseParMetis::PARMETIS_PART_GEOM_KWAY ) );
   auto sbf_geom_edge = bfc.createSetupBlockForest( numTotalBlocks, numProcesses );
   //sbf_geom_edge->writeVTKOutput("sbf_geom_edge");
   WALBERLA_LOG_INFO_ON_ROOT( sbf_geom_edge->toString() );

#endif
}
Exemple #21
0
int main(int argc, char **argv )
{
   walberla::Environment env( argc, argv );

   const uint_t cells [] = { 6,5,3 };
   const uint_t blockCount [] = { 4, 3,2 };
   const uint_t nrOfTimeSteps = 20;

   // Create BlockForest
   auto blocks = blockforest::createUniformBlockGrid(blockCount[0],blockCount[1],blockCount[2],  //blocks
                                        cells[0],cells[1],cells[2], //cells
                                        1,                          //dx
                                        false,                      //one block per process
                                        true,true,true);            //periodicity

   LatticeModel latticeModel( lbm::collision_model::SRT(1.5 ) );

   // In addition to the normal GhostLayerField's  we allocated additionally a field containing the whole global simulation domain for each block
   // we can then check if the GhostLayer communication is correct, by comparing the small field to the corresponding part of the big field

   BlockDataID pdfField     = lbm::addPdfFieldToStorage( blocks, "PdfField", latticeModel );

   BlockDataID scalarField1 = field::addToStorage<ScalarField>( blocks, "ScalarFieldOneGl", real_t(0), field::zyxf,  1 );
   BlockDataID scalarField2 = field::addToStorage<ScalarField>( blocks, "ScalarFieldTwoGl", real_t(0), field::zyxf,  2 );
   BlockDataID vectorField  = field::addToStorage<VectorField>( blocks, "VectorField", Vector3<real_t>(0,0,0) );
   BlockDataID flagField    = field::addFlagFieldToStorage<FField>( blocks, "FlagField" );


   // Init src field with some values
   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) // block loop
   {
      // Init PDF field
      PdfField * src = blockIt->getData<PdfField>(pdfField);
      for( auto cellIt = src->begin(); cellIt != src->end(); ++cellIt ) // over all x,y,z,f
      {
         Cell cell ( cellIt.x(), cellIt.y(), cellIt.z() );
         blocks->transformBlockLocalToGlobalCell(cell, *blockIt);
         *cellIt = real_c( ( cell[0] + cell[1] + cell[2] + cellIt.f() ) % cell_idx_t(42) );
      }

      // Init scalarField1
      ScalarField * sf = blockIt->getData<ScalarField> ( scalarField1 );
      for( auto cellIt = sf->beginWithGhostLayer(); cellIt != sf->end(); ++cellIt ) // over all x,y,z
      {
         Cell cell ( cellIt.x(), cellIt.y(), cellIt.z() );
         blocks->transformBlockLocalToGlobalCell(cell, *blockIt);
         *cellIt = real_c( ( cell[0] + cell[1] + cell[2] ) % cell_idx_t(42) );
      }

      // Init scalarField2
      sf = blockIt->getData<ScalarField> ( scalarField2 );
      for( auto cellIt = sf->beginWithGhostLayer(); cellIt != sf->end(); ++cellIt ) // over all x,y,z
      {
         Cell cell ( cellIt.x(), cellIt.y(), cellIt.z() );
         blocks->transformBlockLocalToGlobalCell(cell, *blockIt);
         *cellIt = real_c( ( cell[0] + cell[1] + cell[2] ) % cell_idx_t(42) );
      }

      // Init vector field
      VectorField * vf = blockIt->getData<VectorField> ( vectorField );
      for ( auto cellIt = vf->beginWithGhostLayer(); cellIt != vf->end(); ++cellIt )
      {
         Cell cell ( cellIt.x(), cellIt.y(), cellIt.z() );
         blocks->transformBlockLocalToGlobalCell(cell, *blockIt);
         *cellIt = Vector3<real_t>( real_c(cell[0]), real_c(cell[1]), real_c(cell[2]) );
      }

      // Init Flag field
      FField * ff = blockIt->getData<FField> ( flagField );
      auto flag1 = ff->registerFlag( "AFlag 1" );
      auto flag2 = ff->registerFlag( "BFlag 2" );
      for ( auto cellIt = ff->beginWithGhostLayer(); cellIt != ff->end(); ++cellIt )
      {
         Cell cell ( cellIt.x(), cellIt.y(), cellIt.z() );
         blocks->transformBlockLocalToGlobalCell( cell, *blockIt );
         if ( ( cell[0] + cell[1] + cell[2] ) % 2 )
            addFlag( cellIt, flag1);
         else
            addFlag( cellIt, flag2);
      }

   }

   // Create TimeLoop
   SweepTimeloop timeloop (blocks, nrOfTimeSteps );

   GUI gui (timeloop, blocks, argc, argv);
   lbm::connectToGui<LatticeModel>( gui );
   gui.run();
   //timeloop.singleStep();
   return EXIT_SUCCESS;
}
void ExprSystemInitFunction::setGlobalCell( const Cell & cell )
{
   cell_[0] = real_c( cell.x() );
   cell_[1] = real_c( cell.y() );
   cell_[2] = real_c( cell.z() );
}
Exemple #23
0
void BKE_simulate_ocean(struct Ocean *o, float t, float scale, float chop_amount)
{
	int i, j;

	scale *= o->normalize_factor;

	BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);

	/* compute a new htilda */
#pragma omp parallel for private(i, j)
	for (i = 0; i < o->_M; ++i) {
		/* note the <= _N/2 here, see the fftw doco about the mechanics of the complex->real fft storage */
		for (j = 0; j <= o->_N / 2; ++j) {
			fftw_complex exp_param1;
			fftw_complex exp_param2;
			fftw_complex conj_param;


			init_complex(exp_param1, 0.0, omega(o->_k[i * (1 + o->_N / 2) + j], o->_depth) * t);
			init_complex(exp_param2, 0.0, -omega(o->_k[i * (1 + o->_N / 2) + j], o->_depth) * t);
			exp_complex(exp_param1, exp_param1);
			exp_complex(exp_param2, exp_param2);
			conj_complex(conj_param, o->_h0_minus[i * o->_N + j]);

			mul_complex_c(exp_param1, o->_h0[i * o->_N + j], exp_param1);
			mul_complex_c(exp_param2, conj_param, exp_param2);

			add_comlex_c(o->_htilda[i * (1 + o->_N / 2) + j], exp_param1, exp_param2);
			mul_complex_f(o->_fft_in[i * (1 + o->_N / 2) + j], o->_htilda[i * (1 + o->_N / 2) + j], scale);
		}
	}

#pragma omp parallel sections private(i, j)
	{

#pragma omp section
		{
			if (o->_do_disp_y) {
				/* y displacement */
				fftw_execute(o->_disp_y_plan);
			}
		} /* section 1 */

#pragma omp section
		{
			if (o->_do_chop) {
				/* x displacement */
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;
						fftw_complex minus_i;

						init_complex(minus_i, 0.0, -1.0);
						init_complex(mul_param, -scale, 0);
						mul_complex_f(mul_param, mul_param, chop_amount);
						mul_complex_c(mul_param, mul_param, minus_i);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param,
						              ((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
						               0.0f :
						               o->_kx[i] / o->_k[i * (1 + o->_N / 2) + j]));
						init_complex(o->_fft_in_x[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_disp_x_plan);
			}
		} /* section 2 */

#pragma omp section
		{
			if (o->_do_chop) {
				/* z displacement */
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;
						fftw_complex minus_i;

						init_complex(minus_i, 0.0, -1.0);
						init_complex(mul_param, -scale, 0);
						mul_complex_f(mul_param, mul_param, chop_amount);
						mul_complex_c(mul_param, mul_param, minus_i);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param,
						              ((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
						               0.0f :
						               o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
						init_complex(o->_fft_in_z[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_disp_z_plan);
			}
		} /* section 3 */

#pragma omp section
		{
			if (o->_do_jacobian) {
				/* Jxx */
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;

						/* init_complex(mul_param, -scale, 0); */
						init_complex(mul_param, -1, 0);

						mul_complex_f(mul_param, mul_param, chop_amount);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param,
						              ((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
						               0.0f :
						               o->_kx[i] * o->_kx[i] / o->_k[i * (1 + o->_N / 2) + j]));
						init_complex(o->_fft_in_jxx[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_Jxx_plan);

				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j < o->_N; ++j) {
						o->_Jxx[i * o->_N + j] += 1.0;
					}
				}
			}
		} /* section 4 */

#pragma omp section
		{
			if (o->_do_jacobian) {
				/* Jzz */
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;

						/* init_complex(mul_param, -scale, 0); */
						init_complex(mul_param, -1, 0);

						mul_complex_f(mul_param, mul_param, chop_amount);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param,
						              ((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
						               0.0f :
						               o->_kz[j] * o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
						init_complex(o->_fft_in_jzz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_Jzz_plan);
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j < o->_N; ++j) {
						o->_Jzz[i * o->_N + j] += 1.0;
					}
				}
			}
		} /* section 5 */

#pragma omp section
		{
			if (o->_do_jacobian) {
				/* Jxz */
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;

						/* init_complex(mul_param, -scale, 0); */
						init_complex(mul_param, -1, 0);

						mul_complex_f(mul_param, mul_param, chop_amount);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param,
						              ((o->_k[i * (1 + o->_N / 2) + j] == 0.0f) ?
						               0.0f :
						               o->_kx[i] * o->_kz[j] / o->_k[i * (1 + o->_N / 2) + j]));
						init_complex(o->_fft_in_jxz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_Jxz_plan);
			}
		} /* section 6 */

#pragma omp section
		{
			/* fft normals */
			if (o->_do_normals) {
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;

						init_complex(mul_param, 0.0, -1.0);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param, o->_kx[i]);
						init_complex(o->_fft_in_nx[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_N_x_plan);

			}
		} /* section 7 */

#pragma omp section
		{
			if (o->_do_normals) {
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j <= o->_N / 2; ++j) {
						fftw_complex mul_param;

						init_complex(mul_param, 0.0, -1.0);
						mul_complex_c(mul_param, mul_param, o->_htilda[i * (1 + o->_N / 2) + j]);
						mul_complex_f(mul_param, mul_param, o->_kz[i]);
						init_complex(o->_fft_in_nz[i * (1 + o->_N / 2) + j], real_c(mul_param), image_c(mul_param));
					}
				}
				fftw_execute(o->_N_z_plan);

#if 0
				for (i = 0; i < o->_M; ++i) {
					for (j = 0; j < o->_N; ++j) {
						o->_N_y[i * o->_N + j] = 1.0f / scale;
					}
				}
				(MEM01)
#endif
				o->_N_y = 1.0f / scale;
			}
		} /* section 8 */

	} /* omp sections */

	BLI_rw_mutex_unlock(&o->oceanmutex);
}
Exemple #24
0
void TimeStep::operator()()
{
   if( numberOfSubIterations_ == 1 )
   {
      forceEvaluationFunc_();

      collisionResponse_.timestep( timeStepSize_ );
      synchronizeFunc_();
   }
   else
   {
      // during the intermediate time steps of the collision response, the currently acting forces
      // (interaction forces, gravitational force, ...) have to remain constant.
      // Since they are reset after the call to collision response, they have to be stored explicitly before.
      // Then they are set again after each intermediate step.

      // generate map from all known bodies (process local) to total forces/torques
      // this has to be done on a block-local basis, since the same body could reside on several blocks from this process
      using BlockID_T = domain_decomposition::IBlockID::IDType;
      std::map< BlockID_T, std::map< walberla::id_t, std::array< real_t, 6 > > > forceTorqueMap;

      for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
      {
         BlockID_T blockID = blockIt->getId().getID();
         auto& blockLocalForceTorqueMap = forceTorqueMap[blockID];

         // iterate over local and remote bodies and store force/torque in map
         for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
         {
            auto & f = blockLocalForceTorqueMap[ bodyIt->getSystemID() ];

            const auto & force = bodyIt->getForce();
            const auto & torque = bodyIt->getTorque();

            f = {{force[0], force[1], force[2], torque[0], torque[1], torque[2] }};
         }
      }

      // perform pe time steps
      const real_t subTimeStepSize = timeStepSize_ / real_c( numberOfSubIterations_ );
      for( uint_t i = 0; i != numberOfSubIterations_; ++i )
      {

         // in the first iteration, forces are already set
         if( i != 0 )
         {
            for( auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt )
            {
               BlockID_T blockID = blockIt->getId().getID();
               auto& blockLocalForceTorqueMap = forceTorqueMap[blockID];

               // re-set stored force/torque on bodies
               for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt )
               {

                  const auto f = blockLocalForceTorqueMap.find( bodyIt->getSystemID() );

                  if( f != blockLocalForceTorqueMap.end() )
                  {
                     const auto & ftValues = f->second;
                     bodyIt->addForce ( ftValues[0], ftValues[1], ftValues[2] );
                     bodyIt->addTorque( ftValues[3], ftValues[4], ftValues[5] );
                  }
               }
            }
         }

         // evaluate forces (e.g. lubrication forces)
         forceEvaluationFunc_();

         collisionResponse_.timestep( subTimeStepSize );
         synchronizeFunc_();
      }
   }
}