From 775a4038b53c7c0fe5077c003d11b6fd257d4f41 Mon Sep 17 00:00:00 2001
From: Stephane Del Pino <stephane.delpino44@gmail.com>
Date: Tue, 2 Jun 2020 09:50:53 +0200
Subject: [PATCH] Extract a part of GmshReader to MeshBuilderBase tool set

This contributes to #7
---
 src/mesh/CMakeLists.txt             |   3 +-
 src/mesh/ConnectivityDispatcher.hpp |   1 -
 src/mesh/GmshReader.cpp             | 505 +---------------------------
 src/mesh/GmshReader.hpp             |  27 +-
 src/mesh/MeshBuilderBase.cpp        | 336 ++++++++++++++++++
 src/mesh/MeshBuilderBase.hpp        | 230 +++++++++++++
 6 files changed, 575 insertions(+), 527 deletions(-)
 create mode 100644 src/mesh/MeshBuilderBase.cpp
 create mode 100644 src/mesh/MeshBuilderBase.hpp

diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt
index 73d686fb2..5baed9b1b 100644
--- a/src/mesh/CMakeLists.txt
+++ b/src/mesh/CMakeLists.txt
@@ -4,8 +4,9 @@ add_library(
   PugsMesh
   Connectivity.cpp
   ConnectivityComputer.cpp
-  GmshReader.cpp
   ConnectivityDispatcher.cpp
+  GmshReader.cpp
+  MeshBuilderBase.cpp
   SynchronizerManager.cpp)
 
 # Additional dependencies
diff --git a/src/mesh/ConnectivityDispatcher.hpp b/src/mesh/ConnectivityDispatcher.hpp
index 3de9c1c59..73ebc548d 100644
--- a/src/mesh/ConnectivityDispatcher.hpp
+++ b/src/mesh/ConnectivityDispatcher.hpp
@@ -3,7 +3,6 @@
 
 #include <mesh/ItemValue.hpp>
 #include <mesh/ItemValueUtils.hpp>
-#include <mesh/Mesh.hpp>
 
 #include <mesh/ConnectivityDescriptor.hpp>
 
diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index 629f32801..e99c04722 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -23,218 +23,6 @@
 #include <sstream>
 #include <unordered_map>
 
