diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp
index b9a65729637128081a7206ad6fa5d98ad95e9978..89e678c3fb4095350842b19a9145502cd7f558a8 100644
--- a/src/language/modules/MeshModule.cpp
+++ b/src/language/modules/MeshModule.cpp
@@ -9,7 +9,7 @@
 #include <language/utils/TypeDescriptor.hpp>
 #include <mesh/CartesianMeshBuilder.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/DiamondDualMeshBuilder.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
 #include <mesh/GmshReader.hpp>
 #include <mesh/Mesh.hpp>
 #include <utils/Exceptions.hpp>
@@ -184,9 +184,30 @@ MeshModule::MeshModule()
                             std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IMesh>(
                               const std::shared_ptr<const IMesh>&)>>(
 
-                              [](const std::shared_ptr<const IMesh>& p_mesh) -> std::shared_ptr<const IMesh> {
-                                DiamondDualMeshBuilder builder{p_mesh};
-                                return builder.mesh();
+                              [](const std::shared_ptr<const IMesh>& i_mesh) -> std::shared_ptr<const IMesh> {
+                                switch (i_mesh->dimension()) {
+                                case 1: {
+                                  using MeshType = Mesh<Connectivity<1>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                }
+                                case 2: {
+                                  using MeshType = Mesh<Connectivity<2>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                }
+                                case 3: {
+                                  using MeshType = Mesh<Connectivity<3>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                }
+                                default: {
+                                  throw UnexpectedError("invalid dimension");
+                                }
+                                }
                               }
 
                               ));
diff --git a/src/language/modules/VTKModule.cpp b/src/language/modules/VTKModule.cpp
index f815a78e763b387ee5707d9ac65ec1f038a36b14..ce84141ee866f7ac6fdbb8448db78d0d1b64de88 100644
--- a/src/language/modules/VTKModule.cpp
+++ b/src/language/modules/VTKModule.cpp
@@ -24,7 +24,10 @@ VTKModule::VTKModule()
                                   const std::shared_ptr<const MeshType> mesh =
                                     std::dynamic_pointer_cast<const MeshType>(p_mesh);
 
-                                  writer.write(mesh, OutputNamedItemValueSet{}, time, true);
+                                  writer.write(mesh,
+                                               OutputNamedItemValueSet{
+                                                 NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}},
+                                               time, true);
                                   break;
                                 }
                                 case 2: {
@@ -32,7 +35,10 @@ VTKModule::VTKModule()
                                   const std::shared_ptr<const MeshType> mesh =
                                     std::dynamic_pointer_cast<const MeshType>(p_mesh);
 
-                                  writer.write(mesh, OutputNamedItemValueSet{}, time, true);
+                                  writer.write(mesh,
+                                               OutputNamedItemValueSet{
+                                                 NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}},
+                                               time, true);
                                   break;
                                 }
                                 case 3: {
@@ -40,7 +46,10 @@ VTKModule::VTKModule()
                                   const std::shared_ptr<const MeshType> mesh =
                                     std::dynamic_pointer_cast<const MeshType>(p_mesh);
 
-                                  writer.write(mesh, OutputNamedItemValueSet{}, time, true);
+                                  writer.write(mesh,
+                                               OutputNamedItemValueSet{
+                                                 NamedItemValue{"cell_number", mesh->connectivity().cellNumber()}},
+                                               time, true);
                                   break;
                                 }
                                 }
diff --git a/src/main.cpp b/src/main.cpp
index 9b853028133fd9035f67f743c62ee2a0a298202a..aad67d58a5d228cdc502e9b024cfc497fef23291 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,8 @@
 #include <utils/PugsUtils.hpp>
 
 #include <language/PugsParser.hpp>
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
 
@@ -11,9 +13,13 @@ main(int argc, char* argv[])
 
   SynchronizerManager::create();
   MeshDataManager::create();
+  DiamondDualConnectivityManager::create();
+  DiamondDualMeshManager::create();
 
   parser(filename);
 
+  DiamondDualMeshManager::destroy();
+  DiamondDualConnectivityManager::destroy();
   MeshDataManager::destroy();
   SynchronizerManager::destroy();
   finalize();
diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt
index 92c865c599767db0d5e5fed84e7d16d0cbec8579..6c0088831c86023e31914b953c9ea137e5411d21 100644
--- a/src/mesh/CMakeLists.txt
+++ b/src/mesh/CMakeLists.txt
@@ -8,8 +8,11 @@ add_library(
   ConnectivityComputer.cpp
   ConnectivityDispatcher.cpp
   DiamondDualConnectivityBuilder.cpp
+  DiamondDualConnectivityManager.cpp
   DiamondDualMeshBuilder.cpp
+  DiamondDualMeshManager.cpp
   GmshReader.cpp
+  IConnectivity.cpp
   IMesh.cpp
   LogicalConnectivityBuilder.cpp
   MeshBuilderBase.cpp
diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp
index 5a888190841e7886d4548a5405dc3126223318ac..3bf5cd4201c574749e5b6a67a2b6c4d667bd88f1 100644
--- a/src/mesh/Connectivity.hpp
+++ b/src/mesh/Connectivity.hpp
@@ -12,7 +12,6 @@
 #include <mesh/RefId.hpp>
 #include <mesh/RefItemList.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
-#include <mesh/SynchronizerManager.hpp>
 #include <utils/CSRGraph.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/PugsAssert.hpp>
@@ -651,11 +650,7 @@ class Connectivity final : public IConnectivity
   void _buildFrom(const ConnectivityDescriptor& descriptor);
 
  public:
-  ~Connectivity()
-  {
-    auto& manager = SynchronizerManager::instance();
-    manager.deleteConnectivitySynchronizer(this);
-  }
+  ~Connectivity() = default;
 };
 
 template <size_t Dim>
diff --git a/src/mesh/ConnectivityBuilderBase.cpp b/src/mesh/ConnectivityBuilderBase.cpp
index f5d47e25003a710e1e2c18ba6b772ffd04bb532f..60c38bfb84fb975c0e2cdc3b07d1d97e39eb0021 100644
--- a/src/mesh/ConnectivityBuilderBase.cpp
+++ b/src/mesh/ConnectivityBuilderBase.cpp
@@ -2,12 +2,13 @@
 
 #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 <map>
+#include <unordered_map>
 #include <vector>
 
 template <size_t Dimension>
