From 319a6ae20429226ac7d058791bf4e0c4fb767eca Mon Sep 17 00:00:00 2001
From: Christoph Rettinger <christoph.rettinger@fau.de>
Date: Thu, 8 Apr 2021 13:12:19 +0200
Subject: [PATCH] Reworked mesa_pd contact test case

---
 tests/mesa_pd/ContactDetection.cpp | 100 ++++++++++++++++++-----------
 1 file changed, 61 insertions(+), 39 deletions(-)

diff --git a/tests/mesa_pd/ContactDetection.cpp b/tests/mesa_pd/ContactDetection.cpp
index 08bf8eaa3..94d6c7575 100644
--- a/tests/mesa_pd/ContactDetection.cpp
+++ b/tests/mesa_pd/ContactDetection.cpp
@@ -15,13 +15,11 @@
 //
 //! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
 
-#include <mesa_pd/vtk/ParticleVtkOutput.h>
-
 #include <mesa_pd/collision_detection/AnalyticContactDetection.h>
-#include <mesa_pd/collision_detection/GeneralContactDetection.h>
 #include <mesa_pd/data/LinkedCells.h>
 #include <mesa_pd/data/ParticleAccessor.h>
 #include <mesa_pd/data/ParticleStorage.h>
@@ -31,10 +29,8 @@
 #include <mesa_pd/kernel/InsertParticleIntoLinkedCells.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
 #include <mesa_pd/mpi/ContactFilter.h>
-#include <mesa_pd/mpi/notifications/ForceTorqueNotification.h>
 #include <mesa_pd/mpi/SyncNextNeighbors.h>
 
-#include <blockforest/BlockForest.h>
 #include <blockforest/Initialization.h>
 #include <core/Abort.h>
 #include <core/Environment.h>
@@ -42,9 +38,7 @@
 #include <core/mpi/Reduce.h>
 #include <core/grid_generator/SCIterator.h>
 #include <core/logging/Logging.h>
-#include <core/waLBerlaBuildInfo.h>
 
-#include <functional>
 #include <memory>
 #include <string>
 
@@ -68,7 +62,13 @@ private:
    std::shared_ptr<data::ShapeStorage> ss_;
 };
 
-int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
+/*
+ * Tests linked cells
+ * Fully periodic, simple cubic sphere array is created
+ * Together with linked cells of size 2 * sphere spacing.
+ * This allows to analytically calculate how many particles should reside in each linked cell and how many contacts should be found and treated.
+ */
+int main( const int particlesPerAxisPerProcess = 2 )
 {
    using namespace walberla::timing;
 
@@ -80,15 +80,18 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
 
    math::seedRandomGenerator( static_cast<unsigned int>(1337 * walberla::mpi::MPIManager::instance()->worldRank()) );
 
+   const real_t radius = real_t(0.9);
    const real_t generationSpacing = 1_r;
+   const int linkedCellMultipleOfGenerationSpacing = 2;
+   WALBERLA_CHECK_EQUAL(particlesPerAxisPerProcess % linkedCellMultipleOfGenerationSpacing, 0, "Only a multiple of " << linkedCellMultipleOfGenerationSpacing << " is allowed");
 
    WALBERLA_LOG_INFO_ON_ROOT("*** BLOCKFOREST ***");
-   const int centerParticles  = particlesPerAxis * particlesPerAxis * particlesPerAxis;
-   const int faceParticles    = particlesPerAxis * particlesPerAxis * 6;
-   const int edgeParticles    = particlesPerAxis * 4 * 3;
+   const int centerParticles  = particlesPerAxisPerProcess * particlesPerAxisPerProcess * particlesPerAxisPerProcess;
+   const int faceParticles    = particlesPerAxisPerProcess * particlesPerAxisPerProcess * 6;
+   const int edgeParticles    = particlesPerAxisPerProcess * 4 * 3;
    const int cornerParticles  = 8;
 
-   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(particlesPerAxis);
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(particlesPerAxisPerProcess);
    WALBERLA_LOG_DEVEL_VAR_ON_ROOT(centerParticles);
    WALBERLA_LOG_DEVEL_VAR_ON_ROOT(faceParticles);
    WALBERLA_LOG_DEVEL_VAR_ON_ROOT(edgeParticles);
@@ -96,14 +99,16 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
 
    // create forest
    const real_t eps = real_c(0.01); //shift to make contact points unambigous
+   Vector3<uint_t>blocksPerDirection (2,2,2);
+   int numBlocks = int(blocksPerDirection[0] * blocksPerDirection[1] * blocksPerDirection[2]);
+   Vector3<bool> periodicity(true, true, true);
    auto forest = blockforest::createBlockForest( math::AABB(real_t(0),
                                                             real_t(0),
                                                             real_t(0),
-                                                            real_c(particlesPerAxis) * real_t(2),
-                                                            real_c(particlesPerAxis) * real_t(2),
-                                                            real_c(particlesPerAxis) * real_t(2)).getTranslated(Vec3(eps,eps,eps)),
-                                                 Vector3<uint_t>(2,2,2),
-                                                 Vector3<bool>(true, true, true) );
+                                                            real_c(particlesPerAxisPerProcess) * generationSpacing * real_c(blocksPerDirection[0]),
+                                                            real_c(particlesPerAxisPerProcess) * generationSpacing * real_c(blocksPerDirection[1]),
+                                                            real_c(particlesPerAxisPerProcess) * generationSpacing * real_c(blocksPerDirection[2])).getTranslated(Vec3(eps,eps,eps)),
+                                                 blocksPerDirection, periodicity);
    domain::BlockForestDomain domain(forest);
 
    auto localDomain = forest->begin()->getAABB();