-template <int Dimension>
-void
-GmshReader::_dispatch()
-{
-  using ConnectivityType = Connectivity<Dimension>;
-  using Rd               = TinyVector<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
-
-  if (not m_mesh) {
-    ConnectivityDescriptor descriptor;
-    std::shared_ptr connectivity = ConnectivityType::build(descriptor);
-    NodeValue<Rd> xr;
-    m_mesh = std::make_shared<MeshType>(connectivity, xr);
-  }
-  const MeshType& mesh = static_cast<const MeshType&>(*m_mesh);
-
-  ConnectivityDispatcher<Dimension> dispatcher(mesh.connectivity());
-
-  std::shared_ptr dispatched_connectivity = dispatcher.dispatchedConnectivity();
-  NodeValue<Rd> dispatched_xr             = dispatcher.dispatch(mesh.xr());
-
-  m_mesh = std::make_shared<MeshType>(dispatched_connectivity, dispatched_xr);
-}
-
-template <size_t Dimension>
-class ConnectivityFace;
-
-template <>
-class ConnectivityFace<2>
-{
- public:
-  friend struct Hash;
-  struct Hash
-  {
-    size_t
-    operator()(const ConnectivityFace& f) const
-    {
-      size_t hash = 0;
-      hash ^= std::hash<unsigned int>()(f.m_node0_id);
-      hash ^= std::hash<unsigned int>()(f.m_node1_id) >> 1;
-      return hash;
-    }
-  };
-
- private:
-  const std::vector<int>& m_node_number_vector;
-
-  unsigned int m_node0_id;
-  unsigned int m_node1_id;
-
-  bool m_reversed;
-
- public:
-  std::vector<unsigned int>
-  nodeIdList() const
-  {
-    return {m_node0_id, m_node1_id};
-  }
-
-  bool
-  reversed() const
-  {
-    return m_reversed;
-  }
-
-  PUGS_INLINE
-  bool
-  operator==(const ConnectivityFace& f) const
-  {
-    return ((m_node0_id == f.m_node0_id) and (m_node1_id == f.m_node1_id));
-  }
-
-  PUGS_INLINE
-  bool
-  operator<(const ConnectivityFace& f) const
-  {
-    return ((m_node_number_vector[m_node0_id] < m_node_number_vector[f.m_node0_id]) or
-            ((m_node_number_vector[m_node0_id] == m_node_number_vector[f.m_node0_id]) and
-             (m_node_number_vector[m_node1_id] < m_node_number_vector[f.m_node1_id])));
-  }
-
-  PUGS_INLINE
-  ConnectivityFace(const std::vector<unsigned int>& node_id_list, const std::vector<int>& node_number_vector)
-    : m_node_number_vector(node_number_vector)
-  {
-    Assert(node_id_list.size() == 2);
-
-    if (m_node_number_vector[node_id_list[0]] < m_node_number_vector[node_id_list[1]]) {
-      m_node0_id = node_id_list[0];
-      m_node1_id = node_id_list[1];
-      m_reversed = false;
-    } else {
-      m_node0_id = node_id_list[1];
-      m_node1_id = node_id_list[0];
-      m_reversed = true;
-    }
-  }
-
-  PUGS_INLINE
-  ConnectivityFace(const ConnectivityFace&) = default;
-
-  PUGS_INLINE
-  ~ConnectivityFace() = default;
-};
-
-template <>
-class ConnectivityFace<3>
-{
- private:
-  friend class GmshReader;
-  friend struct Hash;
-  struct Hash
-  {
-    size_t
-    operator()(const ConnectivityFace& f) const
-    {
-      size_t hash = 0;
-      for (size_t i = 0; i < f.m_node_id_list.size(); ++i) {
-        hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i;
-      }
-      return hash;
-    }
-  };
-
- private:
-  bool m_reversed;
-  std::vector<NodeId::base_type> m_node_id_list;
-  const std::vector<int>& m_node_number_vector;
-
-  PUGS_INLINE
-  std::vector<unsigned int>
-  _sort(const std::vector<unsigned int>& node_list)
-  {
-    const auto min_id = std::min_element(node_list.begin(), node_list.end());
-    const int shift   = std::distance(node_list.begin(), min_id);
-
-    std::vector<unsigned int> rotated_node_list(node_list.size());
-    if (node_list[(shift + 1) % node_list.size()] > node_list[(shift + node_list.size() - 1) % node_list.size()]) {
-      for (size_t i = 0; i < node_list.size(); ++i) {
-        rotated_node_list[i] = node_list[(shift + node_list.size() - i) % node_list.size()];
-        m_reversed           = true;
-      }
-    } else {
-      for (size_t i = 0; i < node_list.size(); ++i) {
-        rotated_node_list[i] = node_list[(shift + i) % node_list.size()];
-      }
-    }
-
-    return rotated_node_list;
-  }
-
- public:
-  PUGS_INLINE
-  const bool&
-  reversed() const
-  {
-    return m_reversed;
-  }
-
-  PUGS_INLINE
-  const std::vector<unsigned int>&
-  nodeIdList() const
-  {
-    return m_node_id_list;
-  }
-
-  PUGS_INLINE
-  ConnectivityFace(const std::vector<unsigned int>& given_node_id_list, const std::vector<int>& node_number_vector)
-    : m_reversed(false), m_node_id_list(_sort(given_node_id_list)), m_node_number_vector(node_number_vector)
-  {
-    ;
-  }
-
- public:
-  bool
-  operator==(const ConnectivityFace& f) const
-  {
-    if (m_node_id_list.size() == f.nodeIdList().size()) {
-      for (size_t j = 0; j < m_node_id_list.size(); ++j) {
-        if (m_node_id_list[j] != f.nodeIdList()[j]) {
-          return false;
-        }
-      }
-      return true;
-    }
-    return false;
-  }
-
-  PUGS_INLINE
-  bool
-  operator<(const ConnectivityFace& f) const
-  {
-    const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size());
-    for (size_t i = 0; i < min_nb_nodes; ++i) {
-      if (m_node_id_list[i] < f.m_node_id_list[i])
-        return true;
-      if (m_node_id_list[i] != f.m_node_id_list[i])
-        return false;
-    }
-    return m_node_id_list.size() < f.m_node_id_list.size();
-  }
-
-  PUGS_INLINE
-  ConnectivityFace(const ConnectivityFace&) = default;
-
-  PUGS_INLINE
-  ConnectivityFace() = delete;
-
-  PUGS_INLINE
-  ~ConnectivityFace() = default;
-};
-
 GmshReader::GmshReader(const std::string& filename) : m_filename(filename)
 {
   if (parallel::rank() == 0) {
@@ -592,293 +380,6 @@ GmshReader::__readPhysicalNames2_2()
   }
 }
 