diff --git a/src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp b/src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d35d2ff9541fbfab7e8e6e30976bd5f88970f56
--- /dev/null
+++ b/src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp
@@ -0,0 +1,153 @@
+#ifndef CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#define CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
+#include <mesh/ItemValue.hpp>
+#include <utils/Array.hpp>
+#include <utils/PugsAssert.hpp>
+
+class IConnectivityToDiamondDualConnectivityDataMapper
+{
+ public:
+  IConnectivityToDiamondDualConnectivityDataMapper(const IConnectivityToDiamondDualConnectivityDataMapper&) = delete;
+  IConnectivityToDiamondDualConnectivityDataMapper(IConnectivityToDiamondDualConnectivityDataMapper&&)      = delete;
+
+  IConnectivityToDiamondDualConnectivityDataMapper()          = default;
+  virtual ~IConnectivityToDiamondDualConnectivityDataMapper() = default;
+};
+
+template <size_t Dimension>
+class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDiamondDualConnectivityDataMapper
+{
+ private:
+  const IConnectivity* m_primal_connectivity;
+  const IConnectivity* m_dual_connectivity;
+
+  NodeIdToNodeIdMap m_primal_node_to_dual_node_map;
+  CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  FaceIdToCellIdMap m_primal_face_to_dual_cell_map;
+
+ public:
+  template <typename OriginDataType1, typename OriginDataType2, typename DestinationDataType>
+  void
+  toDualNode(const NodeValue<OriginDataType1>& primal_node_value,
+             const CellValue<OriginDataType2>& primal_cell_value,
+             const NodeValue<DestinationDataType>& dual_node_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType1>, DestinationDataType>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType2>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get());
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get());
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get());
+
+    parallel_for(
+      m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_node_to_dual_node_map[i];
+
+        dual_node_value[dual_node_id] = primal_node_value[primal_node_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+        dual_node_value[dual_node_id]             = primal_cell_value[primal_cell_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType1, typename DestinationDataType2>
+  void
+  fromDualNode(const NodeValue<OriginDataType>& dual_node_value,
+               const NodeValue<DestinationDataType1>& primal_node_value,
+               const CellValue<DestinationDataType2>& primal_cell_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType1>, "destination data type must not be constant");
+    static_assert(not std::is_const_v<DestinationDataType2>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType1>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType2>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get());
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get());
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get());
+
+    parallel_for(
+      m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_node_to_dual_node_map[i];
+
+        primal_node_value[primal_node_id] = dual_node_value[dual_node_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+        primal_cell_value[primal_cell_id]         = dual_node_value[dual_node_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType>
+  void
+  toDualCell(const FaceValue<OriginDataType>& primal_face_value,
+             const CellValue<DestinationDataType>& dual_cell_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get());
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get());
+
+    parallel_for(
+      m_primal_face_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
+
+        dual_cell_value[dual_cell_id] = primal_face_value[primal_face_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
+
+        dual_cell_value[dual_cell_id] = primal_face_value[primal_face_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType>
+  void
+  fromDualCell(const CellValue<DestinationDataType>& dual_cell_value,
+               const FaceValue<OriginDataType>& primal_face_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get());
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get());
+
+    parallel_for(
+      m_primal_face_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
+
+        primal_face_value[primal_face_id] = dual_cell_value[dual_cell_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
+        primal_face_value[primal_face_id]         = dual_cell_value[dual_cell_id];
+      });
+  }
+
+  ConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>& primal_connectivity,
+                                                  const Connectivity<Dimension>& dual_connectivity,
+                                                  const NodeIdToNodeIdMap& primal_node_to_dual_node_map,
+                                                  const CellIdToNodeIdMap& primal_cell_to_dual_node_map,
+                                                  const FaceIdToCellIdMap& primal_face_to_dual_cell_map)
+    : m_primal_connectivity{&primal_connectivity},
+      m_dual_connectivity{&dual_connectivity},
+      m_primal_node_to_dual_node_map{primal_node_to_dual_node_map},
+      m_primal_cell_to_dual_node_map{primal_cell_to_dual_node_map},
+      m_primal_face_to_dual_cell_map{primal_face_to_dual_cell_map}
+  {}
+};
+
+#endif   // CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
diff --git a/src/mesh/DiamondDualConnectivityBuilder.cpp b/src/mesh/DiamondDualConnectivityBuilder.cpp
index 8bcbe9bad8e15cbef1dfa449c0d6defc115f126f..76adb60de4b047cd5d456662db96d10c6e1677e7 100644
--- a/src/mesh/DiamondDualConnectivityBuilder.cpp
+++ b/src/mesh/DiamondDualConnectivityBuilder.cpp
@@ -3,12 +3,16 @@
 #include <mesh/Connectivity.hpp>
 #include <mesh/ConnectivityDescriptor.hpp>
 #include <mesh/ConnectivityDispatcher.hpp>
+#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/RefId.hpp>
 #include <utils/Array.hpp>
+#include <utils/ArrayUtils.hpp>
 #include <utils/Messenger.hpp>
 
+#include <vector>
+
 template <size_t Dimension>
 void
 DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connectivity<Dimension>& primal_connectivity,
@@ -18,31 +22,55 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
   const size_t primal_number_of_faces = primal_connectivity.numberOfFaces();
   const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
 
+  const auto& primal_node_number = primal_connectivity.nodeNumber();
+  const auto& primal_cell_number = primal_connectivity.cellNumber();
+
   const size_t diamond_number_of_nodes = primal_number_of_cells + primal_number_of_nodes;
+  const size_t diamond_number_of_cells = primal_number_of_faces;
+
+  {
+    m_primal_node_to_dual_node_map = NodeIdToNodeIdMap{primal_number_of_nodes};
+
+    NodeId diamond_node_id = 0;
+    for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
+      m_primal_node_to_dual_node_map[primal_node_id] = std::make_pair(primal_node_id, diamond_node_id++);
+    }
+
+    m_primal_cell_to_dual_node_map = CellIdToNodeIdMap{primal_number_of_cells};
+    for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+      m_primal_cell_to_dual_node_map[primal_cell_id] = std::make_pair(primal_cell_id, diamond_node_id++);
+    }
+  }
 
   diamond_descriptor.node_number_vector.resize(diamond_number_of_nodes);
 
-  const auto& primal_node_number = primal_connectivity.nodeNumber();
+  for (size_t i = 0; i < m_primal_node_to_dual_node_map.size(); ++i) {
+    const auto [primal_node_id, diamond_dual_node_id] = m_primal_node_to_dual_node_map[i];
 
-  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
-    diamond_descriptor.node_number_vector[primal_node_id] = primal_node_number[primal_node_id];
+    diamond_descriptor.node_number_vector[diamond_dual_node_id] = primal_node_number[primal_node_id];
   }
 
-  const auto& primal_cell_number = primal_connectivity.cellNumber();
+  const size_t cell_number_shift = max(primal_node_number) + 1;
+  for (size_t i = 0; i < primal_number_of_cells; ++i) {
+    const auto [primal_cell_id, diamond_dual_node_id] = m_primal_cell_to_dual_node_map[i];
 
-  const size_t max_node_number = max(primal_node_number);
+    diamond_descriptor.node_number_vector[diamond_dual_node_id] =
+      primal_cell_number[primal_cell_id] + cell_number_shift;
+  }
 