@@ -115,8 +120,8 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
    auto ps = std::make_shared<data::ParticleStorage>(100);
    auto ss = std::make_shared<data::ShapeStorage>();
    ParticleAccessorWithShape accessor(ps, ss);
-   const real_t linkedCellSize = 2.1_r * radius;
-   data::LinkedCells         lc(localDomain.getExtended(linkedCellSize), linkedCellSize );
+   const real_t linkedCellSize = real_c(linkedCellMultipleOfGenerationSpacing) * generationSpacing;
+   data::LinkedCells lc(localDomain.getExtended(linkedCellSize), linkedCellSize );
 
    auto  smallSphere = ss->create<data::Sphere>( radius );
    ss->shapes[smallSphere]->updateMassAndInertia(real_t(2707));
@@ -134,7 +139,7 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
       }
    }
    int64_t numParticles = int64_c(ps->size());
-   walberla::mpi::reduceInplace(numParticles, walberla::mpi::SUM);
+   walberla::mpi::allReduceInplace(numParticles, walberla::mpi::SUM);
    WALBERLA_LOG_INFO_ON_ROOT("#particles created: " << numParticles);
 
    WALBERLA_LOG_INFO_ON_ROOT("*** SETUP - END ***");
@@ -147,14 +152,32 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
    // initial sync
    WALBERLA_CHECK_EQUAL(ps->size(), centerParticles);
    SNN(*ps, domain);
-   WALBERLA_CHECK_EQUAL(ps->size(), centerParticles + faceParticles + edgeParticles + cornerParticles);
+   int numKnownParticles = centerParticles + faceParticles + edgeParticles + cornerParticles;
+   WALBERLA_CHECK_EQUAL(ps->size(), numKnownParticles);
 
    lc.clear();
    ps->forEachParticle(true, kernel::SelectAll(), accessor, ipilc, accessor, lc);
 