-template <size_t Dimension>
-void
-GmshReader::__computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor)
-{
-  static_assert((Dimension == 2) or (Dimension == 3), "Invalid dimension to compute cell-face connectivities");
-  using CellFaceInfo = std::tuple<CellId, unsigned short, bool>;
-  using Face         = ConnectivityFace<Dimension>;
-
-  const auto& node_number_vector = descriptor.node_number_vector;
-  Array<unsigned short> cell_nb_faces(descriptor.cell_to_node_vector.size());
-  std::map<Face, std::vector<CellFaceInfo>> face_cells_map;
-  for (CellId j = 0; j < descriptor.cell_to_node_vector.size(); ++j) {
-    const auto& cell_nodes = descriptor.cell_to_node_vector[j];
-
-    if constexpr (Dimension == 2) {
-      switch (descriptor.cell_type_vector[j]) {
-      case CellType::Triangle: {
-        cell_nb_faces[j] = 3;
-        // face 0
-        Face f0({cell_nodes[1], cell_nodes[2]}, node_number_vector);
-        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
-
-        // face 1
-        Face f1({cell_nodes[2], cell_nodes[0]}, node_number_vector);
-        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
-
-        // face 2
-        Face f2({cell_nodes[0], cell_nodes[1]}, node_number_vector);
-        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
-        break;
-      }
-      case CellType::Quadrangle: {
-        cell_nb_faces[j] = 4;
-        // face 0
-        Face f0({cell_nodes[0], cell_nodes[1]}, node_number_vector);
-        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
-
-        // face 1
-        Face f1({cell_nodes[1], cell_nodes[2]}, node_number_vector);
-        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
-
-        // face 2
-        Face f2({cell_nodes[2], cell_nodes[3]}, node_number_vector);
-        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
-
-        // face 3
-        Face f3({cell_nodes[3], cell_nodes[0]}, node_number_vector);
-        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
-        break;
-      }
-      default: {
-        std::ostringstream error_msg;
-        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 2";
-        throw UnexpectedError(error_msg.str());
-      }
-      }
-    } else if constexpr (Dimension == 3) {
-      switch (descriptor.cell_type_vector[j]) {
-      case CellType::Tetrahedron: {
-        cell_nb_faces[j] = 4;
-        // face 0
-        Face f0({cell_nodes[1], cell_nodes[2], cell_nodes[3]}, node_number_vector);
-        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
-
-        // face 1
-        Face f1({cell_nodes[0], cell_nodes[3], cell_nodes[2]}, node_number_vector);
-        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
-
-        // face 2
-        Face f2({cell_nodes[0], cell_nodes[1], cell_nodes[3]}, node_number_vector);
-        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
-
-        // face 3
-        Face f3({cell_nodes[0], cell_nodes[2], cell_nodes[1]}, node_number_vector);
-        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
-        break;
-      }
-      case CellType::Hexahedron: {
-        // face 0
-        Face f0({cell_nodes[3], cell_nodes[2], cell_nodes[1], cell_nodes[0]}, node_number_vector);
-        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
-
-        // face 1
-        Face f1({cell_nodes[4], cell_nodes[5], cell_nodes[6], cell_nodes[7]}, node_number_vector);
-        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
-
-        // face 2
-        Face f2({cell_nodes[0], cell_nodes[4], cell_nodes[7], cell_nodes[3]}, node_number_vector);
-        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
-
-        // face 3
-        Face f3({cell_nodes[1], cell_nodes[2], cell_nodes[6], cell_nodes[5]}, node_number_vector);
-        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
-
-        // face 4
-        Face f4({cell_nodes[0], cell_nodes[1], cell_nodes[5], cell_nodes[4]}, node_number_vector);
-        face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed()));
-
-        // face 5
-        Face f5({cell_nodes[3], cell_nodes[7], cell_nodes[6], cell_nodes[2]}, node_number_vector);
-        face_cells_map[f5].emplace_back(std::make_tuple(j, 5, f5.reversed()));
-
-        cell_nb_faces[j] = 6;
-        break;
-      }
-      default: {
-        std::ostringstream error_msg;
-        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 3";
-        throw UnexpectedError(error_msg.str());
-      }
-      }
-    }
-  }
-
-  {
-    descriptor.cell_to_face_vector.resize(descriptor.cell_to_node_vector.size());
-    for (CellId j = 0; j < descriptor.cell_to_face_vector.size(); ++j) {
-      descriptor.cell_to_face_vector[j].resize(cell_nb_faces[j]);
-    }
-    FaceId l = 0;
-    for (const auto& face_cells_vector : face_cells_map) {
-      const auto& cells_vector = face_cells_vector.second;
-      for (unsigned short lj = 0; lj < cells_vector.size(); ++lj) {
-        const auto& [cell_number, cell_local_face, reversed]         = cells_vector[lj];
-        descriptor.cell_to_face_vector[cell_number][cell_local_face] = l;
-      }
-      ++l;
-    }
-  }
-
-  {
-    descriptor.cell_face_is_reversed_vector.resize(descriptor.cell_to_node_vector.size());
-    for (CellId j = 0; j < descriptor.cell_face_is_reversed_vector.size(); ++j) {
-      descriptor.cell_face_is_reversed_vector[j] = Array<bool>(cell_nb_faces[j]);
-    }
-    for (const auto& face_cells_vector : face_cells_map) {
-      const auto& cells_vector = face_cells_vector.second;
-      for (unsigned short lj = 0; lj < cells_vector.size(); ++lj) {
-        const auto& [cell_number, cell_local_face, reversed]                  = cells_vector[lj];
-        descriptor.cell_face_is_reversed_vector[cell_number][cell_local_face] = reversed;
-      }
-    }
-  }
-
-  {
-    descriptor.face_to_node_vector.resize(face_cells_map.size());
-    int l = 0;
-    for (const auto& face_info : face_cells_map) {
-      const Face& face                  = face_info.first;
-      descriptor.face_to_node_vector[l] = face.nodeIdList();
-      ++l;
-    }
-  }
-
-  {
-    // Face numbers may change if numbers are provided in the file
-    descriptor.face_number_vector.resize(face_cells_map.size());
-    for (size_t l = 0; l < face_cells_map.size(); ++l) {
-      descriptor.face_number_vector[l] = l;
-    }
-  }
-}
-
-template <size_t Dimension>
-void
-GmshReader::__computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor)
-{
-  static_assert(Dimension == 3, "Invalid dimension to compute face-edge connectivities");
-  using FaceEdgeInfo = std::tuple<FaceId, unsigned short, bool>;
-  using Edge         = ConnectivityFace<2>;
-
-  const auto& node_number_vector = descriptor.node_number_vector;
-  Array<unsigned short> face_nb_edges(descriptor.face_to_node_vector.size());
-  std::map<Edge, std::vector<FaceEdgeInfo>> edge_faces_map;
-  for (FaceId l = 0; l < descriptor.face_to_node_vector.size(); ++l) {
-    const auto& face_nodes = descriptor.face_to_node_vector[l];
-
-    face_nb_edges[l] = face_nodes.size();
-    for (size_t r = 0; r < face_nodes.size() - 1; ++r) {
-      Edge e({face_nodes[r], face_nodes[r + 1]}, node_number_vector);
-      edge_faces_map[e].emplace_back(std::make_tuple(l, r, e.reversed()));
-    }
-    {
-      Edge e({face_nodes[face_nodes.size() - 1], face_nodes[0]}, node_number_vector);
-      edge_faces_map[e].emplace_back(std::make_tuple(l, face_nodes.size() - 1, e.reversed()));
-    }
-  }
-
-  std::unordered_map<Edge, EdgeId, typename Edge::Hash> edge_id_map;
-  {
-    descriptor.face_to_edge_vector.resize(descriptor.face_to_node_vector.size());
-    for (FaceId l = 0; l < descriptor.face_to_node_vector.size(); ++l) {
-      descriptor.face_to_edge_vector[l].resize(face_nb_edges[l]);
-    }
-    EdgeId e = 0;
-    for (const auto& edge_faces_vector : edge_faces_map) {
-      const auto& faces_vector = edge_faces_vector.second;
-      for (unsigned short l = 0; l < faces_vector.size(); ++l) {
-        const auto& [face_number, face_local_edge, reversed]         = faces_vector[l];
-        descriptor.face_to_edge_vector[face_number][face_local_edge] = e;
-      }
-      edge_id_map[edge_faces_vector.first] = e;
-      ++e;
-    }
-  }
-
-  {
-    descriptor.face_edge_is_reversed_vector.resize(descriptor.face_to_node_vector.size());
-    for (FaceId j = 0; j < descriptor.face_edge_is_reversed_vector.size(); ++j) {
-      descriptor.face_edge_is_reversed_vector[j] = Array<bool>(face_nb_edges[j]);
-    }
-    for (const auto& edge_faces_vector : edge_faces_map) {
-      const auto& faces_vector = edge_faces_vector.second;
-      for (unsigned short lj = 0; lj < faces_vector.size(); ++lj) {
-        const auto& [face_number, face_local_edge, reversed]                  = faces_vector[lj];
-        descriptor.face_edge_is_reversed_vector[face_number][face_local_edge] = reversed;
-      }
-    }
-  }
-
-  {
-    descriptor.edge_to_node_vector.resize(edge_faces_map.size());
-    int e = 0;
-    for (const auto& edge_info : edge_faces_map) {
-      const Edge& edge                  = edge_info.first;
-      descriptor.edge_to_node_vector[e] = edge.nodeIdList();
-      ++e;
-    }
-  }
-
-  {
-    // Edge numbers may change if numbers are provided in the file
-    descriptor.edge_number_vector.resize(edge_faces_map.size());
-    for (size_t e = 0; e < edge_faces_map.size(); ++e) {
-      descriptor.edge_number_vector[e] = e;
-    }
-  }
-
-  {
-    descriptor.cell_to_node_vector.reserve(descriptor.cell_to_node_vector.size());
-    for (CellId j = 0; j < descriptor.cell_to_node_vector.size(); ++j) {
-      const auto& cell_nodes = descriptor.cell_to_node_vector[j];
-
-      switch (descriptor.cell_type_vector[j]) {
-      case CellType::Tetrahedron: {
-        constexpr int local_edge[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {2, 3}, {3, 1}};
-        std::vector<unsigned int> cell_edge_vector;
-        cell_edge_vector.reserve(6);
-        for (int i_edge = 0; i_edge < 6; ++i_edge) {
-          const auto e = local_edge[i_edge];
-          Edge edge{{cell_nodes[e[0]], cell_nodes[e[1]]}, node_number_vector};
-          auto i = edge_id_map.find(edge);
-          if (i == edge_id_map.end()) {
-            throw NormalError("could not find this edge");
-          }
-          cell_edge_vector.push_back(i->second);
-        }
-        descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
-        break;
-      }
-      case CellType::Hexahedron: {
-        constexpr int local_edge[12][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
-                                           {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
-        std::vector<unsigned int> cell_edge_vector;
-        cell_edge_vector.reserve(12);
-        for (int i_edge = 0; i_edge < 12; ++i_edge) {
-          const auto e = local_edge[i_edge];
-          Edge edge{{cell_nodes[e[0]], cell_nodes[e[1]]}, node_number_vector};
-          auto i = edge_id_map.find(edge);
-          if (i == edge_id_map.end()) {
-            throw NormalError("could not find this edge");
-          }
-          cell_edge_vector.push_back(i->second);
-        }
-        descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
-        break;
-      }
-      default: {
-        std::stringstream error_msg;
-        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 3";
-        throw UnexpectedError(error_msg.str());
-      }
-      }
-    }
-  }
-}
-
 void
 GmshReader::__proceedData()
 {
@@ -1177,7 +678,7 @@ GmshReader::__proceedData()
       descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list));
     }
 