-  for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
-    diamond_descriptor.node_number_vector[primal_number_of_nodes + primal_cell_id] =
-      primal_cell_number[primal_cell_id] + max_node_number;
+  {
+    m_primal_face_to_dual_cell_map = FaceIdToCellIdMap{primal_number_of_faces};
+    CellId diamond_cell_id         = 0;
+    for (FaceId primal_face_id = 0; primal_face_id < primal_number_of_faces; ++primal_face_id) {
+      m_primal_face_to_dual_cell_map[primal_face_id] = std::make_pair(primal_face_id, diamond_cell_id++);
+    }
   }
 
-  const size_t diamond_number_of_cells = primal_number_of_faces;
   diamond_descriptor.cell_number_vector.resize(diamond_number_of_cells);
-
   const auto& primal_face_number = primal_connectivity.faceNumber();
-  for (FaceId i_primal_face = 0; i_primal_face < primal_number_of_faces; ++i_primal_face) {
-    diamond_descriptor.cell_number_vector[i_primal_face] = primal_face_number[i_primal_face];
+  for (size_t i = 0; i < diamond_number_of_cells; ++i) {
+    const auto [primal_face_id, dual_cell_id]           = m_primal_face_to_dual_cell_map[i];
+    diamond_descriptor.cell_number_vector[dual_cell_id] = primal_face_number[primal_face_id];
   }
 
   if constexpr (Dimension == 3) {
@@ -60,7 +88,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
 
   const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
 
-  for (FaceId i_face = 0; i_face < primal_connectivity.numberOfFaces(); ++i_face) {
+  for (FaceId i_face = 0; i_face < primal_number_of_faces; ++i_face) {
     const size_t i_cell               = i_face;
     const auto& primal_face_cell_list = primal_face_to_cell_matrix[i_face];
 
@@ -86,7 +114,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
   const auto& primal_face_to_node_matrix              = primal_connectivity.faceToNodeMatrix();
   const auto& primal_face_local_number_in_their_cells = primal_connectivity.faceLocalNumbersInTheirCells();
   const auto& cell_face_is_reversed                   = primal_connectivity.cellFaceIsReversed();
-  for (FaceId i_face = 0; i_face < primal_connectivity.numberOfFaces(); ++i_face) {
+  for (FaceId i_face = 0; i_face < primal_number_of_faces; ++i_face) {
     const size_t& i_diamond_cell      = i_face;
     const auto& primal_face_cell_list = primal_face_to_cell_matrix[i_face];
     const auto& primal_face_node_list = primal_face_to_node_matrix[i_face];
@@ -162,23 +190,34 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor<1>(const Con
 
   const size_t diamond_number_of_nodes = 2 * primal_number_of_nodes - primal_number_of_cells;
 
-  diamond_descriptor.node_number_vector.resize(diamond_number_of_nodes);
-
-  const auto& primal_node_number = primal_connectivity.nodeNumber();
-
   const size_t diamond_number_of_cells = primal_number_of_faces;
   const size_t number_of_kept_nodes    = 2 * (diamond_number_of_nodes - diamond_number_of_cells);
 
   const auto& primal_node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
   size_t next_kept_node_id               = 0;
 
-  for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
-    const auto& primal_node_cell_list = primal_node_to_cell_matrix[node_id];
+  diamond_descriptor.node_number_vector.resize(diamond_number_of_nodes);
+
+  const auto& primal_node_number = primal_connectivity.nodeNumber();
+
+  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+    const auto& primal_node_cell_list = primal_node_to_cell_matrix[primal_node_id];
     if (primal_node_cell_list.size() == 1) {
-      diamond_descriptor.node_number_vector[next_kept_node_id++] = primal_node_number[node_id];
+      diamond_descriptor.node_number_vector[next_kept_node_id++] = primal_node_number[primal_node_id];
     }
   }
 
+  const size_t primal_number_of_kept_nodes = next_kept_node_id;
+
+  const auto& primal_cell_number = primal_connectivity.cellNumber();
+
+  const size_t cell_number_shift = max(primal_node_number) + 1;
+
+  for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+    diamond_descriptor.node_number_vector[primal_number_of_kept_nodes + primal_cell_id] =
+      primal_cell_number[primal_cell_id] + cell_number_shift;
+  }
+
   if (number_of_kept_nodes != next_kept_node_id) {
     throw UnexpectedError("unexpected number of kept node" + std::to_string(next_kept_node_id) +
                           " != " + std::to_string(number_of_kept_nodes));
@@ -200,8 +239,6 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor<1>(const Con
 
   diamond_descriptor.cell_to_node_vector.resize(diamond_number_of_cells);
 
-  ;
-
   const auto& primal_node_local_number_in_their_cells = primal_connectivity.nodeLocalNumbersInTheirCells();
 
   {
@@ -278,7 +315,6 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
           node_array[i] = diamond_node_list[i];
         }
         diamond_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), node_array});
-        std::cout << "stored " << primal_ref_node_list.refId() << '\n';
       }
     }
   }
@@ -466,15 +502,62 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
 
     diamond_descriptor.edge_owner_vector.resize(edge_cell_owner.size());
     for (size_t i_edge = 0; i_edge < edge_cell_owner.size(); ++i_edge) {
-      diamond_descriptor.face_owner_vector[i_edge] = diamond_descriptor.cell_owner_vector[edge_cell_owner[i_edge]];
+      diamond_descriptor.edge_owner_vector[i_edge] = diamond_descriptor.cell_owner_vector[edge_cell_owner[i_edge]];
     }
   }
 
   m_connectivity = ConnectivityType::build(diamond_descriptor);
