From c779ff9f236829422c6b32b327af0a3640dc444c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Thu, 10 Oct 2024 17:52:35 +0200
Subject: [PATCH] Add tests for arbitrary number of ghost layers construction

---
 src/utils/GlobalVariableManager.hpp   |   3 +
 tests/CMakeLists.txt                  |   1 +
 tests/test_ConnectivityDispatcher.cpp | 214 ++++++++++++++++++++++++++
 3 files changed, 218 insertions(+)
 create mode 100644 tests/test_ConnectivityDispatcher.cpp

diff --git a/src/utils/GlobalVariableManager.hpp b/src/utils/GlobalVariableManager.hpp
index 762c7a0fa..8b7e0a339 100644
--- a/src/utils/GlobalVariableManager.hpp
+++ b/src/utils/GlobalVariableManager.hpp
@@ -10,6 +10,9 @@
 class GlobalVariableManager
 {
  private:
+  // Give some special access for testing
+  friend class NbGhostLayersTester;
+
   size_t m_connectivity_id = 0;
   size_t m_mesh_id         = 0;
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e2dff14cc..feac4de4a 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -161,6 +161,7 @@ add_executable (unit_tests
 add_executable (mpi_unit_tests
   mpi_test_main.cpp
   test_Connectivity.cpp
+  test_ConnectivityDispatcher.cpp
   test_DiscreteFunctionDPk.cpp
   test_DiscreteFunctionDPkVector.cpp
   test_DiscreteFunctionIntegrator.cpp
diff --git a/tests/test_ConnectivityDispatcher.cpp b/tests/test_ConnectivityDispatcher.cpp
new file mode 100644
index 000000000..abe07cc21
--- /dev/null
+++ b/tests/test_ConnectivityDispatcher.cpp
@@ -0,0 +1,214 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <mesh/CartesianMeshBuilder.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/GmshReader.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
+#include <utils/Messenger.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+#include <filesystem>
+
+// clazy:excludeall=non-pod-global-static
+
+class NbGhostLayersTester
+{
+ private:
+  const size_t m_original_number_of_ghost_layers;
+
+ public:
+  NbGhostLayersTester(const size_t number_of_ghost_layers)
+    : m_original_number_of_ghost_layers{GlobalVariableManager::instance().getNumberOfGhostLayers()}
+  {
+    GlobalVariableManager::instance().m_number_of_ghost_layers = number_of_ghost_layers;
+  }
+
+  ~NbGhostLayersTester()
+  {
+    GlobalVariableManager::instance().m_number_of_ghost_layers = m_original_number_of_ghost_layers;
+  }
+};
+
+TEST_CASE("ConnectivityDispatcher", "[mesh]")
+{
+  auto check_number_of_ghost_layers = [](const auto& connectivity, const size_t number_of_layers) {
+    // We assume that the specified number of layers can be built
+    // (there are enough non owned layer of cells in the connectivity)
+    const auto cell_is_owned = connectivity.cellIsOwned();
+
+    CellValue<size_t> cell_layer{connectivity};
+    cell_layer.fill(number_of_layers + 1);
+
+    NodeValue<size_t> node_layer{connectivity};
+    node_layer.fill(number_of_layers + 1);
+
+    auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+    auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+
+    parallel_for(
+      connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+        if (cell_is_owned[cell_id]) {
+          cell_layer[cell_id] = 0;
+        }
+      });
+
+    for (size_t i_layer = 0; i_layer < number_of_layers; ++i_layer) {
+      parallel_for(
+        connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) {
+          auto node_cell_list = node_to_cell_matrix[node_id];
+          size_t min_layer    = cell_layer[node_cell_list[0]];
+          for (size_t i_cell = 1; i_cell < node_cell_list.size(); ++i_cell) {
+            min_layer = std::min(min_layer, cell_layer[node_cell_list[i_cell]]);
+          }
+          if (min_layer < number_of_layers + 1) {
+            node_layer[node_id] = min_layer;
+          }
+        });
+
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          auto cell_node_list = cell_to_node_matrix[cell_id];
+          size_t min_layer    = node_layer[cell_node_list[0]];
+          size_t max_layer    = min_layer;
+          for (size_t i_node = 1; i_node < cell_node_list.size(); ++i_node) {
+            min_layer = std::min(min_layer, node_layer[cell_node_list[i_node]]);
+            max_layer = std::max(max_layer, node_layer[cell_node_list[i_node]]);
+          }
+          if ((min_layer != max_layer) or
+              ((min_layer < number_of_layers + 1) and (cell_layer[cell_id] == number_of_layers + 1))) {
+            cell_layer[cell_id] = min_layer + 1;
+          }
+        });
+    }
+
+    auto is_boundary_face    = connectivity.isBoundaryFace();
+    auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+
+    bool has_required_number_of_ghost_layers = true;
+    for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+      auto face_cell_list = face_to_cell_matrix[face_id];
+      if ((face_cell_list.size() == 1) and (not is_boundary_face[face_id])) {
+        const CellId face_cell_id = face_cell_list[0];
+        has_required_number_of_ghost_layers &= (cell_layer[face_cell_id] == number_of_layers);
+      }
+    }
+
+    REQUIRE(parallel::allReduceAnd(has_required_number_of_ghost_layers));
+    bool first_ghost_layer_is_1 = true;
+    for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+      auto face_cell_list = face_to_cell_matrix[face_id];
+      if (face_cell_list.size() == 2) {
+        const CellId face_cell0_id = face_cell_list[0];
+        const CellId face_cell1_id = face_cell_list[1];
+        if (cell_is_owned[face_cell0_id] xor cell_is_owned[face_cell1_id]) {
+          for (size_t i_cell = 0; i_cell < face_cell_list.size(); ++i_cell) {
+            const CellId face_cell_id = face_cell_list[i_cell];
+            if (not cell_is_owned[face_cell_id]) {
+              first_ghost_layer_is_1 &= (cell_layer[face_cell_id] == 1);
+            }
+          }
+        }
+      }
+    }
+
+    REQUIRE(parallel::allReduceAnd(first_ghost_layer_is_1));
+  };
+
+  SECTION("1 layer meshes")
+  {
+    for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
+      SECTION(named_mesh.name())
+      {
+        const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<1>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), 1);
+      }
+    }
+    for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
+      SECTION(named_mesh.name())
+      {
+        const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<2>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), 1);
+      }
+    }
+    for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
+      SECTION(named_mesh.name())
+      {
+        const std::shared_ptr p_mesh = named_mesh.mesh()->get<const Mesh<3>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), 1);
+      }
+    }
+  }
+
+  for (size_t nb_ghost_layers = 2; nb_ghost_layers < 5; ++nb_ghost_layers) {
+    std::stringstream os;
+    os << nb_ghost_layers << " layer meshes";
+
+    SECTION(os.str())
+    {
+      REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == 1);
+
+      NbGhostLayersTester nb_ghost_layers_tester(nb_ghost_layers);
+
+      REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == nb_ghost_layers);
+
+      SECTION("Cartesian 1D mesh")
+      {
+        auto cartesian_1d_mesh =
+          CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh();
+        const std::shared_ptr p_mesh = cartesian_1d_mesh->get<const Mesh<1>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+
+      SECTION("Cartesian 2D mesh")
+      {
+        auto cartesian_2d_mesh =
+          CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh();
+        const std::shared_ptr p_mesh = cartesian_2d_mesh->get<const Mesh<2>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+
+      SECTION("Cartesian 3D mesh")
+      {
+        auto cartesian_3d_mesh =
+          CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh();
+        const std::shared_ptr p_mesh = cartesian_3d_mesh->get<const Mesh<3>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+
+      SECTION("unordered 1d mesh")
+      {
+        const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("unordered-1d.msh");
+
+        auto mesh_v = GmshReader{filename}.mesh();
+
+        const std::shared_ptr p_mesh = mesh_v->get<const Mesh<1>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+
+      SECTION("hybrid 2d mesh")
+      {
+        const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-2d.msh");
+
+        auto mesh_v = GmshReader{filename}.mesh();
+
+        const std::shared_ptr p_mesh = mesh_v->get<const Mesh<2>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+
+      SECTION("hybrid 3d mesh")
+      {
+        const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-3d.msh");
+
+        auto mesh_v = GmshReader{filename}.mesh();
+
+        const std::shared_ptr p_mesh = mesh_v->get<const Mesh<3>>();
+        check_number_of_ghost_layers(p_mesh->connectivity(), nb_ghost_layers);
+      }
+    }
+
+    REQUIRE(GlobalVariableManager::instance().getNumberOfGhostLayers() == 1);
+  }
+}
-- 
GitLab