-    this->__computeCellFaceAndFaceNodeConnectivities<3>(descriptor);
+    MeshBuilderBase::_computeCellFaceAndFaceNodeConnectivities<3>(descriptor);
 
     const auto& node_number_vector = descriptor.node_number_vector;
 
@@ -1274,7 +775,7 @@ GmshReader::__proceedData()
         descriptor.addRefItemList(RefFaceList{physical_ref_id.refId(), face_list});
       }
     }
-    this->__computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<3>(descriptor);
+    MeshBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<3>(descriptor);
 
     {
       using Edge                                                                 = ConnectivityFace<2>;
@@ -1434,7 +935,7 @@ GmshReader::__proceedData()
       descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list));
     }
 
-    this->__computeCellFaceAndFaceNodeConnectivities<2>(descriptor);
+    MeshBuilderBase::_computeCellFaceAndFaceNodeConnectivities<2>(descriptor);
 
     using Face                                                                 = ConnectivityFace<2>;
     const auto& node_number_vector                                             = descriptor.node_number_vector;
diff --git a/src/mesh/GmshReader.hpp b/src/mesh/GmshReader.hpp
index 1d2d70e4f..4edd1fc7b 100644
--- a/src/mesh/GmshReader.hpp
+++ b/src/mesh/GmshReader.hpp
@@ -2,21 +2,19 @@
 #define GMSH_READER_HPP
 
 #include <algebra/TinyVector.hpp>