+
+  {
+    if constexpr (Dimension == 1) {
+      const auto& node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+
+      NodeId dual_node_id            = 0;
+      m_primal_node_to_dual_node_map = [&]() {
+        std::vector<std::pair<NodeId, NodeId>> primal_node_to_dual_node_vector;
+        for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+          if (node_to_cell_matrix[primal_node_id].size() == 1) {
+            primal_node_to_dual_node_vector.push_back(std::make_pair(primal_node_id, dual_node_id++));
+          }
+        }
+        return convert_to_array(primal_node_to_dual_node_vector);
+      }();
+
+      m_primal_cell_to_dual_node_map = [&]() {
+        CellIdToNodeIdMap primal_cell_to_dual_node_map{primal_number_of_cells};
+        for (CellId primal_cell_id = 0; primal_cell_id < primal_cell_to_dual_node_map.size(); ++primal_cell_id) {
+          primal_cell_to_dual_node_map[primal_cell_id] = std::make_pair(primal_cell_id, dual_node_id++);
+        }
+        return primal_cell_to_dual_node_map;
+      }();
+
+      m_primal_face_to_dual_cell_map = [&]() {
+        FaceIdToCellIdMap primal_face_to_dual_cell_map{primal_connectivity.numberOfFaces()};
+        for (size_t id = 0; id < primal_face_to_dual_cell_map.size(); ++id) {
+          const CellId dual_cell_id   = id;
+          const FaceId primal_face_id = id;
+
+          primal_face_to_dual_cell_map[id] = std::make_pair(primal_face_id, dual_cell_id);
+        }
+        return primal_face_to_dual_cell_map;
+      }();
+    }
+  }
+
+  m_mapper =
+    std::make_shared<ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>(primal_connectivity,
+                                                                                 dynamic_cast<const ConnectivityType&>(
+                                                                                   *m_connectivity),
+                                                                                 m_primal_node_to_dual_node_map,
+                                                                                 m_primal_cell_to_dual_node_map,
+                                                                                 m_primal_face_to_dual_cell_map);
 }
 
 DiamondDualConnectivityBuilder::DiamondDualConnectivityBuilder(const IConnectivity& connectivity)
 {
+  if (parallel::size() > 1) {
+    throw NotImplementedError("Construction of diamond dual mesh is not implemented in parallel");
+  }
   switch (connectivity.dimension()) {
   case 1: {
     this->_buildDiamondConnectivityFrom<1>(connectivity);
diff --git a/src/mesh/DiamondDualConnectivityBuilder.hpp b/src/mesh/DiamondDualConnectivityBuilder.hpp
index 670372df9e53d3bfa8dd3f1b03545d9264b41dc4..36b120811ad6478af90b89112740d272014b2e7a 100644
--- a/src/mesh/DiamondDualConnectivityBuilder.hpp
+++ b/src/mesh/DiamondDualConnectivityBuilder.hpp
@@ -2,6 +2,7 @@
 #define DIAMOND_DUAL_CONNECTIVITY_BUILDER_HPP
 
 #include <mesh/ConnectivityBuilderBase.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
 
 #include <memory>
 
@@ -9,17 +10,33 @@ template <size_t>
 class Connectivity;
 class ConnectivityDescriptor;
 
+class IConnectivityToDiamondDualConnectivityDataMapper;
+
 class DiamondDualConnectivityBuilder : public ConnectivityBuilderBase
 {
  private:
+  NodeIdToNodeIdMap m_primal_node_to_dual_node_map;
+  CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  FaceIdToCellIdMap m_primal_face_to_dual_cell_map;
+
+  std::shared_ptr<IConnectivityToDiamondDualConnectivityDataMapper> m_mapper;
+
   template <size_t Dimension>
   void _buildDiamondConnectivityDescriptor(const Connectivity<Dimension>&, ConnectivityDescriptor&);
 
   template <size_t Dimension>
   void _buildDiamondConnectivityFrom(const IConnectivity&);
 
- public:
+  friend class DiamondDualConnectivityManager;
   DiamondDualConnectivityBuilder(const IConnectivity&);
+
+ public:
+  std::shared_ptr<IConnectivityToDiamondDualConnectivityDataMapper>
+  mapper() const
+  {
+    return m_mapper;
+  }
+
   ~DiamondDualConnectivityBuilder() = default;
 };
 
diff --git a/src/mesh/DiamondDualConnectivityManager.cpp b/src/mesh/DiamondDualConnectivityManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9459010b69e89acaf3f247866d2f6e81ef00cd81
--- /dev/null
+++ b/src/mesh/DiamondDualConnectivityManager.cpp
@@ -0,0 +1,96 @@
+#include <utils/PugsAssert.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/DiamondDualConnectivityBuilder.hpp>
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <utils/Exceptions.hpp>
+
+#include <sstream>
+
+DiamondDualConnectivityManager* DiamondDualConnectivityManager::m_instance{nullptr};
+
+void
+DiamondDualConnectivityManager::create()
+{
+  Assert(m_instance == nullptr, "DiamondDualConnectivityManager is already created");
+  m_instance = new DiamondDualConnectivityManager;
+}
+
+void
+DiamondDualConnectivityManager::destroy()
+{
+  Assert(m_instance != nullptr, "DiamondDualConnectivityManager was not created!");
+
+  if (m_instance->m_connectivity_to_diamond_dual_connectivity_info_map.size() > 0) {
+    std::stringstream error;
+    error << ": some connectivities are still registered\n";
+    for (const auto& [connectivity, diamond_dual_connectivity_info] :
+         m_instance->m_connectivity_to_diamond_dual_connectivity_info_map) {
+      error << " - connectivity " << rang::fgB::magenta << connectivity << rang::style::reset << '\n';
+    }
+    throw UnexpectedError(error.str());
+  }
+  delete m_instance;
+  m_instance = nullptr;
+}
+
+void
+DiamondDualConnectivityManager::deleteConnectivity(const IConnectivity* p_connectivity)
+{
+  m_connectivity_to_diamond_dual_connectivity_info_map.erase(p_connectivity);
+}
+
+DiamondDualConnectivityManager::DiamondDualConnectivityInfo
+DiamondDualConnectivityManager::_getDiamondDualConnectivityInfo(const IConnectivity& connectivity)
+{
+  const IConnectivity* p_connectivity = &connectivity;
+
+  if (auto i_connectivity = m_connectivity_to_diamond_dual_connectivity_info_map.find(p_connectivity);
+      i_connectivity != m_connectivity_to_diamond_dual_connectivity_info_map.end()) {
+    return i_connectivity->second;
+  } else {
+    DiamondDualConnectivityBuilder builder{connectivity};
+
+    DiamondDualConnectivityInfo connectivity_info{builder.connectivity(), builder.mapper()};
+
+    m_connectivity_to_diamond_dual_connectivity_info_map[p_connectivity] = connectivity_info;
+
+    return connectivity_info;
+  }
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Connectivity<Dimension>>
+DiamondDualConnectivityManager::getDiamondDualConnectivity(const Connectivity<Dimension>& connectivity)
+{
+  return std::dynamic_pointer_cast<const Connectivity<Dimension>>(
+    this->_getDiamondDualConnectivityInfo(connectivity).diamondDualConnectivity());
+}
+
+template <size_t Dimension>
+std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>
+DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(
+  const Connectivity<Dimension>& connectivity)
+{
+  return std::dynamic_pointer_cast<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>(
+    this->_getDiamondDualConnectivityInfo(connectivity).connectivityToDiamondDualConnectivityDataMapper());
+}
+
+template std::shared_ptr<const Connectivity<1>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<1>& connectivity);
+
+template std::shared_ptr<const Connectivity<2>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<2>& connectivity);
+
+template std::shared_ptr<const Connectivity<3>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<3>& connectivity);
+
+template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<1>>
+DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<1>&);
+
+template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<2>>
+DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<2>&);
+
+template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<3>>
+DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<3>&);
diff --git a/src/mesh/DiamondDualConnectivityManager.hpp b/src/mesh/DiamondDualConnectivityManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..33dfbaf4cf7cc8e59eebfc095624d43c5bf038b3
--- /dev/null
+++ b/src/mesh/DiamondDualConnectivityManager.hpp
@@ -0,0 +1,94 @@
+#ifndef DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
+#define DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
+
+#include <mesh/IConnectivity.hpp>
+
+#include <memory>
+#include <unordered_map>
+
+template <size_t Dimension>
+class Connectivity;
+
+class IConnectivityToDiamondDualConnectivityDataMapper;
+
+template <size_t Dimension>
+class ConnectivityToDiamondDualConnectivityDataMapper;
+
+class DiamondDualConnectivityManager
+{
+ private:
+  class DiamondDualConnectivityInfo
+  {
+   private:
+    std::shared_ptr<const IConnectivity> m_diamond_dual_connectivity;
+    std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>
+      m_connectivity_to_diamond_dual_connectivity_data_mapper;
+
+   public:
+    PUGS_INLINE
+    std::shared_ptr<const IConnectivity>
+    diamondDualConnectivity() const
+    {
+      return m_diamond_dual_connectivity;
+    }
+
+    PUGS_INLINE
+    std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>
+    connectivityToDiamondDualConnectivityDataMapper()
+    {
+      return m_connectivity_to_diamond_dual_connectivity_data_mapper;
+    }
+
+    DiamondDualConnectivityInfo& operator=(const DiamondDualConnectivityInfo&) = default;
+    DiamondDualConnectivityInfo& operator=(DiamondDualConnectivityInfo&&) = default;
+
+    DiamondDualConnectivityInfo()                                   = default;
+    DiamondDualConnectivityInfo(const DiamondDualConnectivityInfo&) = default;
+    DiamondDualConnectivityInfo(DiamondDualConnectivityInfo&&)      = default;
+
+    DiamondDualConnectivityInfo(const std::shared_ptr<const IConnectivity>& diamond_dual_connectivity,
+                                const std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>&
+                                  connectivity_to_diamond_dual_connectivity_data_mapper)
+      : m_diamond_dual_connectivity{diamond_dual_connectivity},
+        m_connectivity_to_diamond_dual_connectivity_data_mapper{connectivity_to_diamond_dual_connectivity_data_mapper}
+    {}
+
+    ~DiamondDualConnectivityInfo() = default;
+  };
+
+  DiamondDualConnectivityInfo _getDiamondDualConnectivityInfo(const IConnectivity& connectivity);
+
+  std::unordered_map<const IConnectivity*, DiamondDualConnectivityInfo>
+    m_connectivity_to_diamond_dual_connectivity_info_map;
+
+  static DiamondDualConnectivityManager* m_instance;
+
+  DiamondDualConnectivityManager(const DiamondDualConnectivityManager&) = delete;
+  DiamondDualConnectivityManager(DiamondDualConnectivityManager&&)      = delete;
+
+  DiamondDualConnectivityManager()  = default;
+  ~DiamondDualConnectivityManager() = default;
+
+ public:
+  static void create();
+  static void destroy();
+
+  PUGS_INLINE
+  static DiamondDualConnectivityManager&
+  instance()
+  {
+    Assert(m_instance != nullptr, "DiamondDualConnectivityManager was not created!");
+    return *m_instance;
+  }
+
+  void deleteConnectivity(const IConnectivity*);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Connectivity<Dimension>> getDiamondDualConnectivity(const Connectivity<Dimension>&);
+
+  template <size_t Dimension>
+  std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>
+  getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>&);
+};
+
+#endif   // DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
diff --git a/src/mesh/DiamondDualMeshBuilder.cpp b/src/mesh/DiamondDualMeshBuilder.cpp
index 2218541b338e196a913b3c0859b882ad668d252e..df4157c9c19b377f77b754d3f33ac54141539f2f 100644
--- a/src/mesh/DiamondDualMeshBuilder.cpp
+++ b/src/mesh/DiamondDualMeshBuilder.cpp
@@ -1,117 +1,60 @@
 #include <mesh/DiamondDualMeshBuilder.hpp>
 