-   WALBERLA_CHECK_EQUAL(lc.cells_.size(), centerParticles + faceParticles + edgeParticles + cornerParticles);
-   int cell0 = 0;
-   int cell1 = 0;
+   int interiorCellsPerDirection = particlesPerAxisPerProcess / linkedCellMultipleOfGenerationSpacing;
+
+   int numLinkedCellsPerProcess = (interiorCellsPerDirection+2) * (interiorCellsPerDirection+2) * (interiorCellsPerDirection+2);
+   WALBERLA_CHECK_EQUAL(lc.cells_.size(), numLinkedCellsPerProcess);
+
+   const int numLinkedCellsCenter = interiorCellsPerDirection * interiorCellsPerDirection * interiorCellsPerDirection;
+   const int numLinkedCellsFace = interiorCellsPerDirection * interiorCellsPerDirection * 6;
+   const int numLinkedCellsEdge = interiorCellsPerDirection * 12;
+   const int numLinkedCellsCorner = 8;
+
+   const int numParticlesPerCenterCell = centerParticles / numLinkedCellsCenter;
+   const int numParticlesPerFaceCell = faceParticles / numLinkedCellsFace;
+   const int numParticlesPerEdgeCell = edgeParticles / numLinkedCellsEdge;
+   const int numParticlesPerCornerCell = cornerParticles / numLinkedCellsCorner;
+
+   int cellsCenter = 0;
+   int cellsFace = 0;
+   int cellsEdge = 0;
+   int cellsCorner = 0;
+
    for (const auto& idx : lc.cells_)
    {
       int particleCounter = 0;
@@ -164,18 +187,18 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
          ++particleCounter;
          p_idx = ps->getNextParticle(uint_c(p_idx));
       }
-      if (particleCounter==0)
-      {
-         ++cell0;
-      } else if (particleCounter==1)
-      {
-         ++cell1;
-      } else {
-         WALBERLA_CHECK(false);
-      }
+
+      if (particleCounter == numParticlesPerCenterCell) ++cellsCenter;
+      else if (particleCounter == numParticlesPerFaceCell) ++cellsFace;
+      else if (particleCounter == numParticlesPerEdgeCell) ++cellsEdge;
+      else if (particleCounter == numParticlesPerCornerCell) ++cellsCorner;
+      else WALBERLA_CHECK(false, "Linked cell found with unexpected number of particles " << particleCounter);
+
    }
-   WALBERLA_CHECK_EQUAL(cell0, 0);
-   WALBERLA_CHECK_EQUAL(cell1, centerParticles + faceParticles + edgeParticles + cornerParticles); //ghost particles only at face, no ghost particle at edges and corners
+   WALBERLA_CHECK_EQUAL(cellsCenter, numLinkedCellsCenter);
+   WALBERLA_CHECK_EQUAL(cellsFace, numLinkedCellsFace);
+   WALBERLA_CHECK_EQUAL(cellsEdge, numLinkedCellsEdge);
+   WALBERLA_CHECK_EQUAL(cellsCorner, numLinkedCellsCorner);
 
    std::atomic<int64_t> contactsChecked (0);
    std::atomic<int64_t> contactsDetected(0);
@@ -202,9 +225,10 @@ int main( const int particlesPerAxis = 2, const real_t radius = real_t(0.9) )
 
    WALBERLA_LOG_DEVEL_ON_ROOT( "contacts checked/detected/treated: " << contactsChecked << " / " << contactsDetected << " / " << contactsTreated );
 
-   WALBERLA_CHECK_EQUAL(contactsChecked, (centerParticles*26 + faceParticles*17 + edgeParticles*11 + cornerParticles*7) / 2 );
+   WALBERLA_CHECK_LESS_EQUAL(contactsChecked, numKnownParticles * numKnownParticles / 2 ); // has to be better than n^2 checking
    WALBERLA_CHECK_EQUAL(contactsDetected, (centerParticles*26 + faceParticles*17 + edgeParticles*11 + cornerParticles*7) / 2 );
-   //TODO: ContactsTreated
+   const int totalNumberPairWiseContactsInDomain = 26 * int(numParticles) / 2; //fully-periodic simple cubic grid
+   WALBERLA_CHECK_EQUAL(contactsTreated, totalNumberPairWiseContactsInDomain / numBlocks);
 
    return EXIT_SUCCESS;
 }
@@ -217,8 +241,6 @@ int main( int argc, char* argv[] )
    walberla::Environment env(argc, argv);
    WALBERLA_UNUSED(env);
    walberla::mesa_pd::main( 2 );
-   walberla::mesa_pd::main( 3 );
    walberla::mesa_pd::main( 4 );
-   walberla::mesa_pd::main( 5 );
    return EXIT_SUCCESS;
 }
-- 
GitLab