-
-#include <utils/Array.hpp>
-
 #include <mesh/IMesh.hpp>
+#include <mesh/MeshBuilderBase.hpp>
 #include <mesh/RefId.hpp>
+#include <utils/Array.hpp>
 
 #include <array>
 #include <fstream>
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
-class ConnectivityDescriptor;
-
-class GmshReader
+class GmshReader : public MeshBuilderBase
 {
  public:
   using R3 = TinyVector<3, double>;
@@ -263,24 +261,7 @@ class GmshReader
    */
   void __readPhysicalNames2_2();
 
-  template <size_t Dimension>
-  void __computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor);
-
-  template <size_t Dimension>
-  void __computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor);
-
-  template <int Dimension>
-  void _dispatch();
-
-  std::shared_ptr<IMesh> m_mesh;
-
  public:
-  std::shared_ptr<IMesh>
-  mesh() const
-  {
-    return m_mesh;
-  }
-
   GmshReader(const std::string& filename);
   ~GmshReader() = default;
 };
diff --git a/src/mesh/MeshBuilderBase.cpp b/src/mesh/MeshBuilderBase.cpp
new file mode 100644
index 000000000..74b2e0a2d
--- /dev/null
+++ b/src/mesh/MeshBuilderBase.cpp
@@ -0,0 +1,336 @@
+#include <mesh/MeshBuilderBase.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityDescriptor.hpp>
+#include <mesh/ConnectivityDispatcher.hpp>
+#include <mesh/ItemId.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <vector>
+
+template <int Dimension>
+void
+MeshBuilderBase::_dispatch()
+{
+  if (parallel::size() == 1) {
+    return;
+  }
+
+  using ConnectivityType = Connectivity<Dimension>;
+  using Rd               = TinyVector<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  if (not m_mesh) {
+    ConnectivityDescriptor descriptor;
+    std::shared_ptr connectivity = ConnectivityType::build(descriptor);
+    NodeValue<Rd> xr;
+    m_mesh = std::make_shared<MeshType>(connectivity, xr);
+  }
+  const MeshType& mesh = static_cast<const MeshType&>(*m_mesh);
+
+  ConnectivityDispatcher<Dimension> dispatcher(mesh.connectivity());
+
+  std::shared_ptr dispatched_connectivity = dispatcher.dispatchedConnectivity();
+  NodeValue<Rd> dispatched_xr             = dispatcher.dispatch(mesh.xr());
+
+  m_mesh = std::make_shared<MeshType>(dispatched_connectivity, dispatched_xr);
+}
+
+template <size_t Dimension>
+void
+MeshBuilderBase::_computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor)
+{
+  static_assert((Dimension == 2) or (Dimension == 3), "Invalid dimension to compute cell-face connectivities");
+  using CellFaceInfo = std::tuple<CellId, unsigned short, bool>;
+  using Face         = ConnectivityFace<Dimension>;
+
+  const auto& node_number_vector = descriptor.node_number_vector;
+  Array<unsigned short> cell_nb_faces(descriptor.cell_to_node_vector.size());
+  std::map<Face, std::vector<CellFaceInfo>> face_cells_map;
+  for (CellId j = 0; j < descriptor.cell_to_node_vector.size(); ++j) {
+    const auto& cell_nodes = descriptor.cell_to_node_vector[j];
+
+    if constexpr (Dimension == 2) {
+      switch (descriptor.cell_type_vector[j]) {
+      case CellType::Triangle: {
+        cell_nb_faces[j] = 3;
+        // face 0
+        Face f0({cell_nodes[1], cell_nodes[2]}, node_number_vector);
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes[2], cell_nodes[0]}, node_number_vector);
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes[0], cell_nodes[1]}, node_number_vector);
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+        break;
+      }
+      case CellType::Quadrangle: {
+        cell_nb_faces[j] = 4;
+        // face 0
+        Face f0({cell_nodes[0], cell_nodes[1]}, node_number_vector);
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes[1], cell_nodes[2]}, node_number_vector);
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes[2], cell_nodes[3]}, node_number_vector);
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes[3], cell_nodes[0]}, node_number_vector);
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+        break;
+      }
+      default: {
+        std::ostringstream error_msg;
+        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 2";
+        throw UnexpectedError(error_msg.str());
+      }
+      }
+    } else if constexpr (Dimension == 3) {
+      switch (descriptor.cell_type_vector[j]) {
+      case CellType::Tetrahedron: {
+        cell_nb_faces[j] = 4;
+        // face 0
+        Face f0({cell_nodes[1], cell_nodes[2], cell_nodes[3]}, node_number_vector);
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes[0], cell_nodes[3], cell_nodes[2]}, node_number_vector);
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes[0], cell_nodes[1], cell_nodes[3]}, node_number_vector);
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes[0], cell_nodes[2], cell_nodes[1]}, node_number_vector);
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+        break;
+      }
+      case CellType::Hexahedron: {
+        // face 0
+        Face f0({cell_nodes[3], cell_nodes[2], cell_nodes[1], cell_nodes[0]}, node_number_vector);
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes[4], cell_nodes[5], cell_nodes[6], cell_nodes[7]}, node_number_vector);
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes[0], cell_nodes[4], cell_nodes[7], cell_nodes[3]}, node_number_vector);
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes[1], cell_nodes[2], cell_nodes[6], cell_nodes[5]}, node_number_vector);
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+
+        // face 4
+        Face f4({cell_nodes[0], cell_nodes[1], cell_nodes[5], cell_nodes[4]}, node_number_vector);
+        face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed()));
+
+        // face 5
+        Face f5({cell_nodes[3], cell_nodes[7], cell_nodes[6], cell_nodes[2]}, node_number_vector);
+        face_cells_map[f5].emplace_back(std::make_tuple(j, 5, f5.reversed()));
+
+        cell_nb_faces[j] = 6;
+        break;
+      }
+      default: {
+        std::ostringstream error_msg;
+        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 3";
+        throw UnexpectedError(error_msg.str());
+      }
+      }
+    }
+  }
+
+  {
+    descriptor.cell_to_face_vector.resize(descriptor.cell_to_node_vector.size());
+    for (CellId j = 0; j < descriptor.cell_to_face_vector.size(); ++j) {
+      descriptor.cell_to_face_vector[j].resize(cell_nb_faces[j]);
+    }
+    FaceId l = 0;
+    for (const auto& face_cells_vector : face_cells_map) {
+      const auto& cells_vector = face_cells_vector.second;
+      for (unsigned short lj = 0; lj < cells_vector.size(); ++lj) {
+        const auto& [cell_number, cell_local_face, reversed]         = cells_vector[lj];
+        descriptor.cell_to_face_vector[cell_number][cell_local_face] = l;
+      }
+      ++l;
+    }
+  }
+
+  {
+    descriptor.cell_face_is_reversed_vector.resize(descriptor.cell_to_node_vector.size());
+    for (CellId j = 0; j < descriptor.cell_face_is_reversed_vector.size(); ++j) {
+      descriptor.cell_face_is_reversed_vector[j] = Array<bool>(cell_nb_faces[j]);
+    }
+    for (const auto& face_cells_vector : face_cells_map) {
+      const auto& cells_vector = face_cells_vector.second;
+      for (unsigned short lj = 0; lj < cells_vector.size(); ++lj) {
+        const auto& [cell_number, cell_local_face, reversed]                  = cells_vector[lj];
+        descriptor.cell_face_is_reversed_vector[cell_number][cell_local_face] = reversed;
+      }
+    }
+  }
+
+  {
+    descriptor.face_to_node_vector.resize(face_cells_map.size());
+    int l = 0;
+    for (const auto& face_info : face_cells_map) {
+      const Face& face                  = face_info.first;
+      descriptor.face_to_node_vector[l] = face.nodeIdList();
+      ++l;
+    }
+  }
+
+  {
+    // Face numbers may change if numbers are provided in the file
+    descriptor.face_number_vector.resize(face_cells_map.size());
+    for (size_t l = 0; l < face_cells_map.size(); ++l) {
+      descriptor.face_number_vector[l] = l;
+    }
+  }
+}
+
+template <size_t Dimension>
+void
+MeshBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor)
+{
+  static_assert(Dimension == 3, "Invalid dimension to compute face-edge connectivities");
+  using FaceEdgeInfo = std::tuple<FaceId, unsigned short, bool>;
+  using Edge         = ConnectivityFace<2>;
+
+  const auto& node_number_vector = descriptor.node_number_vector;
+  Array<unsigned short> face_nb_edges(descriptor.face_to_node_vector.size());
+  std::map<Edge, std::vector<FaceEdgeInfo>> edge_faces_map;
+  for (FaceId l = 0; l < descriptor.face_to_node_vector.size(); ++l) {
+    const auto& face_nodes = descriptor.face_to_node_vector[l];
+
+    face_nb_edges[l] = face_nodes.size();
+    for (size_t r = 0; r < face_nodes.size() - 1; ++r) {
+      Edge e({face_nodes[r], face_nodes[r + 1]}, node_number_vector);
+      edge_faces_map[e].emplace_back(std::make_tuple(l, r, e.reversed()));
+    }
+    {
+      Edge e({face_nodes[face_nodes.size() - 1], face_nodes[0]}, node_number_vector);
+      edge_faces_map[e].emplace_back(std::make_tuple(l, face_nodes.size() - 1, e.reversed()));
+    }
+  }
+
+  std::unordered_map<Edge, EdgeId, typename Edge::Hash> edge_id_map;
+  {
+    descriptor.face_to_edge_vector.resize(descriptor.face_to_node_vector.size());
+    for (FaceId l = 0; l < descriptor.face_to_node_vector.size(); ++l) {
+      descriptor.face_to_edge_vector[l].resize(face_nb_edges[l]);
+    }
+    EdgeId e = 0;
+    for (const auto& edge_faces_vector : edge_faces_map) {
+      const auto& faces_vector = edge_faces_vector.second;
+      for (unsigned short l = 0; l < faces_vector.size(); ++l) {
+        const auto& [face_number, face_local_edge, reversed]         = faces_vector[l];
+        descriptor.face_to_edge_vector[face_number][face_local_edge] = e;
+      }
+      edge_id_map[edge_faces_vector.first] = e;
+      ++e;
+    }
+  }
+
+  {
+    descriptor.face_edge_is_reversed_vector.resize(descriptor.face_to_node_vector.size());
+    for (FaceId j = 0; j < descriptor.face_edge_is_reversed_vector.size(); ++j) {
+      descriptor.face_edge_is_reversed_vector[j] = Array<bool>(face_nb_edges[j]);
+    }
+    for (const auto& edge_faces_vector : edge_faces_map) {
+      const auto& faces_vector = edge_faces_vector.second;
+      for (unsigned short lj = 0; lj < faces_vector.size(); ++lj) {
+        const auto& [face_number, face_local_edge, reversed]                  = faces_vector[lj];
+        descriptor.face_edge_is_reversed_vector[face_number][face_local_edge] = reversed;
+      }
+    }
+  }
+
+  {
+    descriptor.edge_to_node_vector.resize(edge_faces_map.size());
+    int e = 0;
+    for (const auto& edge_info : edge_faces_map) {
+      const Edge& edge                  = edge_info.first;
+      descriptor.edge_to_node_vector[e] = edge.nodeIdList();
+      ++e;
+    }
+  }
+
+  {
+    // Edge numbers may change if numbers are provided in the file
+    descriptor.edge_number_vector.resize(edge_faces_map.size());
+    for (size_t e = 0; e < edge_faces_map.size(); ++e) {
+      descriptor.edge_number_vector[e] = e;
+    }
+  }
+
+  {
+    descriptor.cell_to_node_vector.reserve(descriptor.cell_to_node_vector.size());
+    for (CellId j = 0; j < descriptor.cell_to_node_vector.size(); ++j) {
+      const auto& cell_nodes = descriptor.cell_to_node_vector[j];
+
+      switch (descriptor.cell_type_vector[j]) {
+      case CellType::Tetrahedron: {
+        constexpr int local_edge[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {2, 3}, {3, 1}};
+        std::vector<unsigned int> cell_edge_vector;
+        cell_edge_vector.reserve(6);
+        for (int i_edge = 0; i_edge < 6; ++i_edge) {
+          const auto e = local_edge[i_edge];
+          Edge edge{{cell_nodes[e[0]], cell_nodes[e[1]]}, node_number_vector};
+          auto i = edge_id_map.find(edge);
+          if (i == edge_id_map.end()) {
+            throw NormalError("could not find this edge");
+          }
+          cell_edge_vector.push_back(i->second);
+        }
+        descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
+        break;
+      }
+      case CellType::Hexahedron: {
+        constexpr int local_edge[12][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6},
+                                           {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}};
+        std::vector<unsigned int> cell_edge_vector;
+        cell_edge_vector.reserve(12);
+        for (int i_edge = 0; i_edge < 12; ++i_edge) {
+          const auto e = local_edge[i_edge];
+          Edge edge{{cell_nodes[e[0]], cell_nodes[e[1]]}, node_number_vector};
+          auto i = edge_id_map.find(edge);
+          if (i == edge_id_map.end()) {
+            throw NormalError("could not find this edge");
+          }
+          cell_edge_vector.push_back(i->second);
+        }
+        descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
+        break;
+      }
+      default: {
+        std::stringstream error_msg;
+        error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 3";
+        throw UnexpectedError(error_msg.str());
+      }
+      }
+    }
+  }
+}
+
+template void MeshBuilderBase::_dispatch<1>();
+template void MeshBuilderBase::_dispatch<2>();
+template void MeshBuilderBase::_dispatch<3>();
+
+template void MeshBuilderBase::_computeCellFaceAndFaceNodeConnectivities<2>(ConnectivityDescriptor& descriptor);
+template void MeshBuilderBase::_computeCellFaceAndFaceNodeConnectivities<3>(ConnectivityDescriptor& descriptor);
+
+template void MeshBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<3>(
+  ConnectivityDescriptor& descriptor);
diff --git a/src/mesh/MeshBuilderBase.hpp b/src/mesh/MeshBuilderBase.hpp
new file mode 100644
index 000000000..dcca07f5a
--- /dev/null
+++ b/src/mesh/MeshBuilderBase.hpp
@@ -0,0 +1,230 @@
+#ifndef MESH_BUILDER_BASE_HPP
+#define MESH_BUILDER_BASE_HPP
+
+#include <mesh/IMesh.hpp>
+#include <mesh/ItemId.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <memory>
+#include <vector>
+
+class ConnectivityDescriptor;
+
+class MeshBuilderBase
+{
+ protected:
+  template <size_t Dimension>
+  class ConnectivityFace;
+
+  std::shared_ptr<IMesh> m_mesh;
+
+  template <size_t Dimension>
+  static void _computeCellFaceAndFaceNodeConnectivities(ConnectivityDescriptor& descriptor);
+
+  template <size_t Dimension>
+  static void _computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(ConnectivityDescriptor& descriptor);
+
+  template <int Dimension>
+  void _dispatch();
+
+ public:
+  std::shared_ptr<IMesh>
+  mesh() const
+  {
+    return m_mesh;
+  }
+
+  MeshBuilderBase()  = default;
+  ~MeshBuilderBase() = default;
+};
+
+template <>
+class MeshBuilderBase::ConnectivityFace<2>
+{
+ public:
+  friend struct Hash;
+  friend class CartesianMeshBuilder;
+
+  struct Hash
+  {
+    size_t
+    operator()(const ConnectivityFace& f) const
+    {
+      size_t hash = 0;
+      hash ^= std::hash<unsigned int>()(f.m_node0_id);
+      hash ^= std::hash<unsigned int>()(f.m_node1_id) >> 1;
+      return hash;
+    }
+  };
+
+ private:
+  const std::vector<int>& m_node_number_vector;
+
+  unsigned int m_node0_id;
+  unsigned int m_node1_id;
+
+  bool m_reversed;
+
+ public:
+  std::vector<unsigned int>
+  nodeIdList() const
+  {
+    return {m_node0_id, m_node1_id};
+  }
+
+  bool
+  reversed() const
+  {
+    return m_reversed;
+  }
+
+  PUGS_INLINE
+  bool
+  operator==(const ConnectivityFace& f) const
+  {
+    return ((m_node0_id == f.m_node0_id) and (m_node1_id == f.m_node1_id));
+  }
+
+  PUGS_INLINE
+  bool
+  operator<(const ConnectivityFace& f) const
+  {
+    return ((m_node_number_vector[m_node0_id] < m_node_number_vector[f.m_node0_id]) or
+            ((m_node_number_vector[m_node0_id] == m_node_number_vector[f.m_node0_id]) and
+             (m_node_number_vector[m_node1_id] < m_node_number_vector[f.m_node1_id])));
+  }
+
+  PUGS_INLINE
+  ConnectivityFace(const std::vector<unsigned int>& node_id_list, const std::vector<int>& node_number_vector)
+    : m_node_number_vector(node_number_vector)
+  {
+    Assert(node_id_list.size() == 2);
+
+    if (m_node_number_vector[node_id_list[0]] < m_node_number_vector[node_id_list[1]]) {
+      m_node0_id = node_id_list[0];
+      m_node1_id = node_id_list[1];
+      m_reversed = false;
+    } else {
+      m_node0_id = node_id_list[1];
+      m_node1_id = node_id_list[0];
+      m_reversed = true;
+    }
+  }
+
+  PUGS_INLINE
+  ConnectivityFace(const ConnectivityFace&) = default;
+
+  PUGS_INLINE
+  ~ConnectivityFace() = default;
+};
+
+template <>
+class MeshBuilderBase::ConnectivityFace<3>
+{
+ private:
+  friend class GmshReader;
+  friend class CartesianMeshBuilder;
+  friend struct Hash;
+  struct Hash
+  {
+    size_t
+    operator()(const ConnectivityFace& f) const
+    {
+      size_t hash = 0;
+      for (size_t i = 0; i < f.m_node_id_list.size(); ++i) {
+        hash ^= std::hash<unsigned int>()(f.m_node_id_list[i]) >> i;
+      }
+      return hash;
+    }
+  };
+
+ private:
+  bool m_reversed;
+  std::vector<NodeId::base_type> m_node_id_list;
+  const std::vector<int>& m_node_number_vector;
+
+  PUGS_INLINE
+  std::vector<unsigned int>
+  _sort(const std::vector<unsigned int>& node_list)
+  {
+    const auto min_id = std::min_element(node_list.begin(), node_list.end());
+    const int shift   = std::distance(node_list.begin(), min_id);
+
+    std::vector<unsigned int> rotated_node_list(node_list.size());
+    if (node_list[(shift + 1) % node_list.size()] > node_list[(shift + node_list.size() - 1) % node_list.size()]) {
+      for (size_t i = 0; i < node_list.size(); ++i) {
+        rotated_node_list[i] = node_list[(shift + node_list.size() - i) % node_list.size()];
+        m_reversed           = true;
+      }
+    } else {
+      for (size_t i = 0; i < node_list.size(); ++i) {
+        rotated_node_list[i] = node_list[(shift + i) % node_list.size()];
+      }
+    }
+
+    return rotated_node_list;
+  }
+
+ public:
+  PUGS_INLINE
+  const bool&
+  reversed() const
+  {
+    return m_reversed;
+  }
+
+  PUGS_INLINE
+  const std::vector<unsigned int>&
+  nodeIdList() const
+  {
+    return m_node_id_list;
+  }
+
+  PUGS_INLINE
+  ConnectivityFace(const std::vector<unsigned int>& given_node_id_list, const std::vector<int>& node_number_vector)
+    : m_reversed(false), m_node_id_list(_sort(given_node_id_list)), m_node_number_vector(node_number_vector)
+  {
+    ;
+  }
+
+ public:
+  bool
+  operator==(const ConnectivityFace& f) const
+  {
+    if (m_node_id_list.size() == f.nodeIdList().size()) {
+      for (size_t j = 0; j < m_node_id_list.size(); ++j) {
+        if (m_node_id_list[j] != f.nodeIdList()[j]) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  PUGS_INLINE
+  bool
+  operator<(const ConnectivityFace& f) const
+  {
+    const size_t min_nb_nodes = std::min(f.m_node_id_list.size(), m_node_id_list.size());
+    for (size_t i = 0; i < min_nb_nodes; ++i) {
+      if (m_node_id_list[i] < f.m_node_id_list[i])
+        return true;
+      if (m_node_id_list[i] != f.m_node_id_list[i])
+        return false;
+    }
+    return m_node_id_list.size() < f.m_node_id_list.size();
+  }
+
+  PUGS_INLINE
+  ConnectivityFace(const ConnectivityFace&) = default;
+
+  PUGS_INLINE
+  ConnectivityFace() = delete;
+
+  PUGS_INLINE
+  ~ConnectivityFace() = default;
+};
+
+#endif   // MESH_BUILDER_BASE_HPP
-- 
GitLab