-#include <mesh/DiamondDualConnectivityBuilder.hpp>
-
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityDescriptor.hpp>
-#include <mesh/ConnectivityDispatcher.hpp>
+#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/DiamondDualConnectivityBuilder.hpp>
+#include <mesh/DiamondDualConnectivityManager.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
-#include <mesh/RefId.hpp>
-#include <utils/Array.hpp>
-#include <utils/Messenger.hpp>
 
 template <size_t Dimension>
 void
-DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(
-  const Mesh<Connectivity<Dimension>>& primal_mesh,
-  const std::shared_ptr<const Connectivity<Dimension>>& p_diamond_connectivity)
+DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const std::shared_ptr<const IMesh>& p_i_mesh)
 {
   using ConnectivityType = Connectivity<Dimension>;
   using MeshType         = Mesh<Connectivity<Dimension>>;
 
-  const ConnectivityType& diamond_connectivity = *p_diamond_connectivity;
-
-  NodeValue<TinyVector<Dimension>> diamond_xr{diamond_connectivity};
-
-  const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
-  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
-  const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
-
-  NodeId i_node = 0;
-  for (; i_node < primal_mesh.numberOfNodes(); ++i_node) {
-    diamond_xr[i_node] = primal_xr[i_node];
-  }
+  std::shared_ptr p_primal_mesh = std::dynamic_pointer_cast<const MeshType>(p_i_mesh);
+  const MeshType& primal_mesh   = *p_primal_mesh;
 
-  for (CellId i_cell = 0; i_cell < primal_mesh.numberOfCells(); ++i_cell) {
-    diamond_xr[i_node++] = primal_xj[i_cell];
-  }
+  DiamondDualConnectivityManager& manager = DiamondDualConnectivityManager::instance();
 
-  m_mesh = std::make_shared<MeshType>(p_diamond_connectivity, diamond_xr);
-}
-
-template <>
-void
-DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const Mesh<Connectivity<1>>& primal_mesh,
-                                                  const std::shared_ptr<const Connectivity<1>>& p_diamond_connectivity)
-{
-  using ConnectivityType = Connectivity<1>;
-  using MeshType         = Mesh<Connectivity<1>>;
-
-  const ConnectivityType& primal_connectivity = primal_mesh.connectivity();
-  const auto& node_to_cell_matrix             = primal_connectivity.nodeToCellMatrix();
+  std::shared_ptr<const ConnectivityType> p_diamond_connectivity =
+    manager.getDiamondDualConnectivity(primal_mesh.connectivity());
 
   const ConnectivityType& diamond_connectivity = *p_diamond_connectivity;
 
-  NodeValue<TinyVector<1>> diamond_xr{diamond_connectivity};
+  const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
 
-  const NodeValue<const TinyVector<1>> primal_xr = primal_mesh.xr();
-  MeshData<1>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
-  const CellValue<const TinyVector<1>> primal_xj = primal_mesh_data.xj();
+  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
 
-  NodeId next_node_id = 0;
-  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
-    if (node_to_cell_matrix[primal_node_id].size() == 1) {
-      diamond_xr[next_node_id++] = primal_xr[primal_node_id];
-    }
-  }
+  std::shared_ptr connectivity_to_diamond_dual_connectivity_data_mapper =
+    manager.getConnectivityToDiamondDualConnectivityDataMapper(primal_mesh.connectivity());
 
-  for (CellId i_cell = 0; i_cell < primal_mesh.numberOfCells(); ++i_cell) {
-    diamond_xr[next_node_id++] = primal_xj[i_cell];
-  }
+  NodeValue<TinyVector<Dimension>> diamond_xr{diamond_connectivity};
+  connectivity_to_diamond_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, diamond_xr);
 
   m_mesh = std::make_shared<MeshType>(p_diamond_connectivity, diamond_xr);
 }
 
 DiamondDualMeshBuilder::DiamondDualMeshBuilder(const std::shared_ptr<const IMesh>& p_mesh)
 {
+  std::cout << "building DiamondDualMesh\n";
+
   switch (p_mesh->dimension()) {
   case 1: {
-    using ConnectivityType = Connectivity<1>;
-    using MeshType         = Mesh<ConnectivityType>;
-
-    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(p_mesh);
-    DiamondDualConnectivityBuilder builder(mesh->connectivity());
-
-    std::shared_ptr p_diamond_connectivity = std::dynamic_pointer_cast<const ConnectivityType>(builder.connectivity());
-
-    this->_buildDualDiamondMeshFrom(*mesh, p_diamond_connectivity);
+    this->_buildDualDiamondMeshFrom<1>(p_mesh);
     break;
   }
   case 2: {
-    using ConnectivityType = Connectivity<2>;
-    using MeshType         = Mesh<ConnectivityType>;
-
-    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(p_mesh);
-    DiamondDualConnectivityBuilder builder(mesh->connectivity());
-
-    std::shared_ptr p_diamond_connectivity = std::dynamic_pointer_cast<const ConnectivityType>(builder.connectivity());
-
-    this->_buildDualDiamondMeshFrom(*mesh, p_diamond_connectivity);
+    this->_buildDualDiamondMeshFrom<2>(p_mesh);
     break;
   }
   case 3: {
-    using ConnectivityType = Connectivity<3>;
-    using MeshType         = Mesh<ConnectivityType>;
-
-    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(p_mesh);
-    DiamondDualConnectivityBuilder builder(mesh->connectivity());
-
-    std::shared_ptr p_diamond_connectivity = std::dynamic_pointer_cast<const ConnectivityType>(builder.connectivity());
-
-    this->_buildDualDiamondMeshFrom(*mesh, p_diamond_connectivity);
+    this->_buildDualDiamondMeshFrom<3>(p_mesh);
     break;
   }
   default: {
diff --git a/src/mesh/DiamondDualMeshBuilder.hpp b/src/mesh/DiamondDualMeshBuilder.hpp
index ded3cfb0f40ca4d1b1bd9dc06f7981391f9401c6..2e47cfe21b4790a63e0e4aeba2fcfb9c59699286 100644
--- a/src/mesh/DiamondDualMeshBuilder.hpp
+++ b/src/mesh/DiamondDualMeshBuilder.hpp
@@ -5,21 +5,16 @@
 
 #include <memory>
 
-template <size_t>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
 class DiamondDualMeshBuilder : public MeshBuilderBase
 {
  private:
   template <size_t Dimension>
-  void _buildDualDiamondMeshFrom(const Mesh<Connectivity<Dimension>>&,
-                                 const std::shared_ptr<const Connectivity<Dimension>>&);
+  void _buildDualDiamondMeshFrom(const std::shared_ptr<const IMesh>&);
 
- public:
+  friend class DiamondDualMeshManager;
   DiamondDualMeshBuilder(const std::shared_ptr<const IMesh>&);
+
+ public:
   ~DiamondDualMeshBuilder() = default;
 };
 
diff --git a/src/mesh/DiamondDualMeshManager.cpp b/src/mesh/DiamondDualMeshManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0619736753f2b49a4229c60492ea6fd4c0de7212
--- /dev/null
+++ b/src/mesh/DiamondDualMeshManager.cpp
@@ -0,0 +1,65 @@
+#include <mesh/DiamondDualMeshManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DiamondDualMeshBuilder.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/Exceptions.hpp>
+#include <utils/PugsAssert.hpp>
+
+#include <sstream>
+
+DiamondDualMeshManager* DiamondDualMeshManager::m_instance{nullptr};
+
+void
+DiamondDualMeshManager::create()
+{
+  Assert(m_instance == nullptr, "DiamondDualMeshManager is already created");
+  m_instance = new DiamondDualMeshManager;
+}
+
+void
+DiamondDualMeshManager::destroy()
+{
+  Assert(m_instance != nullptr, "DiamondDualMeshManager was not created!");
+
+  if (m_instance->m_mesh_to_diamond_dual_mesh_map.size() > 0) {
+    std::stringstream error;
+    error << ": some meshes are still registered\n";
+    for (const auto& i_mesh_data : m_instance->m_mesh_to_diamond_dual_mesh_map) {
+      error << " - mesh " << rang::fgB::magenta << i_mesh_data.first << rang::style::reset << '\n';
+    }
+    throw UnexpectedError(error.str());
+  }
+  delete m_instance;
+  m_instance = nullptr;
+}
+
+void
+DiamondDualMeshManager::deleteMesh(const IMesh* p_mesh)
+{
+  m_mesh_to_diamond_dual_mesh_map.erase(p_mesh);
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Mesh<Connectivity<Dimension>>>
+DiamondDualMeshManager::getDiamondDualMesh(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh)
+{
+  const IMesh* p_mesh = mesh.get();
+
+  if (auto i_mesh_data = m_mesh_to_diamond_dual_mesh_map.find(p_mesh);
+      i_mesh_data != m_mesh_to_diamond_dual_mesh_map.end()) {
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
+  } else {
+    DiamondDualMeshBuilder builder{mesh};
+
+    m_mesh_to_diamond_dual_mesh_map[p_mesh] = builder.mesh();
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
+  }
+}
+
+template std::shared_ptr<const Mesh<Connectivity<1>>> DiamondDualMeshManager::getDiamondDualMesh(
+  std::shared_ptr<const Mesh<Connectivity<1>>>);
+template std::shared_ptr<const Mesh<Connectivity<2>>> DiamondDualMeshManager::getDiamondDualMesh(
+  std::shared_ptr<const Mesh<Connectivity<2>>>);
+template std::shared_ptr<const Mesh<Connectivity<3>>> DiamondDualMeshManager::getDiamondDualMesh(
+  std::shared_ptr<const Mesh<Connectivity<3>>>);
diff --git a/src/mesh/DiamondDualMeshManager.hpp b/src/mesh/DiamondDualMeshManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f802eb96d13704235f50e82ad75e84ba9be8a64
--- /dev/null
+++ b/src/mesh/DiamondDualMeshManager.hpp
@@ -0,0 +1,49 @@
+#ifndef DIAMOND_DUAL_MESH_MANAGER_HPP
+#define DIAMOND_DUAL_MESH_MANAGER_HPP
+
+#include <mesh/IMesh.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <memory>
+#include <unordered_map>
+
+template <size_t Dimension>
+class Connectivity;
+
+template <typename ConnectivityType>
+class Mesh;
+
+class DiamondDualMeshManager
+{
+ private:
+  std::unordered_map<const IMesh*, std::shared_ptr<const IMesh>> m_mesh_to_diamond_dual_mesh_map;
+
+  static DiamondDualMeshManager* m_instance;
+
+  DiamondDualMeshManager(const DiamondDualMeshManager&) = delete;
+  DiamondDualMeshManager(DiamondDualMeshManager&&)      = delete;
+
+  DiamondDualMeshManager()  = default;
+  ~DiamondDualMeshManager() = default;
+
+ public:
+  static void create();
+  static void destroy();
+
+  PUGS_INLINE
+  static DiamondDualMeshManager&
+  instance()
+  {
+    Assert(m_instance != nullptr, "DiamondDualMeshManager was not created!");
+    return *m_instance;
+  }
+
+  void deleteMesh(const IMesh*);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getDiamondDualMesh(
+    std::shared_ptr<const Mesh<Connectivity<Dimension>>>);
+};
+
+#endif   // DIAMOND_DUAL_MESH_MANAGER_HPP
diff --git a/src/mesh/IConnectivity.cpp b/src/mesh/IConnectivity.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..48cf57d55ebceb7818f755fd9214807a247f6575
--- /dev/null
+++ b/src/mesh/IConnectivity.cpp
@@ -0,0 +1,10 @@
+#include <mesh/IConnectivity.hpp>
+
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/SynchronizerManager.hpp>
+
+IConnectivity::~IConnectivity()
+{
+  SynchronizerManager::instance().deleteConnectivitySynchronizer(this);
+  DiamondDualConnectivityManager::instance().deleteConnectivity(this);
+}
diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp
index 27be707cbcc4976b17e9db1e7bf6f37195c9d63a..9bfb8e5043088a18880440235cd72b1677c1f519 100644
--- a/src/mesh/IConnectivity.hpp
+++ b/src/mesh/IConnectivity.hpp
@@ -35,7 +35,7 @@ class IConnectivity : public std::enable_shared_from_this<IConnectivity>
   IConnectivity()                     = default;
   IConnectivity(IConnectivity&&)      = delete;
   IConnectivity(const IConnectivity&) = delete;
-  ~IConnectivity()                    = default;
+  virtual ~IConnectivity();
 };
 
 template <>
diff --git a/src/mesh/IMesh.cpp b/src/mesh/IMesh.cpp
index cb2accc141ed09c778237587373494797fee726d..f315f4b74b01eb371c42eb4bf029f81e93ae6016 100644
--- a/src/mesh/IMesh.cpp
+++ b/src/mesh/IMesh.cpp
@@ -1,8 +1,10 @@
 #include <mesh/IMesh.hpp>
 
+#include <mesh/DiamondDualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 
 IMesh::~IMesh()
 {
-  MeshDataManager::instance().deleteMeshData(*this);
+  MeshDataManager::instance().deleteMeshData(this);
+  DiamondDualMeshManager::instance().deleteMesh(this);
 }
diff --git a/src/mesh/ItemIdToItemIdMap.hpp b/src/mesh/ItemIdToItemIdMap.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e92eb13ce3943c4e2487081ee2e48fc3deac8ee
--- /dev/null
+++ b/src/mesh/ItemIdToItemIdMap.hpp
@@ -0,0 +1,30 @@
+#ifndef ITEM_ID_TO_ITEM_ID_MAP_HPP
+#define ITEM_ID_TO_ITEM_ID_MAP_HPP
+
+#include <mesh/ItemId.hpp>
+#include <utils/Array.hpp>
+
+template <ItemType type1, ItemType type2>
+using ItemIdToItemIdMap = Array<std::pair<ItemIdT<type1>, ItemIdT<type2>>>;
+
+using NodeIdToNodeIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::node>;
+using NodeIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::edge>;
+using NodeIdToFaceIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::face>;
+using NodeIdToCellIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::cell>;
+
+using EdgeIdToNodeIdMap = ItemIdToItemIdMap<ItemType::edge, ItemType::node>;
+using EdgeIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::edge, ItemType::edge>;
+using EdgeIdToFaceIdMap = ItemIdToItemIdMap<ItemType::edge, ItemType::face>;
+using EdgeIdToCellIdMap = ItemIdToItemIdMap<ItemType::edge, ItemType::cell>;
+
+using FaceIdToNodeIdMap = ItemIdToItemIdMap<ItemType::face, ItemType::node>;
+using FaceIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::face, ItemType::edge>;
+using FaceIdToFaceIdMap = ItemIdToItemIdMap<ItemType::face, ItemType::face>;
+using FaceIdToCellIdMap = ItemIdToItemIdMap<ItemType::face, ItemType::cell>;
+
+using CellIdToNodeIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::node>;
+using CellIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::edge>;
+using CellIdToFaceIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::face>;
+using CellIdToCellIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::cell>;
+
+#endif   // ITEM_ID_TO_ITEM_ID_MAP_HPP
diff --git a/src/mesh/MeshDataManager.cpp b/src/mesh/MeshDataManager.cpp
index 281e7517aa704e6c4352769e88f854b86cbc7366..8ca184a6013c083f5e8df7412d19f80b8f50f95a 100644
--- a/src/mesh/MeshDataManager.cpp
+++ b/src/mesh/MeshDataManager.cpp
@@ -35,9 +35,9 @@ MeshDataManager::destroy()
 }
 
 void
-MeshDataManager::deleteMeshData(const IMesh& mesh)
+MeshDataManager::deleteMeshData(const IMesh* p_mesh)
 {
-  m_mesh_mesh_data_map.erase(&mesh);
+  m_mesh_mesh_data_map.erase(p_mesh);
 }
 
 template <size_t Dimension>
diff --git a/src/mesh/MeshDataManager.hpp b/src/mesh/MeshDataManager.hpp
index 7b127148b889bc585082d626b79c435293fa4cd7..fd04101dea29a2d15e0feb2193f22f785ff65dc3 100644
--- a/src/mesh/MeshDataManager.hpp
+++ b/src/mesh/MeshDataManager.hpp
@@ -5,8 +5,8 @@
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
-#include <map>
 #include <memory>
+#include <unordered_map>
 
 class IMesh;
 
@@ -22,7 +22,7 @@ class MeshData;
 class MeshDataManager
 {
  private:
-  std::map<const IMesh*, std::shared_ptr<IMeshData>> m_mesh_mesh_data_map;
+  std::unordered_map<const IMesh*, std::shared_ptr<IMeshData>> m_mesh_mesh_data_map;
 
   static MeshDataManager* m_instance;
 
@@ -44,7 +44,7 @@ class MeshDataManager
     return *m_instance;
   }
 
-  void deleteMeshData(const IMesh&);
+  void deleteMeshData(const IMesh*);
 
   template <size_t Dimension>
   MeshData<Dimension>& getMeshData(const Mesh<Connectivity<Dimension>>&);
diff --git a/src/mesh/SynchronizerManager.hpp b/src/mesh/SynchronizerManager.hpp
index 79cb3db0a3473bdbe9f88d4d20836a835aca9260..8bcb441f05c4e841a71c103f9a252bb52c59dcb2 100644
--- a/src/mesh/SynchronizerManager.hpp
+++ b/src/mesh/SynchronizerManager.hpp
@@ -4,8 +4,8 @@
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
-#include <map>
 #include <memory>
+#include <unordered_map>
 
 class IConnectivity;
 class Synchronizer;
@@ -13,7 +13,7 @@ class Synchronizer;
 class SynchronizerManager
 {
  private:
-  std::map<const IConnectivity*, std::shared_ptr<Synchronizer>> m_connectivity_synchronizer_map;
+  std::unordered_map<const IConnectivity*, std::shared_ptr<Synchronizer>> m_connectivity_synchronizer_map;
 
   static SynchronizerManager* m_instance;
 
diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp
index 5d85b3630e2afc77ef7f2a3422b2f7d207abe5b7..d2459353e4bffa282eac8ff27e21c63d93273664 100644
--- a/src/output/VTKWriter.hpp
+++ b/src/output/VTKWriter.hpp
@@ -8,12 +8,14 @@
 #include <output/OutputNamedItemValueSet.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/Messenger.hpp>
+#include <utils/PugsAssert.hpp>
 
 #include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <sstream>
 #include <string>
+#include <unordered_map>
 
 class VTKWriter
 {
@@ -234,6 +236,10 @@ class VTKWriter
         std::visit([&, name = name](auto&& item_value) { return this->_write_cell_pvtu(fout, name, item_value); },
                    item_value_variant);
       }
+      if constexpr (MeshType::Dimension == 3) {
+        fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faces\"/>\n";
+        fout << "<PDataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\"/>\n";
+      }
       fout << "</PCells>\n";
 
       fout << "<PPointData>\n";
@@ -347,6 +353,10 @@ class VTKWriter
               types[j] = 12;
               break;
             }
+            case CellType::Diamond: {
+              types[j] = 41;
+              break;
+            }
             default: {
               std::ostringstream os;
               os << __FILE__ << ':' << __LINE__ << ": unknown cell type";
@@ -356,7 +366,62 @@ class VTKWriter
           });
         _write_array(fout, "types", types);
       }
+      if constexpr (MeshType::Dimension == 3) {
+        const auto& cell_to_face_matrix   = mesh->connectivity().cellToFaceMatrix();
+        const auto& cell_to_node_matrix   = mesh->connectivity().cellToNodeMatrix();
+        const auto& face_to_node_matrix   = mesh->connectivity().faceToNodeMatrix();
+        const auto& cell_face_is_reversed = mesh->connectivity().cellFaceIsReversed();
+
+        Array<size_t> faces_offsets(mesh->numberOfCells());
+        size_t next_offset = 0;
+        fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faces\">\n";
+        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+          const auto& cell_nodes = cell_to_node_matrix[cell_id];
+          std::unordered_map<size_t, size_t> node_id_to_local_number_in_cell;
+          for (size_t i_cell_node = 0; i_cell_node < cell_nodes.size(); ++i_cell_node) {
+            node_id_to_local_number_in_cell[cell_nodes[i_cell_node]] = i_cell_node;
+          }
+
+          const auto& cell_faces = cell_to_face_matrix[cell_id];
+          fout << cell_faces.size() << '\n';
+          next_offset++;
+          for (size_t i_cell_face = 0; i_cell_face < cell_faces.size(); ++i_cell_face) {
+            const FaceId& face_id  = cell_faces[i_cell_face];
+            const auto& face_nodes = face_to_node_matrix[face_id];
+            fout << face_nodes.size();
+            next_offset++;
+            Array<size_t> face_node_in_cell(face_nodes.size());
+
+            for (size_t i_face_node = 0; i_face_node < face_nodes.size(); ++i_face_node) {
+              const NodeId& node_id                  = face_nodes[i_face_node];
+              auto i_node_id_to_local_number_in_cell = node_id_to_local_number_in_cell.find(node_id);
+              Assert(i_node_id_to_local_number_in_cell != node_id_to_local_number_in_cell.end());
+              face_node_in_cell[i_face_node] = i_node_id_to_local_number_in_cell->second;
+            }
+
+            if (cell_face_is_reversed(cell_id, i_cell_face)) {
+              for (size_t i = 0; i < face_node_in_cell.size(); ++i) {
+                fout << ' ' << face_node_in_cell[face_node_in_cell.size() - 1 - i];
+              }
+            } else {
+              for (size_t i = 0; i < face_node_in_cell.size(); ++i) {
+                fout << ' ' << face_node_in_cell[i];
+              }
+            }
+
+            next_offset += face_nodes.size();
 
+            fout << '\n';
+          }
+          faces_offsets[cell_id] = next_offset;
+        }
+        fout << "</DataArray>\n";
+        fout << "<DataArray type=\"Int64\" IdType=\"1\" Name=\"faceoffsets\">\n";
+        for (size_t i_face_offsets = 0; i_face_offsets < faces_offsets.size(); ++i_face_offsets) {
+          fout << faces_offsets[i_face_offsets] << '\n';
+        }
+        fout << "</DataArray>\n";
+      }
       fout << "</Cells>\n";
       fout << "</Piece>\n";
       fout << "</UnstructuredGrid>\n";