diff --git a/src/dev/ParallelChecker.hpp b/src/dev/ParallelChecker.hpp
index 97d82ee4bac3badfdc760c973d81454e89102200..91b4bc1ab39907d681123387a016086fa3c1ca0e 100644
--- a/src/dev/ParallelChecker.hpp
+++ b/src/dev/ParallelChecker.hpp
@@ -1432,18 +1432,18 @@ parallel_check(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>&
   }
 }
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 void PUGS_INLINE
-parallel_check(const DiscreteFunctionP0<Dimension, DataType>& discrete_function,
+parallel_check(const DiscreteFunctionP0<DataType>& discrete_function,
                const std::string& name,
                const SourceLocation& source_location = SourceLocation{})
 {
   parallel_check(discrete_function.cellValues(), name, source_location);
 }
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 void PUGS_INLINE
-parallel_check(const DiscreteFunctionP0Vector<Dimension, DataType>& discrete_function,
+parallel_check(const DiscreteFunctionP0Vector<DataType>& discrete_function,
                const std::string& name,
                const SourceLocation& source_location = SourceLocation{})
 {
diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp
index 9f709e2fd5cf70f31fa491ae8cd62fbe0986022f..548f42ddac09caded1533a96c572e9722bf6a63b 100644
--- a/src/language/modules/MeshModule.cpp
+++ b/src/language/modules/MeshModule.cpp
@@ -23,6 +23,8 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshRelaxer.hpp>
 #include <mesh/MeshTransformer.hpp>
+#include <mesh/MeshUtils.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/NamedBoundaryDescriptor.hpp>
 #include <mesh/NamedInterfaceDescriptor.hpp>
 #include <mesh/NamedZoneDescriptor.hpp>
@@ -37,7 +39,7 @@
 
 MeshModule::MeshModule()
 {
-  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IMesh>>);
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const MeshVariant>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryDescriptor>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IInterfaceDescriptor>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IZoneDescriptor>>);
@@ -83,7 +85,7 @@ MeshModule::MeshModule()
 
   this->_addBuiltinFunction("readGmsh", std::function(
 
-                                          [](const std::string& file_name) -> std::shared_ptr<const IMesh> {
+                                          [](const std::string& file_name) -> std::shared_ptr<const MeshVariant> {
                                             GmshReader gmsh_reader(file_name);
                                             return gmsh_reader.mesh();
                                           }
@@ -140,42 +142,44 @@ MeshModule::MeshModule()
 
                                           ));
 
-  this->_addBuiltinFunction("interpolate",
-                            std::function(
+  this
+    ->_addBuiltinFunction("interpolate",
+                          std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh, std::shared_ptr<const ItemType> item_type,
-                                 const FunctionSymbolId& function_id) -> std::shared_ptr<const ItemValueVariant> {
-                                return ItemValueVariantFunctionInterpoler{mesh, *item_type, function_id}.interpolate();
-                              }
+                            [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type,
+                               const FunctionSymbolId& function_id) -> std::shared_ptr<const ItemValueVariant> {
+                              return ItemValueVariantFunctionInterpoler{mesh_v, *item_type, function_id}.interpolate();
+                            }
 
-                              ));
+                            ));
 
   this->_addBuiltinFunction(
     "interpolate_array",
     std::function(
 
-      [](std::shared_ptr<const IMesh> mesh, std::shared_ptr<const ItemType> item_type,
+      [](std::shared_ptr<const MeshVariant> mesh_v, std::shared_ptr<const ItemType> item_type,
          const std::vector<FunctionSymbolId>& function_id_list) -> std::shared_ptr<const ItemArrayVariant> {
-        return ItemArrayVariantFunctionInterpoler{mesh, *item_type, function_id_list}.interpolate();
+        return ItemArrayVariantFunctionInterpoler{mesh_v, *item_type, function_id_list}.interpolate();
       }
 
       ));
 
-  this->_addBuiltinFunction("transform", std::function(
+  this->_addBuiltinFunction("transform",
+                            std::function(
 
-                                           [](std::shared_ptr<const IMesh> p_mesh,
-                                              const FunctionSymbolId& function_id) -> std::shared_ptr<const IMesh> {
-                                             return MeshTransformer{}.transform(function_id, p_mesh);
-                                           }
+                              [](std::shared_ptr<const MeshVariant> mesh_v,
+                                 const FunctionSymbolId& function_id) -> std::shared_ptr<const MeshVariant> {
+                                return MeshTransformer{}.transform(function_id, mesh_v);
+                              }
 
-                                           ));
+                              ));
 
   this->_addBuiltinFunction("relax", std::function(
 
-                                       [](const std::shared_ptr<const IMesh>& source_mesh,
-                                          const std::shared_ptr<const IMesh>& destination_mesh,
-                                          const double& theta) -> std::shared_ptr<const IMesh> {
-                                         return MeshRelaxer{}.relax(source_mesh, destination_mesh, theta);
+                                       [](const std::shared_ptr<const MeshVariant>& source_mesh_v,
+                                          const std::shared_ptr<const MeshVariant>& destination_mesh_v,
+                                          const double& theta) -> std::shared_ptr<const MeshVariant> {
+                                         return MeshRelaxer{}.relax(source_mesh_v, destination_mesh_v, theta);
                                        }
 
                                        ));
@@ -183,30 +187,8 @@ MeshModule::MeshModule()
   this->_addBuiltinFunction("check_connectivity_ordering",
                             std::function(
 
-                              [](const std::shared_ptr<const IMesh>& i_mesh) -> bool {
-                                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 checkConnectivityOrdering(p_mesh->connectivity());
-                                }
-                                case 2: {
-                                  using MeshType = Mesh<Connectivity<2>>;
-
-                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return checkConnectivityOrdering(p_mesh->connectivity());
-                                }
-                                case 3: {
-                                  using MeshType = Mesh<Connectivity<3>>;
-
-                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return checkConnectivityOrdering(p_mesh->connectivity());
-                                }
-                                default: {
-                                  throw UnexpectedError("invalid dimension");
-                                }
-                                }
+                              [](const std::shared_ptr<const MeshVariant>& mesh_v) -> bool {
+                                return checkConnectivityOrdering(mesh_v);
                               }
 
                               ));
@@ -215,7 +197,7 @@ MeshModule::MeshModule()
                             std::function(
 
                               [](const TinyVector<1> a, const TinyVector<1> b,
-                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const IMesh> {
+                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const MeshVariant> {
                                 constexpr uint64_t dimension = 1;
 
                                 if (box_sizes.size() != dimension) {
@@ -241,7 +223,7 @@ MeshModule::MeshModule()
                             std::function(
 
                               [](const TinyVector<2> a, const TinyVector<2> b,
-                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const IMesh> {
+                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const MeshVariant> {
                                 constexpr uint64_t dimension = 2;
 
                                 if (box_sizes.size() != dimension) {
@@ -267,7 +249,7 @@ MeshModule::MeshModule()
                             std::function(
 
                               [](const TinyVector<3>& a, const TinyVector<3>& b,
-                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const IMesh> {
+                                 const std::vector<uint64_t>& box_sizes) -> std::shared_ptr<const MeshVariant> {
                                 constexpr uint64_t dimension = 3;
 
                                 if (box_sizes.size() != dimension) {
@@ -289,67 +271,23 @@ MeshModule::MeshModule()
 
                               ));
 
-  this->_addBuiltinFunction("diamondDual",
-                            std::function(
-
-                              [](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 DualMeshManager::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 DualMeshManager::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 DualMeshManager::instance().getDiamondDualMesh(*p_mesh);
-                                }
-                                default: {
-                                  throw UnexpectedError("invalid dimension");
-                                }
-                                }
-                              }
-
-                              ));
-
-  this->_addBuiltinFunction("medianDual",
-                            std::function(
+  this->_addBuiltinFunction("diamondDual", std::function(
 
-                              [](const std::shared_ptr<const IMesh>& i_mesh) -> std::shared_ptr<const IMesh> {
-                                switch (i_mesh->dimension()) {
-                                case 1: {
-                                  using MeshType = Mesh<Connectivity<1>>;
+                                             [](const std::shared_ptr<const MeshVariant>& mesh_v)
+                                               -> std::shared_ptr<const MeshVariant> {
+                                               return DualMeshManager::instance().getDiamondDualMesh(mesh_v);
+                                             }
 
-                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
-                                }
-                                case 2: {
-                                  using MeshType = Mesh<Connectivity<2>>;
+                                             ));
 
-                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
-                                }
-                                case 3: {
-                                  using MeshType = Mesh<Connectivity<3>>;
+  this->_addBuiltinFunction("medianDual", std::function(
 
-                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
-                                }
-                                default: {
-                                  throw UnexpectedError("invalid dimension");
-                                }
-                                }
-                              }
+                                            [](const std::shared_ptr<const MeshVariant>& mesh_v)
+                                              -> std::shared_ptr<const MeshVariant> {
+                                              return DualMeshManager::instance().getMedianDualMesh(mesh_v);
+                                            }
 
-                              ));
+                                            ));
 }
 
 void
@@ -358,6 +296,7 @@ MeshModule::registerOperators() const
   OperatorRepository& repository = OperatorRepository::instance();
 
   repository.addBinaryOperator<language::shift_left_op>(
-    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
-                                                    std::shared_ptr<const OStream>, std::shared_ptr<const IMesh>>>());
+    std::make_shared<
+      BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                     std::shared_ptr<const OStream>, std::shared_ptr<const MeshVariant>>>());
 }
diff --git a/src/language/modules/MeshModule.hpp b/src/language/modules/MeshModule.hpp
index 0ea7e5762f164734527aec0e62f7f70967bef01b..10bfc9e1cae386702386508d6861e97ba7857857 100644
--- a/src/language/modules/MeshModule.hpp
+++ b/src/language/modules/MeshModule.hpp
@@ -5,9 +5,9 @@
 #include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <utils/PugsMacros.hpp>
 
-class IMesh;
+class MeshVariant;
 template <>
-inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IMesh>> =
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const MeshVariant>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("mesh");
 
 class IBoundaryDescriptor;
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 813bde93a055a514e35a1e949ca6ea2241609ad2..7b404fcf670c5217721efba890728ce97e3e91a4 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -17,6 +17,7 @@
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshRandomizer.hpp>
 #include <mesh/MeshSmoother.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/AcousticSolver.hpp>
 #include <scheme/AxisBoundaryConditionDescriptor.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
@@ -108,7 +109,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("integrate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  const std::vector<std::shared_ptr<const IZoneDescriptor>>& integration_zone_list,
                                  std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
                                  std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor,
@@ -125,7 +126,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("integrate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
                                  std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor,
                                  const std::vector<FunctionSymbolId>& function_id_list)
@@ -141,7 +142,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("integrate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  const std::vector<std::shared_ptr<const IZoneDescriptor>>& integration_zone_list,
                                  std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
                                  const FunctionSymbolId& function_id)
@@ -157,7 +158,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("integrate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
                                  const FunctionSymbolId& function_id)
                                 -> std::shared_ptr<const DiscreteFunctionVariant> {
@@ -170,7 +171,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("interpolate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  const std::vector<std::shared_ptr<const IZoneDescriptor>>& interpolation_zone_list,
                                  std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor,
                                  const std::vector<FunctionSymbolId>& function_id_list)
@@ -202,7 +203,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("interpolate",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh,
                                  std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor,
                                  const std::vector<FunctionSymbolId>& function_id_list)
                                 -> std::shared_ptr<const DiscreteFunctionVariant> {
@@ -232,11 +233,11 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("randomizeMesh",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> p_mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh_v,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
-                                   bc_descriptor_list) -> std::shared_ptr<const IMesh> {
+                                   bc_descriptor_list) -> std::shared_ptr<const MeshVariant> {
                                 MeshRandomizerHandler handler;
-                                return handler.getRandomizedMesh(*p_mesh, bc_descriptor_list);
+                                return handler.getRandomizedMesh(mesh_v, bc_descriptor_list);
                               }
 
                               ));
@@ -244,23 +245,23 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("randomizeMesh",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> p_mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh_v,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const IMesh> {
+                                 const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const MeshVariant> {
                                 MeshRandomizerHandler handler;
-                                return handler.getRandomizedMesh(*p_mesh, bc_descriptor_list, function_symbol_id);
+                                return handler.getRandomizedMesh(mesh_v, bc_descriptor_list, function_symbol_id);
                               }
 
                               ));
 
   this->_addBuiltinFunction("smoothMesh", std::function(
 
-                                            [](std::shared_ptr<const IMesh> p_mesh,
+                                            [](std::shared_ptr<const MeshVariant> mesh_v,
                                                const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
-                                                 bc_descriptor_list) -> std::shared_ptr<const IMesh> {
+                                                 bc_descriptor_list) -> std::shared_ptr<const MeshVariant> {
                                               MeshSmootherHandler handler;
-                                              return handler.getSmoothedMesh(p_mesh, bc_descriptor_list);
+                                              return handler.getSmoothedMesh(mesh_v, bc_descriptor_list);
                                             }
 
                                             ));
@@ -268,12 +269,12 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("smoothMesh",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> p_mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh_v,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const IMesh> {
+                                 const FunctionSymbolId& function_symbol_id) -> std::shared_ptr<const MeshVariant> {
                                 MeshSmootherHandler handler;
-                                return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, function_symbol_id);
+                                return handler.getSmoothedMesh(mesh_v, bc_descriptor_list, function_symbol_id);
                               }
 
                               ));
@@ -281,26 +282,26 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("smoothMesh",
                             std::function(
 
-                              [](std::shared_ptr<const IMesh> p_mesh,
+                              [](std::shared_ptr<const MeshVariant> mesh_v,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
                                  const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list)
-                                -> std::shared_ptr<const IMesh> {
+                                -> std::shared_ptr<const MeshVariant> {
                                 MeshSmootherHandler handler;
-                                return handler.getSmoothedMesh(p_mesh, bc_descriptor_list, smoothing_zone_list);
+                                return handler.getSmoothedMesh(mesh_v, bc_descriptor_list, smoothing_zone_list);
                               }
 
                               ));
 
   this->_addBuiltinFunction("smoothMesh", std::function(
 
-                                            [](std::shared_ptr<const IMesh> p_mesh,
+                                            [](std::shared_ptr<const MeshVariant> mesh_v,
                                                const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                                  bc_descriptor_list,
                                                const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
-                                                 discrete_function_variant_list) -> std::shared_ptr<const IMesh> {
+                                                 discrete_function_variant_list) -> std::shared_ptr<const MeshVariant> {
                                               MeshSmootherHandler handler;
-                                              return handler.getSmoothedMesh(p_mesh, bc_descriptor_list,
+                                              return handler.getSmoothedMesh(mesh_v, bc_descriptor_list,
                                                                              discrete_function_variant_list);
                                             }
 
@@ -429,7 +430,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>> {
@@ -470,7 +471,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& p,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>> {
@@ -490,7 +491,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& E,        //
                                  const std::shared_ptr<const ItemValueVariant>& ur,              //
                                  const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr,   //
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>> {
@@ -533,7 +534,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& sigma,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
@@ -578,7 +579,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& sigma,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
@@ -600,7 +601,7 @@ SchemeModule::SchemeModule()
                                  const std::shared_ptr<const DiscreteFunctionVariant>& CG,       //
                                  const std::shared_ptr<const ItemValueVariant>& ur,              //
                                  const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr,   //
-                                 const double& dt) -> std::tuple<std::shared_ptr<const IMesh>,
+                                 const double& dt) -> std::tuple<std::shared_ptr<const MeshVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
                                                                  std::shared_ptr<const DiscreteFunctionVariant>,
@@ -615,7 +616,7 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("lagrangian",
                             std::function(
 
-                              [](const std::shared_ptr<const IMesh>& mesh,
+                              [](const std::shared_ptr<const MeshVariant>& mesh,
                                  const std::shared_ptr<const DiscreteFunctionVariant>& v)
                                 -> std::shared_ptr<const DiscreteFunctionVariant> { return shallowCopy(mesh, v); }
 
@@ -632,40 +633,20 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("cell_volume",
                             std::function(
 
-                              [](const std::shared_ptr<const IMesh>& i_mesh)
+                              [](const std::shared_ptr<const MeshVariant>& mesh_v)
                                 -> std::shared_ptr<const DiscreteFunctionVariant> {
-                                switch (i_mesh->dimension()) {
-                                case 1: {
-                                  constexpr size_t Dimension = 1;
-                                  using MeshType             = Mesh<Connectivity<Dimension>>;
-                                  std::shared_ptr<const MeshType> mesh =
-                                    std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh);
-
-                                  return std::make_shared<DiscreteFunctionVariant>(
-                                    DiscreteFunctionP0(mesh, MeshDataManager::instance().getMeshData(*mesh).Vj()));
-                                }
-                                case 2: {
-                                  constexpr size_t Dimension = 2;
-                                  using MeshType             = Mesh<Connectivity<Dimension>>;
-                                  std::shared_ptr<const MeshType> mesh =
-                                    std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh);
-
-                                  return std::make_shared<DiscreteFunctionVariant>(
-                                    DiscreteFunctionP0(mesh, MeshDataManager::instance().getMeshData(*mesh).Vj()));
-                                }
-                                case 3: {
-                                  constexpr size_t Dimension = 3;
-                                  using MeshType             = Mesh<Connectivity<Dimension>>;
-                                  std::shared_ptr<const MeshType> mesh =
-                                    std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh);
-
-                                  return std::make_shared<DiscreteFunctionVariant>(
-                                    DiscreteFunctionP0(mesh, MeshDataManager::instance().getMeshData(*mesh).Vj()));
-                                }
-                                default: {
-                                  throw UnexpectedError("invalid mesh dimension");
-                                }
-                                }
+                                return std::visit(
+                                  [&](auto&& mesh) {
+                                    using MeshType = mesh_type_t<decltype(mesh)>;
+                                    if constexpr (is_polygonal_mesh_v<MeshType>) {
+                                      return std::make_shared<DiscreteFunctionVariant>(
+                                        DiscreteFunctionP0(mesh_v,
+                                                           MeshDataManager::instance().getMeshData(*mesh).Vj()));
+                                    } else {
+                                      throw NormalError("unexpected mesh type");
+                                    }
+                                  },
+                                  mesh_v->variant());
                               }
 
                               ));
@@ -680,7 +661,7 @@ SchemeModule::SchemeModule()
 
   this->_addBuiltinFunction("fluxing_advection", std::function(
 
-                                                   [](const std::shared_ptr<const IMesh> new_mesh,
+                                                   [](const std::shared_ptr<const MeshVariant> new_mesh,
                                                       const std::vector<std::shared_ptr<const VariableBCDescriptor>>&
                                                         remapped_quantity_with_bc)
                                                      -> std::vector<std::shared_ptr<const DiscreteFunctionVariant>> {
diff --git a/src/language/modules/WriterModule.cpp b/src/language/modules/WriterModule.cpp
index af7d0cb4b1cf50de79462a56d07f262cf1bf95ad..fc9d5703ed793f23c7f9768f4f2137585c3be318 100644
--- a/src/language/modules/WriterModule.cpp
+++ b/src/language/modules/WriterModule.cpp
@@ -106,9 +106,8 @@ WriterModule::WriterModule()
   this->_addBuiltinFunction("write_mesh",
                             std::function(
 
-                              [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const IMesh> p_mesh) -> void {
-                                writer->writeMesh(p_mesh);
-                              }
+                              [](std::shared_ptr<const IWriter> writer,
+                                 std::shared_ptr<const MeshVariant> mesh_v) -> void { writer->writeMesh(mesh_v); }
 
                               ));
 
@@ -144,35 +143,37 @@ WriterModule::WriterModule()
 
                                              ));
 
-  this->_addBuiltinFunction("write", std::function(
+  this->_addBuiltinFunction("write",
+                            std::function(
 
-                                       [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const IMesh> mesh,
-                                          const std::vector<std::shared_ptr<const INamedDiscreteData>>&
-                                            named_discrete_function_list) -> void {
-                                         writer->writeOnMesh(mesh, named_discrete_function_list);
-                                       }
+                              [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const MeshVariant> mesh_v,
+                                 const std::vector<std::shared_ptr<const INamedDiscreteData>>&
+                                   named_discrete_function_list) -> void {
+                                writer->writeOnMesh(mesh_v, named_discrete_function_list);
+                              }
 
-                                       ));
+                              ));
 
-  this->_addBuiltinFunction("write", std::function(
+  this->_addBuiltinFunction("write",
+                            std::function(
 
-                                       [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const IMesh> mesh,
-                                          const std::vector<std::shared_ptr<const INamedDiscreteData>>&
-                                            named_discrete_function_list,
-                                          const double& time) -> void {
-                                         writer->writeOnMeshIfNeeded(mesh, named_discrete_function_list, time);
-                                       }
+                              [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const MeshVariant> mesh_v,
+                                 const std::vector<std::shared_ptr<const INamedDiscreteData>>&
+                                   named_discrete_function_list,
+                                 const double& time) -> void {
+                                writer->writeOnMeshIfNeeded(mesh_v, named_discrete_function_list, time);
+                              }
 
-                                       ));
+                              ));
 
   this->_addBuiltinFunction("force_write",
                             std::function(
 
-                              [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const IMesh> mesh,
+                              [](std::shared_ptr<const IWriter> writer, std::shared_ptr<const MeshVariant> mesh_v,
                                  const std::vector<std::shared_ptr<const INamedDiscreteData>>&
                                    named_discrete_function_list,
                                  const double& time) -> void {
-                                writer->writeOnMeshForced(mesh, named_discrete_function_list, time);
+                                writer->writeOnMeshForced(mesh_v, named_discrete_function_list, time);
                               }
 
                               ));
diff --git a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
index 0f9224fed185541ca8da8cdaaa10ffa777c978ad..715de1be2019070e517cb30639c199f721258cd2 100644
--- a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
+++ b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
@@ -1,7 +1,7 @@
 #include <language/utils/EmbeddedDiscreteFunctionMathFunctions.hpp>
 
 #include <language/utils/EmbeddedDiscreteFunctionUtils.hpp>
-#include <mesh/IMesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionP0Vector.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
@@ -10,32 +10,28 @@
 
 #include <utils/Demangle.hpp>
 
-#define DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(FUNCTION, ARG)                                   \
-  return std::visit(                                                                          \
-    [&](auto&& discrete_function) -> std::shared_ptr<DiscreteFunctionVariant> {               \
-      using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                    \
-      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<1, const double>> or \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<2, const double>> or \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<3, const double>>) { \
-        return std::make_shared<DiscreteFunctionVariant>(FUNCTION(discrete_function));        \
-      } else {                                                                                \
-        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(ARG));            \
-      }                                                                                       \
-    },                                                                                        \
+#define DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(FUNCTION, ARG)                                \
+  return std::visit(                                                                       \
+    [&](auto&& discrete_function) -> std::shared_ptr<DiscreteFunctionVariant> {            \
+      using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                 \
+      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<const double>>) { \
+        return std::make_shared<DiscreteFunctionVariant>(FUNCTION(discrete_function));     \
+      } else {                                                                             \
+        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(ARG));         \
+      }                                                                                    \
+    },                                                                                     \
     ARG->discreteFunction());
 
-#define DISCRETE_VH_TO_R_CALL(FUNCTION, ARG)                                                  \
-  return std::visit(                                                                          \
-    [&](auto&& discrete_function) -> double {                                                 \
-      using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                    \
-      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<1, const double>> or \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<2, const double>> or \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<3, const double>>) { \
-        return FUNCTION(discrete_function);                                                   \
-      } else {                                                                                \
-        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(ARG));            \
-      }                                                                                       \
-    },                                                                                        \
+#define DISCRETE_VH_TO_R_CALL(FUNCTION, ARG)                                               \
+  return std::visit(                                                                       \
+    [&](auto&& discrete_function) -> double {                                              \
+      using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                 \
+      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<const double>>) { \
+        return FUNCTION(discrete_function);                                                \
+      } else {                                                                             \
+        throw NormalError(EmbeddedDiscreteFunctionUtils::invalidOperandType(ARG));         \
+      }                                                                                    \
+    },                                                                                     \
     ARG->discreteFunction());
 
 #define DISCRETE_VH_VH_TO_VH_REAL_FUNCTION_CALL(FUNCTION, ARG0, ARG1)                               \
@@ -46,9 +42,7 @@
     [&](auto&& arg_f, auto&& arg_g) -> std::shared_ptr<DiscreteFunctionVariant> {                   \
       using TypeOfF = std::decay_t<decltype(arg_f)>;                                                \
       using TypeOfG = std::decay_t<decltype(arg_g)>;                                                \
-      if constexpr (std::is_same_v<TypeOfF, DiscreteFunctionP0<1, const double>> or                 \
-                    std::is_same_v<TypeOfF, DiscreteFunctionP0<2, const double>> or                 \
-                    std::is_same_v<TypeOfF, DiscreteFunctionP0<3, const double>>) {                 \
+      if constexpr (std::is_same_v<TypeOfF, DiscreteFunctionP0<const double>>) {                    \
         if constexpr (std::is_same_v<TypeOfF, TypeOfG>) {                                           \
           return std::make_shared<DiscreteFunctionVariant>(FUNCTION(arg_f, arg_g));                 \
         } else {                                                                                    \
@@ -64,9 +58,7 @@
   return std::visit(                                                                                         \
     [&](auto&& discrete_function) -> std::shared_ptr<DiscreteFunctionVariant> {                              \
       using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                                   \
-      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<1, const double>> or                \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<2, const double>> or                \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<3, const double>>) {                \
+      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<const double>>) {                   \
         return std::make_shared<DiscreteFunctionVariant>(FUNCTION(ARG0, discrete_function));                 \
       } else {                                                                                               \
         throw NormalError(EmbeddedDiscreteFunctionUtils::incompatibleOperandTypes(ARG0, discrete_function)); \
@@ -78,9 +70,7 @@
   return std::visit(                                                                                         \
     [&](auto&& discrete_function) -> std::shared_ptr<DiscreteFunctionVariant> {                              \
       using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;                                   \
-      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<1, const double>> or                \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<2, const double>> or                \
-                    std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<3, const double>>) {                \
+      if constexpr (std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<const double>>) {                   \
         return std::make_shared<DiscreteFunctionVariant>(FUNCTION(discrete_function, ARG1));                 \
       } else {                                                                                               \
         throw NormalError(EmbeddedDiscreteFunctionUtils::incompatibleOperandTypes(discrete_function, ARG1)); \
@@ -506,23 +496,24 @@ sum_of_Vh_components(const std::shared_ptr<const DiscreteFunctionVariant>& f)
     f->discreteFunction());
 }
 
-template <size_t Dimension>
-void
-vectorize_to(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_list,
-             const Mesh<Connectivity<Dimension>>& mesh,
-             DiscreteFunctionP0Vector<Dimension, double>& discrete_vector_function)
+std::shared_ptr<const DiscreteFunctionVariant>
+vectorize(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_list)
 {
   if (hasSameMesh(discrete_function_list)) {
+    std::shared_ptr mesh_v = getCommonMesh(discrete_function_list);
+    Assert(mesh_v.use_count() > 0);
+
+    DiscreteFunctionP0Vector<double> discrete_vector_function(mesh_v, discrete_function_list.size());
+
     for (size_t i_discrete_function = 0; i_discrete_function < discrete_function_list.size(); ++i_discrete_function) {
       std::visit(
         [&](auto&& discrete_function) {
           using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;
           if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
             using DataType = std::remove_const_t<typename DiscreteFunctionT::data_type>;
-            if constexpr (std::is_same_v<DataType, double> and (Dimension == DiscreteFunctionT::MeshType::Dimension)) {
-              const auto& connectivity = mesh.connectivity();
+            if constexpr (std::is_same_v<DataType, double>) {
               parallel_for(
-                connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                   discrete_vector_function[cell_id][i_discrete_function] = discrete_function[cell_id];
                 });
             } else {
@@ -535,60 +526,7 @@ vectorize_to(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
         discrete_function_list[i_discrete_function]->discreteFunction());
     }
 
-  } else {
-    // LCOV_EXCL_START
-    throw UnexpectedError("discrete functions are not defined on the same mesh");
-    // LCOV_EXCL_STOP
-  }
-}
-
-std::shared_ptr<const DiscreteFunctionVariant>
-vectorize(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_list)
-{
-  if (hasSameMesh(discrete_function_list)) {
-    std::shared_ptr p_i_mesh = getCommonMesh(discrete_function_list);
-    Assert(p_i_mesh.use_count() > 0);
-
-    switch (p_i_mesh->dimension()) {
-    case 1: {
-      constexpr size_t Dimension       = 1;
-      using DiscreteFunctionVectorType = DiscreteFunctionP0Vector<Dimension, double>;
-      std::shared_ptr<const Mesh<Connectivity<Dimension>>> p_mesh =
-        std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(p_i_mesh);
-
-      DiscreteFunctionVectorType vector_function(p_mesh, discrete_function_list.size());
-      vectorize_to(discrete_function_list, *p_mesh, vector_function);
-
-      return std::make_shared<DiscreteFunctionVariant>(vector_function);
-    }
-    case 2: {
-      constexpr size_t Dimension       = 2;
-      using DiscreteFunctionVectorType = DiscreteFunctionP0Vector<Dimension, double>;
-      std::shared_ptr<const Mesh<Connectivity<Dimension>>> p_mesh =
-        std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(p_i_mesh);
-
-      DiscreteFunctionVectorType vector_function(p_mesh, discrete_function_list.size());
-      vectorize_to(discrete_function_list, *p_mesh, vector_function);
-
-      return std::make_shared<DiscreteFunctionVariant>(vector_function);
-    }
-    case 3: {
-      constexpr size_t Dimension       = 3;
-      using DiscreteFunctionVectorType = DiscreteFunctionP0Vector<Dimension, double>;
-      std::shared_ptr<const Mesh<Connectivity<Dimension>>> p_mesh =
-        std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(p_i_mesh);
-
-      DiscreteFunctionVectorType vector_function(p_mesh, discrete_function_list.size());
-      vectorize_to(discrete_function_list, *p_mesh, vector_function);
-
-      return std::make_shared<DiscreteFunctionVariant>(vector_function);
-    }
-      // LCOV_EXCL_START
-    default: {
-      throw UnexpectedError("invalid mesh dimension");
-    }
-      // LCOV_EXCL_STOP
-    }
+    return std::make_shared<DiscreteFunctionVariant>(discrete_vector_function);
   } else {
     throw NormalError("discrete functions are not defined on the same mesh");
   }
diff --git a/src/language/utils/EmbeddedDiscreteFunctionOperators.cpp b/src/language/utils/EmbeddedDiscreteFunctionOperators.cpp
index 0b47153cf0660abd807774d477e7fcd8ae712b62..d339a4a79c546ab5aaa93d276ff93b893dea9d35 100644
--- a/src/language/utils/EmbeddedDiscreteFunctionOperators.cpp
+++ b/src/language/utils/EmbeddedDiscreteFunctionOperators.cpp
@@ -141,13 +141,7 @@ operator*(const std::shared_ptr<const DiscreteFunctionVariant>& f_v,
       if constexpr (std::is_same_v<TypeOfF, TypeOfG> and not is_discrete_function_P0_vector_v<TypeOfF>) {
         return innerCompositionLaw<TypeOfF, language::multiply_op>(f, g);
       } else {
-        if constexpr (std::is_same_v<typename TypeOfF::MeshType, typename TypeOfG::MeshType>) {
-          return applyBinaryOperation<TypeOfF, TypeOfG, language::multiply_op>(f, g);
-        } else {
-          // LCOV_EXCL_START
-          throw UnexpectedError("operands are defined on different meshes");
-          // LCOV_EXCL_STOP
-        }
+        return applyBinaryOperation<TypeOfF, TypeOfG, language::multiply_op>(f, g);
       }
     },
     f_v->discreteFunction(), g_v->discreteFunction());
diff --git a/src/language/utils/IntegrateCellArray.hpp b/src/language/utils/IntegrateCellArray.hpp
index 41450de3b9675fbe929fcaf0fde855ccb1be2fe4..380971323f28fd45faf05597558d9f5277ab6a8b 100644
--- a/src/language/utils/IntegrateCellArray.hpp
+++ b/src/language/utils/IntegrateCellArray.hpp
@@ -4,6 +4,7 @@
 #include <language/utils/IntegrateCellValue.hpp>
 #include <mesh/CellType.hpp>
 #include <mesh/ItemArray.hpp>
+#include <mesh/MeshTraits.hpp>
 
 template <typename T>
 class IntegrateCellArray;
@@ -13,7 +14,7 @@ class IntegrateCellArray<OutputType(InputType)>
   static constexpr size_t Dimension = OutputType::Dimension;
 
  public:
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   PUGS_INLINE static CellArray<OutputType>
   integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -33,7 +34,7 @@ class IntegrateCellArray<OutputType(InputType)>
     return cell_array;
   }
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   PUGS_INLINE static Table<OutputType>
   integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -55,7 +56,7 @@ class IntegrateCellArray<OutputType(InputType)>
     return table;
   }
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   PUGS_INLINE static Table<OutputType>
   integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
             const IQuadratureDescriptor& quadrature_descriptor,
diff --git a/src/language/utils/IntegrateCellValue.hpp b/src/language/utils/IntegrateCellValue.hpp
index cd8bacc072bef0f5c68b1702fa9fef33c6d94f7d..d76e9af6716ee2245691b91cefd27d355db1d8e4 100644
--- a/src/language/utils/IntegrateCellValue.hpp
+++ b/src/language/utils/IntegrateCellValue.hpp
@@ -6,6 +6,8 @@
 #include <mesh/ItemType.hpp>
 #include <mesh/ItemValue.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 
 template <typename T>
 class IntegrateCellValue;
@@ -13,12 +15,13 @@ template <typename OutputType, typename InputType>
 class IntegrateCellValue<OutputType(InputType)>
 {
  public:
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   PUGS_INLINE static CellValue<OutputType>
   integrate(const FunctionSymbolId& function_symbol_id,
             const IQuadratureDescriptor& quadrature_descriptor,
             const MeshType& mesh)
   {
+    static_assert(is_polygonal_mesh_v<MeshType>);
     CellValue<OutputType> value(mesh.connectivity());
     IntegrateOnCells<OutputType(const InputType)>::template integrateTo<MeshType>(function_symbol_id,
                                                                                   quadrature_descriptor, mesh, value);
@@ -26,26 +29,81 @@ class IntegrateCellValue<OutputType(InputType)>
     return value;
   }
 
-  template <typename MeshType>
+  PUGS_INLINE static CellValue<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const std::shared_ptr<const MeshVariant>& mesh_v)
+  {
+    return std::visit(
+      [&](auto&& p_mesh) -> CellValue<OutputType> {
+        using MeshType = mesh_type_t<decltype(p_mesh)>;
+        if constexpr ((is_polygonal_mesh_v<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+          return integrate(function_symbol_id, quadrature_descriptor, *p_mesh);
+        } else {
+          throw NormalError("invalid mesh type");
+        }
+      },
+      mesh_v->variant());
+  }
+
+  template <MeshConcept MeshType>
   PUGS_INLINE static Array<OutputType>
   integrate(const FunctionSymbolId& function_symbol_id,
             const IQuadratureDescriptor& quadrature_descriptor,
             const MeshType& mesh,
             const Array<const CellId>& list_of_cells)
   {
+    static_assert(is_polygonal_mesh_v<MeshType>);
     return IntegrateOnCells<OutputType(const InputType)>::integrate(function_symbol_id, quadrature_descriptor, mesh,
                                                                     Array<const CellId>{list_of_cells});
   }
 
-  template <typename MeshType>
+  PUGS_INLINE static Array<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const std::shared_ptr<const MeshVariant>& mesh_v,
+            const Array<const CellId>& list_of_cells)
+  {
+    return std::visit(
+      [&](auto&& p_mesh) -> Array<OutputType> {
+        using MeshType = mesh_type_t<decltype(p_mesh)>;
+        if constexpr ((is_polygonal_mesh_v<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+          return integrate(function_symbol_id, quadrature_descriptor, *p_mesh, list_of_cells);
+        } else {
+          throw NormalError("invalid mesh type");
+        }
+      },
+      mesh_v->variant());
+  }
+
+  template <MeshConcept MeshType>
   PUGS_INLINE static Array<OutputType>
   integrate(const FunctionSymbolId& function_symbol_id,
             const IQuadratureDescriptor& quadrature_descriptor,
             const MeshType& mesh,
             const Array<CellId>& list_of_cells)
   {
+    static_assert(is_polygonal_mesh_v<MeshType>);
     return integrate(function_symbol_id, quadrature_descriptor, mesh, Array<const CellId>{list_of_cells});
   }
+
+  PUGS_INLINE static Array<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const std::shared_ptr<const MeshVariant>& mesh_v,
+            const Array<CellId>& list_of_cells)
+  {
+    return std::visit(
+      [&](auto&& p_mesh) -> Array<OutputType> {
+        using MeshType = mesh_type_t<decltype(p_mesh)>;
+        if constexpr ((is_polygonal_mesh_v<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+          return integrate(function_symbol_id, quadrature_descriptor, *p_mesh, list_of_cells);
+        } else {
+          throw NormalError("invalid mesh type");
+        }
+      },
+      mesh_v->variant());
+  }
 };
 
 #endif   // INTEGRATE_CELL_VALUE_HPP
diff --git a/src/language/utils/IntegrateOnCells.hpp b/src/language/utils/IntegrateOnCells.hpp
index 79357544738109bc5c5861e01ca22a1ccaf95af8..2f359b265e670c16c621d9ba4fb9876362605a9d 100644
--- a/src/language/utils/IntegrateOnCells.hpp
+++ b/src/language/utils/IntegrateOnCells.hpp
@@ -14,6 +14,7 @@
 #include <mesh/CellType.hpp>
 #include <mesh/Connectivity.hpp>
 #include <mesh/ItemId.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <utils/Array.hpp>
 
 class FunctionSymbolId;
@@ -79,7 +80,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
     CellList(const Array<const CellId>& cell_list) : m_cell_list{cell_list} {}
   };
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT>
   static PUGS_INLINE void
   _tensorialIntegrateTo(const FunctionSymbolId& function_symbol_id,
                         const IQuadratureDescriptor& quadrature_descriptor,
@@ -340,7 +341,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
     // LCOV_EXCL_STOP
   }
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT>
   static PUGS_INLINE void
   _directIntegrateTo(const FunctionSymbolId& function_symbol_id,
                      const IQuadratureDescriptor& quadrature_descriptor,
@@ -586,7 +587,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
   }
 
  public:
-  template <typename MeshType, typename ArrayT>
+  template <MeshConcept MeshType, typename ArrayT>
   static PUGS_INLINE void
   integrateTo(const FunctionSymbolId& function_symbol_id,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -604,7 +605,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
     }
   }
 
-  template <typename MeshType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE ArrayT<OutputType>
   integrate(const FunctionSymbolId& function_symbol_id,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -623,7 +624,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
     return value;
   }
 
-  template <typename MeshType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE ArrayT<OutputType>
   integrate(const FunctionSymbolId& function_symbol_id,
             const IQuadratureDescriptor& quadrature_descriptor,
diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
index 2ed94cbff4c8ee846e04674fd3e421f718577bfc..a386e81698b24b9d225cac6dd978e7e3aedbb32e 100644
--- a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
@@ -6,16 +6,19 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <memory>
 
-template <size_t Dimension, typename DataType, typename ArrayType>
+template <MeshConcept MeshType, typename DataType>
 std::shared_ptr<ItemArrayVariant>
 ItemArrayVariantFunctionInterpoler::_interpolate() const
 {
-  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
-  using MeshDataType     = MeshData<Dimension>;
+  std::shared_ptr p_mesh     = m_mesh_v->get<MeshType>();
+  constexpr size_t Dimension = MeshType::Dimension;
+  using MeshDataType         = MeshData<MeshType>;
 
   switch (m_item_type) {
   case ItemType::cell: {
@@ -49,7 +52,7 @@ ItemArrayVariantFunctionInterpoler::_interpolate() const
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 std::shared_ptr<ItemArrayVariant>
 ItemArrayVariantFunctionInterpoler::_interpolate() const
 {
@@ -74,27 +77,27 @@ ItemArrayVariantFunctionInterpoler::_interpolate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_interpolate<Dimension, bool>();
+    return this->_interpolate<MeshType, bool>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_interpolate<Dimension, uint64_t>();
+    return this->_interpolate<MeshType, uint64_t>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_interpolate<Dimension, int64_t>();
+    return this->_interpolate<MeshType, int64_t>();
   }
   case ASTNodeDataType::double_t: {
-    return this->_interpolate<Dimension, double>();
+    return this->_interpolate<MeshType, double>();
   }
   case ASTNodeDataType::vector_t: {
     switch (data_type.dimension()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyVector<1>>();
+      return this->_interpolate<MeshType, TinyVector<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyVector<2>>();
+      return this->_interpolate<MeshType, TinyVector<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyVector<3>>();
+      return this->_interpolate<MeshType, TinyVector<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -110,13 +113,13 @@ ItemArrayVariantFunctionInterpoler::_interpolate() const
     Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
     switch (data_type.numberOfColumns()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyMatrix<1>>();
+      return this->_interpolate<MeshType, TinyMatrix<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyMatrix<2>>();
+      return this->_interpolate<MeshType, TinyMatrix<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyMatrix<3>>();
+      return this->_interpolate<MeshType, TinyMatrix<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -142,20 +145,14 @@ ItemArrayVariantFunctionInterpoler::_interpolate() const
 std::shared_ptr<ItemArrayVariant>
 ItemArrayVariantFunctionInterpoler::interpolate() const
 {
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_interpolate<1>();
-  }
-  case 2: {
-    return this->_interpolate<2>();
-  }
-  case 3: {
-    return this->_interpolate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_interpolate<MeshType>();
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    m_mesh_v->variant());
 }
diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp
index eac61b5fb067e83c2cf8f132060e68884316cad4..095614ffe1e3ff96f2cc47099d05a8777760f175 100644
--- a/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.hpp
@@ -2,31 +2,33 @@
 #define ITEM_ARRAY_VARIANT_FUNCTION_INTERPOLER_HPP
 
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
 #include <mesh/ItemArrayVariant.hpp>
 #include <mesh/ItemType.hpp>
+#include <mesh/MeshTraits.hpp>
+
+class MeshVariant;
 
 class ItemArrayVariantFunctionInterpoler
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh_v;
   const ItemType m_item_type;
   const std::vector<FunctionSymbolId> m_function_id_list;
 
-  template <size_t Dimension, typename DataType, typename ArrayType = DataType>
+  template <MeshConcept MeshType, typename DataType>
   std::shared_ptr<ItemArrayVariant> _interpolate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   std::shared_ptr<ItemArrayVariant> _interpolate() const;
 
  public:
   std::shared_ptr<ItemArrayVariant> interpolate() const;
 
-  ItemArrayVariantFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh,
+  ItemArrayVariantFunctionInterpoler(const std::shared_ptr<const MeshVariant>& mesh_v,
                                      const ItemType& item_type,
                                      const std::vector<FunctionSymbolId>& function_id_list)
-    : m_mesh{mesh}, m_item_type{item_type}, m_function_id_list{function_id_list}
+    : m_mesh_v{mesh_v}, m_item_type{item_type}, m_function_id_list{function_id_list}
   {}
 
   ItemArrayVariantFunctionInterpoler(const ItemArrayVariantFunctionInterpoler&) = delete;
diff --git a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
index 98dbe1658b45b8b8613892d0cfa72e67ccc67700..6759e5763dd66bf22b40cb771ae79b6f0541fe3d 100644
--- a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
@@ -6,16 +6,19 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <memory>
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType>
 std::shared_ptr<ItemValueVariant>
 ItemValueVariantFunctionInterpoler::_interpolate() const
 {
-  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
-  using MeshDataType     = MeshData<Dimension>;
+  std::shared_ptr p_mesh     = m_mesh_v->get<MeshType>();
+  constexpr size_t Dimension = MeshType::Dimension;
+  using MeshDataType         = MeshData<MeshType>;
 
   switch (m_item_type) {
   case ItemType::cell: {
@@ -49,7 +52,7 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 std::shared_ptr<ItemValueVariant>
 ItemValueVariantFunctionInterpoler::_interpolate() const
 {
@@ -60,27 +63,27 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_interpolate<Dimension, bool>();
+    return this->_interpolate<MeshType, bool>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_interpolate<Dimension, uint64_t>();
+    return this->_interpolate<MeshType, uint64_t>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_interpolate<Dimension, int64_t>();
+    return this->_interpolate<MeshType, int64_t>();
   }
   case ASTNodeDataType::double_t: {
-    return this->_interpolate<Dimension, double>();
+    return this->_interpolate<MeshType, double>();
   }
   case ASTNodeDataType::vector_t: {
     switch (data_type.dimension()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyVector<1>>();
+      return this->_interpolate<MeshType, TinyVector<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyVector<2>>();
+      return this->_interpolate<MeshType, TinyVector<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyVector<3>>();
+      return this->_interpolate<MeshType, TinyVector<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -96,13 +99,13 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
     Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
     switch (data_type.numberOfColumns()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyMatrix<1>>();
+      return this->_interpolate<MeshType, TinyMatrix<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyMatrix<2>>();
+      return this->_interpolate<MeshType, TinyMatrix<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyMatrix<3>>();
+      return this->_interpolate<MeshType, TinyMatrix<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -128,20 +131,14 @@ ItemValueVariantFunctionInterpoler::_interpolate() const
 std::shared_ptr<ItemValueVariant>
 ItemValueVariantFunctionInterpoler::interpolate() const
 {
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_interpolate<1>();
-  }
-  case 2: {
-    return this->_interpolate<2>();
-  }
-  case 3: {
-    return this->_interpolate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_interpolate<MeshType>();
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    m_mesh_v->variant());
 }
diff --git a/src/language/utils/ItemValueVariantFunctionInterpoler.hpp b/src/language/utils/ItemValueVariantFunctionInterpoler.hpp
index 4e03f72fc41ec74cf628e4ced7d331b6354d6f6a..046d9bcf51aa8042016b09d2bff137b37fe44fa4 100644
--- a/src/language/utils/ItemValueVariantFunctionInterpoler.hpp
+++ b/src/language/utils/ItemValueVariantFunctionInterpoler.hpp
@@ -2,31 +2,33 @@
 #define ITEM_VALUE_VARIANT_FUNCTION_INTERPOLER_HPP
 
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
 #include <mesh/ItemType.hpp>
 #include <mesh/ItemValueVariant.hpp>
+#include <mesh/MeshTraits.hpp>
+
+class MeshVariant;
 
 class ItemValueVariantFunctionInterpoler
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh_v;
   const ItemType m_item_type;
   const FunctionSymbolId m_function_id;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType>
   std::shared_ptr<ItemValueVariant> _interpolate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   std::shared_ptr<ItemValueVariant> _interpolate() const;
 
  public:
   std::shared_ptr<ItemValueVariant> interpolate() const;
 
-  ItemValueVariantFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh,
+  ItemValueVariantFunctionInterpoler(const std::shared_ptr<const MeshVariant>& mesh_v,
                                      const ItemType& item_type,
                                      const FunctionSymbolId& function_id)
-    : m_mesh{mesh}, m_item_type{item_type}, m_function_id{function_id}
+    : m_mesh_v{mesh_v}, m_item_type{item_type}, m_function_id{function_id}
   {}
 
   ItemValueVariantFunctionInterpoler(const ItemValueVariantFunctionInterpoler&) = delete;
diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt
index 9b82b62f1818e9dc785c62618b5878f71ab7db35..78b72bcafc640ecc888c0997eeb3ff8f3056db58 100644
--- a/src/mesh/CMakeLists.txt
+++ b/src/mesh/CMakeLists.txt
@@ -16,10 +16,10 @@ add_library(
   DualMeshManager.cpp
   GmshReader.cpp
   IConnectivity.cpp
-  IMesh.cpp
   LogicalConnectivityBuilder.cpp
   MedianDualConnectivityBuilder.cpp
   MedianDualMeshBuilder.cpp
+  Mesh.cpp
   MeshBuilderBase.cpp
   MeshCellZone.cpp
   MeshData.cpp
@@ -31,15 +31,18 @@ add_library(
   MeshFlatEdgeBoundary.cpp
   MeshFlatFaceBoundary.cpp
   MeshFlatNodeBoundary.cpp
-  MeshRelaxer.cpp
   MeshLineEdgeBoundary.cpp
   MeshLineFaceBoundary.cpp
   MeshLineNodeBoundary.cpp
   MeshNodeBoundary.cpp
+  MeshNodeBoundaryUtils.cpp
   MeshNodeInterface.cpp
   MeshRandomizer.cpp
+  MeshRelaxer.cpp
   MeshSmoother.cpp
   MeshTransformer.cpp
+  MeshUtils.cpp
+  MeshVariant.cpp
   SynchronizerManager.cpp
 )
 
diff --git a/src/mesh/CartesianMeshBuilder.cpp b/src/mesh/CartesianMeshBuilder.cpp
index 226ff751fdf0b3316d66e2cb0c6e5366a9852d73..4ae7410ce7b896e7cf1f8147a0b64365cebc9352 100644
--- a/src/mesh/CartesianMeshBuilder.cpp
+++ b/src/mesh/CartesianMeshBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <mesh/Connectivity.hpp>
 #include <mesh/LogicalConnectivityBuilder.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Array.hpp>
 #include <utils/Messenger.hpp>
 
@@ -116,7 +117,7 @@ CartesianMeshBuilder::_buildCartesianMesh(const TinyVector<Dimension>& a,
 
   NodeValue<TinyVector<Dimension>> xr = _getNodeCoordinates(a, b, cell_size, connectivity);
 
-  m_mesh = std::make_shared<Mesh<ConnectivityType>>(p_connectivity, xr);
+  m_mesh = std::make_shared<MeshVariant>(std::make_shared<const Mesh<Dimension>>(p_connectivity, xr));
 }
 
 template <size_t Dimension>
diff --git a/src/mesh/DiamondDualMeshBuilder.cpp b/src/mesh/DiamondDualMeshBuilder.cpp
index 64711edc72055effd9e5608bc01b71ea184ee1fc..d542cee7f2d34c51bfd339d13c373a7008460a49 100644
--- a/src/mesh/DiamondDualMeshBuilder.cpp
+++ b/src/mesh/DiamondDualMeshBuilder.cpp
@@ -6,19 +6,19 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <utils/Stringify.hpp>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const IMesh& i_mesh)
+DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const MeshType& primal_mesh)
 {
-  static_assert(Dimension > 1);
-
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<Connectivity<Dimension>>;
+  constexpr size_t Dimension = MeshType::Dimension;
+  using ConnectivityType     = typename MeshType::Connectivity;
 
-  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
+  static_assert(Dimension > 1);
 
   DualConnectivityManager& manager = DualConnectivityManager::instance();
 
@@ -29,7 +29,7 @@ DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const IMesh& i_mesh)
 
   const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
 
-  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data                   = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
 
   std::shared_ptr primal_to_diamond_dual_connectivity_data_mapper =
@@ -38,24 +38,23 @@ DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const IMesh& i_mesh)
   NodeValue<TinyVector<Dimension>> diamond_xr{diamond_connectivity};
   primal_to_diamond_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, diamond_xr);
 
-  m_mesh = std::make_shared<MeshType>(p_diamond_connectivity, diamond_xr);
+  m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(p_diamond_connectivity, diamond_xr));
 }
 
-DiamondDualMeshBuilder::DiamondDualMeshBuilder(const IMesh& i_mesh)
+DiamondDualMeshBuilder::DiamondDualMeshBuilder(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  switch (i_mesh.dimension()) {
-  case 2: {
-    this->_buildDualDiamondMeshFrom<2>(i_mesh);
-    break;
-  }
-  case 3: {
-    this->_buildDualDiamondMeshFrom<3>(i_mesh);
-    break;
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid mesh dimension: " + stringify(i_mesh.dimension()));
-  }
-    // LCOV_EXCL_STOP
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        if constexpr (MeshType::Dimension > 1) {
+          this->_buildDualDiamondMeshFrom(*p_mesh);
+        } else {
+          throw UnexpectedError("invalid polygonal mesh dimension");
+        }
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    mesh_v->variant());
 }
diff --git a/src/mesh/DiamondDualMeshBuilder.hpp b/src/mesh/DiamondDualMeshBuilder.hpp
index 1dc670638796be11a686173b7bc07bff6728fbf8..ae22c2667b9d8e43710ebc243ec53de39a37d34b 100644
--- a/src/mesh/DiamondDualMeshBuilder.hpp
+++ b/src/mesh/DiamondDualMeshBuilder.hpp
@@ -2,17 +2,17 @@
 #define DIAMOND_DUAL_MESH_BUILDER_HPP
 
 #include <mesh/MeshBuilderBase.hpp>
+#include <mesh/MeshTraits.hpp>
 
-#include <memory>
-
+class MeshVariant;
 class DiamondDualMeshBuilder : public MeshBuilderBase
 {
  private:
-  template <size_t Dimension>
-  void _buildDualDiamondMeshFrom(const IMesh&);
+  template <MeshConcept MeshType>
+  void _buildDualDiamondMeshFrom(const MeshType&);
 
   friend class DualMeshManager;
-  DiamondDualMeshBuilder(const IMesh&);
+  DiamondDualMeshBuilder(const std::shared_ptr<const MeshVariant>&);
 
  public:
   ~DiamondDualMeshBuilder() = default;
diff --git a/src/mesh/Dual1DMeshBuilder.cpp b/src/mesh/Dual1DMeshBuilder.cpp
index db47aa6d441619b1d1c5e2061a9838209851d5d9..64c48423ba0ecc70ef2d6c29018c9b5e3cd8c129 100644
--- a/src/mesh/Dual1DMeshBuilder.cpp
+++ b/src/mesh/Dual1DMeshBuilder.cpp
@@ -1,22 +1,24 @@
 #include <mesh/Dual1DMeshBuilder.hpp>
 
-#include <mesh/Connectivity.hpp>
 #include <mesh/DualConnectivityManager.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
 #include <utils/Stringify.hpp>
 
+template <MeshConcept MeshType>
 void
-Dual1DMeshBuilder::_buildDual1DMeshFrom(const IMesh& i_mesh)
+Dual1DMeshBuilder::_buildDual1DMeshFrom(const MeshType& primal_mesh)
 {
-  using ConnectivityType = Connectivity<1>;
-  using MeshType         = Mesh<Connectivity<1>>;
+  static_assert(is_polygonal_mesh_v<MeshType>);
+  static_assert(MeshType::Dimension == 1);
 
-  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
+  using ConnectivityType = typename MeshType::Connectivity;
 
   DualConnectivityManager& manager = DualConnectivityManager::instance();
 
@@ -27,7 +29,7 @@ Dual1DMeshBuilder::_buildDual1DMeshFrom(const IMesh& i_mesh)
 
   const NodeValue<const TinyVector<1>> primal_xr = primal_mesh.xr();
 
-  MeshData<1>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data           = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<1>> primal_xj = primal_mesh_data.xj();
 
   std::shared_ptr primal_to_dual_1d_connectivity_data_mapper =
@@ -36,18 +38,11 @@ Dual1DMeshBuilder::_buildDual1DMeshFrom(const IMesh& i_mesh)
   NodeValue<TinyVector<1>> dual_xr{dual_connectivity};
   primal_to_dual_1d_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, dual_xr);
 
-  m_mesh = std::make_shared<MeshType>(p_dual_connectivity, dual_xr);
+  m_mesh = std::make_shared<MeshVariant>(std::make_shared<const Mesh<1>>(p_dual_connectivity, dual_xr));
 }
 
-Dual1DMeshBuilder::Dual1DMeshBuilder(const IMesh& i_mesh)
+Dual1DMeshBuilder::Dual1DMeshBuilder(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
   std::cout << "building Dual1DMesh\n";
-
-  if (i_mesh.dimension() == 1) {
-    this->_buildDual1DMeshFrom(i_mesh);
-  } else {
-    // LCOV_EXCL_START
-    throw UnexpectedError("invalid mesh dimension: " + stringify(i_mesh.dimension()));
-    // LCOV_EXCL_STOP
-  }
+  this->_buildDual1DMeshFrom(*(mesh_v->get<Mesh<1>>()));
 }
diff --git a/src/mesh/Dual1DMeshBuilder.hpp b/src/mesh/Dual1DMeshBuilder.hpp
index d2b271aeebdfc9c8147ee7259610e344b9bdaae8..42810f5c089fb00a2aa7c834536b4cca5f6d4075 100644
--- a/src/mesh/Dual1DMeshBuilder.hpp
+++ b/src/mesh/Dual1DMeshBuilder.hpp
@@ -2,16 +2,17 @@
 #define DUAL_1D_MESH_BUILDER_HPP
 
 #include <mesh/MeshBuilderBase.hpp>
+#include <mesh/MeshTraits.hpp>
 
-#include <memory>
-
+class MeshVariant;
 class Dual1DMeshBuilder : public MeshBuilderBase
 {
  private:
-  void _buildDual1DMeshFrom(const IMesh&);
+  template <MeshConcept MeshType>
+  void _buildDual1DMeshFrom(const MeshType&);
 
   friend class DualMeshManager;
-  Dual1DMeshBuilder(const IMesh&);
+  Dual1DMeshBuilder(const std::shared_ptr<const MeshVariant>&);
 
  public:
   ~Dual1DMeshBuilder() = default;
diff --git a/src/mesh/DualMeshManager.cpp b/src/mesh/DualMeshManager.cpp
index 86384b6ad66d651b952a8ef1f1f86fd74a7b9176..a09413776fd46fc0e89affc9f92bbd7e20622e98 100644
--- a/src/mesh/DualMeshManager.cpp
+++ b/src/mesh/DualMeshManager.cpp
@@ -5,6 +5,8 @@
 #include <mesh/Dual1DMeshBuilder.hpp>
 #include <mesh/MedianDualMeshBuilder.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/PugsAssert.hpp>
 
@@ -40,14 +42,14 @@ DualMeshManager::destroy()
 }
 
 void
-DualMeshManager::deleteMesh(const IMesh* p_mesh)
+DualMeshManager::deleteMesh(const size_t mesh_id)
 {
   bool has_removed = false;
   do {
     has_removed = false;
     for (const auto& [key, dual_mesh] : m_mesh_to_dual_mesh_map) {
-      const auto& [type, p_parent_mesh] = key;
-      if (p_mesh == p_parent_mesh) {
+      const auto& [type, p_parent_mesh_id] = key;
+      if (mesh_id == p_parent_mesh_id) {
         m_mesh_to_dual_mesh_map.erase(key);
         has_removed = true;
         break;
@@ -56,75 +58,63 @@ DualMeshManager::deleteMesh(const IMesh* p_mesh)
   } while (has_removed);
 }
 
-std::shared_ptr<const Mesh<Connectivity<1>>>
-DualMeshManager::getDual1DMesh(const Mesh<Connectivity<1>>& mesh)
+std::shared_ptr<const MeshVariant>
+DualMeshManager::getDual1DMesh(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  const IMesh* p_mesh = &mesh;
-
-  auto key = std::make_pair(DualMeshType::Dual1D, p_mesh);
-  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(i_mesh_data->second);
+  auto key = std::make_pair(DualMeshType::Dual1D, mesh_v->id());
+  if (auto i_mesh_to_dual_mesh = m_mesh_to_dual_mesh_map.find(key);
+      i_mesh_to_dual_mesh != m_mesh_to_dual_mesh_map.end()) {
+    return i_mesh_to_dual_mesh->second;
   } else {
-    Dual1DMeshBuilder builder{mesh};
-
+    Dual1DMeshBuilder builder{mesh_v};
     m_mesh_to_dual_mesh_map[key] = builder.mesh();
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(builder.mesh());
+    return builder.mesh();
+    ;
   }
 }
 
-template <>
-std::shared_ptr<const Mesh<Connectivity<1>>>
-DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<1>>& mesh)
-{
-  return this->getDual1DMesh(mesh);
-}
-
-template <size_t Dimension>
-std::shared_ptr<const Mesh<Connectivity<Dimension>>>
-DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<Dimension>>& mesh)
+std::shared_ptr<const MeshVariant>
+DualMeshManager::getMedianDualMesh(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  static_assert(Dimension > 1);
-  const IMesh* p_mesh = &mesh;
-
-  auto key = std::make_pair(DualMeshType::Median, p_mesh);
-  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
-  } else {
-    MedianDualMeshBuilder builder{mesh};
-
-    m_mesh_to_dual_mesh_map[key] = builder.mesh();
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
-  }
-}
-
-template std::shared_ptr<const Mesh<Connectivity<2>>> DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<2>>&);
-template std::shared_ptr<const Mesh<Connectivity<3>>> DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<3>>&);
-
-template <>
-std::shared_ptr<const Mesh<Connectivity<1>>>
-DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<1>>& mesh)
-{
-  return this->getDual1DMesh(mesh);
+  return std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (MeshType::Dimension == 1) {
+        return this->getDual1DMesh(mesh_v);
+      } else {
+        auto key = std::make_pair(DualMeshType::Median, mesh_v->id());
+        if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
+          return i_mesh_data->second;
+        } else {
+          MedianDualMeshBuilder builder{mesh_v};
+
+          m_mesh_to_dual_mesh_map[key] = builder.mesh();
+          return builder.mesh();
+        }
+      }
+    },
+    mesh_v->variant());
 }
 
-template <size_t Dimension>
-std::shared_ptr<const Mesh<Connectivity<Dimension>>>
-DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<Dimension>>& mesh)
+std::shared_ptr<const MeshVariant>
+DualMeshManager::getDiamondDualMesh(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  static_assert(Dimension > 1);
-
-  const IMesh* p_mesh = &mesh;
-
-  auto key = std::make_pair(DualMeshType::Diamond, p_mesh);
-  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
-  } else {
-    DiamondDualMeshBuilder builder{mesh};
-
-    m_mesh_to_dual_mesh_map[key] = builder.mesh();
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
-  }
+  return std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (MeshType::Dimension == 1) {
+        return this->getDual1DMesh(mesh_v);
+      } else {
+        auto key = std::make_pair(DualMeshType::Diamond, mesh_v->id());
+        if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
+          return i_mesh_data->second;
+        } else {
+          DiamondDualMeshBuilder builder{mesh_v};
+
+          m_mesh_to_dual_mesh_map[key] = builder.mesh();
+          return builder.mesh();
+        }
+      }
+    },
+    mesh_v->variant());
 }
-
-template std::shared_ptr<const Mesh<Connectivity<2>>> DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<2>>&);
-template std::shared_ptr<const Mesh<Connectivity<3>>> DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<3>>&);
diff --git a/src/mesh/DualMeshManager.hpp b/src/mesh/DualMeshManager.hpp
index 7b6358b7010d6e8a2487f53697e143640da79235..46ad9c9f8d62cbff40a7d33efcc72c02e79f1ac7 100644
--- a/src/mesh/DualMeshManager.hpp
+++ b/src/mesh/DualMeshManager.hpp
@@ -2,23 +2,18 @@
 #define DUAL_MESH_MANAGER_HPP
 
 #include <mesh/DualMeshType.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 MeshVariant;
 
 class DualMeshManager
 {
  private:
-  using Key = std::pair<DualMeshType, const IMesh*>;
+  using Key = std::pair<DualMeshType, size_t>;
   struct HashKey
   {
     size_t
@@ -28,7 +23,7 @@ class DualMeshManager
     }
   };
 
-  std::unordered_map<Key, std::shared_ptr<const IMesh>, HashKey> m_mesh_to_dual_mesh_map;
+  std::unordered_map<Key, std::shared_ptr<const MeshVariant>, HashKey> m_mesh_to_dual_mesh_map;
 
   static DualMeshManager* m_instance;
 
@@ -50,15 +45,13 @@ class DualMeshManager
     return *m_instance;
   }
 
-  void deleteMesh(const IMesh*);
+  void deleteMesh(const size_t mesh_id);
 
-  std::shared_ptr<const Mesh<Connectivity<1>>> getDual1DMesh(const Mesh<Connectivity<1>>&);
+  std::shared_ptr<const MeshVariant> getDual1DMesh(const std::shared_ptr<const MeshVariant>&);
 
-  template <size_t Dimension>
-  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getDiamondDualMesh(const Mesh<Connectivity<Dimension>>&);
+  std::shared_ptr<const MeshVariant> getDiamondDualMesh(const std::shared_ptr<const MeshVariant>&);
 
-  template <size_t Dimension>
-  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getMedianDualMesh(const Mesh<Connectivity<Dimension>>&);
+  std::shared_ptr<const MeshVariant> getMedianDualMesh(const std::shared_ptr<const MeshVariant>&);
 };
 
 #endif   // DUAL_MESH_MANAGER_HPP
diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index e05dcaae592bcd4871f11de760ea1f17ee5221dd..bb409dd28c7e80b12537beb584b421e81045893a 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -8,6 +8,7 @@
 #include <mesh/ConnectivityDispatcher.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/Stringify.hpp>
@@ -1065,7 +1066,7 @@ GmshReader::GmshReader(const std::string& filename) : m_filename(filename)
     const int mesh_dimension = [&]() {
       int mutable_mesh_dimension = -1;   // unknown mesh dimension
       if (m_mesh) {
-        mutable_mesh_dimension = m_mesh->dimension();
+        mutable_mesh_dimension = std::visit([](auto&& mesh) { return mesh->dimension(); }, m_mesh->variant());
       }
 
       Array<int> dimensions = parallel::allGather(mutable_mesh_dimension);
@@ -1501,7 +1502,7 @@ GmshReader::__proceedData()
       std::dynamic_pointer_cast<const Connectivity3D>(connectivity_builder.connectivity());
     const Connectivity3D& connectivity = *p_connectivity;
 
-    using MeshType = Mesh<Connectivity3D>;
+    using MeshType = Mesh<3>;
     using Rd       = TinyVector<3, double>;
 
     NodeValue<Rd> xr(connectivity);
@@ -1510,7 +1511,7 @@ GmshReader::__proceedData()
       xr[i][1] = m_mesh_data.__vertices[i][1];
       xr[i][2] = m_mesh_data.__vertices[i][2];
     }
-    m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(p_connectivity, xr));
     this->_checkMesh<3>();
 
   } else if (dot(dimension2_mask, elementNumber) > 0) {
@@ -1522,7 +1523,7 @@ GmshReader::__proceedData()
       std::dynamic_pointer_cast<const Connectivity2D>(connectivity_builder.connectivity());
     const Connectivity2D& connectivity = *p_connectivity;
 
-    using MeshType = Mesh<Connectivity2D>;
+    using MeshType = Mesh<2>;
     using Rd       = TinyVector<2, double>;
 
     NodeValue<Rd> xr(connectivity);
@@ -1530,7 +1531,7 @@ GmshReader::__proceedData()
       xr[i][0] = m_mesh_data.__vertices[i][0];
       xr[i][1] = m_mesh_data.__vertices[i][1];
     }
-    m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(p_connectivity, xr));
     this->_checkMesh<2>();
 
   } else if (dot(dimension1_mask, elementNumber) > 0) {
@@ -1542,7 +1543,7 @@ GmshReader::__proceedData()
       std::dynamic_pointer_cast<const Connectivity1D>(connectivity_builder.connectivity());
     const Connectivity1D& connectivity = *p_connectivity;
 
-    using MeshType = Mesh<Connectivity1D>;
+    using MeshType = Mesh<1>;
     using Rd       = TinyVector<1, double>;
 
     NodeValue<Rd> xr(connectivity);
@@ -1550,7 +1551,7 @@ GmshReader::__proceedData()
       xr[i][0] = m_mesh_data.__vertices[i][0];
     }
 
-    m_mesh = std::make_shared<MeshType>(p_connectivity, xr);
+    m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(p_connectivity, xr));
     this->_checkMesh<1>();
 
   } else {
diff --git a/src/mesh/GmshReader.hpp b/src/mesh/GmshReader.hpp
index 860695ef57143b1b67575f54da95c12f8c7c2a01..12be8c593e17ceeaae79722510f12aa412d96e49 100644
--- a/src/mesh/GmshReader.hpp
+++ b/src/mesh/GmshReader.hpp
@@ -2,7 +2,6 @@
 #define GMSH_READER_HPP
 
 #include <algebra/TinyVector.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/MeshBuilderBase.hpp>
 #include <mesh/RefId.hpp>
 #include <utils/Array.hpp>
diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp
index 3ea34ab6e4fcf6935b456ed0c9cec1b65b79d44f..dca7043339bb57d3a68fdf73525160b1f12c2779 100644
--- a/src/mesh/IConnectivity.hpp
+++ b/src/mesh/IConnectivity.hpp
@@ -35,6 +35,8 @@ class IConnectivity : public std::enable_shared_from_this<IConnectivity>
     return this->shared_from_this();
   }
 
+  virtual size_t id() const = 0;
+
   virtual size_t numberOfNodes() const = 0;
   virtual size_t numberOfEdges() const = 0;
   virtual size_t numberOfFaces() const = 0;
diff --git a/src/mesh/IMesh.cpp b/src/mesh/IMesh.cpp
deleted file mode 100644
index e2a1739a8c6a928c0f29d6ef321d7c722f43ff65..0000000000000000000000000000000000000000
--- a/src/mesh/IMesh.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <mesh/IMesh.hpp>
-
-#include <mesh/DualMeshManager.hpp>
-#include <mesh/MeshDataManager.hpp>
-
-IMesh::~IMesh()
-{
-  MeshDataManager::instance().deleteMeshData(this);
-  DualMeshManager::instance().deleteMesh(this);
-}
diff --git a/src/mesh/IMesh.hpp b/src/mesh/IMesh.hpp
deleted file mode 100644
index 350f0f2e660df2ae7070e3435b25eb53d19f61b5..0000000000000000000000000000000000000000
--- a/src/mesh/IMesh.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef I_MESH_HPP
-#define I_MESH_HPP
-
-#include <cstddef>
-#include <iostream>
-
-class IMesh
-{
- protected:
-  virtual std::ostream& _write(std::ostream&) const = 0;
-
- public:
-  virtual size_t dimension() const = 0;
-
-  friend std::ostream&
-  operator<<(std::ostream& os, const IMesh& mesh)
-  {
-    return mesh._write(os);
-  }
-
-  IMesh(const IMesh&) = delete;
-  IMesh(IMesh&&)      = delete;
-
-  IMesh() = default;
-  virtual ~IMesh();
-};
-
-#endif   // I_MESH_HPP
diff --git a/src/mesh/IMeshData.hpp b/src/mesh/IMeshData.hpp
deleted file mode 100644
index 8cad9586d201de6b383bbbfaf94f09e5797078e5..0000000000000000000000000000000000000000
--- a/src/mesh/IMeshData.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef I_MESH_DATA_HPP
-#define I_MESH_DATA_HPP
-
-class IMeshData
-{
- public:
-  virtual ~IMeshData() = default;
-};
-
-#endif   // I_MESH_DATA_HPP
diff --git a/src/mesh/MedianDualMeshBuilder.cpp b/src/mesh/MedianDualMeshBuilder.cpp
index 79d71dadc0e619457dadb205db053935283c170a..016f729dc6ff39f8e089c7108744f148284cad17 100644
--- a/src/mesh/MedianDualMeshBuilder.cpp
+++ b/src/mesh/MedianDualMeshBuilder.cpp
@@ -6,19 +6,19 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
 #include <utils/Stringify.hpp>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-MedianDualMeshBuilder::_buildMedianDualMeshFrom(const IMesh& i_mesh)
+MedianDualMeshBuilder::_buildMedianDualMeshFrom(const MeshType& primal_mesh)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
   static_assert(Dimension > 1);
 
   using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<Connectivity<Dimension>>;
-
-  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
 
   DualConnectivityManager& manager = DualConnectivityManager::instance();
 
@@ -29,7 +29,7 @@ MedianDualMeshBuilder::_buildMedianDualMeshFrom(const IMesh& i_mesh)
 
   const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
 
-  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  MeshData<MeshType>& primal_mesh_data                   = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
   const FaceValue<const TinyVector<Dimension>> primal_xl = primal_mesh_data.xl();
 
@@ -39,24 +39,23 @@ MedianDualMeshBuilder::_buildMedianDualMeshFrom(const IMesh& i_mesh)
   NodeValue<TinyVector<Dimension>> dual_xr{dual_connectivity};
   primal_to_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xl, primal_xj, dual_xr);
 
-  m_mesh = std::make_shared<MeshType>(p_dual_connectivity, dual_xr);
+  m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(p_dual_connectivity, dual_xr));
 }
 
-MedianDualMeshBuilder::MedianDualMeshBuilder(const IMesh& i_mesh)
+MedianDualMeshBuilder::MedianDualMeshBuilder(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  switch (i_mesh.dimension()) {
-  case 2: {
-    this->_buildMedianDualMeshFrom<2>(i_mesh);
-    break;
-  }
-  case 3: {
-    throw NotImplementedError("median dual mesh");
-    break;
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid mesh dimension: " + stringify(i_mesh.dimension()));
-  }
-    // LCOV_EXCL_STOP
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        if constexpr (MeshType::Dimension > 1) {
+          this->_buildMedianDualMeshFrom(*p_mesh);
+        } else {
+          throw UnexpectedError("invalid polygonal mesh dimension");
+        }
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    mesh_v->variant());
 }
diff --git a/src/mesh/MedianDualMeshBuilder.hpp b/src/mesh/MedianDualMeshBuilder.hpp
index 6f8f1842c83bd272dd685984eb6dd91f84b0da8c..eb33440c5379548c5282c4380fd37dd9b646b9a8 100644
--- a/src/mesh/MedianDualMeshBuilder.hpp
+++ b/src/mesh/MedianDualMeshBuilder.hpp
@@ -2,17 +2,17 @@
 #define MEDIAN_DUAL_MESH_BUILDER_HPP
 
 #include <mesh/MeshBuilderBase.hpp>
+#include <mesh/MeshTraits.hpp>
 
-#include <memory>
-
+class MeshVariant;
 class MedianDualMeshBuilder : public MeshBuilderBase
 {
  private:
-  template <size_t Dimension>
-  void _buildMedianDualMeshFrom(const IMesh&);
+  template <MeshConcept MeshType>
+  void _buildMedianDualMeshFrom(const MeshType&);
 
   friend class DualMeshManager;
-  MedianDualMeshBuilder(const IMesh&);
+  MedianDualMeshBuilder(const std::shared_ptr<const MeshVariant>&);
 
  public:
   ~MedianDualMeshBuilder() = default;
diff --git a/src/mesh/Mesh.cpp b/src/mesh/Mesh.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..baa0d00d71e7371514aad465870b7fafb0d3f460
--- /dev/null
+++ b/src/mesh/Mesh.cpp
@@ -0,0 +1,16 @@
+#include <mesh/Mesh.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+template <size_t Dimension>
+Mesh<Dimension>::~Mesh()
+{
+  MeshDataManager::instance().deleteMeshData(this->id());
+  DualMeshManager::instance().deleteMesh(this->id());
+}
+
+template Mesh<1>::~Mesh();
+template Mesh<2>::~Mesh();
+template Mesh<3>::~Mesh();
diff --git a/src/mesh/Mesh.hpp b/src/mesh/Mesh.hpp
index 52cdd8bbfb834c98aaf6242cefa6b0d726e06ef2..60e3656bcd91acdb4feb5a14baeb2a1be0552ceb 100644
--- a/src/mesh/Mesh.hpp
+++ b/src/mesh/Mesh.hpp
@@ -2,33 +2,64 @@
 #define MESH_HPP
 
 #include <algebra/TinyVector.hpp>
-#include <mesh/IMesh.hpp>
+#include <mesh/Connectivity.hpp>
 #include <mesh/ItemValue.hpp>
+#include <mesh/MeshVariant.hpp>
+#include <utils/GlobalVariableManager.hpp>
 
 #include <memory>
 
-template <typename ConnectivityType>
-class Mesh final : public IMesh
+template <size_t MeshDimension>
+class Mesh : public std::enable_shared_from_this<Mesh<MeshDimension>>
 {
  public:
-  using Connectivity = ConnectivityType;
+  static constexpr size_t Dimension = MeshDimension;
+  using Connectivity                = ::Connectivity<Dimension>;
 
-  static constexpr size_t Dimension = ConnectivityType::Dimension;
-  using Rd                          = TinyVector<Dimension>;
+  using Rd = TinyVector<Dimension>;
 
  private:
+  const size_t m_id;
+
   const std::shared_ptr<const Connectivity> m_connectivity;
   const NodeValue<const Rd> m_xr;
 
   std::ostream&
-  _write(std::ostream& os) const final
+  _write(std::ostream& os) const
   {
     return os << *m_connectivity;
   }
 
  public:
+  friend std::ostream&
+  operator<<(std::ostream& os, const Mesh& mesh)
+  {
+    return mesh._write(os);
+  }
+
   PUGS_INLINE
   size_t
+  id() const
+  {
+    return m_id;
+  }
+
+  PUGS_INLINE
+  std::shared_ptr<const Mesh<Dimension>>
+  shared_ptr() const
+  {
+    return this->shared_from_this();
+  }
+
+  PUGS_INLINE
+  std::shared_ptr<const MeshVariant>
+  meshVariant() const
+  {
+    return std::make_shared<const MeshVariant>(this->shared_ptr());
+  }
+
+  PUGS_INLINE
+  constexpr size_t
   dimension() const
   {
     return Dimension;
@@ -90,19 +121,19 @@ class Mesh final : public IMesh
     return m_xr;
   }
 
+  Mesh() = delete;
+
   PUGS_INLINE
   Mesh(const std::shared_ptr<const Connectivity>& connectivity, const NodeValue<const Rd>& xr)
-    : m_connectivity{connectivity}, m_xr{xr}
+    : m_id{GlobalVariableManager::instance().getAndIncrementMeshId()}, m_connectivity{connectivity}, m_xr{xr}
   {
     ;
   }
 
-  Mesh() = delete;
+  Mesh(const Mesh&) = delete;
+  Mesh(Mesh&&)      = delete;
 
-  ~Mesh()
-  {
-    ;
-  }
+  ~Mesh();
 };
 
 #endif   // MESH_HPP
diff --git a/src/mesh/MeshBuilderBase.cpp b/src/mesh/MeshBuilderBase.cpp
index d2b27cd14c6680a99c4d6aba3281d36bcae1071b..4537f83b2177210f2bb0bf7e8e55b3219b57e6f8 100644
--- a/src/mesh/MeshBuilderBase.cpp
+++ b/src/mesh/MeshBuilderBase.cpp
@@ -7,6 +7,7 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
@@ -22,22 +23,22 @@ MeshBuilderBase::_dispatch()
 
   using ConnectivityType = Connectivity<Dimension>;
   using Rd               = TinyVector<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType         = Mesh<Dimension>;
 
   if (not m_mesh) {
     ConnectivityDescriptor descriptor;
     std::shared_ptr connectivity = ConnectivityType::build(descriptor);
     NodeValue<Rd> xr;
-    m_mesh = std::make_shared<MeshType>(connectivity, xr);
+    m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(connectivity, xr));
   }
 
-  const MeshType& mesh = dynamic_cast<const MeshType&>(*m_mesh);
+  const MeshType& mesh = *(m_mesh->get<const MeshType>());
   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);
+  m_mesh = std::make_shared<MeshVariant>(std::make_shared<const MeshType>(dispatched_connectivity, dispatched_xr));
 }
 
 template void MeshBuilderBase::_dispatch<1>();
@@ -48,14 +49,14 @@ template <size_t Dimension>
 void
 MeshBuilderBase::_checkMesh() const
 {
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType         = Mesh<Dimension>;
+  using ConnectivityType = typename MeshType::Connectivity;
 
   if (not m_mesh) {
     throw UnexpectedError("mesh is not built yet");
   }
 
-  const MeshType& mesh = dynamic_cast<const MeshType&>(*m_mesh);
+  const MeshType& mesh = *(m_mesh->get<const MeshType>());
 
   const ConnectivityType& connectivity = mesh.connectivity();
 
diff --git a/src/mesh/MeshBuilderBase.hpp b/src/mesh/MeshBuilderBase.hpp
index 63c55ec565b1f48023906f5aa602db13ef76d62c..665f0a88cc5a7f766ceb792594c6f5ace23529d6 100644
--- a/src/mesh/MeshBuilderBase.hpp
+++ b/src/mesh/MeshBuilderBase.hpp
@@ -1,14 +1,14 @@
 #ifndef MESH_BUILDER_BASE_HPP
 #define MESH_BUILDER_BASE_HPP
 
-#include <mesh/IMesh.hpp>
+class MeshVariant;
 
 #include <memory>
 
 class MeshBuilderBase
 {
  protected:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
 
   template <size_t Dimension>
   void _dispatch();
@@ -17,7 +17,7 @@ class MeshBuilderBase
   void _checkMesh() const;
 
  public:
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   mesh() const
   {
     return m_mesh;
diff --git a/src/mesh/MeshCellZone.cpp b/src/mesh/MeshCellZone.cpp
index ac63998b6edc658f39b9b9a1a11cf72f3bcf1c0a..08bdf64ba40c72449cabf2670a9c8c1112d0f2e8 100644
--- a/src/mesh/MeshCellZone.cpp
+++ b/src/mesh/MeshCellZone.cpp
@@ -5,13 +5,13 @@
 #include <utils/Messenger.hpp>
 
 template <size_t Dimension>
-MeshCellZone<Dimension>::MeshCellZone(const Mesh<Connectivity<Dimension>>&, const RefCellList& ref_cell_list)
+MeshCellZone<Dimension>::MeshCellZone(const Mesh<Dimension>&, const RefCellList& ref_cell_list)
   : m_cell_list(ref_cell_list.list()), m_zone_name(ref_cell_list.refId().tagName())
 {}
 
 template <size_t Dimension>
 MeshCellZone<Dimension>
-getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const IZoneDescriptor& zone_descriptor)
+getMeshCellZone(const Mesh<Dimension>& mesh, const IZoneDescriptor& zone_descriptor)
 {
   for (size_t i_ref_cell_list = 0; i_ref_cell_list < mesh.connectivity().template numberOfRefItemList<ItemType::cell>();
        ++i_ref_cell_list) {
@@ -28,6 +28,6 @@ getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const IZoneDescriptor
   throw NormalError(ost.str());
 }
 
-template MeshCellZone<1> getMeshCellZone(const Mesh<Connectivity<1>>&, const IZoneDescriptor&);
-template MeshCellZone<2> getMeshCellZone(const Mesh<Connectivity<2>>&, const IZoneDescriptor&);
-template MeshCellZone<3> getMeshCellZone(const Mesh<Connectivity<3>>&, const IZoneDescriptor&);
+template MeshCellZone<1> getMeshCellZone(const Mesh<1>&, const IZoneDescriptor&);
+template MeshCellZone<2> getMeshCellZone(const Mesh<2>&, const IZoneDescriptor&);
+template MeshCellZone<3> getMeshCellZone(const Mesh<3>&, const IZoneDescriptor&);
diff --git a/src/mesh/MeshCellZone.hpp b/src/mesh/MeshCellZone.hpp
index de4903689b328bddbac3f0bacf363497374f8fe4..1f8827b1b2998678f9f73ddbbdd2c18a18ed753d 100644
--- a/src/mesh/MeshCellZone.hpp
+++ b/src/mesh/MeshCellZone.hpp
@@ -6,9 +6,6 @@
 #include <utils/Array.hpp>
 
 template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
 class Mesh;
 
 template <size_t Dimension>
@@ -20,30 +17,30 @@ class [[nodiscard]] MeshCellZone   // clazy:exclude=copyable-polymorphic
 
  public:
   template <size_t MeshDimension>
-  friend MeshCellZone<MeshDimension> getMeshCellZone(const Mesh<Connectivity<MeshDimension>>& mesh,
+  friend MeshCellZone<MeshDimension> getMeshCellZone(const Mesh<MeshDimension>& mesh,
                                                      const IZoneDescriptor& zone_descriptor);
 
   MeshCellZone& operator=(const MeshCellZone&) = default;
-  MeshCellZone& operator=(MeshCellZone&&) = default;
+  MeshCellZone& operator=(MeshCellZone&&)      = default;
 
-  const Array<const CellId>& cellList() const
+  const Array<const CellId>&
+  cellList() const
   {
     return m_cell_list;
   }
 
  protected:
-  MeshCellZone(const Mesh<Connectivity<Dimension>>& mesh, const RefCellList& ref_cell_list);
+  MeshCellZone(const Mesh<Dimension>& mesh, const RefCellList& ref_cell_list);
 
  public:
   MeshCellZone(const MeshCellZone&) = default;
-  MeshCellZone(MeshCellZone &&)     = default;
+  MeshCellZone(MeshCellZone&&)      = default;
 
   MeshCellZone()          = default;
   virtual ~MeshCellZone() = default;
 };
 
 template <size_t Dimension>
-MeshCellZone<Dimension> getMeshCellZone(const Mesh<Connectivity<Dimension>>& mesh,
-                                        const IZoneDescriptor& zone_descriptor);
+MeshCellZone<Dimension> getMeshCellZone(const Mesh<Dimension>& mesh, const IZoneDescriptor& zone_descriptor);
 
 #endif   // MESH_CELL_ZONE_HPP
diff --git a/src/mesh/MeshData.cpp b/src/mesh/MeshData.cpp
index 2fdddb387554258120bb36eba31e57a940e2576c..40e3878624b1ae84d1923063df6bc6b707926104 100644
--- a/src/mesh/MeshData.cpp
+++ b/src/mesh/MeshData.cpp
@@ -3,16 +3,18 @@
 #include <mesh/Connectivity.hpp>
 #include <mesh/ItemValueVariant.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <output/NamedItemValueVariant.hpp>
 #include <output/VTKWriter.hpp>
 #include <utils/Exceptions.hpp>
 
 template <size_t Dimension>
 void
-MeshData<Dimension>::_storeBadMesh()
+MeshData<Mesh<Dimension>>::_storeBadMesh()
 {
   VTKWriter writer("bad_mesh");
-  writer.writeOnMesh(std::make_shared<MeshType>(m_mesh.shared_connectivity(), m_mesh.xr()),
+  writer.writeOnMesh(std::make_shared<MeshVariant>(
+                       std::make_shared<const Mesh<Dimension>>(m_mesh.shared_connectivity(), m_mesh.xr())),
                      {std::make_shared<NamedItemValueVariant>(std::make_shared<ItemValueVariant>(m_Vj), "volume")});
   std::ostringstream error_msg;
   error_msg << "mesh contains cells of non-positive volume (see " << rang::fgB::yellow << "bad_mesh.pvd"
@@ -20,6 +22,6 @@ MeshData<Dimension>::_storeBadMesh()
   throw NormalError(error_msg.str());
 }
 
-template void MeshData<1>::_storeBadMesh();
-template void MeshData<2>::_storeBadMesh();
-template void MeshData<3>::_storeBadMesh();
+template void MeshData<Mesh<1>>::_storeBadMesh();
+template void MeshData<Mesh<2>>::_storeBadMesh();
+template void MeshData<Mesh<3>>::_storeBadMesh();
diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp
index 78b9b81e69daf5e3fc0010bd8c737b7303759353..4fb87bc5ad4aee87c7dd92e9232ec4508145a14f 100644
--- a/src/mesh/MeshData.hpp
+++ b/src/mesh/MeshData.hpp
@@ -2,27 +2,27 @@
 #define MESH_DATA_HPP
 
 #include <algebra/TinyVector.hpp>
-#include <mesh/IMeshData.hpp>
 #include <mesh/ItemValue.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/PugsUtils.hpp>
 
 template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
 class Mesh;
 
+template <MeshConcept MeshType>
+class MeshData;
+
 template <size_t Dimension>
-class MeshData : public IMeshData
+class MeshData<Mesh<Dimension>>
 {
  public:
+  using MeshType = Mesh<Dimension>;
+
   static_assert(Dimension > 0, "dimension must be strictly positive");
   static_assert((Dimension <= 3), "only 1d, 2d and 3d are implemented");
 
-  using MeshType = Mesh<Connectivity<Dimension>>;
-
   using Rd = TinyVector<Dimension>;
 
   static constexpr double inv_Dimension = 1. / Dimension;
diff --git a/src/mesh/MeshDataManager.cpp b/src/mesh/MeshDataManager.cpp
index 8ca184a6013c083f5e8df7412d19f80b8f50f95a..e95a717d8f90b9aaba86f9c3702f15a06a0df54a 100644
--- a/src/mesh/MeshDataManager.cpp
+++ b/src/mesh/MeshDataManager.cpp
@@ -4,6 +4,7 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshDataVariant.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <sstream>
@@ -22,10 +23,10 @@ MeshDataManager::destroy()
 {
   Assert(m_instance != nullptr, "MeshDataManager was not created!");
 
-  if (m_instance->m_mesh_mesh_data_map.size() > 0) {
+  if (m_instance->m_mesh_id_mesh_data_map.size() > 0) {
     std::stringstream error;
     error << ": some mesh data is still registered\n";
-    for (const auto& i_mesh_data : m_instance->m_mesh_mesh_data_map) {
+    for (const auto& i_mesh_data : m_instance->m_mesh_id_mesh_data_map) {
       error << " - mesh data " << rang::fgB::magenta << i_mesh_data.first << rang::style::reset << '\n';
     }
     throw UnexpectedError(error.str());
@@ -35,28 +36,27 @@ MeshDataManager::destroy()
 }
 
 void
-MeshDataManager::deleteMeshData(const IMesh* p_mesh)
+MeshDataManager::deleteMeshData(const size_t mesh_id)
 {
-  m_mesh_mesh_data_map.erase(p_mesh);
+  m_mesh_id_mesh_data_map.erase(mesh_id);
 }
 
-template <size_t Dimension>
-MeshData<Dimension>&
-MeshDataManager::getMeshData(const Mesh<Connectivity<Dimension>>& mesh)
+template <MeshConcept MeshType>
+MeshData<MeshType>&
+MeshDataManager::getMeshData(const MeshType& mesh)
 {
-  const IMesh* p_mesh = &mesh;
-
-  if (auto i_mesh_data = m_mesh_mesh_data_map.find(p_mesh); i_mesh_data != m_mesh_mesh_data_map.end()) {
-    return dynamic_cast<MeshData<Dimension>&>(*i_mesh_data->second);
+  if (auto i_mesh_data = m_mesh_id_mesh_data_map.find(mesh.id()); i_mesh_data != m_mesh_id_mesh_data_map.end()) {
+    const auto& mesh_data_v = *i_mesh_data->second;
+    return *mesh_data_v.template get<MeshType>();
   } else {
     // **cannot** use make_shared since MeshData constructor is **private**
-    std::shared_ptr<MeshData<Dimension>> mesh_data{new MeshData<Dimension>(mesh)};
+    std::shared_ptr<MeshData<MeshType>> mesh_data{new MeshData<MeshType>(mesh)};
 
-    m_mesh_mesh_data_map[p_mesh] = mesh_data;
+    m_mesh_id_mesh_data_map[mesh.id()] = std::make_shared<MeshDataVariant>(mesh_data);
     return *mesh_data;
   }
 }
 
-template MeshData<1>& MeshDataManager::getMeshData(const Mesh<Connectivity<1>>&);
-template MeshData<2>& MeshDataManager::getMeshData(const Mesh<Connectivity<2>>&);
-template MeshData<3>& MeshDataManager::getMeshData(const Mesh<Connectivity<3>>&);
+template MeshData<Mesh<1>>& MeshDataManager::getMeshData(const Mesh<1>&);
+template MeshData<Mesh<2>>& MeshDataManager::getMeshData(const Mesh<2>&);
+template MeshData<Mesh<3>>& MeshDataManager::getMeshData(const Mesh<3>&);
diff --git a/src/mesh/MeshDataManager.hpp b/src/mesh/MeshDataManager.hpp
index fd04101dea29a2d15e0feb2193f22f785ff65dc3..4ec3bd7fe2064c726f0a86c53170f6d4e1f2dfff 100644
--- a/src/mesh/MeshDataManager.hpp
+++ b/src/mesh/MeshDataManager.hpp
@@ -1,28 +1,22 @@
 #ifndef MESH_DATA_MANAGER_HPP
 #define MESH_DATA_MANAGER_HPP
 
-#include <mesh/IMeshData.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 
 #include <memory>
 #include <unordered_map>
 
-class IMesh;
+class MeshDataVariant;
 
-template <size_t>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class MeshData;
 
 class MeshDataManager
 {
  private:
-  std::unordered_map<const IMesh*, std::shared_ptr<IMeshData>> m_mesh_mesh_data_map;
+  std::unordered_map<size_t, std::shared_ptr<MeshDataVariant>> m_mesh_id_mesh_data_map;
 
   static MeshDataManager* m_instance;
 
@@ -44,10 +38,10 @@ class MeshDataManager
     return *m_instance;
   }
 
-  void deleteMeshData(const IMesh*);
+  void deleteMeshData(const size_t mesh_id);
 
-  template <size_t Dimension>
-  MeshData<Dimension>& getMeshData(const Mesh<Connectivity<Dimension>>&);
+  template <MeshConcept MeshType>
+  MeshData<MeshType>& getMeshData(const MeshType&);
 };
 
 #endif   // MESH_DATA_MANAGER_HPP
diff --git a/src/mesh/MeshDataVariant.hpp b/src/mesh/MeshDataVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1f5e77325df2900b5fe0969f4d71347980470217
--- /dev/null
+++ b/src/mesh/MeshDataVariant.hpp
@@ -0,0 +1,62 @@
+#ifndef MESH_DATA_VARIANT_HPP
+#define MESH_DATA_VARIANT_HPP
+
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <utils/Demangle.hpp>
+#include <utils/Exceptions.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <rang.hpp>
+
+#include <memory>
+#include <sstream>
+#include <variant>
+
+template <size_t Dimension>
+class Mesh;
+
+class MeshDataVariant
+{
+ private:
+  using Variant = std::variant<std::shared_ptr<MeshData<Mesh<1>>>,   //
+                               std::shared_ptr<MeshData<Mesh<2>>>,   //
+                               std::shared_ptr<MeshData<Mesh<3>>>>;
+
+  Variant m_p_mesh_data_variant;
+
+ public:
+  template <MeshConcept MeshType>
+  PUGS_INLINE std::shared_ptr<MeshData<MeshType>>
+  get() const
+  {
+    if (not std::holds_alternative<std::shared_ptr<MeshData<MeshType>>>(this->m_p_mesh_data_variant)) {
+      std::ostringstream error_msg;
+      error_msg << "invalid mesh type type\n";
+      error_msg << "- required " << rang::fgB::red << demangle<MeshData<MeshType>>() << rang::fg::reset << '\n';
+      error_msg << "- contains " << rang::fgB::yellow
+                << std::visit(
+                     [](auto&& p_mesh_data) -> std::string {
+                       using FoundMeshDataType = typename std::decay_t<decltype(p_mesh_data)>::element_type;
+                       return demangle<FoundMeshDataType>();
+                     },
+                     this->m_p_mesh_data_variant)
+                << rang::fg::reset;
+      throw NormalError(error_msg.str());
+    }
+    return std::get<std::shared_ptr<MeshData<MeshType>>>(m_p_mesh_data_variant);
+  }
+
+  MeshDataVariant() = delete;
+
+  template <MeshConcept MeshType>
+  MeshDataVariant(const std::shared_ptr<MeshData<MeshType>>& p_mesh_data) : m_p_mesh_data_variant{p_mesh_data}
+  {}
+
+  MeshDataVariant(const MeshDataVariant&) = default;
+  MeshDataVariant(MeshDataVariant&&)      = default;
+
+  ~MeshDataVariant() = default;
+};
+
+#endif   // MESH_DATA_VARIANT_HPP
diff --git a/src/mesh/MeshEdgeBoundary.cpp b/src/mesh/MeshEdgeBoundary.cpp
index 4c60b3424e93470e0135ee34fb81e870058e2fa9..65d95e9f64ed53a60587fec05bffc085a9b541d6 100644
--- a/src/mesh/MeshEdgeBoundary.cpp
+++ b/src/mesh/MeshEdgeBoundary.cpp
@@ -5,19 +5,19 @@
 #include <mesh/Mesh.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshEdgeBoundary<Dimension>::MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>&, const RefEdgeList& ref_edge_list)
-  : m_ref_edge_list(ref_edge_list)
+template <MeshConcept MeshType>
+MeshEdgeBoundary::MeshEdgeBoundary(const MeshType&, const RefEdgeList& ref_edge_list) : m_ref_edge_list(ref_edge_list)
 {}
 
-template MeshEdgeBoundary<1>::MeshEdgeBoundary(const Mesh<Connectivity<1>>&, const RefEdgeList&);
-template MeshEdgeBoundary<2>::MeshEdgeBoundary(const Mesh<Connectivity<2>>&, const RefEdgeList&);
-template MeshEdgeBoundary<3>::MeshEdgeBoundary(const Mesh<Connectivity<3>>&, const RefEdgeList&);
+template MeshEdgeBoundary::MeshEdgeBoundary(const Mesh<1>&, const RefEdgeList&);
+template MeshEdgeBoundary::MeshEdgeBoundary(const Mesh<2>&, const RefEdgeList&);
+template MeshEdgeBoundary::MeshEdgeBoundary(const Mesh<3>&, const RefEdgeList&);
 
-template <size_t Dimension>
-MeshEdgeBoundary<Dimension>::MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                              const RefFaceList& ref_face_list)
+template <MeshConcept MeshType>
+MeshEdgeBoundary::MeshEdgeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
+
   const Array<const FaceId>& face_list = ref_face_list.list();
   static_assert(Dimension > 1, "conversion from to edge from face is valid in dimension > 1");
 
@@ -55,13 +55,15 @@ MeshEdgeBoundary<Dimension>::MeshEdgeBoundary(const Mesh<Connectivity<Dimension>
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_edge_list);
 }
 
-template MeshEdgeBoundary<2>::MeshEdgeBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshEdgeBoundary<3>::MeshEdgeBoundary(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshEdgeBoundary::MeshEdgeBoundary(const Mesh<2>&, const RefFaceList&);
+template MeshEdgeBoundary::MeshEdgeBoundary(const Mesh<3>&, const RefFaceList&);
 
-template <size_t Dimension>
-MeshEdgeBoundary<Dimension>
-getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshEdgeBoundary
+getMeshEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
+
   for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>();
        ++i_ref_edge_list) {
     const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list);
@@ -76,7 +78,7 @@ getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
         throw NormalError(ost.str());
       }
 
-      return MeshEdgeBoundary<Dimension>{mesh, ref_edge_list};
+      return MeshEdgeBoundary{mesh, ref_edge_list};
     }
   }
   if constexpr (Dimension > 1) {
@@ -94,7 +96,7 @@ getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
           throw NormalError(ost.str());
         }
 
-        return MeshEdgeBoundary<Dimension>{mesh, ref_face_list};
+        return MeshEdgeBoundary{mesh, ref_face_list};
       }
     }
   }
@@ -105,6 +107,6 @@ getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
   throw NormalError(ost.str());
 }
 
-template MeshEdgeBoundary<1> getMeshEdgeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshEdgeBoundary<2> getMeshEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshEdgeBoundary<3> getMeshEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshEdgeBoundary getMeshEdgeBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshEdgeBoundary getMeshEdgeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshEdgeBoundary getMeshEdgeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshEdgeBoundary.hpp b/src/mesh/MeshEdgeBoundary.hpp
index e37c4fdffefdc494c97bd8ddbea61f656ef938b7..411de18857e3f64165538a8a0f5ce283dc46b3dc 100644
--- a/src/mesh/MeshEdgeBoundary.hpp
+++ b/src/mesh/MeshEdgeBoundary.hpp
@@ -3,25 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshEdgeBoundary   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefEdgeList m_ref_edge_list;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshEdgeBoundary<MeshDimension> getMeshEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                             const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshType>
+  friend MeshEdgeBoundary getMeshEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
   MeshEdgeBoundary& operator=(const MeshEdgeBoundary&) = default;
   MeshEdgeBoundary& operator=(MeshEdgeBoundary&&) = default;
@@ -39,8 +32,10 @@ class [[nodiscard]] MeshEdgeBoundary   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list);
-  MeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshEdgeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list);
+  template <MeshConcept MeshType>
+  MeshEdgeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list);
 
  public:
   MeshEdgeBoundary(const MeshEdgeBoundary&) = default;   // LCOV_EXCL_LINE
@@ -50,8 +45,7 @@ class [[nodiscard]] MeshEdgeBoundary   // clazy:exclude=copyable-polymorphic
   virtual ~MeshEdgeBoundary() = default;
 };
 
-template <size_t Dimension>
-MeshEdgeBoundary<Dimension> getMeshEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshEdgeBoundary getMeshEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_EDGE_BOUNDARY_HPP
diff --git a/src/mesh/MeshEdgeInterface.cpp b/src/mesh/MeshEdgeInterface.cpp
index da4d0bc9f1ec42fd68150fab5944d3d446e42dcb..b74964eeec64c09c8dcc7fdcbb201e7af2b2fac3 100644
--- a/src/mesh/MeshEdgeInterface.cpp
+++ b/src/mesh/MeshEdgeInterface.cpp
@@ -5,19 +5,19 @@
 #include <mesh/Mesh.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshEdgeInterface<Dimension>::MeshEdgeInterface(const Mesh<Connectivity<Dimension>>&, const RefEdgeList& ref_edge_list)
-  : m_ref_edge_list(ref_edge_list)
+template <MeshConcept MeshType>
+MeshEdgeInterface::MeshEdgeInterface(const MeshType&, const RefEdgeList& ref_edge_list) : m_ref_edge_list(ref_edge_list)
 {}
 
-template MeshEdgeInterface<1>::MeshEdgeInterface(const Mesh<Connectivity<1>>&, const RefEdgeList&);
-template MeshEdgeInterface<2>::MeshEdgeInterface(const Mesh<Connectivity<2>>&, const RefEdgeList&);
-template MeshEdgeInterface<3>::MeshEdgeInterface(const Mesh<Connectivity<3>>&, const RefEdgeList&);
+template MeshEdgeInterface::MeshEdgeInterface(const Mesh<1>&, const RefEdgeList&);
+template MeshEdgeInterface::MeshEdgeInterface(const Mesh<2>&, const RefEdgeList&);
+template MeshEdgeInterface::MeshEdgeInterface(const Mesh<3>&, const RefEdgeList&);
 
-template <size_t Dimension>
-MeshEdgeInterface<Dimension>::MeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const RefFaceList& ref_face_list)
+template <MeshConcept MeshType>
+MeshEdgeInterface::MeshEdgeInterface(const MeshType& mesh, const RefFaceList& ref_face_list)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
+
   const Array<const FaceId>& face_list = ref_face_list.list();
   static_assert(Dimension > 1, "conversion from to edge from face is valid in dimension > 1");
 
@@ -55,13 +55,15 @@ MeshEdgeInterface<Dimension>::MeshEdgeInterface(const Mesh<Connectivity<Dimensio
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_edge_list);
 }
 
-template MeshEdgeInterface<2>::MeshEdgeInterface(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshEdgeInterface<3>::MeshEdgeInterface(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshEdgeInterface::MeshEdgeInterface(const Mesh<2>&, const RefFaceList&);
+template MeshEdgeInterface::MeshEdgeInterface(const Mesh<3>&, const RefFaceList&);
 
-template <size_t Dimension>
-MeshEdgeInterface<Dimension>
-getMeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterfaceDescriptor& interface_descriptor)
+template <MeshConcept MeshType>
+MeshEdgeInterface
+getMeshEdgeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
+
   for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>();
        ++i_ref_edge_list) {
     const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list);
@@ -76,7 +78,7 @@ getMeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
         throw NormalError(ost.str());
       }
 
-      return MeshEdgeInterface<Dimension>{mesh, ref_edge_list};
+      return MeshEdgeInterface{mesh, ref_edge_list};
     }
   }
   if constexpr (Dimension > 1) {
@@ -95,7 +97,7 @@ getMeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
           throw NormalError(ost.str());
         }
 
-        return MeshEdgeInterface<Dimension>{mesh, ref_face_list};
+        return MeshEdgeInterface{mesh, ref_face_list};
       }
     }
   }
@@ -106,6 +108,6 @@ getMeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
   throw NormalError(ost.str());
 }
 
-template MeshEdgeInterface<1> getMeshEdgeInterface(const Mesh<Connectivity<1>>&, const IInterfaceDescriptor&);
-template MeshEdgeInterface<2> getMeshEdgeInterface(const Mesh<Connectivity<2>>&, const IInterfaceDescriptor&);
-template MeshEdgeInterface<3> getMeshEdgeInterface(const Mesh<Connectivity<3>>&, const IInterfaceDescriptor&);
+template MeshEdgeInterface getMeshEdgeInterface(const Mesh<1>&, const IInterfaceDescriptor&);
+template MeshEdgeInterface getMeshEdgeInterface(const Mesh<2>&, const IInterfaceDescriptor&);
+template MeshEdgeInterface getMeshEdgeInterface(const Mesh<3>&, const IInterfaceDescriptor&);
diff --git a/src/mesh/MeshEdgeInterface.hpp b/src/mesh/MeshEdgeInterface.hpp
index 25226264c2b906066d2940e6a92a8a65b53042cc..b4a662d04424827fc9fc3bd0cc01a01570d2857b 100644
--- a/src/mesh/MeshEdgeInterface.hpp
+++ b/src/mesh/MeshEdgeInterface.hpp
@@ -3,25 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IInterfaceDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshEdgeInterface   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefEdgeList m_ref_edge_list;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshEdgeInterface<MeshDimension> getMeshEdgeInterface(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                               const IInterfaceDescriptor& interface_descriptor);
+  template <MeshConcept MeshType>
+  friend MeshEdgeInterface getMeshEdgeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
   MeshEdgeInterface& operator=(const MeshEdgeInterface&) = default;
   MeshEdgeInterface& operator=(MeshEdgeInterface&&) = default;
@@ -39,8 +32,10 @@ class [[nodiscard]] MeshEdgeInterface   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list);
-  MeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshEdgeInterface(const MeshType& mesh, const RefEdgeList& ref_edge_list);
+  template <MeshConcept MeshType>
+  MeshEdgeInterface(const MeshType& mesh, const RefFaceList& ref_face_list);
 
  public:
   MeshEdgeInterface(const MeshEdgeInterface&) = default;   // LCOV_EXCL_LINE
@@ -50,8 +45,7 @@ class [[nodiscard]] MeshEdgeInterface   // clazy:exclude=copyable-polymorphic
   virtual ~MeshEdgeInterface() = default;
 };
 
-template <size_t Dimension>
-MeshEdgeInterface<Dimension> getMeshEdgeInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                  const IInterfaceDescriptor& interface_descriptor);
+template <MeshConcept MeshType>
+MeshEdgeInterface getMeshEdgeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
 #endif   // MESH_EDGE_INTERFACE_HPP
diff --git a/src/mesh/MeshFaceBoundary.cpp b/src/mesh/MeshFaceBoundary.cpp
index ad77830f5cbcb10070cfb7cb4989a7065a650f16..9d059861286d2a623a6ddff3d7f3040954f9a9c9 100644
--- a/src/mesh/MeshFaceBoundary.cpp
+++ b/src/mesh/MeshFaceBoundary.cpp
@@ -4,18 +4,17 @@
 #include <mesh/Mesh.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshFaceBoundary<Dimension>::MeshFaceBoundary(const Mesh<Connectivity<Dimension>>&, const RefFaceList& ref_face_list)
-  : m_ref_face_list(ref_face_list)
+template <MeshConcept MeshType>
+MeshFaceBoundary::MeshFaceBoundary(const MeshType&, const RefFaceList& ref_face_list) : m_ref_face_list(ref_face_list)
 {}
 
-template MeshFaceBoundary<1>::MeshFaceBoundary(const Mesh<Connectivity<1>>&, const RefFaceList&);
-template MeshFaceBoundary<2>::MeshFaceBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshFaceBoundary<3>::MeshFaceBoundary(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshFaceBoundary::MeshFaceBoundary(const Mesh<1>&, const RefFaceList&);
+template MeshFaceBoundary::MeshFaceBoundary(const Mesh<2>&, const RefFaceList&);
+template MeshFaceBoundary::MeshFaceBoundary(const Mesh<3>&, const RefFaceList&);
 
-template <size_t Dimension>
-MeshFaceBoundary<Dimension>
-getMeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshFaceBoundary
+getMeshFaceBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
        ++i_ref_face_list) {
@@ -31,7 +30,7 @@ getMeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
         throw NormalError(ost.str());
       }
 
-      return MeshFaceBoundary<Dimension>{mesh, ref_face_list};
+      return MeshFaceBoundary{mesh, ref_face_list};
     }
   }
 
@@ -41,6 +40,6 @@ getMeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
   throw NormalError(ost.str());
 }
 
-template MeshFaceBoundary<1> getMeshFaceBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshFaceBoundary<2> getMeshFaceBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshFaceBoundary<3> getMeshFaceBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshFaceBoundary getMeshFaceBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshFaceBoundary getMeshFaceBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshFaceBoundary getMeshFaceBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshFaceBoundary.hpp b/src/mesh/MeshFaceBoundary.hpp
index 98a63774138e60bbb01437ee4fb6d88bd1400e40..443f868c0520b6031221093a07d0af1c193538ed 100644
--- a/src/mesh/MeshFaceBoundary.hpp
+++ b/src/mesh/MeshFaceBoundary.hpp
@@ -3,25 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefFaceList m_ref_face_list;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshFaceBoundary<MeshDimension> getMeshFaceBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                             const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshFaceBoundary getMeshFaceBoundary(const MeshTypeT& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
   MeshFaceBoundary& operator=(const MeshFaceBoundary&) = default;
   MeshFaceBoundary& operator=(MeshFaceBoundary&&) = default;
@@ -39,7 +32,8 @@ class [[nodiscard]] MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshFaceBoundary(const MeshType& mesh, const RefFaceList& ref_face_list);
 
  public:
   MeshFaceBoundary(const MeshFaceBoundary&) = default;   // LCOV_EXCL_LINE
@@ -49,8 +43,7 @@ class [[nodiscard]] MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
   virtual ~MeshFaceBoundary() = default;
 };
 
-template <size_t Dimension>
-MeshFaceBoundary<Dimension> getMeshFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshFaceBoundary getMeshFaceBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_FACE_BOUNDARY_HPP
diff --git a/src/mesh/MeshFaceInterface.cpp b/src/mesh/MeshFaceInterface.cpp
index afb73bf1872ece258788d1a4c6818f9d588250b6..8dfe8cb49011af58b2ab1e67a9170ba3a7f8e5e1 100644
--- a/src/mesh/MeshFaceInterface.cpp
+++ b/src/mesh/MeshFaceInterface.cpp
@@ -4,18 +4,17 @@
 #include <mesh/Mesh.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshFaceInterface<Dimension>::MeshFaceInterface(const Mesh<Connectivity<Dimension>>&, const RefFaceList& ref_face_list)
-  : m_ref_face_list(ref_face_list)
+template <MeshConcept MeshType>
+MeshFaceInterface::MeshFaceInterface(const MeshType&, const RefFaceList& ref_face_list) : m_ref_face_list(ref_face_list)
 {}
 
-template MeshFaceInterface<1>::MeshFaceInterface(const Mesh<Connectivity<1>>&, const RefFaceList&);
-template MeshFaceInterface<2>::MeshFaceInterface(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshFaceInterface<3>::MeshFaceInterface(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshFaceInterface::MeshFaceInterface(const Mesh<1>&, const RefFaceList&);
+template MeshFaceInterface::MeshFaceInterface(const Mesh<2>&, const RefFaceList&);
+template MeshFaceInterface::MeshFaceInterface(const Mesh<3>&, const RefFaceList&);
 
-template <size_t Dimension>
-MeshFaceInterface<Dimension>
-getMeshFaceInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterfaceDescriptor& interface_descriptor)
+template <MeshConcept MeshType>
+MeshFaceInterface
+getMeshFaceInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor)
 {
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
        ++i_ref_face_list) {
@@ -30,7 +29,7 @@ getMeshFaceInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
         throw NormalError(ost.str());
       }
 
-      return MeshFaceInterface<Dimension>{mesh, ref_face_list};
+      return MeshFaceInterface{mesh, ref_face_list};
     }
   }
 
@@ -40,6 +39,6 @@ getMeshFaceInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
   throw NormalError(ost.str());
 }
 
-template MeshFaceInterface<1> getMeshFaceInterface(const Mesh<Connectivity<1>>&, const IInterfaceDescriptor&);
-template MeshFaceInterface<2> getMeshFaceInterface(const Mesh<Connectivity<2>>&, const IInterfaceDescriptor&);
-template MeshFaceInterface<3> getMeshFaceInterface(const Mesh<Connectivity<3>>&, const IInterfaceDescriptor&);
+template MeshFaceInterface getMeshFaceInterface(const Mesh<1>&, const IInterfaceDescriptor&);
+template MeshFaceInterface getMeshFaceInterface(const Mesh<2>&, const IInterfaceDescriptor&);
+template MeshFaceInterface getMeshFaceInterface(const Mesh<3>&, const IInterfaceDescriptor&);
diff --git a/src/mesh/MeshFaceInterface.hpp b/src/mesh/MeshFaceInterface.hpp
index 87d836a3705199aab546396d24052025b59b5729..64c75e14a758ad203f5773466c5010bb5938dda8 100644
--- a/src/mesh/MeshFaceInterface.hpp
+++ b/src/mesh/MeshFaceInterface.hpp
@@ -3,25 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IInterfaceDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshFaceInterface   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefFaceList m_ref_face_list;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshFaceInterface<MeshDimension> getMeshFaceInterface(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                               const IInterfaceDescriptor& interface_descriptor);
+  template <MeshConcept MeshType>
+  friend MeshFaceInterface getMeshFaceInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
   MeshFaceInterface& operator=(const MeshFaceInterface&) = default;
   MeshFaceInterface& operator=(MeshFaceInterface&&) = default;
@@ -39,7 +32,8 @@ class [[nodiscard]] MeshFaceInterface   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshFaceInterface(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshFaceInterface(const MeshType& mesh, const RefFaceList& ref_face_list);
 
  public:
   MeshFaceInterface(const MeshFaceInterface&) = default;   // LCOV_EXCL_LINE
@@ -49,8 +43,7 @@ class [[nodiscard]] MeshFaceInterface   // clazy:exclude=copyable-polymorphic
   virtual ~MeshFaceInterface() = default;
 };
 
-template <size_t Dimension>
-MeshFaceInterface<Dimension> getMeshFaceInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                  const IInterfaceDescriptor& interface_descriptor);
+template <MeshConcept MeshType>
+MeshFaceInterface getMeshFaceInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
 #endif   // MESH_FACE_INTERFACE_HPP
diff --git a/src/mesh/MeshFlatEdgeBoundary.cpp b/src/mesh/MeshFlatEdgeBoundary.cpp
index d4c1ec155a0e8c4d863facbf6280d71265944933..c20b9c1a5967fa511fdd4c9f99359696d7117cc2 100644
--- a/src/mesh/MeshFlatEdgeBoundary.cpp
+++ b/src/mesh/MeshFlatEdgeBoundary.cpp
@@ -4,17 +4,17 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshFlatNodeBoundary.hpp>
 
-template <size_t Dimension>
-MeshFlatEdgeBoundary<Dimension>
-getMeshFlatEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshFlatEdgeBoundary<MeshType>
+getMeshFlatEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
-  MeshEdgeBoundary<Dimension> mesh_edge_boundary          = getMeshEdgeBoundary(mesh, boundary_descriptor);
-  MeshFlatNodeBoundary<Dimension> mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor);
+  MeshEdgeBoundary mesh_edge_boundary          = getMeshEdgeBoundary(mesh, boundary_descriptor);
+  MeshFlatNodeBoundary mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor);
 
-  return MeshFlatEdgeBoundary<Dimension>{mesh, mesh_edge_boundary.refEdgeList(),
-                                         mesh_flat_node_boundary.outgoingNormal()};
+  return MeshFlatEdgeBoundary<MeshType>{mesh, mesh_edge_boundary.refEdgeList(),
+                                        mesh_flat_node_boundary.outgoingNormal()};
 }
 
-template MeshFlatEdgeBoundary<1> getMeshFlatEdgeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshFlatEdgeBoundary<2> getMeshFlatEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshFlatEdgeBoundary<3> getMeshFlatEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshFlatEdgeBoundary<Mesh<1>> getMeshFlatEdgeBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshFlatEdgeBoundary<Mesh<2>> getMeshFlatEdgeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshFlatEdgeBoundary<Mesh<3>> getMeshFlatEdgeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshFlatEdgeBoundary.hpp b/src/mesh/MeshFlatEdgeBoundary.hpp
index a104d1413a7319f08b74bf1ef103a5157b803f18..10ab8edb835e8c7652665cae4b9db9cfe75c4418 100644
--- a/src/mesh/MeshFlatEdgeBoundary.hpp
+++ b/src/mesh/MeshFlatEdgeBoundary.hpp
@@ -3,11 +3,13 @@
 
 #include <mesh/MeshEdgeBoundary.hpp>
 
-template <size_t Dimension>
-class MeshFlatEdgeBoundary final : public MeshEdgeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class MeshFlatEdgeBoundary final : public MeshEdgeBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  using Rd = TinyVector<Dimension, double>;
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
+
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_outgoing_normal;
@@ -20,16 +22,15 @@ class MeshFlatEdgeBoundary final : public MeshEdgeBoundary<Dimension>   // clazy
   }
 
   MeshFlatEdgeBoundary& operator=(const MeshFlatEdgeBoundary&) = default;
-  MeshFlatEdgeBoundary& operator=(MeshFlatEdgeBoundary&&) = default;
+  MeshFlatEdgeBoundary& operator=(MeshFlatEdgeBoundary&&)      = default;
 
-  template <size_t MeshDimension>
-  friend MeshFlatEdgeBoundary<MeshDimension> getMeshFlatEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshFlatEdgeBoundary<MeshTypeT> getMeshFlatEdgeBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
  private:
-  template <typename MeshType>
   MeshFlatEdgeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list, const Rd& outgoing_normal)
-    : MeshEdgeBoundary<Dimension>(mesh, ref_edge_list), m_outgoing_normal(outgoing_normal)
+    : MeshEdgeBoundary(mesh, ref_edge_list), m_outgoing_normal(outgoing_normal)
   {}
 
  public:
@@ -39,8 +40,8 @@ class MeshFlatEdgeBoundary final : public MeshEdgeBoundary<Dimension>   // clazy
   ~MeshFlatEdgeBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshFlatEdgeBoundary<Dimension> getMeshFlatEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshFlatEdgeBoundary<MeshType> getMeshFlatEdgeBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_FLAT_EDGE_BOUNDARY_HPP
diff --git a/src/mesh/MeshFlatFaceBoundary.cpp b/src/mesh/MeshFlatFaceBoundary.cpp
index 87d353e25404c3b5996a63945a9e220e49e97e55..632c6a033dee7fc5593363660cc179faa966fb80 100644
--- a/src/mesh/MeshFlatFaceBoundary.cpp
+++ b/src/mesh/MeshFlatFaceBoundary.cpp
@@ -4,17 +4,17 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshFlatNodeBoundary.hpp>
 
-template <size_t Dimension>
-MeshFlatFaceBoundary<Dimension>
-getMeshFlatFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshFlatFaceBoundary<MeshType>
+getMeshFlatFaceBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
-  MeshFaceBoundary<Dimension> mesh_face_boundary          = getMeshFaceBoundary(mesh, boundary_descriptor);
-  MeshFlatNodeBoundary<Dimension> mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor);
+  MeshFaceBoundary mesh_face_boundary          = getMeshFaceBoundary(mesh, boundary_descriptor);
+  MeshFlatNodeBoundary mesh_flat_node_boundary = getMeshFlatNodeBoundary(mesh, boundary_descriptor);
 
-  return MeshFlatFaceBoundary<Dimension>{mesh, mesh_face_boundary.refFaceList(),
-                                         mesh_flat_node_boundary.outgoingNormal()};
+  return MeshFlatFaceBoundary<MeshType>{mesh, mesh_face_boundary.refFaceList(),
+                                        mesh_flat_node_boundary.outgoingNormal()};
 }
 
-template MeshFlatFaceBoundary<1> getMeshFlatFaceBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshFlatFaceBoundary<2> getMeshFlatFaceBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshFlatFaceBoundary<3> getMeshFlatFaceBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshFlatFaceBoundary<Mesh<1>> getMeshFlatFaceBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshFlatFaceBoundary<Mesh<2>> getMeshFlatFaceBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshFlatFaceBoundary<Mesh<3>> getMeshFlatFaceBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshFlatFaceBoundary.hpp b/src/mesh/MeshFlatFaceBoundary.hpp
index f3d34e16c5d52af38033e82dcd75e6897f392a3d..0c53a31e095251c6ffaa70b7f6d58bfce10a6864 100644
--- a/src/mesh/MeshFlatFaceBoundary.hpp
+++ b/src/mesh/MeshFlatFaceBoundary.hpp
@@ -3,11 +3,13 @@
 
 #include <mesh/MeshFaceBoundary.hpp>
 
-template <size_t Dimension>
-class MeshFlatFaceBoundary final : public MeshFaceBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class MeshFlatFaceBoundary final : public MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  using Rd = TinyVector<Dimension, double>;
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
+
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_outgoing_normal;
@@ -20,16 +22,15 @@ class MeshFlatFaceBoundary final : public MeshFaceBoundary<Dimension>   // clazy
   }
 
   MeshFlatFaceBoundary& operator=(const MeshFlatFaceBoundary&) = default;
-  MeshFlatFaceBoundary& operator=(MeshFlatFaceBoundary&&) = default;
+  MeshFlatFaceBoundary& operator=(MeshFlatFaceBoundary&&)      = default;
 
-  template <size_t MeshDimension>
-  friend MeshFlatFaceBoundary<MeshDimension> getMeshFlatFaceBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshFlatFaceBoundary<MeshTypeT> getMeshFlatFaceBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
  private:
-  template <typename MeshType>
   MeshFlatFaceBoundary(const MeshType& mesh, const RefFaceList& ref_face_list, const Rd& outgoing_normal)
-    : MeshFaceBoundary<Dimension>(mesh, ref_face_list), m_outgoing_normal(outgoing_normal)
+    : MeshFaceBoundary(mesh, ref_face_list), m_outgoing_normal(outgoing_normal)
   {}
 
  public:
@@ -39,8 +40,8 @@ class MeshFlatFaceBoundary final : public MeshFaceBoundary<Dimension>   // clazy
   ~MeshFlatFaceBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshFlatFaceBoundary<Dimension> getMeshFlatFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshFlatFaceBoundary<MeshType> getMeshFlatFaceBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_FLAT_FACE_BOUNDARY_HPP
diff --git a/src/mesh/MeshFlatNodeBoundary.cpp b/src/mesh/MeshFlatNodeBoundary.cpp
index f5e60af046b49903304762aec86a237d7b2a868a..bdc7c20a2c854c2a9a6492d8f06f46a026b38b0b 100644
--- a/src/mesh/MeshFlatNodeBoundary.cpp
+++ b/src/mesh/MeshFlatNodeBoundary.cpp
@@ -2,14 +2,15 @@
 
 #include <mesh/Connectivity.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshNodeBoundaryUtils.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-MeshFlatNodeBoundary<Dimension>::_checkBoundaryIsFlat(const TinyVector<Dimension, double>& normal,
-                                                      const TinyVector<Dimension, double>& origin,
-                                                      const double length,
-                                                      const Mesh<Connectivity<Dimension>>& mesh) const
+MeshFlatNodeBoundary<MeshType>::_checkBoundaryIsFlat(const TinyVector<MeshType::Dimension, double>& normal,
+                                                     const TinyVector<MeshType::Dimension, double>& origin,
+                                                     const double length,
+                                                     const MeshType& mesh) const
 {
   const NodeValue<const Rd>& xr = mesh.xr();
 
@@ -33,7 +34,7 @@ MeshFlatNodeBoundary<Dimension>::_checkBoundaryIsFlat(const TinyVector<Dimension
 
 template <>
 TinyVector<1, double>
-MeshFlatNodeBoundary<1>::_getNormal(const Mesh<Connectivity<1>>& mesh)
+MeshFlatNodeBoundary<Mesh<1>>::_getNormal(const Mesh<1>& mesh)
 {
   using R = TinyVector<1, double>;
 
@@ -59,11 +60,11 @@ MeshFlatNodeBoundary<1>::_getNormal(const Mesh<Connectivity<1>>& mesh)
 
 template <>
 TinyVector<2, double>
-MeshFlatNodeBoundary<2>::_getNormal(const Mesh<Connectivity<2>>& mesh)
+MeshFlatNodeBoundary<Mesh<2>>::_getNormal(const Mesh<2>& mesh)
 {
   using R2 = TinyVector<2, double>;
 
-  std::array<R2, 2> bounds = this->_getBounds(mesh);
+  std::array<R2, 2> bounds = getBounds(mesh, m_ref_node_list);
 
   const R2& xmin = bounds[0];
   const R2& xmax = bounds[1];
@@ -87,7 +88,7 @@ MeshFlatNodeBoundary<2>::_getNormal(const Mesh<Connectivity<2>>& mesh)
 
 template <>
 TinyVector<3, double>
-MeshFlatNodeBoundary<3>::_getFarestNode(const Mesh<Connectivity<3>>& mesh, const Rd& x0, const Rd& x1)
+MeshFlatNodeBoundary<Mesh<3>>::_getFarestNode(const Mesh<3>& mesh, const Rd& x0, const Rd& x1)
 {
   const NodeValue<const Rd>& xr = mesh.xr();
   const auto node_number        = mesh.connectivity().nodeNumber();
@@ -138,7 +139,7 @@ MeshFlatNodeBoundary<3>::_getFarestNode(const Mesh<Connectivity<3>>& mesh, const
 
 template <>
 TinyVector<3, double>
-MeshFlatNodeBoundary<3>::_getNormal(const Mesh<Connectivity<3>>& mesh)
+MeshFlatNodeBoundary<Mesh<3>>::_getNormal(const Mesh<3>& mesh)
 {
   using R3 = TinyVector<3, double>;
 
@@ -159,7 +160,7 @@ MeshFlatNodeBoundary<3>::_getNormal(const Mesh<Connectivity<3>>& mesh)
     }
 
     return std::array<R3, 2>{bounds[max_i], bounds[max_j]};
-  }(this->_getBounds(mesh));
+  }(getBounds(mesh, m_ref_node_list));
 
   const R3& x0 = diagonal[0];
   const R3& x1 = diagonal[1];
@@ -195,7 +196,7 @@ MeshFlatNodeBoundary<3>::_getNormal(const Mesh<Connectivity<3>>& mesh)
 
 template <>
 TinyVector<1, double>
-MeshFlatNodeBoundary<1>::_getOutgoingNormal(const Mesh<Connectivity<1>>& mesh)
+MeshFlatNodeBoundary<Mesh<1>>::_getOutgoingNormal(const Mesh<1>& mesh)
 {
   using R = TinyVector<1, double>;
 
@@ -238,7 +239,7 @@ MeshFlatNodeBoundary<1>::_getOutgoingNormal(const Mesh<Connectivity<1>>& mesh)
 
 template <>
 TinyVector<2, double>
-MeshFlatNodeBoundary<2>::_getOutgoingNormal(const Mesh<Connectivity<2>>& mesh)
+MeshFlatNodeBoundary<Mesh<2>>::_getOutgoingNormal(const Mesh<2>& mesh)
 {
   using R2 = TinyVector<2, double>;
 
@@ -281,7 +282,7 @@ MeshFlatNodeBoundary<2>::_getOutgoingNormal(const Mesh<Connectivity<2>>& mesh)
 
 template <>
 TinyVector<3, double>
-MeshFlatNodeBoundary<3>::_getOutgoingNormal(const Mesh<Connectivity<3>>& mesh)
+MeshFlatNodeBoundary<Mesh<3>>::_getOutgoingNormal(const Mesh<3>& mesh)
 {
   using R3 = TinyVector<3, double>;
 
@@ -322,16 +323,16 @@ MeshFlatNodeBoundary<3>::_getOutgoingNormal(const Mesh<Connectivity<3>>& mesh)
   }
 }
 
-template <size_t Dimension>
-MeshFlatNodeBoundary<Dimension>
-getMeshFlatNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshFlatNodeBoundary<MeshType>
+getMeshFlatNodeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
   for (size_t i_ref_node_list = 0; i_ref_node_list < mesh.connectivity().template numberOfRefItemList<ItemType::node>();
        ++i_ref_node_list) {
     const auto& ref_node_list = mesh.connectivity().template refItemList<ItemType::node>(i_ref_node_list);
     const RefId& ref          = ref_node_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshFlatNodeBoundary<Dimension>{mesh, ref_node_list};
+      return MeshFlatNodeBoundary<MeshType>{mesh, ref_node_list};
     }
   }
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
@@ -339,7 +340,7 @@ getMeshFlatNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda
     const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list);
     const RefId& ref          = ref_face_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshFlatNodeBoundary<Dimension>{mesh, ref_face_list};
+      return MeshFlatNodeBoundary<MeshType>{mesh, ref_face_list};
     }
   }
 
@@ -349,6 +350,6 @@ getMeshFlatNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda
   throw NormalError(ost.str());
 }
 
-template MeshFlatNodeBoundary<1> getMeshFlatNodeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshFlatNodeBoundary<2> getMeshFlatNodeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshFlatNodeBoundary<3> getMeshFlatNodeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshFlatNodeBoundary<Mesh<1>> getMeshFlatNodeBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshFlatNodeBoundary<Mesh<2>> getMeshFlatNodeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshFlatNodeBoundary<Mesh<3>> getMeshFlatNodeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshFlatNodeBoundary.hpp b/src/mesh/MeshFlatNodeBoundary.hpp
index 54a10ae1c1412005440cd9e6cc49973c017265e0..2eb28d14899a1d70f47f8cd139b435c4a81390b7 100644
--- a/src/mesh/MeshFlatNodeBoundary.hpp
+++ b/src/mesh/MeshFlatNodeBoundary.hpp
@@ -2,62 +2,60 @@
 #define MESH_FLAT_NODE_BOUNDARY_HPP
 
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshTraits.hpp>
 
-template <size_t Dimension>
-class [[nodiscard]] MeshFlatNodeBoundary final
-  : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class [[nodiscard]] MeshFlatNodeBoundary final : public MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  using Rd = TinyVector<Dimension, double>;
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
+
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_outgoing_normal;
 
-  Rd _getFarestNode(const Mesh<Connectivity<Dimension>>& mesh, const Rd& x0, const Rd& x1);
+  Rd _getFarestNode(const MeshType& mesh, const Rd& x0, const Rd& x1);
 
-  Rd _getNormal(const Mesh<Connectivity<Dimension>>& mesh);
+  Rd _getNormal(const MeshType& mesh);
 
-  void _checkBoundaryIsFlat(const TinyVector<Dimension, double>& normal,
-                            const TinyVector<Dimension, double>& origin,
-                            const double length,
-                            const Mesh<Connectivity<Dimension>>& mesh) const;
+  void _checkBoundaryIsFlat(const TinyVector<MeshType::Dimension, double>& normal,
+                            const TinyVector<MeshType::Dimension, double>& origin, const double length,
+                            const MeshType& mesh) const;
 
-  Rd _getOutgoingNormal(const Mesh<Connectivity<Dimension>>& mesh);
+  Rd _getOutgoingNormal(const MeshType& mesh);
 
  public:
-  const Rd&
-  outgoingNormal() const
+  const Rd& outgoingNormal() const
   {
     return m_outgoing_normal;
   }
 
   MeshFlatNodeBoundary& operator=(const MeshFlatNodeBoundary&) = default;
-  MeshFlatNodeBoundary& operator=(MeshFlatNodeBoundary&&)      = default;
+  MeshFlatNodeBoundary& operator=(MeshFlatNodeBoundary&&) = default;
 
-  template <size_t MeshDimension>
-  friend MeshFlatNodeBoundary<MeshDimension> getMeshFlatNodeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshFlatNodeBoundary<MeshTypeT> getMeshFlatNodeBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
  private:
-  template <typename MeshType>
   MeshFlatNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list)
-    : MeshNodeBoundary<Dimension>(mesh, ref_face_list), m_outgoing_normal(_getOutgoingNormal(mesh))
+    : MeshNodeBoundary(mesh, ref_face_list), m_outgoing_normal(_getOutgoingNormal(mesh))
   {}
 
-  template <typename MeshType>
   MeshFlatNodeBoundary(const MeshType& mesh, const RefNodeList& ref_node_list)
-    : MeshNodeBoundary<Dimension>(mesh, ref_node_list), m_outgoing_normal(_getOutgoingNormal(mesh))
+    : MeshNodeBoundary(mesh, ref_node_list), m_outgoing_normal(_getOutgoingNormal(mesh))
   {}
 
  public:
   MeshFlatNodeBoundary()                            = default;
   MeshFlatNodeBoundary(const MeshFlatNodeBoundary&) = default;
-  MeshFlatNodeBoundary(MeshFlatNodeBoundary&&)      = default;
+  MeshFlatNodeBoundary(MeshFlatNodeBoundary &&)     = default;
   ~MeshFlatNodeBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshFlatNodeBoundary<Dimension> getMeshFlatNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshFlatNodeBoundary<MeshType> getMeshFlatNodeBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_FLAT_NODE_BOUNDARY_HPP
diff --git a/src/mesh/MeshLineEdgeBoundary.cpp b/src/mesh/MeshLineEdgeBoundary.cpp
index 16e2e3e9764d1465cd767c130fbf27b0e7589afd..40de23d1e2dc86bbd82b8542b75e32d1cac40352 100644
--- a/src/mesh/MeshLineEdgeBoundary.cpp
+++ b/src/mesh/MeshLineEdgeBoundary.cpp
@@ -5,15 +5,15 @@
 #include <mesh/MeshLineNodeBoundary.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshLineEdgeBoundary<Dimension>
-getMeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshLineEdgeBoundary<MeshType>
+getMeshLineEdgeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
-  MeshEdgeBoundary<Dimension> mesh_edge_boundary          = getMeshEdgeBoundary(mesh, boundary_descriptor);
-  MeshLineNodeBoundary<Dimension> mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor);
+  MeshEdgeBoundary mesh_edge_boundary          = getMeshEdgeBoundary(mesh, boundary_descriptor);
+  MeshLineNodeBoundary mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor);
 
-  return MeshLineEdgeBoundary<Dimension>{mesh, mesh_edge_boundary.refEdgeList(), mesh_line_node_boundary.direction()};
+  return MeshLineEdgeBoundary<MeshType>{mesh, mesh_edge_boundary.refEdgeList(), mesh_line_node_boundary.direction()};
 }
 
-template MeshLineEdgeBoundary<2> getMeshLineEdgeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshLineEdgeBoundary<3> getMeshLineEdgeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshLineEdgeBoundary<Mesh<2>> getMeshLineEdgeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshLineEdgeBoundary<Mesh<3>> getMeshLineEdgeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshLineEdgeBoundary.hpp b/src/mesh/MeshLineEdgeBoundary.hpp
index 160444648524f266941a8d2903124bf91b77ee8b..0fc5db0ba6b73960cd1a64c5a0626585c857a4b8 100644
--- a/src/mesh/MeshLineEdgeBoundary.hpp
+++ b/src/mesh/MeshLineEdgeBoundary.hpp
@@ -4,46 +4,48 @@
 #include <algebra/TinyMatrix.hpp>
 #include <mesh/MeshEdgeBoundary.hpp>
 
-template <size_t Dimension>
-class [[nodiscard]] MeshLineEdgeBoundary final
-  : public MeshEdgeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class [[nodiscard]] MeshLineEdgeBoundary final : public MeshEdgeBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  static_assert(Dimension > 1, "MeshLineEdgeBoundary makes only sense in dimension 1");
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
 
-  using Rd = TinyVector<Dimension, double>;
+  static_assert(MeshType::Dimension > 1, "MeshLineEdgeBoundary makes only sense in dimension 1");
+
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_direction;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshLineEdgeBoundary<MeshDimension> getMeshLineEdgeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshLineEdgeBoundary<MeshTypeT> getMeshLineEdgeBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
   PUGS_INLINE
-  const Rd& direction() const
+  const Rd&
+  direction() const
   {
     return m_direction;
   }
 
   MeshLineEdgeBoundary& operator=(const MeshLineEdgeBoundary&) = default;
-  MeshLineEdgeBoundary& operator=(MeshLineEdgeBoundary&&) = default;
+  MeshLineEdgeBoundary& operator=(MeshLineEdgeBoundary&&)      = default;
 
  private:
-  MeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list, const Rd& direction)
-    : MeshEdgeBoundary<Dimension>(mesh, ref_edge_list), m_direction(direction)
+  MeshLineEdgeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list, const Rd& direction)
+    : MeshEdgeBoundary(mesh, ref_edge_list), m_direction(direction)
   {}
 
  public:
   MeshLineEdgeBoundary()                            = default;
   MeshLineEdgeBoundary(const MeshLineEdgeBoundary&) = default;
-  MeshLineEdgeBoundary(MeshLineEdgeBoundary &&)     = default;
+  MeshLineEdgeBoundary(MeshLineEdgeBoundary&&)      = default;
   ~MeshLineEdgeBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshLineEdgeBoundary<Dimension> getMeshLineEdgeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshLineEdgeBoundary<MeshType> getMeshLineEdgeBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_LINE_EDGE_BOUNDARY_HPP
diff --git a/src/mesh/MeshLineFaceBoundary.cpp b/src/mesh/MeshLineFaceBoundary.cpp
index cd67176349d0edb475001c58acc99d9cac9792ec..3608e7aef37dfeddf9eb2c6ad7d207d1acae3f07 100644
--- a/src/mesh/MeshLineFaceBoundary.cpp
+++ b/src/mesh/MeshLineFaceBoundary.cpp
@@ -5,14 +5,14 @@
 #include <mesh/MeshLineNodeBoundary.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshLineFaceBoundary<Dimension>
-getMeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshLineFaceBoundary<MeshType>
+getMeshLineFaceBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
-  MeshFaceBoundary<Dimension> mesh_face_boundary          = getMeshFaceBoundary(mesh, boundary_descriptor);
-  MeshLineNodeBoundary<Dimension> mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor);
+  MeshFaceBoundary mesh_face_boundary          = getMeshFaceBoundary(mesh, boundary_descriptor);
+  MeshLineNodeBoundary mesh_line_node_boundary = getMeshLineNodeBoundary(mesh, boundary_descriptor);
 
-  return MeshLineFaceBoundary<Dimension>{mesh, mesh_face_boundary.refFaceList(), mesh_line_node_boundary.direction()};
+  return MeshLineFaceBoundary<MeshType>{mesh, mesh_face_boundary.refFaceList(), mesh_line_node_boundary.direction()};
 }
 
-template MeshLineFaceBoundary<2> getMeshLineFaceBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
+template MeshLineFaceBoundary<Mesh<2>> getMeshLineFaceBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshLineFaceBoundary.hpp b/src/mesh/MeshLineFaceBoundary.hpp
index 5631361f5f995883d80c5317a178eab122ddcda9..206961a08f88f9a4008a14a7a223b9d90ff323ed 100644
--- a/src/mesh/MeshLineFaceBoundary.hpp
+++ b/src/mesh/MeshLineFaceBoundary.hpp
@@ -4,46 +4,47 @@
 #include <algebra/TinyMatrix.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
 
-template <size_t Dimension>
-class [[nodiscard]] MeshLineFaceBoundary final
-  : public MeshFaceBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class [[nodiscard]] MeshLineFaceBoundary final : public MeshFaceBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  static_assert(Dimension == 2, "MeshLineFaceBoundary makes only sense in dimension 2");
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
+  static_assert(MeshType::Dimension == 2, "MeshLineFaceBoundary makes only sense in dimension 2");
 
-  using Rd = TinyVector<Dimension, double>;
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_direction;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshLineFaceBoundary<MeshDimension> getMeshLineFaceBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshLineFaceBoundary<MeshTypeT> getMeshLineFaceBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
   PUGS_INLINE
-  const Rd& direction() const
+  const Rd&
+  direction() const
   {
     return m_direction;
   }
 
   MeshLineFaceBoundary& operator=(const MeshLineFaceBoundary&) = default;
-  MeshLineFaceBoundary& operator=(MeshLineFaceBoundary&&) = default;
+  MeshLineFaceBoundary& operator=(MeshLineFaceBoundary&&)      = default;
 
  private:
-  MeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list, const Rd& direction)
-    : MeshFaceBoundary<Dimension>(mesh, ref_face_list), m_direction(direction)
+  MeshLineFaceBoundary(const MeshType& mesh, const RefFaceList& ref_face_list, const Rd& direction)
+    : MeshFaceBoundary(mesh, ref_face_list), m_direction(direction)
   {}
 
  public:
   MeshLineFaceBoundary()                            = default;
   MeshLineFaceBoundary(const MeshLineFaceBoundary&) = default;
-  MeshLineFaceBoundary(MeshLineFaceBoundary &&)     = default;
+  MeshLineFaceBoundary(MeshLineFaceBoundary&&)      = default;
   ~MeshLineFaceBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshLineFaceBoundary<Dimension> getMeshLineFaceBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshLineFaceBoundary<MeshType> getMeshLineFaceBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_LINE_FACE_BOUNDARY_HPP
diff --git a/src/mesh/MeshLineNodeBoundary.cpp b/src/mesh/MeshLineNodeBoundary.cpp
index 1b126f6207719d648415edbc41be6bb83c36abe7..1cd3bb64d749d49de82d925bff9eaa3ecf88ead9 100644
--- a/src/mesh/MeshLineNodeBoundary.cpp
+++ b/src/mesh/MeshLineNodeBoundary.cpp
@@ -2,16 +2,17 @@
 
 #include <mesh/Connectivity.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshNodeBoundaryUtils.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-MeshLineNodeBoundary<Dimension>::_checkBoundaryIsLine(const TinyVector<Dimension, double>& direction,
-                                                      const TinyVector<Dimension, double>& origin,
-                                                      const double length,
-                                                      const Mesh<Connectivity<Dimension>>& mesh) const
+MeshLineNodeBoundary<MeshType>::_checkBoundaryIsLine(const TinyVector<MeshType::Dimension, double>& direction,
+                                                     const TinyVector<MeshType::Dimension, double>& origin,
+                                                     const double length,
+                                                     const MeshType& mesh) const
 {
-  using Rdxd = TinyMatrix<Dimension>;
+  using Rdxd = TinyMatrix<MeshType::Dimension>;
 
   const NodeValue<const Rd>& xr = mesh.xr();
 
@@ -35,14 +36,13 @@ MeshLineNodeBoundary<Dimension>::_checkBoundaryIsLine(const TinyVector<Dimension
   }
 }
 
-template <>
 template <>
 TinyVector<2>
-MeshLineNodeBoundary<2>::_getDirection(const Mesh<Connectivity<2>>& mesh)
+MeshLineNodeBoundary<Mesh<2>>::_getDirection(const Mesh<2>& mesh)
 {
   using R2 = TinyVector<2, double>;
 
-  std::array<R2, 2> bounds = this->_getBounds(mesh);
+  std::array<R2, 2> bounds = getBounds(mesh, m_ref_node_list);
 
   const R2& xmin = bounds[0];
   const R2& xmax = bounds[1];
@@ -63,14 +63,13 @@ MeshLineNodeBoundary<2>::_getDirection(const Mesh<Connectivity<2>>& mesh)
   return direction;
 }
 
-template <>
 template <>
 TinyVector<3>
-MeshLineNodeBoundary<3>::_getDirection(const Mesh<Connectivity<3>>& mesh)
+MeshLineNodeBoundary<Mesh<3>>::_getDirection(const Mesh<3>& mesh)
 {
   using R3 = TinyVector<3, double>;
 
-  std::array<R3, 6> bounds = this->_getBounds(mesh);
+  std::array<R3, 6> bounds = getBounds(mesh, m_ref_node_list);
 
   const R3& xmin = bounds[0];
   const R3& ymin = bounds[1];
@@ -106,16 +105,16 @@ MeshLineNodeBoundary<3>::_getDirection(const Mesh<Connectivity<3>>& mesh)
   return direction;
 }
 
-template <size_t Dimension>
-MeshLineNodeBoundary<Dimension>
-getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshLineNodeBoundary<MeshType>
+getMeshLineNodeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
   for (size_t i_ref_node_list = 0; i_ref_node_list < mesh.connectivity().template numberOfRefItemList<ItemType::node>();
        ++i_ref_node_list) {
     const auto& ref_node_list = mesh.connectivity().template refItemList<ItemType::node>(i_ref_node_list);
     const RefId& ref          = ref_node_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshLineNodeBoundary<Dimension>{mesh, ref_node_list};
+      return MeshLineNodeBoundary<MeshType>{mesh, ref_node_list};
     }
   }
   for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>();
@@ -123,7 +122,7 @@ getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda
     const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list);
     const RefId& ref          = ref_edge_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshLineNodeBoundary<Dimension>{mesh, ref_edge_list};
+      return MeshLineNodeBoundary<MeshType>{mesh, ref_edge_list};
     }
   }
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
@@ -131,7 +130,7 @@ getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda
     const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list);
     const RefId& ref          = ref_face_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshLineNodeBoundary<Dimension>{mesh, ref_face_list};
+      return MeshLineNodeBoundary<MeshType>{mesh, ref_face_list};
     }
   }
 
@@ -141,5 +140,5 @@ getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBounda
   throw NormalError(ost.str());
 }
 
-template MeshLineNodeBoundary<2> getMeshLineNodeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshLineNodeBoundary<3> getMeshLineNodeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshLineNodeBoundary<Mesh<2>> getMeshLineNodeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshLineNodeBoundary<Mesh<3>> getMeshLineNodeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshLineNodeBoundary.hpp b/src/mesh/MeshLineNodeBoundary.hpp
index 730b9f536bbf5563d656b59c0d246ff71193ea57..fb75ef9a49d77095c47d1a45db42e636b905c572 100644
--- a/src/mesh/MeshLineNodeBoundary.hpp
+++ b/src/mesh/MeshLineNodeBoundary.hpp
@@ -4,60 +4,62 @@
 #include <algebra/TinyMatrix.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
 
-template <size_t Dimension>
-class [[nodiscard]] MeshLineNodeBoundary final
-  : public MeshNodeBoundary<Dimension>   // clazy:exclude=copyable-polymorphic
+template <MeshConcept MeshType>
+class [[nodiscard]] MeshLineNodeBoundary final : public MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
 {
  public:
-  static_assert(Dimension > 1, "MeshLineNodeBoundary makes only sense in dimension greater than 1");
+  static_assert(not std::is_const_v<MeshType>, "MeshType must be non-const");
+  static_assert(MeshType::Dimension > 1, "MeshLineNodeBoundary makes only sense in dimension greater than 1");
 
-  using Rd = TinyVector<Dimension, double>;
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
   const Rd m_direction;
 
-  template <size_t MeshDimension>
-  TinyVector<MeshDimension> _getDirection(const Mesh<Connectivity<MeshDimension>>&);
+  TinyVector<MeshType::Dimension> _getDirection(const MeshType&);
 
-  void _checkBoundaryIsLine(const TinyVector<Dimension, double>& direction, const TinyVector<Dimension, double>& origin,
-                            const double length, const Mesh<Connectivity<Dimension>>& mesh) const;
+  void _checkBoundaryIsLine(const TinyVector<MeshType::Dimension, double>& direction,
+                            const TinyVector<MeshType::Dimension, double>& origin,
+                            const double length,
+                            const MeshType& mesh) const;
 
  public:
-  template <size_t MeshDimension>
-  friend MeshLineNodeBoundary<MeshDimension> getMeshLineNodeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                                     const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshLineNodeBoundary<MeshTypeT> getMeshLineNodeBoundary(const MeshTypeT& mesh,
+                                                                 const IBoundaryDescriptor& boundary_descriptor);
 
   PUGS_INLINE
-  const Rd& direction() const
+  const Rd&
+  direction() const
   {
     return m_direction;
   }
 
   MeshLineNodeBoundary& operator=(const MeshLineNodeBoundary&) = default;
-  MeshLineNodeBoundary& operator=(MeshLineNodeBoundary&&) = default;
+  MeshLineNodeBoundary& operator=(MeshLineNodeBoundary&&)      = default;
 
  private:
-  MeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list)
-    : MeshNodeBoundary<Dimension>(mesh, ref_edge_list), m_direction(_getDirection(mesh))
+  MeshLineNodeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list)
+    : MeshNodeBoundary(mesh, ref_edge_list), m_direction(_getDirection(mesh))
   {}
 
-  MeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list)
-    : MeshNodeBoundary<Dimension>(mesh, ref_face_list), m_direction(_getDirection(mesh))
+  MeshLineNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list)
+    : MeshNodeBoundary(mesh, ref_face_list), m_direction(_getDirection(mesh))
   {}
 
-  MeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefNodeList& ref_node_list)
-    : MeshNodeBoundary<Dimension>(mesh, ref_node_list), m_direction(_getDirection(mesh))
+  MeshLineNodeBoundary(const MeshType& mesh, const RefNodeList& ref_node_list)
+    : MeshNodeBoundary(mesh, ref_node_list), m_direction(_getDirection(mesh))
   {}
 
  public:
   MeshLineNodeBoundary()                            = default;
   MeshLineNodeBoundary(const MeshLineNodeBoundary&) = default;
-  MeshLineNodeBoundary(MeshLineNodeBoundary &&)     = default;
+  MeshLineNodeBoundary(MeshLineNodeBoundary&&)      = default;
   ~MeshLineNodeBoundary()                           = default;
 };
 
-template <size_t Dimension>
-MeshLineNodeBoundary<Dimension> getMeshLineNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                        const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshLineNodeBoundary<MeshType> getMeshLineNodeBoundary(const MeshType& mesh,
+                                                       const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_LINE_NODE_BOUNDARY_HPP
diff --git a/src/mesh/MeshNodeBoundary.cpp b/src/mesh/MeshNodeBoundary.cpp
index e883aa4eeb80b40962218208fc044f964a7d4a8d..8cdcad2fb91700aaeed9bd8cd9301ac4173c7294 100644
--- a/src/mesh/MeshNodeBoundary.cpp
+++ b/src/mesh/MeshNodeBoundary.cpp
@@ -5,175 +5,11 @@
 #include <mesh/Mesh.hpp>
 #include <utils/Messenger.hpp>
 
-template <>
-std::array<TinyVector<2>, 2>
-MeshNodeBoundary<2>::_getBounds(const Mesh<Connectivity<2>>& mesh) const
+template <MeshConcept MeshType>
+MeshNodeBoundary::MeshNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list)
 {
-  using R2 = TinyVector<2, double>;
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  const NodeValue<const R2>& xr = mesh.xr();
-
-  std::array<R2, 2> bounds;
-  R2& xmin = bounds[0];
-  R2& xmax = bounds[1];
-
-  xmin = R2{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-  xmax = R2{-std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()};
-
-  auto update_xmin = [](const R2& x, R2& x_min) {
-    if ((x[0] < x_min[0]) or ((x[0] == x_min[0]) and (x[1] < x_min[1]))) {
-      x_min = x;
-    }
-  };
-
-  auto update_xmax = [](const R2& x, R2& x_max) {
-    if ((x[0] > x_max[0]) or ((x[0] == x_max[0]) and (x[1] > x_max[1]))) {
-      x_max = x;
-    }
-  };
-
-  auto node_list = m_ref_node_list.list();
-  for (size_t r = 0; r < node_list.size(); ++r) {
-    const R2& x = xr[node_list[r]];
-    update_xmin(x, xmin);
-    update_xmax(x, xmax);
-  }
-
-  if (parallel::size() > 1) {
-    Array<R2> xmin_array = parallel::allGather(xmin);
-    Array<R2> xmax_array = parallel::allGather(xmax);
-    for (size_t i = 0; i < xmin_array.size(); ++i) {
-      update_xmin(xmin_array[i], xmin);
-    }
-    for (size_t i = 0; i < xmax_array.size(); ++i) {
-      update_xmax(xmax_array[i], xmax);
-    }
-  }
-
-  return bounds;
-}
-
-template <>
-std::array<TinyVector<3>, 6>
-MeshNodeBoundary<3>::_getBounds(const Mesh<Connectivity<3>>& mesh) const
-{
-  using R3 = TinyVector<3, double>;
-
-  auto update_xmin = [](const R3& x, R3& xmin) {
-    // XMIN: X.xmin X.ymax X.zmax
-    if ((x[0] < xmin[0]) or ((x[0] == xmin[0]) and (x[1] > xmin[1])) or
-        ((x[0] == xmin[0]) and (x[1] == xmin[1]) and (x[2] > xmin[2]))) {
-      xmin = x;
-    }
-  };
-
-  auto update_xmax = [](const R3& x, R3& xmax) {
-    // XMAX: X.xmax X.ymin X.zmin
-    if ((x[0] > xmax[0]) or ((x[0] == xmax[0]) and (x[1] < xmax[1])) or
-        ((x[0] == xmax[0]) and (x[1] == xmax[1]) and (x[2] < xmax[2]))) {
-      xmax = x;
-    }
-  };
-
-  auto update_ymin = [](const R3& x, R3& ymin) {
-    // YMIN: X.ymin X.zmax X.xmin
-    if ((x[1] < ymin[1]) or ((x[1] == ymin[1]) and (x[2] > ymin[2])) or
-        ((x[1] == ymin[1]) and (x[2] == ymin[2]) and (x[0] < ymin[0]))) {
-      ymin = x;
-    }
-  };
-
-  auto update_ymax = [](const R3& x, R3& ymax) {
-    // YMAX: X.ymax X.zmin X.xmax
-    if ((x[1] > ymax[1]) or ((x[1] == ymax[1]) and (x[2] < ymax[2])) or
-        ((x[1] == ymax[1]) and (x[2] == ymax[2]) and (x[0] > ymax[0]))) {
-      ymax = x;
-    }
-  };
-
-  auto update_zmin = [](const R3& x, R3& zmin) {
-    // ZMIN: X.zmin X.xmin X.ymin
-    if ((x[2] < zmin[2]) or ((x[2] == zmin[2]) and (x[0] < zmin[0])) or
-        ((x[2] == zmin[2]) and (x[0] == zmin[0]) and (x[1] < zmin[1]))) {
-      zmin = x;
-    }
-  };
-
-  auto update_zmax = [](const R3& x, R3& zmax) {
-    // ZMAX: X.zmax X.xmax X.ymax
-    if ((x[2] > zmax[2]) or ((x[2] == zmax[2]) and (x[0] > zmax[0])) or
-        ((x[2] == zmax[2]) and (x[0] == zmax[0]) and (x[1] > zmax[1]))) {
-      zmax = x;
-    }
-  };
-
-  std::array<R3, 6> bounds;
-  R3& xmin = bounds[0];
-  R3& ymin = bounds[1];
-  R3& zmin = bounds[2];
-  R3& xmax = bounds[3];
-  R3& ymax = bounds[4];
-  R3& zmax = bounds[5];
-
-  xmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-  ymin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-  zmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-
-  xmax =
-    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-  ymax =
-    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-  zmax =
-    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
-
-  const NodeValue<const R3>& xr = mesh.xr();
-
-  auto node_list = m_ref_node_list.list();
-  for (size_t r = 0; r < node_list.size(); ++r) {
-    const R3& x = xr[node_list[r]];
-    update_xmin(x, xmin);
-    update_ymin(x, ymin);
-    update_zmin(x, zmin);
-    update_xmax(x, xmax);
-    update_ymax(x, ymax);
-    update_zmax(x, zmax);
-  }
-
-  if (parallel::size() > 1) {
-    Array<const R3> xmin_array = parallel::allGather(xmin);
-    Array<const R3> ymin_array = parallel::allGather(ymin);
-    Array<const R3> zmin_array = parallel::allGather(zmin);
-    Array<const R3> xmax_array = parallel::allGather(xmax);
-    Array<const R3> ymax_array = parallel::allGather(ymax);
-    Array<const R3> zmax_array = parallel::allGather(zmax);
-
-    for (size_t i = 0; i < xmin_array.size(); ++i) {
-      update_xmin(xmin_array[i], xmin);
-    }
-    for (size_t i = 0; i < ymin_array.size(); ++i) {
-      update_ymin(ymin_array[i], ymin);
-    }
-    for (size_t i = 0; i < zmin_array.size(); ++i) {
-      update_zmin(zmin_array[i], zmin);
-    }
-    for (size_t i = 0; i < xmax_array.size(); ++i) {
-      update_xmax(xmax_array[i], xmax);
-    }
-    for (size_t i = 0; i < ymax_array.size(); ++i) {
-      update_ymax(ymax_array[i], ymax);
-    }
-    for (size_t i = 0; i < zmax_array.size(); ++i) {
-      update_zmax(zmax_array[i], zmax);
-    }
-  }
-
-  return bounds;
-}
-
-template <size_t Dimension>
-MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                              const RefFaceList& ref_face_list)
-{
   const Array<const FaceId>& face_list = ref_face_list.list();
   if (ref_face_list.type() != RefItemListBase::Type::boundary) {
     std::ostringstream ost;
@@ -216,10 +52,11 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list);
 }
 
-template <size_t Dimension>
-MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                              const RefEdgeList& ref_edge_list)
+template <MeshConcept MeshType>
+MeshNodeBoundary::MeshNodeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list)
 {
+  constexpr size_t Dimension = MeshType::Dimension;
+
   const Array<const EdgeId>& edge_list = ref_edge_list.list();
   if (ref_edge_list.type() != RefItemListBase::Type::boundary) {
     std::ostringstream ost;
@@ -261,9 +98,8 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list);
 }
 
-template <size_t Dimension>
-MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>>&, const RefNodeList& ref_node_list)
-  : m_ref_node_list(ref_node_list)
+template <MeshConcept MeshType>
+MeshNodeBoundary::MeshNodeBoundary(const MeshType&, const RefNodeList& ref_node_list) : m_ref_node_list(ref_node_list)
 {
   if (ref_node_list.type() != RefItemListBase::Type::boundary) {
     std::ostringstream ost;
@@ -273,28 +109,28 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
   }
 }
 
-template MeshNodeBoundary<1>::MeshNodeBoundary(const Mesh<Connectivity<1>>&, const RefFaceList&);
-template MeshNodeBoundary<2>::MeshNodeBoundary(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshNodeBoundary<3>::MeshNodeBoundary(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<1>&, const RefFaceList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<2>&, const RefFaceList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<3>&, const RefFaceList&);
 
-template MeshNodeBoundary<1>::MeshNodeBoundary(const Mesh<Connectivity<1>>&, const RefEdgeList&);
-template MeshNodeBoundary<2>::MeshNodeBoundary(const Mesh<Connectivity<2>>&, const RefEdgeList&);
-template MeshNodeBoundary<3>::MeshNodeBoundary(const Mesh<Connectivity<3>>&, const RefEdgeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<1>&, const RefEdgeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<2>&, const RefEdgeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<3>&, const RefEdgeList&);
 
-template MeshNodeBoundary<1>::MeshNodeBoundary(const Mesh<Connectivity<1>>&, const RefNodeList&);
-template MeshNodeBoundary<2>::MeshNodeBoundary(const Mesh<Connectivity<2>>&, const RefNodeList&);
-template MeshNodeBoundary<3>::MeshNodeBoundary(const Mesh<Connectivity<3>>&, const RefNodeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<1>&, const RefNodeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<2>&, const RefNodeList&);
+template MeshNodeBoundary::MeshNodeBoundary(const Mesh<3>&, const RefNodeList&);
 
-template <size_t Dimension>
-MeshNodeBoundary<Dimension>
-getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDescriptor& boundary_descriptor)
+template <MeshConcept MeshType>
+MeshNodeBoundary
+getMeshNodeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor)
 {
   for (size_t i_ref_node_list = 0; i_ref_node_list < mesh.connectivity().template numberOfRefItemList<ItemType::node>();
        ++i_ref_node_list) {
     const auto& ref_node_list = mesh.connectivity().template refItemList<ItemType::node>(i_ref_node_list);
     const RefId& ref          = ref_node_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshNodeBoundary<Dimension>{mesh, ref_node_list};
+      return MeshNodeBoundary{mesh, ref_node_list};
     }
   }
   for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>();
@@ -302,7 +138,7 @@ getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
     const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list);
     const RefId& ref          = ref_edge_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshNodeBoundary<Dimension>{mesh, ref_edge_list};
+      return MeshNodeBoundary{mesh, ref_edge_list};
     }
   }
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
@@ -310,7 +146,7 @@ getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
     const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list);
     const RefId& ref          = ref_face_list.refId();
     if (ref == boundary_descriptor) {
-      return MeshNodeBoundary<Dimension>{mesh, ref_face_list};
+      return MeshNodeBoundary{mesh, ref_face_list};
     }
   }
 
@@ -320,6 +156,6 @@ getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const IBoundaryDe
   throw NormalError(ost.str());
 }
 
-template MeshNodeBoundary<1> getMeshNodeBoundary(const Mesh<Connectivity<1>>&, const IBoundaryDescriptor&);
-template MeshNodeBoundary<2> getMeshNodeBoundary(const Mesh<Connectivity<2>>&, const IBoundaryDescriptor&);
-template MeshNodeBoundary<3> getMeshNodeBoundary(const Mesh<Connectivity<3>>&, const IBoundaryDescriptor&);
+template MeshNodeBoundary getMeshNodeBoundary(const Mesh<1>&, const IBoundaryDescriptor&);
+template MeshNodeBoundary getMeshNodeBoundary(const Mesh<2>&, const IBoundaryDescriptor&);
+template MeshNodeBoundary getMeshNodeBoundary(const Mesh<3>&, const IBoundaryDescriptor&);
diff --git a/src/mesh/MeshNodeBoundary.hpp b/src/mesh/MeshNodeBoundary.hpp
index ce56579e853171e4b5be0d1f195936a75b838f78..36d79a308e8edd730d0fab33d2ced9a6af0e4e95 100644
--- a/src/mesh/MeshNodeBoundary.hpp
+++ b/src/mesh/MeshNodeBoundary.hpp
@@ -3,28 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IBoundaryDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefNodeList m_ref_node_list;
 
-  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh)
-    const;
-
  public:
-  template <size_t MeshDimension>
-  friend MeshNodeBoundary<MeshDimension> getMeshNodeBoundary(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                             const IBoundaryDescriptor& boundary_descriptor);
+  template <MeshConcept MeshTypeT>
+  friend MeshNodeBoundary getMeshNodeBoundary(const MeshTypeT& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
   MeshNodeBoundary& operator=(const MeshNodeBoundary&) = default;
   MeshNodeBoundary& operator=(MeshNodeBoundary&&) = default;
@@ -42,9 +32,12 @@ class [[nodiscard]] MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
-  MeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list);
-  MeshNodeBoundary(const Mesh<Connectivity<Dimension>>&, const RefNodeList& ref_node_list);
+  template <MeshConcept MeshType>
+  MeshNodeBoundary(const MeshType& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshNodeBoundary(const MeshType& mesh, const RefEdgeList& ref_edge_list);
+  template <MeshConcept MeshType>
+  MeshNodeBoundary(const MeshType&, const RefNodeList& ref_node_list);
 
  public:
   MeshNodeBoundary(const MeshNodeBoundary&) = default;
@@ -54,8 +47,7 @@ class [[nodiscard]] MeshNodeBoundary   // clazy:exclude=copyable-polymorphic
   virtual ~MeshNodeBoundary() = default;
 };
 
-template <size_t Dimension>
-MeshNodeBoundary<Dimension> getMeshNodeBoundary(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const IBoundaryDescriptor& boundary_descriptor);
+template <MeshConcept MeshType>
+MeshNodeBoundary getMeshNodeBoundary(const MeshType& mesh, const IBoundaryDescriptor& boundary_descriptor);
 
 #endif   // MESH_NODE_BOUNDARY_HPP
diff --git a/src/mesh/MeshNodeBoundaryUtils.cpp b/src/mesh/MeshNodeBoundaryUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6a0e31cd9a671530216b9dce15747e07f06c9b4
--- /dev/null
+++ b/src/mesh/MeshNodeBoundaryUtils.cpp
@@ -0,0 +1,170 @@
+#include <mesh/MeshNodeBoundaryUtils.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/Messenger.hpp>
+
+template <>
+std::array<TinyVector<2>, 2>
+getBounds(const Mesh<2>& mesh, const RefNodeList& ref_node_list)
+{
+  using R2 = TinyVector<2, double>;
+
+  const NodeValue<const R2>& xr = mesh.xr();
+
+  std::array<R2, 2> bounds;
+  R2& xmin = bounds[0];
+  R2& xmax = bounds[1];
+
+  xmin = R2{std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+  xmax = R2{-std::numeric_limits<double>::max(), -std::numeric_limits<double>::max()};
+
+  auto update_xmin = [](const R2& x, R2& x_min) {
+    if ((x[0] < x_min[0]) or ((x[0] == x_min[0]) and (x[1] < x_min[1]))) {
+      x_min = x;
+    }
+  };
+
+  auto update_xmax = [](const R2& x, R2& x_max) {
+    if ((x[0] > x_max[0]) or ((x[0] == x_max[0]) and (x[1] > x_max[1]))) {
+      x_max = x;
+    }
+  };
+
+  auto node_list = ref_node_list.list();
+  for (size_t r = 0; r < node_list.size(); ++r) {
+    const R2& x = xr[node_list[r]];
+    update_xmin(x, xmin);
+    update_xmax(x, xmax);
+  }
+
+  if (parallel::size() > 1) {
+    Array<R2> xmin_array = parallel::allGather(xmin);
+    Array<R2> xmax_array = parallel::allGather(xmax);
+    for (size_t i = 0; i < xmin_array.size(); ++i) {
+      update_xmin(xmin_array[i], xmin);
+    }
+    for (size_t i = 0; i < xmax_array.size(); ++i) {
+      update_xmax(xmax_array[i], xmax);
+    }
+  }
+
+  return bounds;
+}
+
+template <>
+std::array<TinyVector<3>, 6>
+getBounds(const Mesh<3>& mesh, const RefNodeList& ref_node_list)
+{
+  using R3 = TinyVector<3, double>;
+
+  auto update_xmin = [](const R3& x, R3& xmin) {
+    // XMIN: X.xmin X.ymax X.zmax
+    if ((x[0] < xmin[0]) or ((x[0] == xmin[0]) and (x[1] > xmin[1])) or
+        ((x[0] == xmin[0]) and (x[1] == xmin[1]) and (x[2] > xmin[2]))) {
+      xmin = x;
+    }
+  };
+
+  auto update_xmax = [](const R3& x, R3& xmax) {
+    // XMAX: X.xmax X.ymin X.zmin
+    if ((x[0] > xmax[0]) or ((x[0] == xmax[0]) and (x[1] < xmax[1])) or
+        ((x[0] == xmax[0]) and (x[1] == xmax[1]) and (x[2] < xmax[2]))) {
+      xmax = x;
+    }
+  };
+
+  auto update_ymin = [](const R3& x, R3& ymin) {
+    // YMIN: X.ymin X.zmax X.xmin
+    if ((x[1] < ymin[1]) or ((x[1] == ymin[1]) and (x[2] > ymin[2])) or
+        ((x[1] == ymin[1]) and (x[2] == ymin[2]) and (x[0] < ymin[0]))) {
+      ymin = x;
+    }
+  };
+
+  auto update_ymax = [](const R3& x, R3& ymax) {
+    // YMAX: X.ymax X.zmin X.xmax
+    if ((x[1] > ymax[1]) or ((x[1] == ymax[1]) and (x[2] < ymax[2])) or
+        ((x[1] == ymax[1]) and (x[2] == ymax[2]) and (x[0] > ymax[0]))) {
+      ymax = x;
+    }
+  };
+
+  auto update_zmin = [](const R3& x, R3& zmin) {
+    // ZMIN: X.zmin X.xmin X.ymin
+    if ((x[2] < zmin[2]) or ((x[2] == zmin[2]) and (x[0] < zmin[0])) or
+        ((x[2] == zmin[2]) and (x[0] == zmin[0]) and (x[1] < zmin[1]))) {
+      zmin = x;
+    }
+  };
+
+  auto update_zmax = [](const R3& x, R3& zmax) {
+    // ZMAX: X.zmax X.xmax X.ymax
+    if ((x[2] > zmax[2]) or ((x[2] == zmax[2]) and (x[0] > zmax[0])) or
+        ((x[2] == zmax[2]) and (x[0] == zmax[0]) and (x[1] > zmax[1]))) {
+      zmax = x;
+    }
+  };
+
+  std::array<R3, 6> bounds;
+  R3& xmin = bounds[0];
+  R3& ymin = bounds[1];
+  R3& zmin = bounds[2];
+  R3& xmax = bounds[3];
+  R3& ymax = bounds[4];
+  R3& zmax = bounds[5];
+
+  xmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+  ymin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+  zmin = R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+
+  xmax =
+    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+  ymax =
+    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+  zmax =
+    -R3{std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()};
+
+  const NodeValue<const R3>& xr = mesh.xr();
+
+  auto node_list = ref_node_list.list();
+  for (size_t r = 0; r < node_list.size(); ++r) {
+    const R3& x = xr[node_list[r]];
+    update_xmin(x, xmin);
+    update_ymin(x, ymin);
+    update_zmin(x, zmin);
+    update_xmax(x, xmax);
+    update_ymax(x, ymax);
+    update_zmax(x, zmax);
+  }
+
+  if (parallel::size() > 1) {
+    Array<const R3> xmin_array = parallel::allGather(xmin);
+    Array<const R3> ymin_array = parallel::allGather(ymin);
+    Array<const R3> zmin_array = parallel::allGather(zmin);
+    Array<const R3> xmax_array = parallel::allGather(xmax);
+    Array<const R3> ymax_array = parallel::allGather(ymax);
+    Array<const R3> zmax_array = parallel::allGather(zmax);
+
+    for (size_t i = 0; i < xmin_array.size(); ++i) {
+      update_xmin(xmin_array[i], xmin);
+    }
+    for (size_t i = 0; i < ymin_array.size(); ++i) {
+      update_ymin(ymin_array[i], ymin);
+    }
+    for (size_t i = 0; i < zmin_array.size(); ++i) {
+      update_zmin(zmin_array[i], zmin);
+    }
+    for (size_t i = 0; i < xmax_array.size(); ++i) {
+      update_xmax(xmax_array[i], xmax);
+    }
+    for (size_t i = 0; i < ymax_array.size(); ++i) {
+      update_ymax(ymax_array[i], ymax);
+    }
+    for (size_t i = 0; i < zmax_array.size(); ++i) {
+      update_zmax(zmax_array[i], zmax);
+    }
+  }
+
+  return bounds;
+}
diff --git a/src/mesh/MeshNodeBoundaryUtils.hpp b/src/mesh/MeshNodeBoundaryUtils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f187a210d2c3db4ec40fbafc3ad276454df3cbe5
--- /dev/null
+++ b/src/mesh/MeshNodeBoundaryUtils.hpp
@@ -0,0 +1,15 @@
+#ifndef MESH_NODE_BOUNDARY_UTILS_HPP
+#define MESH_NODE_BOUNDARY_UTILS_HPP
+
+#include <algebra/TinyVector.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/RefItemList.hpp>
+
+#include <array>
+
+template <MeshConcept MeshType>
+std::array<TinyVector<MeshType::Dimension>, MeshType::Dimension*(MeshType::Dimension - 1)> getBounds(
+  const MeshType& mesh,
+  const RefNodeList& ref_node_list);
+
+#endif   // MESH_NODE_BOUNDARY_UTILS_HPP
diff --git a/src/mesh/MeshNodeInterface.cpp b/src/mesh/MeshNodeInterface.cpp
index 84a5b2669a27bedbd8e11e8ffbe67d1860051f56..57eed14cb4f90037cf9a382a62f5a6917886bd88 100644
--- a/src/mesh/MeshNodeInterface.cpp
+++ b/src/mesh/MeshNodeInterface.cpp
@@ -1,14 +1,16 @@
 #include <mesh/MeshNodeInterface.hpp>
 
 #include <Kokkos_Vector.hpp>
-#include <mesh/Connectivity.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <utils/Messenger.hpp>
 
-template <size_t Dimension>
-MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const RefFaceList& ref_face_list)
+template <MeshConcept MeshType>
+MeshNodeInterface::MeshNodeInterface(const MeshType& mesh, const RefFaceList& ref_face_list)
 {
+  static_assert(is_polygonal_mesh_v<MeshType>);
+  constexpr size_t Dimension = MeshType::Dimension;
+
   const Array<const FaceId>& face_list = ref_face_list.list();
   if (ref_face_list.type() != RefItemListBase::Type::interface) {
     std::ostringstream ost;
@@ -51,10 +53,12 @@ MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimensio
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list);
 }
 
-template <size_t Dimension>
-MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                const RefEdgeList& ref_edge_list)
+template <MeshConcept MeshType>
+MeshNodeInterface::MeshNodeInterface(const MeshType& mesh, const RefEdgeList& ref_edge_list)
 {
+  static_assert(is_polygonal_mesh_v<MeshType>);
+  constexpr size_t Dimension = MeshType::Dimension;
+
   const Array<const EdgeId>& edge_list = ref_edge_list.list();
   if (ref_edge_list.type() != RefItemListBase::Type::interface) {
     std::ostringstream ost;
@@ -96,9 +100,8 @@ MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimensio
   const_cast<Connectivity<Dimension>&>(mesh.connectivity()).addRefItemList(m_ref_node_list);
 }
 
-template <size_t Dimension>
-MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimension>>&, const RefNodeList& ref_node_list)
-  : m_ref_node_list(ref_node_list)
+template <MeshConcept MeshType>
+MeshNodeInterface::MeshNodeInterface(const MeshType&, const RefNodeList& ref_node_list) : m_ref_node_list(ref_node_list)
 {
   if (ref_node_list.type() != RefItemListBase::Type::interface) {
     std::ostringstream ost;
@@ -108,28 +111,28 @@ MeshNodeInterface<Dimension>::MeshNodeInterface(const Mesh<Connectivity<Dimensio
   }
 }
 
-template MeshNodeInterface<1>::MeshNodeInterface(const Mesh<Connectivity<1>>&, const RefFaceList&);
-template MeshNodeInterface<2>::MeshNodeInterface(const Mesh<Connectivity<2>>&, const RefFaceList&);
-template MeshNodeInterface<3>::MeshNodeInterface(const Mesh<Connectivity<3>>&, const RefFaceList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<1>&, const RefFaceList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<2>&, const RefFaceList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<3>&, const RefFaceList&);
 
-template MeshNodeInterface<1>::MeshNodeInterface(const Mesh<Connectivity<1>>&, const RefEdgeList&);
-template MeshNodeInterface<2>::MeshNodeInterface(const Mesh<Connectivity<2>>&, const RefEdgeList&);
-template MeshNodeInterface<3>::MeshNodeInterface(const Mesh<Connectivity<3>>&, const RefEdgeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<1>&, const RefEdgeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<2>&, const RefEdgeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<3>&, const RefEdgeList&);
 
-template MeshNodeInterface<1>::MeshNodeInterface(const Mesh<Connectivity<1>>&, const RefNodeList&);
-template MeshNodeInterface<2>::MeshNodeInterface(const Mesh<Connectivity<2>>&, const RefNodeList&);
-template MeshNodeInterface<3>::MeshNodeInterface(const Mesh<Connectivity<3>>&, const RefNodeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<1>&, const RefNodeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<2>&, const RefNodeList&);
+template MeshNodeInterface::MeshNodeInterface(const Mesh<3>&, const RefNodeList&);
 
-template <size_t Dimension>
-MeshNodeInterface<Dimension>
-getMeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterfaceDescriptor& interface_descriptor)
+template <MeshConcept MeshType>
+MeshNodeInterface
+getMeshNodeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor)
 {
   for (size_t i_ref_node_list = 0; i_ref_node_list < mesh.connectivity().template numberOfRefItemList<ItemType::node>();
        ++i_ref_node_list) {
     const auto& ref_node_list = mesh.connectivity().template refItemList<ItemType::node>(i_ref_node_list);
     const RefId& ref          = ref_node_list.refId();
     if (ref == interface_descriptor) {
-      return MeshNodeInterface<Dimension>{mesh, ref_node_list};
+      return MeshNodeInterface{mesh, ref_node_list};
     }
   }
   for (size_t i_ref_edge_list = 0; i_ref_edge_list < mesh.connectivity().template numberOfRefItemList<ItemType::edge>();
@@ -137,7 +140,7 @@ getMeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
     const auto& ref_edge_list = mesh.connectivity().template refItemList<ItemType::edge>(i_ref_edge_list);
     const RefId& ref          = ref_edge_list.refId();
     if (ref == interface_descriptor) {
-      return MeshNodeInterface<Dimension>{mesh, ref_edge_list};
+      return MeshNodeInterface{mesh, ref_edge_list};
     }
   }
   for (size_t i_ref_face_list = 0; i_ref_face_list < mesh.connectivity().template numberOfRefItemList<ItemType::face>();
@@ -145,7 +148,7 @@ getMeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
     const auto& ref_face_list = mesh.connectivity().template refItemList<ItemType::face>(i_ref_face_list);
     const RefId& ref          = ref_face_list.refId();
     if (ref == interface_descriptor) {
-      return MeshNodeInterface<Dimension>{mesh, ref_face_list};
+      return MeshNodeInterface{mesh, ref_face_list};
     }
   }
 
@@ -155,6 +158,6 @@ getMeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const IInterface
   throw NormalError(ost.str());
 }
 
-template MeshNodeInterface<1> getMeshNodeInterface(const Mesh<Connectivity<1>>&, const IInterfaceDescriptor&);
-template MeshNodeInterface<2> getMeshNodeInterface(const Mesh<Connectivity<2>>&, const IInterfaceDescriptor&);
-template MeshNodeInterface<3> getMeshNodeInterface(const Mesh<Connectivity<3>>&, const IInterfaceDescriptor&);
+template MeshNodeInterface getMeshNodeInterface(const Mesh<1>&, const IInterfaceDescriptor&);
+template MeshNodeInterface getMeshNodeInterface(const Mesh<2>&, const IInterfaceDescriptor&);
+template MeshNodeInterface getMeshNodeInterface(const Mesh<3>&, const IInterfaceDescriptor&);
diff --git a/src/mesh/MeshNodeInterface.hpp b/src/mesh/MeshNodeInterface.hpp
index 8fcbb6704df80ffa48697fd20109e4fab36e2b98..bb2105649bb5acee027821a9c227b7323789cc99 100644
--- a/src/mesh/MeshNodeInterface.hpp
+++ b/src/mesh/MeshNodeInterface.hpp
@@ -3,28 +3,18 @@
 
 #include <algebra/TinyVector.hpp>
 #include <mesh/IInterfaceDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/RefItemList.hpp>
 #include <utils/Array.hpp>
 
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-template <size_t Dimension>
 class [[nodiscard]] MeshNodeInterface   // clazy:exclude=copyable-polymorphic
 {
  protected:
   RefNodeList m_ref_node_list;
 
-  std::array<TinyVector<Dimension>, Dimension*(Dimension - 1)> _getBounds(const Mesh<Connectivity<Dimension>>& mesh)
-    const;
-
  public:
-  template <size_t MeshDimension>
-  friend MeshNodeInterface<MeshDimension> getMeshNodeInterface(const Mesh<Connectivity<MeshDimension>>& mesh,
-                                                               const IInterfaceDescriptor& interface_descriptor);
+  template <MeshConcept MeshType>
+  friend MeshNodeInterface getMeshNodeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
   MeshNodeInterface& operator=(const MeshNodeInterface&) = default;
   MeshNodeInterface& operator=(MeshNodeInterface&&) = default;
@@ -42,9 +32,12 @@ class [[nodiscard]] MeshNodeInterface   // clazy:exclude=copyable-polymorphic
   }
 
  protected:
-  MeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const RefFaceList& ref_face_list);
-  MeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh, const RefEdgeList& ref_edge_list);
-  MeshNodeInterface(const Mesh<Connectivity<Dimension>>&, const RefNodeList& ref_node_list);
+  template <MeshConcept MeshType>
+  MeshNodeInterface(const MeshType& mesh, const RefFaceList& ref_face_list);
+  template <MeshConcept MeshType>
+  MeshNodeInterface(const MeshType& mesh, const RefEdgeList& ref_edge_list);
+  template <MeshConcept MeshType>
+  MeshNodeInterface(const MeshType&, const RefNodeList& ref_node_list);
 
  public:
   MeshNodeInterface(const MeshNodeInterface&) = default;
@@ -54,8 +47,7 @@ class [[nodiscard]] MeshNodeInterface   // clazy:exclude=copyable-polymorphic
   virtual ~MeshNodeInterface() = default;
 };
 
-template <size_t Dimension>
-MeshNodeInterface<Dimension> getMeshNodeInterface(const Mesh<Connectivity<Dimension>>& mesh,
-                                                  const IInterfaceDescriptor& interface_descriptor);
+template <MeshConcept MeshType>
+MeshNodeInterface getMeshNodeInterface(const MeshType& mesh, const IInterfaceDescriptor& interface_descriptor);
 
 #endif   // MESH_NODE_INTERFACE_HPP
diff --git a/src/mesh/MeshRandomizer.cpp b/src/mesh/MeshRandomizer.cpp
index 06028bab1c002c1898bed0e94d3e3d091d1db15c..8c94807db37ad95e4f905e992f9a1c000d14c59e 100644
--- a/src/mesh/MeshRandomizer.cpp
+++ b/src/mesh/MeshRandomizer.cpp
@@ -9,21 +9,22 @@
 #include <mesh/MeshFlatNodeBoundary.hpp>
 #include <mesh/MeshLineNodeBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/AxisBoundaryConditionDescriptor.hpp>
 #include <scheme/FixedBoundaryConditionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 #include <utils/RandomEngine.hpp>
 
-#include <variant>
-
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class MeshRandomizerHandler::MeshRandomizer
 {
  private:
+  static constexpr size_t Dimension = MeshType::Dimension;
+
   using Rd               = TinyVector<Dimension>;
   using Rdxd             = TinyMatrix<Dimension>;
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using ConnectivityType = typename MeshType::Connectivity;
 
   const MeshType& m_given_mesh;
 
@@ -232,7 +233,7 @@ class MeshRandomizerHandler::MeshRandomizer
   }
 
  public:
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getRandomizedMesh() const
   {
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
@@ -242,10 +243,10 @@ class MeshRandomizerHandler::MeshRandomizer
     parallel_for(
       m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { xr[node_id] += given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getRandomizedMesh(const FunctionSymbolId& function_symbol_id) const
   {
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
@@ -259,7 +260,7 @@ class MeshRandomizerHandler::MeshRandomizer
       m_given_mesh.numberOfNodes(),
       PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
   MeshRandomizer(const MeshRandomizer&) = delete;
@@ -273,14 +274,14 @@ class MeshRandomizerHandler::MeshRandomizer
   ~MeshRandomizer() = default;
 };
 
-template <size_t Dimension>
-class MeshRandomizerHandler::MeshRandomizer<Dimension>::AxisBoundaryCondition
+template <MeshConcept MeshType>
+class MeshRandomizerHandler::MeshRandomizer<MeshType>::AxisBoundaryCondition
 {
  public:
   using Rd = TinyVector<Dimension, double>;
 
  private:
-  const MeshLineNodeBoundary<Dimension> m_mesh_line_node_boundary;
+  const MeshLineNodeBoundary<MeshType> m_mesh_line_node_boundary;
 
  public:
   const Rd&
@@ -295,7 +296,7 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::AxisBoundaryCondition
     return m_mesh_line_node_boundary.nodeList();
   }
 
-  AxisBoundaryCondition(MeshLineNodeBoundary<Dimension>&& mesh_line_node_boundary)
+  AxisBoundaryCondition(MeshLineNodeBoundary<MeshType>&& mesh_line_node_boundary)
     : m_mesh_line_node_boundary(mesh_line_node_boundary)
   {
     ;
@@ -305,18 +306,18 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::AxisBoundaryCondition
 };
 
 template <>
-class MeshRandomizerHandler::MeshRandomizer<1>::AxisBoundaryCondition
+class MeshRandomizerHandler::MeshRandomizer<Mesh<1>>::AxisBoundaryCondition
 {
  public:
   AxisBoundaryCondition()  = default;
   ~AxisBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class MeshRandomizerHandler::MeshRandomizer<Dimension>::FixedBoundaryCondition
+template <MeshConcept MeshType>
+class MeshRandomizerHandler::MeshRandomizer<MeshType>::FixedBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
  public:
   const Array<const NodeId>&
@@ -325,19 +326,19 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::FixedBoundaryCondition
     return m_mesh_node_boundary.nodeList();
   }
 
-  FixedBoundaryCondition(MeshNodeBoundary<Dimension>&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
+  FixedBoundaryCondition(MeshNodeBoundary&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
 
   ~FixedBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class MeshRandomizerHandler::MeshRandomizer<Dimension>::SymmetryBoundaryCondition
+template <MeshConcept MeshType>
+class MeshRandomizerHandler::MeshRandomizer<MeshType>::SymmetryBoundaryCondition
 {
  public:
-  using Rd = TinyVector<Dimension, double>;
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
-  const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+  const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary;
 
  public:
   const Rd&
@@ -352,7 +353,7 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::SymmetryBoundaryConditio
     return m_mesh_flat_node_boundary.nodeList();
   }
 
-  SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary)
+  SymmetryBoundaryCondition(MeshFlatNodeBoundary<MeshType>&& mesh_flat_node_boundary)
     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
   {
     ;
@@ -361,63 +362,39 @@ class MeshRandomizerHandler::MeshRandomizer<Dimension>::SymmetryBoundaryConditio
   ~SymmetryBoundaryCondition() = default;
 };
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshRandomizerHandler::getRandomizedMesh(
-  const IMesh& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const
 {
-  switch (mesh.dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh();
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh();
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh();
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        MeshRandomizer randomizer(*mesh, bc_descriptor_list);
+        return randomizer.getRandomizedMesh();
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    mesh_v->variant());
 }
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshRandomizerHandler::getRandomizedMesh(
-  const IMesh& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
   const FunctionSymbolId& function_symbol_id) const
 {
-  switch (mesh.dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh(function_symbol_id);
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh(function_symbol_id);
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshRandomizer randomizer(dynamic_cast<const MeshType&>(mesh), bc_descriptor_list);
-    return randomizer.getRandomizedMesh(function_symbol_id);
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        MeshRandomizer randomizer(*mesh, bc_descriptor_list);
+        return randomizer.getRandomizedMesh(function_symbol_id);
+      } else {
+        throw UnexpectedError("invalid mesh type");
+      }
+    },
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshRandomizer.hpp b/src/mesh/MeshRandomizer.hpp
index f09aa78634919818fe6fe8f581f2b12976dea3b9..6db719cb8fac9082624be806e75a6e4a59091f25 100644
--- a/src/mesh/MeshRandomizer.hpp
+++ b/src/mesh/MeshRandomizer.hpp
@@ -1,27 +1,28 @@
 #ifndef MESH_RANDOMIZER_HPP
 #define MESH_RANDOMIZER_HPP
 
-#include <mesh/IMesh.hpp>
-#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 
 #include <memory>
 #include <vector>
 
+class MeshVariant;
+class IBoundaryConditionDescriptor;
 class FunctionSymbolId;
 
 class MeshRandomizerHandler
 {
  private:
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   class MeshRandomizer;
 
  public:
-  std::shared_ptr<const IMesh> getRandomizedMesh(
-    const IMesh& mesh,
+  std::shared_ptr<const MeshVariant> getRandomizedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const;
 
-  std::shared_ptr<const IMesh> getRandomizedMesh(
-    const IMesh& mesh,
+  std::shared_ptr<const MeshVariant> getRandomizedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
     const FunctionSymbolId& function_symbol_id) const;
 
diff --git a/src/mesh/MeshRelaxer.cpp b/src/mesh/MeshRelaxer.cpp
index bda3cc7d17680bf8e2b43596d2e9ad2c7d9759ed..65302649d995851ced9a485788f8e1fc2b9ef14b 100644
--- a/src/mesh/MeshRelaxer.cpp
+++ b/src/mesh/MeshRelaxer.cpp
@@ -2,18 +2,22 @@
 
 #include <mesh/Connectivity.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 
-template <typename ConnectivityType>
-std::shared_ptr<const Mesh<ConnectivityType>>
-MeshRelaxer::_relax(const Mesh<ConnectivityType>& source_mesh,
-                    const Mesh<ConnectivityType>& destination_mesh,
-                    const double& theta) const
+template <MeshConcept MeshType>
+std::shared_ptr<const MeshVariant>
+MeshRelaxer::_relax(const MeshType& source_mesh, const MeshType& destination_mesh, const double& theta) const
 {
-  if (source_mesh.shared_connectivity() == destination_mesh.shared_connectivity()) {
+  static_assert(is_polygonal_mesh_v<MeshType>);
+  if (source_mesh.connectivity().id() == destination_mesh.connectivity().id()) {
+    using ConnectivityType               = typename MeshType::Connectivity;
+    constexpr size_t Dimension           = MeshType::Dimension;
     const ConnectivityType& connectivity = source_mesh.connectivity();
-    NodeValue<TinyVector<ConnectivityType::Dimension>> theta_xr{connectivity};
-    const NodeValue<const TinyVector<ConnectivityType::Dimension>> source_xr      = source_mesh.xr();
-    const NodeValue<const TinyVector<ConnectivityType::Dimension>> destination_xr = destination_mesh.xr();
+
+    NodeValue<TinyVector<Dimension>> theta_xr{connectivity};
+    const NodeValue<const TinyVector<Dimension>> source_xr      = source_mesh.xr();
+    const NodeValue<const TinyVector<Dimension>> destination_xr = destination_mesh.xr();
 
     const double one_minus_theta = 1 - theta;
     parallel_for(
@@ -21,39 +25,26 @@ MeshRelaxer::_relax(const Mesh<ConnectivityType>& source_mesh,
         theta_xr[node_id] = one_minus_theta * source_xr[node_id] + theta * destination_xr[node_id];
       });
 
-    return std::make_shared<Mesh<ConnectivityType>>(source_mesh.shared_connectivity(), theta_xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(source_mesh.shared_connectivity(), theta_xr));
   } else {
     throw NormalError("relaxed meshes must share the same connectivity");
   }
 }
 
-std::shared_ptr<const IMesh>
-MeshRelaxer::relax(const std::shared_ptr<const IMesh>& p_source_mesh,
-                   const std::shared_ptr<const IMesh>& p_destination_mesh,
+std::shared_ptr<const MeshVariant>
+MeshRelaxer::relax(const std::shared_ptr<const MeshVariant>& p_source_mesh,
+                   const std::shared_ptr<const MeshVariant>& p_destination_mesh,
                    const double& theta) const
 {
-  if (p_source_mesh->dimension() != p_destination_mesh->dimension()) {
-    throw NormalError("incompatible mesh dimensions");
-  } else {
-    switch (p_source_mesh->dimension()) {
-    case 1: {
-      using MeshType = Mesh<Connectivity<1>>;
-      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
-                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
-    }
-    case 2: {
-      using MeshType = Mesh<Connectivity<2>>;
-      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
-                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
-    }
-    case 3: {
-      using MeshType = Mesh<Connectivity<3>>;
-      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
-                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
-    }
-    default: {
-      throw UnexpectedError("invalid mesh dimension");
-    }
-    }
-  }
+  return std::visit(
+    [&](auto&& source_mesh, auto&& destination_mesh) -> std::shared_ptr<const MeshVariant> {
+      using SourceMeshType      = mesh_type_t<decltype(source_mesh)>;
+      using DestinationMeshType = mesh_type_t<decltype(destination_mesh)>;
+      if constexpr (std::is_same_v<SourceMeshType, DestinationMeshType>) {
+        return this->_relax(*source_mesh, *destination_mesh, theta);
+      } else {
+        throw UnexpectedError("invalid mesh dimension");
+      }
+    },
+    p_source_mesh->variant(), p_destination_mesh->variant());
 }
diff --git a/src/mesh/MeshRelaxer.hpp b/src/mesh/MeshRelaxer.hpp
index 96846cbadfefe61902199e927c99ecf96aaf176a..c771842cf947fb4cf9b00c05a0a6cb7fd301fe83 100644
--- a/src/mesh/MeshRelaxer.hpp
+++ b/src/mesh/MeshRelaxer.hpp
@@ -1,25 +1,24 @@
 #ifndef MESH_RELAXER_HPP
 #define MESH_RELAXER_HPP
 
-class IMesh;
+#include <mesh/MeshTraits.hpp>
 
-template <typename ConnectivityType>
-class Mesh;
+class MeshVariant;
 
 #include <memory>
 
 class MeshRelaxer
 {
  private:
-  template <typename ConnectivityType>
-  std::shared_ptr<const Mesh<ConnectivityType>> _relax(const Mesh<ConnectivityType>& source_mesh,
-                                                       const Mesh<ConnectivityType>& destination_mesh,
-                                                       const double& theta) const;
+  template <MeshConcept MeshType>
+  std::shared_ptr<const MeshVariant> _relax(const MeshType& source_mesh,
+                                            const MeshType& destination_mesh,
+                                            const double& theta) const;
 
  public:
-  std::shared_ptr<const IMesh> relax(const std::shared_ptr<const IMesh>& p_source_mesh,
-                                     const std::shared_ptr<const IMesh>& p_destination_mesh,
-                                     const double& theta) const;
+  std::shared_ptr<const MeshVariant> relax(const std::shared_ptr<const MeshVariant>& p_source_mesh,
+                                           const std::shared_ptr<const MeshVariant>& p_destination_mesh,
+                                           const double& theta) const;
   MeshRelaxer()  = default;
   ~MeshRelaxer() = default;
 };
diff --git a/src/mesh/MeshSmoother.cpp b/src/mesh/MeshSmoother.cpp
index a9af80c0129fdb20267b3c62e4430535f00f154f..7950c732abe7a07961e95fbd7da1728a8f4ba937 100644
--- a/src/mesh/MeshSmoother.cpp
+++ b/src/mesh/MeshSmoother.cpp
@@ -10,6 +10,7 @@
 #include <mesh/MeshFlatNodeBoundary.hpp>
 #include <mesh/MeshLineNodeBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/AxisBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
@@ -19,14 +20,15 @@
 
 #include <variant>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class MeshSmootherHandler::MeshSmoother
 {
  private:
+  static constexpr size_t Dimension = MeshType::Dimension;
+
   using Rd               = TinyVector<Dimension>;
   using Rdxd             = TinyMatrix<Dimension>;
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using ConnectivityType = typename MeshType::Connectivity;
 
   const MeshType& m_given_mesh;
 
@@ -201,7 +203,7 @@ class MeshSmootherHandler::MeshSmoother
   }
 
  public:
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getSmoothedMesh() const
   {
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
@@ -211,10 +213,10 @@ class MeshSmootherHandler::MeshSmoother
     parallel_for(
       m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) { xr[node_id] += given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getSmoothedMesh(const FunctionSymbolId& function_symbol_id) const
   {
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
@@ -228,10 +230,10 @@ class MeshSmootherHandler::MeshSmoother
       m_given_mesh.numberOfNodes(),
       PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getSmoothedMesh(const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_descriptor_list) const
   {
     NodeValue<const Rd> given_xr = m_given_mesh.xr();
@@ -268,10 +270,10 @@ class MeshSmootherHandler::MeshSmoother
       m_given_mesh.numberOfNodes(),
       PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
-  std::shared_ptr<const IMesh>
+  std::shared_ptr<const MeshVariant>
   getSmoothedMesh(
     const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const
   {
@@ -283,7 +285,7 @@ class MeshSmootherHandler::MeshSmoother
     is_displaced.fill(false);
 
     for (size_t i_zone = 0; i_zone < discrete_function_variant_list.size(); ++i_zone) {
-      auto is_zone_cell = discrete_function_variant_list[i_zone]->get<DiscreteFunctionP0<Dimension, const double>>();
+      auto is_zone_cell = discrete_function_variant_list[i_zone]->get<DiscreteFunctionP0<const double>>();
 
       parallel_for(
         m_given_mesh.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) {
@@ -305,7 +307,7 @@ class MeshSmootherHandler::MeshSmoother
       m_given_mesh.numberOfNodes(),
       PUGS_LAMBDA(const NodeId node_id) { xr[node_id] = is_displaced[node_id] * xr[node_id] + given_xr[node_id]; });
 
-    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+    return std::make_shared<MeshVariant>(std::make_shared<const MeshType>(m_given_mesh.shared_connectivity(), xr));
   }
 
   MeshSmoother(const MeshSmoother&) = delete;
@@ -319,14 +321,14 @@ class MeshSmootherHandler::MeshSmoother
   ~MeshSmoother() = default;
 };
 
-template <size_t Dimension>
-class MeshSmootherHandler::MeshSmoother<Dimension>::AxisBoundaryCondition
+template <MeshConcept MeshType>
+class MeshSmootherHandler::MeshSmoother<MeshType>::AxisBoundaryCondition
 {
  public:
   using Rd = TinyVector<Dimension, double>;
 
  private:
-  const MeshLineNodeBoundary<Dimension> m_mesh_line_node_boundary;
+  const MeshLineNodeBoundary<MeshType> m_mesh_line_node_boundary;
 
  public:
   const Rd&
@@ -341,7 +343,7 @@ class MeshSmootherHandler::MeshSmoother<Dimension>::AxisBoundaryCondition
     return m_mesh_line_node_boundary.nodeList();
   }
 
-  AxisBoundaryCondition(MeshLineNodeBoundary<Dimension>&& mesh_line_node_boundary)
+  AxisBoundaryCondition(MeshLineNodeBoundary<MeshType>&& mesh_line_node_boundary)
     : m_mesh_line_node_boundary(mesh_line_node_boundary)
   {
     ;
@@ -351,18 +353,18 @@ class MeshSmootherHandler::MeshSmoother<Dimension>::AxisBoundaryCondition
 };
 
 template <>
-class MeshSmootherHandler::MeshSmoother<1>::AxisBoundaryCondition
+class MeshSmootherHandler::MeshSmoother<Mesh<1>>::AxisBoundaryCondition
 {
  public:
   AxisBoundaryCondition()  = default;
   ~AxisBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class MeshSmootherHandler::MeshSmoother<Dimension>::FixedBoundaryCondition
+template <MeshConcept MeshType>
+class MeshSmootherHandler::MeshSmoother<MeshType>::FixedBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
  public:
   const Array<const NodeId>&
@@ -371,19 +373,19 @@ class MeshSmootherHandler::MeshSmoother<Dimension>::FixedBoundaryCondition
     return m_mesh_node_boundary.nodeList();
   }
 
-  FixedBoundaryCondition(MeshNodeBoundary<Dimension>&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
+  FixedBoundaryCondition(MeshNodeBoundary&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
 
   ~FixedBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class MeshSmootherHandler::MeshSmoother<Dimension>::SymmetryBoundaryCondition
+template <MeshConcept MeshType>
+class MeshSmootherHandler::MeshSmoother<MeshType>::SymmetryBoundaryCondition
 {
  public:
-  using Rd = TinyVector<Dimension, double>;
+  using Rd = TinyVector<MeshType::Dimension, double>;
 
  private:
-  const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+  const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary;
 
  public:
   const Rd&
@@ -398,7 +400,7 @@ class MeshSmootherHandler::MeshSmoother<Dimension>::SymmetryBoundaryCondition
     return m_mesh_flat_node_boundary.nodeList();
   }
 
-  SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary)
+  SymmetryBoundaryCondition(MeshFlatNodeBoundary<MeshType>&& mesh_flat_node_boundary)
     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
   {
     ;
@@ -407,135 +409,61 @@ class MeshSmootherHandler::MeshSmoother<Dimension>::SymmetryBoundaryCondition
   ~SymmetryBoundaryCondition() = default;
 };
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshSmootherHandler::getSmoothedMesh(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const
 {
-  switch (mesh->dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh();
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh();
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh();
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      MeshSmoother smoother(*mesh, bc_descriptor_list);
+      return smoother.getSmoothedMesh();
+    },
+    mesh_v->variant());
 }
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshSmootherHandler::getSmoothedMesh(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
   const FunctionSymbolId& function_symbol_id) const
 {
-  switch (mesh->dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(function_symbol_id);
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(function_symbol_id);
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(function_symbol_id);
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      MeshSmoother smoother(*mesh, bc_descriptor_list);
+      return smoother.getSmoothedMesh(function_symbol_id);
+    },
+    mesh_v->variant());
 }
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshSmootherHandler::getSmoothedMesh(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
   const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) const
 {
-  switch (mesh->dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(smoothing_zone_list);
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(smoothing_zone_list);
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(smoothing_zone_list);
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      MeshSmoother smoother(*mesh, bc_descriptor_list);
+      return smoother.getSmoothedMesh(smoothing_zone_list);
+    },
+    mesh_v->variant());
 }
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 MeshSmootherHandler::getSmoothedMesh(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
   const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list) const
 {
-  if (not hasSameMesh(discrete_function_variant_list)) {
-    throw NormalError("discrete functions are not defined on the same mesh");
-  }
-
-  std::shared_ptr<const IMesh> common_mesh = getCommonMesh(discrete_function_variant_list);
-
-  if (common_mesh != mesh) {
-    throw NormalError("discrete functions are not defined on the smoothed mesh");
+  if (not hasSameMesh(discrete_function_variant_list, mesh_v)) {
+    throw NormalError("discrete functions are not defined on the smooth mesh");
   }
 
-  switch (mesh->dimension()) {
-  case 1: {
-    constexpr size_t Dimension = 1;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(discrete_function_variant_list);
-  }
-  case 2: {
-    constexpr size_t Dimension = 2;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(discrete_function_variant_list);
-  }
-  case 3: {
-    constexpr size_t Dimension = 3;
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    MeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
-    return smoother.getSmoothedMesh(discrete_function_variant_list);
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      MeshSmoother smoother(*mesh, bc_descriptor_list);
+      return smoother.getSmoothedMesh(discrete_function_variant_list);
+    },
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshSmoother.hpp b/src/mesh/MeshSmoother.hpp
index 99db9bf76bf9ea771b1ebbc41c1b948c6d451632..d43e380a97987e495c785a509f85f61743e2f50f 100644
--- a/src/mesh/MeshSmoother.hpp
+++ b/src/mesh/MeshSmoother.hpp
@@ -1,39 +1,40 @@
 #ifndef MESH_SMOOTHER_HPP
 #define MESH_SMOOTHER_HPP
 
-#include <mesh/IMesh.hpp>
-#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 
 #include <memory>
 #include <vector>
 
+class MeshVariant;
 class FunctionSymbolId;
+class IBoundaryConditionDescriptor;
 class IZoneDescriptor;
 class DiscreteFunctionVariant;
 
 class MeshSmootherHandler
 {
  private:
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   class MeshSmoother;
 
  public:
-  std::shared_ptr<const IMesh> getSmoothedMesh(
-    const std::shared_ptr<const IMesh>& mesh,
+  std::shared_ptr<const MeshVariant> getSmoothedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const;
 
-  std::shared_ptr<const IMesh> getSmoothedMesh(
-    const std::shared_ptr<const IMesh>& mesh,
+  std::shared_ptr<const MeshVariant> getSmoothedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
     const FunctionSymbolId& function_symbol_id) const;
 
-  std::shared_ptr<const IMesh> getSmoothedMesh(
-    const std::shared_ptr<const IMesh>& mesh,
+  std::shared_ptr<const MeshVariant> getSmoothedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
     const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) const;
 
-  std::shared_ptr<const IMesh> getSmoothedMesh(
-    const std::shared_ptr<const IMesh>& mesh,
+  std::shared_ptr<const MeshVariant> getSmoothedMesh(
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
     const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& smoothing_zone_list) const;
 
diff --git a/src/mesh/MeshTraits.hpp b/src/mesh/MeshTraits.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f55b5393455e53dae022b340b60303f80adf2c44
--- /dev/null
+++ b/src/mesh/MeshTraits.hpp
@@ -0,0 +1,59 @@
+#ifndef MESH_TRAITS_HPP
+#define MESH_TRAITS_HPP
+
+#include <utils/PugsTraits.hpp>
+
+#include <memory>
+#include <type_traits>
+
+template <size_t Dimension>
+class Mesh;
+
+template <typename T>
+constexpr inline bool is_mesh_v = false;
+
+template <size_t Dimension>
+constexpr inline bool is_mesh_v<Mesh<Dimension>> = true;
+
+template <typename T>
+constexpr bool is_mesh_v<const T> = is_mesh_v<std::remove_cvref_t<T>>;
+
+template <typename T>
+constexpr bool is_mesh_v<T&> = is_mesh_v<std::remove_cvref_t<T>>;
+
+template <typename T>
+concept MeshConcept = is_mesh_v<T>;
+
+// Get mesh type (from std::shared_ptr for instance)
+
+template <typename T>
+struct mesh_type_t_from
+{
+  static_assert(is_mesh_v<T>, "Requires mesh type");
+  using type = std::remove_const_t<T>;
+};
+
+template <MeshConcept T, template <typename T1> typename C>
+struct mesh_type_t_from<C<T>>
+{
+  static_assert(is_shared_ptr_v<std::decay_t<C<T>>>, "Requires std::shared_ptr container");
+  using type = std::remove_const_t<T>;
+};
+
+template <typename T>
+using mesh_type_t = typename mesh_type_t_from<std::decay_t<T>>::type;
+
+// Check mesh kind
+template <MeshConcept MeshType>
+constexpr inline bool is_polygonal_mesh_v = false;
+
+template <size_t Dimension>
+constexpr inline bool is_polygonal_mesh_v<Mesh<Dimension>> = true;
+
+template <typename T>
+constexpr bool is_polygonal_mesh_v<const T> = is_polygonal_mesh_v<std::remove_cvref_t<T>>;
+
+template <typename T>
+constexpr bool is_polygonal_mesh_v<T&> = is_polygonal_mesh_v<std::remove_cvref_t<T>>;
+
+#endif   // MESH_TRAITS_HPP
diff --git a/src/mesh/MeshTransformer.cpp b/src/mesh/MeshTransformer.cpp
index 167c4f337cd5c48147594623f3ae436874a47a22..81f723a6cee92a43ab4b06f52b4dffc7a3b92095 100644
--- a/src/mesh/MeshTransformer.cpp
+++ b/src/mesh/MeshTransformer.cpp
@@ -1,7 +1,10 @@
 #include <mesh/MeshTransformer.hpp>
 
 #include <mesh/Connectivity.hpp>
+#include <mesh/ItemArray.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 
 #include <language/utils/EvaluateAtPoints.hpp>
 
@@ -11,39 +14,29 @@ class MeshTransformer::MeshTransformation<OutputType(InputType)>
   static constexpr size_t Dimension = OutputType::Dimension;
 
  public:
-  static inline std::shared_ptr<Mesh<Connectivity<Dimension>>>
-  transform(const FunctionSymbolId& function_symbol_id, std::shared_ptr<const IMesh> p_mesh)
+  template <size_t Dimension>
+  static std::shared_ptr<const Mesh<Dimension>>
+  transform(const FunctionSymbolId& function_symbol_id, const Mesh<Dimension>& mesh)
   {
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    const MeshType& given_mesh = dynamic_cast<const MeshType&>(*p_mesh);
-
-    NodeValue<OutputType> xr(given_mesh.connectivity());
-    NodeValue<const InputType> given_xr = given_mesh.xr();
+    NodeValue<OutputType> xr(mesh.connectivity());
+    NodeValue<const InputType> given_xr = mesh.xr();
     EvaluateAtPoints<OutputType(InputType)>::evaluateTo(function_symbol_id, given_xr, xr);
 
-    return std::make_shared<MeshType>(given_mesh.shared_connectivity(), xr);
+    return std::make_shared<const Mesh<Dimension>>(mesh.shared_connectivity(), xr);
   }
 };
 
-std::shared_ptr<const IMesh>
-MeshTransformer::transform(const FunctionSymbolId& function_id, std::shared_ptr<const IMesh> p_mesh)
+std::shared_ptr<const MeshVariant>
+MeshTransformer::transform(const FunctionSymbolId& function_id, std::shared_ptr<const MeshVariant> mesh_v)
 
 {
-  switch (p_mesh->dimension()) {
-  case 1: {
-    using TransformT = TinyVector<1>(TinyVector<1>);
-    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-  }
-  case 2: {
-    using TransformT = TinyVector<2>(TinyVector<2>);
-    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-  }
-  case 3: {
-    using TransformT = TinyVector<3>(TinyVector<3>);
-    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType             = mesh_type_t<decltype(mesh)>;
+      constexpr size_t Dimension = MeshType::Dimension;
+      using TransformT           = TinyVector<Dimension>(TinyVector<Dimension>);
+
+      return std::make_shared<MeshVariant>(MeshTransformation<TransformT>::transform(function_id, *mesh));
+    },
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshTransformer.hpp b/src/mesh/MeshTransformer.hpp
index ce0a4d47eab0557004234d22ea5331a0d2b0f793..57687c395b040b2d68c1cad462568bf37ed54854 100644
--- a/src/mesh/MeshTransformer.hpp
+++ b/src/mesh/MeshTransformer.hpp
@@ -1,11 +1,7 @@
 #ifndef MESH_TRANSFORMER_HPP
 #define MESH_TRANSFORMER_HPP
 
-class IMesh;
-
-template <typename ConnectivityType>
-class Mesh;
-
+class MeshVariant;
 class FunctionSymbolId;
 
 #include <memory>
@@ -16,8 +12,8 @@ class MeshTransformer
   class MeshTransformation;
 
  public:
-  std::shared_ptr<const IMesh> transform(const FunctionSymbolId& function_symbol_id,
-                                         std::shared_ptr<const IMesh> p_mesh);
+  std::shared_ptr<const MeshVariant> transform(const FunctionSymbolId& function_symbol_id,
+                                               std::shared_ptr<const MeshVariant> p_mesh);
 
   MeshTransformer()  = default;
   ~MeshTransformer() = default;
diff --git a/src/mesh/MeshUtils.cpp b/src/mesh/MeshUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73fabd86f6366b6d8e0f5c6dbb16a7e0c25d80c9
--- /dev/null
+++ b/src/mesh/MeshUtils.cpp
@@ -0,0 +1,12 @@
+#include <mesh/MeshUtils.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
+
+bool
+checkConnectivityOrdering(const std::shared_ptr<const MeshVariant>& mesh_v)
+{
+  return std::visit([](auto&& mesh) { return checkConnectivityOrdering(mesh->connectivity()); }, mesh_v->variant());
+}
diff --git a/src/mesh/MeshUtils.hpp b/src/mesh/MeshUtils.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..27585f576e15e2021ddd88aedd5ecbf33aaa4ff0
--- /dev/null
+++ b/src/mesh/MeshUtils.hpp
@@ -0,0 +1,10 @@
+#ifndef MESH_UTILS_HPP
+#define MESH_UTILS_HPP
+
+#include <memory>
+
+class MeshVariant;
+
+bool checkConnectivityOrdering(const std::shared_ptr<const MeshVariant>& mesh_v);
+
+#endif   // MESH_UTILS_HPP
diff --git a/src/mesh/MeshVariant.cpp b/src/mesh/MeshVariant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2232e4f73231c543e1c2ab79e919c233bbc0a0d2
--- /dev/null
+++ b/src/mesh/MeshVariant.cpp
@@ -0,0 +1,54 @@
+#include <mesh/MeshVariant.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+size_t
+MeshVariant::id() const
+{
+  return std::visit([](auto&& mesh) { return mesh->id(); }, m_p_mesh_variant);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const MeshVariant& mesh_v)
+{
+  std::visit([&](auto&& p_mesh) { os << *p_mesh; }, mesh_v.variant());
+  return os;
+}
+
+size_t
+MeshVariant::numberOfCells() const
+{
+  return std::visit([](auto&& mesh) { return mesh->numberOfCells(); }, m_p_mesh_variant);
+}
+
+size_t
+MeshVariant::numberOfFaces() const
+{
+  return std::visit([](auto&& mesh) { return mesh->numberOfFaces(); }, m_p_mesh_variant);
+}
+
+size_t
+MeshVariant::numberOfEdges() const
+{
+  return std::visit([](auto&& mesh) { return mesh->numberOfEdges(); }, m_p_mesh_variant);
+}
+
+size_t
+MeshVariant::numberOfNodes() const
+{
+  return std::visit([](auto&& mesh) { return mesh->numberOfNodes(); }, m_p_mesh_variant);
+}
+
+const IConnectivity&
+MeshVariant::connectivity() const
+{
+  return std::visit([](auto&& mesh) -> const IConnectivity& { return mesh->connectivity(); }, m_p_mesh_variant);
+}
+
+std::shared_ptr<const IConnectivity>
+MeshVariant::shared_connectivity() const
+{
+  return std::visit([](auto&& mesh) -> std::shared_ptr<const IConnectivity> { return mesh->shared_connectivity(); },
+                    m_p_mesh_variant);
+}
diff --git a/src/mesh/MeshVariant.hpp b/src/mesh/MeshVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c37d2dd01343f0b4e58f785b05200a45a0c2cb44
--- /dev/null
+++ b/src/mesh/MeshVariant.hpp
@@ -0,0 +1,79 @@
+#ifndef MESH_VARIANT_HPP
+#define MESH_VARIANT_HPP
+
+#include <mesh/MeshTraits.hpp>
+#include <utils/Demangle.hpp>
+#include <utils/Exceptions.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <rang.hpp>
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <variant>
+
+class IConnectivity;
+
+template <size_t Dimension>
+class Mesh;
+
+class MeshVariant
+{
+ private:
+  using Variant = std::variant<std::shared_ptr<const Mesh<1>>,   //
+                               std::shared_ptr<const Mesh<2>>,   //
+                               std::shared_ptr<const Mesh<3>>>;
+
+  Variant m_p_mesh_variant;
+
+ public:
+  friend std::ostream& operator<<(std::ostream& os, const MeshVariant& mesh_v);
+
+  size_t id() const;
+
+  size_t numberOfCells() const;
+  size_t numberOfFaces() const;
+  size_t numberOfEdges() const;
+  size_t numberOfNodes() const;
+
+  const IConnectivity& connectivity() const;
+  std::shared_ptr<const IConnectivity> shared_connectivity() const;
+
+  template <MeshConcept MeshType>
+  PUGS_INLINE std::shared_ptr<const MeshType>
+  get() const
+  {
+    if (not std::holds_alternative<std::shared_ptr<const MeshType>>(this->m_p_mesh_variant)) {
+      std::ostringstream error_msg;
+      error_msg << "invalid mesh type type\n";
+      error_msg << "- required " << rang::fgB::red << demangle<MeshType>() << rang::fg::reset << '\n';
+      error_msg << "- contains " << rang::fgB::yellow
+                << std::visit([](auto&& p_mesh) -> std::string { return demangle<mesh_type_t<decltype(p_mesh)>>(); },
+                              this->m_p_mesh_variant)
+                << rang::fg::reset;
+      throw NormalError(error_msg.str());
+    }
+    return std::get<std::shared_ptr<const MeshType>>(m_p_mesh_variant);
+  }
+
+  PUGS_INLINE
+  Variant
+  variant() const
+  {
+    return m_p_mesh_variant;
+  }
+
+  MeshVariant() = delete;
+
+  template <MeshConcept MeshType>
+  MeshVariant(const std::shared_ptr<const MeshType>& p_mesh) : m_p_mesh_variant{p_mesh}
+  {}
+
+  MeshVariant(const MeshVariant&) = default;
+  MeshVariant(MeshVariant&&)      = default;
+
+  ~MeshVariant() = default;
+};
+
+#endif   // MESH_VARIANT_HPP
diff --git a/src/output/GnuplotWriter.cpp b/src/output/GnuplotWriter.cpp
index 8ccf7767180b30524ba54f7c4f085c3ad35783d2..991ec74537fffaaa2bef855f5c4af88f7acfa03f 100644
--- a/src/output/GnuplotWriter.cpp
+++ b/src/output/GnuplotWriter.cpp
@@ -5,6 +5,8 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Filesystem.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/PugsTraits.hpp>
@@ -146,7 +148,7 @@ GnuplotWriter::_writeData(const ItemDataT& item_data,
   }
 }
 
-template <typename MeshType>
+template <MeshConcept MeshType>
 void
 GnuplotWriter::_writeDataAtNodes(const MeshType& mesh,
                                  const OutputNamedItemDataSet& output_named_item_data_set,
@@ -238,7 +240,7 @@ GnuplotWriter::_writeNodeData(const NodeArray<DataType>& node_array, NodeId node
   }
 }
 
-template <typename MeshType>
+template <MeshConcept MeshType>
 void
 GnuplotWriter::_write(const MeshType& mesh,
                       const OutputNamedItemDataSet& output_named_item_data_set,
@@ -311,64 +313,55 @@ GnuplotWriter::_write(const MeshType& mesh,
 }
 
 void
-GnuplotWriter::_writeAtTime(const IMesh& mesh,
+GnuplotWriter::_writeAtTime(const MeshVariant& mesh_v,
                             const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                             double time) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  default: {
-    throw NormalError("gnuplot format is not available in dimension " + stringify(mesh.dimension()));
-  }
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr ((MeshType::Dimension == 1) or (MeshType::Dimension == 2)) {
+        this->_write(*p_mesh, output_named_item_data_set, time);
+      } else {
+        throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
+      }
+    },
+    mesh_v.variant());
 }
 
 void
-GnuplotWriter::_writeMesh(const IMesh& mesh) const
+GnuplotWriter::_writeMesh(const MeshVariant& mesh_v) const
 {
   OutputNamedItemDataSet output_named_item_data_set{};
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  default: {
-    throw NormalError("gnuplot format is not available in dimension " + stringify(mesh.dimension()));
-  }
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr ((MeshType::Dimension == 1) or (MeshType::Dimension == 2)) {
+        this->_write(*p_mesh, output_named_item_data_set, {});
+      } else {
+        throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
+      }
+    },
+    mesh_v.variant());
 }
 
 void
-GnuplotWriter::_write(const IMesh& mesh,
+GnuplotWriter::_write(const MeshVariant& mesh_v,
                       const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  default: {
-    throw NormalError("gnuplot format is not available in dimension " + stringify(mesh.dimension()));
-  }
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr ((MeshType::Dimension == 1) or (MeshType::Dimension == 2)) {
+        this->_write(*p_mesh, output_named_item_data_set, {});
+      } else {
+        throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
+      }
+    },
+    mesh_v.variant());
 }
diff --git a/src/output/GnuplotWriter.hpp b/src/output/GnuplotWriter.hpp
index 6a4c6bd5b4765c6402f56fdc5b700cd2b584cac7..3069b79da716d2640e8446822e23fcab37b98316 100644
--- a/src/output/GnuplotWriter.hpp
+++ b/src/output/GnuplotWriter.hpp
@@ -5,9 +5,10 @@
 
 #include <algebra/TinyMatrix.hpp>
 #include <algebra/TinyVector.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <output/OutputNamedItemValueSet.hpp>
 
-class IMesh;
+class MeshVariant;
 
 #include <optional>
 #include <string>
@@ -40,24 +41,24 @@ class GnuplotWriter final : public WriterBase
                   [[maybe_unused]] NodeId node_id,
                   std::ostream& fout) const;
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   void _writeDataAtNodes(const MeshType& mesh,
                          const OutputNamedItemDataSet& output_named_item_data_set,
                          std::ostream& fout) const;
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   void _write(const MeshType& mesh,
               const OutputNamedItemDataSet& output_named_item_data_set,
               std::optional<double> time) const;
 
-  void _writeAtTime(const IMesh& mesh,
+  void _writeAtTime(const MeshVariant& mesh_v,
                     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                     double time) const final;
 
-  void _write(const IMesh& mesh,
+  void _write(const MeshVariant& mesh_v,
               const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const final;
 
-  void _writeMesh(const IMesh& mesh) const final;
+  void _writeMesh(const MeshVariant& mesh_v) const final;
 
  public:
   GnuplotWriter(const std::string& base_filename) : WriterBase(base_filename) {}
diff --git a/src/output/GnuplotWriter1D.cpp b/src/output/GnuplotWriter1D.cpp
index 6b8da7a7e063caeac4cbcd64e577ba75d913fe13..84e8aa080c6802eb546a4d0183fb446201e31ac8 100644
--- a/src/output/GnuplotWriter1D.cpp
+++ b/src/output/GnuplotWriter1D.cpp
@@ -5,6 +5,8 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Filesystem.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/PugsTraits.hpp>
@@ -140,7 +142,7 @@ GnuplotWriter1D::_itemDataNbRow(const ItemArray<DataType, item_type>& item_array
   return item_array.sizeOfArrays();
 }
 
-template <typename MeshType, ItemType item_type>
+template <MeshConcept MeshType, ItemType item_type>
 void
 GnuplotWriter1D::_writeItemDatas(const MeshType& mesh,
                                  const OutputNamedItemDataSet& output_named_item_data_set,
@@ -273,7 +275,7 @@ GnuplotWriter1D::_writeItemDatas(const MeshType& mesh,
   }
 }
 
-template <typename MeshType>
+template <MeshConcept MeshType>
 void
 GnuplotWriter1D::_write(const MeshType& mesh,
                         const OutputNamedItemDataSet& output_named_item_data_set,
@@ -348,7 +350,7 @@ GnuplotWriter1D::_write(const MeshType& mesh,
 }
 
 void
-GnuplotWriter1D::_writeMesh(const IMesh&) const
+GnuplotWriter1D::_writeMesh(const MeshVariant&) const
 {
   std::ostringstream errorMsg;
   errorMsg << "gnuplot_1d_writer does not write meshes\n"
@@ -358,47 +360,50 @@ GnuplotWriter1D::_writeMesh(const IMesh&) const
 }
 
 void
-GnuplotWriter1D::_writeAtTime(const IMesh& mesh,
+GnuplotWriter1D::_writeAtTime(const MeshVariant& mesh_v,
                               const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                               double time) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  case 2: {
-    std::ostringstream errorMsg;
-    errorMsg << "gnuplot_1d_writer is not available in dimension " << stringify(mesh.dimension()) << '\n'
-             << rang::style::bold << "note:" << rang::style::reset << " one can use " << rang::fgB::blue
-             << "gnuplot_writer" << rang::fg::reset << " in dimension 2";
-    throw NormalError(errorMsg.str());
-  }
-  default: {
-    throw NormalError("gnuplot format is not available in dimension " + stringify(mesh.dimension()));
-  }
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (MeshType::Dimension == 1) {
+        this->_write(*p_mesh, output_named_item_data_set, time);
+      } else if constexpr (MeshType::Dimension == 2) {
+        std::ostringstream errorMsg;
+        errorMsg << "gnuplot_1d_writer is not available in dimension " << stringify(MeshType::Dimension) << '\n'
+                 << rang::style::bold << "note:" << rang::style::reset << " one can use " << rang::fgB::blue
+                 << "gnuplot_writer" << rang::fg::reset << " in dimension 2";
+        throw NormalError(errorMsg.str());
+      } else {
+        throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
+      }
+    },
+    mesh_v.variant());
 }
 
 void
-GnuplotWriter1D::_write(const IMesh& mesh,
+GnuplotWriter1D::_write(const MeshVariant& mesh_v,
                         const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  default: {
-    throw NormalError("gnuplot format is not available in dimension " + stringify(mesh.dimension()));
-  }
-  }
+  std::visit(
+    [&](auto&& p_mesh) {
+      using MeshType = mesh_type_t<decltype(p_mesh)>;
+      if constexpr (MeshType::Dimension == 1) {
+        this->_write(*p_mesh, output_named_item_data_set, {});
+      } else if constexpr (MeshType::Dimension == 2) {
+        std::ostringstream errorMsg;
+        errorMsg << "gnuplot_1d_writer is not available in dimension " << stringify(MeshType::Dimension) << '\n'
+                 << rang::style::bold << "note:" << rang::style::reset << " one can use " << rang::fgB::blue
+                 << "gnuplot_writer" << rang::fg::reset << " in dimension 2";
+        throw NormalError(errorMsg.str());
+      } else {
+        throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
+      }
+    },
+    mesh_v.variant());
 }
diff --git a/src/output/GnuplotWriter1D.hpp b/src/output/GnuplotWriter1D.hpp
index fd0cac99ba56322100536483d14e78335015265d..66661918252856d8b6a069773c931c705a9b9330 100644
--- a/src/output/GnuplotWriter1D.hpp
+++ b/src/output/GnuplotWriter1D.hpp
@@ -5,9 +5,10 @@
 
 #include <algebra/TinyMatrix.hpp>
 #include <algebra/TinyVector.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <output/OutputNamedItemValueSet.hpp>
 
-class IMesh;
+class MeshVariant;
 
 #include <optional>
 #include <string>
@@ -37,26 +38,26 @@ class GnuplotWriter1D final : public WriterBase
   template <typename DataType, ItemType item_type>
   size_t _itemDataNbRow(const ItemArray<DataType, item_type>&) const;
 
-  template <typename MeshType, ItemType item_type>
+  template <MeshConcept MeshType, ItemType item_type>
   void _writeItemDatas(const MeshType& mesh,
                        const OutputNamedItemDataSet& output_named_item_data_set,
                        std::ostream& fout) const;
 
   void _writePreamble(const OutputNamedItemDataSet& output_named_item_value_set, std::ostream& fout) const;
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   void _write(const MeshType& mesh,
               const OutputNamedItemDataSet& output_named_item_value_set,
               std::optional<double> time) const;
 
-  void _writeAtTime(const IMesh& mesh,
+  void _writeAtTime(const MeshVariant& mesh_v,
                     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                     double time) const final;
 
-  void _write(const IMesh& mesh,
+  void _write(const MeshVariant& mesh_v,
               const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const final;
 
-  void _writeMesh(const IMesh& mesh) const final;
+  void _writeMesh(const MeshVariant& mesh_v) const final;
 
  public:
   GnuplotWriter1D(const std::string& base_filename) : WriterBase(base_filename) {}
diff --git a/src/output/IWriter.hpp b/src/output/IWriter.hpp
index 14c77330065fbf35b7edf707f110ae621e4b22bf..3d821fdacaaf12b3ec28560cf6197fde29e9494c 100644
--- a/src/output/IWriter.hpp
+++ b/src/output/IWriter.hpp
@@ -6,13 +6,13 @@
 #include <memory>
 #include <vector>
 
-class IMesh;
+class MeshVariant;
 
 class IWriter
 {
  public:
-  virtual void writeMesh(const std::shared_ptr<const IMesh>& mesh) const = 0;
-  virtual void writeMesh(const IMesh& mesh) const                        = 0;
+  virtual void writeMesh(const std::shared_ptr<const MeshVariant>& mesh_v) const = 0;
+  virtual void writeMesh(const MeshVariant& mesh_v) const                        = 0;
 
   virtual void write(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const = 0;
 
@@ -23,15 +23,15 @@ class IWriter
                            double time) const = 0;
 
   virtual void writeOnMesh(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const = 0;
 
   virtual void writeOnMeshIfNeeded(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh_v,
     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
     double time) const = 0;
 
-  virtual void writeOnMeshForced(const std::shared_ptr<const IMesh>& mesh,
+  virtual void writeOnMeshForced(const std::shared_ptr<const MeshVariant>& mesh_v,
                                  const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                                  double time) const = 0;
 
diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp
index 6ec6295e44b54bef9ea21c110120fdd873f71488..cd7834d713d2f9ca94d6dd65aa301ed444c6340f 100644
--- a/src/output/VTKWriter.cpp
+++ b/src/output/VTKWriter.cpp
@@ -4,6 +4,7 @@
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Filesystem.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/RevisionInfo.hpp>
@@ -354,7 +355,7 @@ VTKWriter::_write_node_data(std::ofstream& os,
   }
 }
 
-template <typename MeshType>
+template <MeshConcept MeshType>
 void
 VTKWriter::_write(const MeshType& mesh,
                   const OutputNamedItemDataSet& given_output_named_item_data_set,
@@ -682,76 +683,28 @@ VTKWriter::_write(const MeshType& mesh,
 }
 
 void
-VTKWriter::_writeMesh(const IMesh& mesh) const
+VTKWriter::_writeMesh(const MeshVariant& mesh_v) const
 {
   OutputNamedItemDataSet output_named_item_data_set;
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 3: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<3>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.variant());
 }
 
 void
-VTKWriter::_writeAtTime(const IMesh& mesh,
+VTKWriter::_writeAtTime(const MeshVariant& mesh_v,
                         const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                         double time) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  case 3: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<3>>&>(mesh), output_named_item_data_set, time);
-    break;
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, time); }, mesh_v.variant());
 }
 
 void
-VTKWriter::_write(const IMesh& mesh,
+VTKWriter::_write(const MeshVariant& mesh_v,
                   const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  switch (mesh.dimension()) {
-  case 1: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<1>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 2: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<2>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  case 3: {
-    this->_write(dynamic_cast<const Mesh<Connectivity<3>>&>(mesh), output_named_item_data_set, {});
-    break;
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.variant());
 }
diff --git a/src/output/VTKWriter.hpp b/src/output/VTKWriter.hpp
index 1fa439e94c8f1b48930df2835d68caa4030d7061..6c40f1005952d1649dcbd989f639907826a312fd 100644
--- a/src/output/VTKWriter.hpp
+++ b/src/output/VTKWriter.hpp
@@ -5,10 +5,9 @@
 
 #include <algebra/TinyMatrix.hpp>
 #include <algebra/TinyVector.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <output/OutputNamedItemValueSet.hpp>
 
-class IMesh;
-
 #include <optional>
 #include <string>
 
@@ -95,19 +94,19 @@ class VTKWriter final : public WriterBase
                         const ItemDataT& item_data,
                         SerializedDataList& serialized_data_list) const;
 
-  template <typename MeshType>
+  template <MeshConcept MeshType>
   void _write(const MeshType& mesh,
               const OutputNamedItemDataSet& output_named_item_data_set,
               std::optional<double> time) const;
 
-  void _writeAtTime(const IMesh& mesh,
+  void _writeAtTime(const MeshVariant& mesh_v,
                     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                     double time) const final;
 
-  void _write(const IMesh& mesh,
+  void _write(const MeshVariant& mesh_v,
               const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const final;
 
-  void _writeMesh(const IMesh& mesh) const final;
+  void _writeMesh(const MeshVariant& mesh_v) const final;
 
  public:
   VTKWriter(const std::string& base_filename) : WriterBase(base_filename) {}
diff --git a/src/output/WriterBase.cpp b/src/output/WriterBase.cpp
index a96da1e4e0a5e1eab52c4c3859dc9f0ae2304f08..bc7f90be252c42465ec3107f6f362f9658997909 100644
--- a/src/output/WriterBase.cpp
+++ b/src/output/WriterBase.cpp
@@ -1,8 +1,9 @@
 #include <output/WriterBase.hpp>
 
-#include <mesh/IMesh.hpp>
 #include <mesh/ItemArrayVariant.hpp>
 #include <mesh/ItemValueVariant.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <output/NamedDiscreteFunction.hpp>
 #include <output/NamedItemArrayVariant.hpp>
 #include <output/NamedItemValueVariant.hpp>
@@ -29,27 +30,14 @@ WriterBase::_registerDiscreteFunction(const std::string& name,
 
 void
 WriterBase::_checkConnectivity(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   Assert(named_discrete_data_list.size() > 0);
 
-  std::shared_ptr<const IConnectivity> connectivity = [&]() -> std::shared_ptr<const IConnectivity> {
-    switch (mesh->dimension()) {
-    case 1: {
-      return dynamic_cast<const Mesh<Connectivity<1>>&>(*mesh).shared_connectivity();
-    }
-    case 2: {
-      return dynamic_cast<const Mesh<Connectivity<2>>&>(*mesh).shared_connectivity();
-    }
-    case 3: {
-      return dynamic_cast<const Mesh<Connectivity<3>>&>(*mesh).shared_connectivity();
-    }
-    default: {
-      throw UnexpectedError("invalid dimension");
-    }
-    }
-  }();
+  std::shared_ptr<const IConnectivity> connectivity =
+    std::visit([&](auto&& p_mesh) -> std::shared_ptr<const IConnectivity> { return p_mesh->shared_connectivity(); },
+               mesh_v->variant());
 
   for (size_t i = 0; i < named_discrete_data_list.size(); ++i) {
     const auto& named_discrete_data = named_discrete_data_list[i];
@@ -142,11 +130,13 @@ WriterBase::_checkSignature(
 }
 
 void
-WriterBase::_checkMesh(const std::shared_ptr<const IMesh>& mesh,
+WriterBase::_checkMesh(const std::shared_ptr<const MeshVariant>& mesh_v,
                        const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   Assert(named_discrete_data_list.size() > 0);
 
+  const size_t mesh_id = std::visit([](auto&& p_mesh) { return p_mesh->id(); }, mesh_v->variant());
+
   for (size_t i = 0; i < named_discrete_data_list.size(); ++i) {
     const auto& named_discrete_data = named_discrete_data_list[i];
 
@@ -154,11 +144,11 @@ WriterBase::_checkMesh(const std::shared_ptr<const IMesh>& mesh,
       const NamedDiscreteFunction& named_discrete_function =
         dynamic_cast<const NamedDiscreteFunction&>(*named_discrete_data);
 
-      std::shared_ptr<const IMesh> discrete_function_mesh =
-        std::visit([](auto&& f) { return f.mesh(); },
+      const size_t discrete_function_mesh_id =
+        std::visit([](auto&& f) { return f.meshVariant()->id(); },
                    named_discrete_function.discreteFunctionVariant()->discreteFunction());
 
-      if (mesh != discrete_function_mesh) {
+      if (mesh_id != discrete_function_mesh_id) {
         std::ostringstream error_msg;
         error_msg << "The variable " << rang::fgB::yellow << named_discrete_function.name() << rang::fg::reset
                   << " is not defined on the provided mesh\n";
@@ -168,12 +158,14 @@ WriterBase::_checkMesh(const std::shared_ptr<const IMesh>& mesh,
   }
 }
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   Assert(named_discrete_data_list.size() > 0);
 
-  std::map<std::shared_ptr<const IMesh>, std::string> mesh_set;
+  std::shared_ptr<const MeshVariant> mesh_v;
+
+  std::map<size_t, std::string> mesh_id_to_function_name_map;
   std::map<std::shared_ptr<const IConnectivity>, std::string> connectivity_set;
 
   for (size_t i = 0; i < named_discrete_data_list.size(); ++i) {
@@ -184,30 +176,16 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
       const NamedDiscreteFunction& named_discrete_function =
         dynamic_cast<const NamedDiscreteFunction&>(*named_discrete_data);
 
-      std::shared_ptr mesh = std::visit([&](auto&& f) { return f.mesh(); },
-                                        named_discrete_function.discreteFunctionVariant()->discreteFunction());
-      mesh_set[mesh]       = named_discrete_function.name();
+      std::visit(
+        [&](auto&& f) {
+          mesh_id_to_function_name_map[f.meshVariant()->id()] = named_discrete_function.name();
+          if (not mesh_v) {
+            mesh_v = f.meshVariant();
+          }
+          connectivity_set[f.meshVariant()->shared_connectivity()] = named_discrete_function.name();
+        },
+        named_discrete_function.discreteFunctionVariant()->discreteFunction());
 
-      switch (mesh->dimension()) {
-      case 1: {
-        connectivity_set[dynamic_cast<const Mesh<Connectivity<1>>&>(*mesh).shared_connectivity()] =
-          named_discrete_function.name();
-        break;
-      }
-      case 2: {
-        connectivity_set[dynamic_cast<const Mesh<Connectivity<2>>&>(*mesh).shared_connectivity()] =
-          named_discrete_function.name();
-        break;
-      }
-      case 3: {
-        connectivity_set[dynamic_cast<const Mesh<Connectivity<3>>&>(*mesh).shared_connectivity()] =
-          named_discrete_function.name();
-        break;
-      }
-      default: {
-        throw UnexpectedError("invalid dimension");
-      }
-      }
       break;
     }
     case INamedDiscreteData::Type::item_value: {
@@ -233,8 +211,8 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
     }
   }
 
-  if (mesh_set.size() != 1) {
-    if (mesh_set.size() == 0) {
+  if (mesh_id_to_function_name_map.size() != 1) {
+    if (mesh_id_to_function_name_map.size() == 0) {
       throw NormalError("cannot find any mesh associated to output quantities");
     } else {
       std::ostringstream error_msg;
@@ -242,7 +220,7 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
                 << " in the same file!\n";
       error_msg << rang::fgB::yellow << "note:" << rang::fg::reset
                 << "the following variables are defined on different meshes:";
-      for (const auto& [mesh, name] : mesh_set) {
+      for (const auto& [mesh, name] : mesh_id_to_function_name_map) {
         error_msg << "\n- " << name;
       }
       throw NormalError(error_msg.str());
@@ -261,7 +239,7 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
     throw NormalError(error_msg.str());
   }
 
-  return mesh_set.begin()->first;
+  return mesh_v;
 }
 
 OutputNamedItemDataSet
@@ -334,7 +312,7 @@ WriterBase::write(const std::vector<std::shared_ptr<const INamedDiscreteData>>&
   if (m_period_manager.has_value()) {
     throw NormalError("this writer requires time value");
   } else {
-    std::shared_ptr<const IMesh> mesh = _getMesh(named_discrete_data_list);
+    std::shared_ptr<const MeshVariant> mesh = _getMesh(named_discrete_data_list);
     this->_write(*mesh, named_discrete_data_list);
   }
 }
@@ -349,7 +327,7 @@ WriterBase::writeIfNeeded(const std::vector<std::shared_ptr<const INamedDiscrete
       return;   // output already performed
 
     if (time >= m_period_manager->nextTime()) {
-      std::shared_ptr<const IMesh> mesh = _getMesh(named_discrete_data_list);
+      std::shared_ptr<const MeshVariant> mesh = _getMesh(named_discrete_data_list);
       this->_checkSignature(named_discrete_data_list);
       this->_writeAtTime(*mesh, named_discrete_data_list, time);
       m_period_manager->setSaveTime(time);
@@ -367,7 +345,7 @@ WriterBase::writeForced(const std::vector<std::shared_ptr<const INamedDiscreteDa
     if (time == m_period_manager->getLastTime())
       return;   // output already performed
 
-    std::shared_ptr<const IMesh> mesh = _getMesh(named_discrete_data_list);
+    std::shared_ptr<const MeshVariant> mesh = _getMesh(named_discrete_data_list);
     this->_checkSignature(named_discrete_data_list);
     this->_writeAtTime(*mesh, named_discrete_data_list, time);
     m_period_manager->setSaveTime(time);
@@ -377,7 +355,7 @@ WriterBase::writeForced(const std::vector<std::shared_ptr<const INamedDiscreteDa
 }
 
 void
-WriterBase::writeOnMesh(const std::shared_ptr<const IMesh>& mesh,
+WriterBase::writeOnMesh(const std::shared_ptr<const MeshVariant>& mesh,
                         const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const
 {
   if (m_period_manager.has_value()) {
@@ -390,7 +368,7 @@ WriterBase::writeOnMesh(const std::shared_ptr<const IMesh>& mesh,
 }
 
 void
-WriterBase::writeOnMeshIfNeeded(const std::shared_ptr<const IMesh>& mesh,
+WriterBase::writeOnMeshIfNeeded(const std::shared_ptr<const MeshVariant>& mesh,
                                 const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                                 double time) const
 {
@@ -409,7 +387,7 @@ WriterBase::writeOnMeshIfNeeded(const std::shared_ptr<const IMesh>& mesh,
 }
 
 void
-WriterBase::writeOnMeshForced(const std::shared_ptr<const IMesh>& mesh,
+WriterBase::writeOnMeshForced(const std::shared_ptr<const MeshVariant>& mesh,
                               const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                               double time) const
 {
@@ -427,13 +405,13 @@ WriterBase::writeOnMeshForced(const std::shared_ptr<const IMesh>& mesh,
 }
 
 void
-WriterBase::writeMesh(const std::shared_ptr<const IMesh>& mesh) const
+WriterBase::writeMesh(const std::shared_ptr<const MeshVariant>& mesh) const
 {
   writeMesh(*mesh);
 }
 
 void
-WriterBase::writeMesh(const IMesh& mesh) const
+WriterBase::writeMesh(const MeshVariant& mesh) const
 {
   if (m_period_manager.has_value()) {
     throw NormalError("write_mesh requires a writer without time period");
diff --git a/src/output/WriterBase.hpp b/src/output/WriterBase.hpp
index 707ac52d3a3c69c749c46047bd594b0be46c408b..1964068a0222bfb35304d30afab2861f3cb97383 100644
--- a/src/output/WriterBase.hpp
+++ b/src/output/WriterBase.hpp
@@ -8,7 +8,7 @@
 #include <optional>
 #include <string>
 
-class IMesh;
+class MeshVariant;
 class OutputNamedItemDataSet;
 class NamedDiscreteFunction;
 
@@ -94,26 +94,26 @@ class WriterBase : public IWriter
   static void _registerDiscreteFunction(const std::string& name, const DiscreteFunctionType&, OutputNamedItemDataSet&);
 
  protected:
-  void _checkConnectivity(const std::shared_ptr<const IMesh>& mesh,
+  void _checkConnectivity(const std::shared_ptr<const MeshVariant>& mesh_v,
                           const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const;
 
-  void _checkMesh(const std::shared_ptr<const IMesh>& mesh,
+  void _checkMesh(const std::shared_ptr<const MeshVariant>& mesh_v,
                   const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const;
 
-  std::shared_ptr<const IMesh> _getMesh(
+  std::shared_ptr<const MeshVariant> _getMesh(
     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const;
 
   OutputNamedItemDataSet _getOutputNamedItemDataSet(
     const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const;
 
-  virtual void _writeAtTime(const IMesh& mesh,
+  virtual void _writeAtTime(const MeshVariant& mesh_v,
                             const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                             double time) const = 0;
 
-  virtual void _write(const IMesh& mesh,
+  virtual void _write(const MeshVariant& mesh_v,
                       const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const = 0;
 
-  virtual void _writeMesh(const IMesh& mesh) const = 0;
+  virtual void _writeMesh(const MeshVariant& mesh_v) const = 0;
 
  public:
   void write(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const final;
@@ -124,18 +124,18 @@ class WriterBase : public IWriter
   void writeForced(const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                    double time) const final;
 
-  void writeOnMesh(const std::shared_ptr<const IMesh>& mesh,
+  void writeOnMesh(const std::shared_ptr<const MeshVariant>& mesh_v,
                    const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list) const final;
 
-  void writeOnMeshIfNeeded(const std::shared_ptr<const IMesh>& mesh,
+  void writeOnMeshIfNeeded(const std::shared_ptr<const MeshVariant>& mesh_v,
                            const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                            double time) const final;
-  void writeOnMeshForced(const std::shared_ptr<const IMesh>& mesh,
+  void writeOnMeshForced(const std::shared_ptr<const MeshVariant>& mesh_v,
                          const std::vector<std::shared_ptr<const INamedDiscreteData>>& named_discrete_data_list,
                          double time) const final;
 
-  void writeMesh(const std::shared_ptr<const IMesh>& mesh) const final;
-  void writeMesh(const IMesh& mesh) const final;
+  void writeMesh(const std::shared_ptr<const MeshVariant>& mesh_v) const final;
+  void writeMesh(const MeshVariant& mesh_v) const final;
 
   WriterBase() = delete;
 
diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp
index f61f27cb46c4ed9dbee0481c433e6f9aa3ea4da0..90998d3aba2e9ecf0a9b5c4e8961c7b87889eaa5 100644
--- a/src/scheme/AcousticSolver.cpp
+++ b/src/scheme/AcousticSolver.cpp
@@ -6,6 +6,8 @@
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshFlatNodeBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <mesh/SubItemValuePerItemVariant.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
@@ -20,55 +22,45 @@
 #include <variant>
 #include <vector>
 
-template <size_t Dimension>
 double
-acoustic_dt(const DiscreteFunctionP0<Dimension, const double>& c)
+acoustic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c_v)
 {
-  const Mesh<Connectivity<Dimension>>& mesh = dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*c.mesh());
+  const auto& c = c_v->get<DiscreteFunctionP0<const double>>();
 
-  const auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj();
-  const auto Sj = MeshDataManager::instance().getMeshData(mesh).sumOverRLjr();
+  return std::visit(
+    [&](auto&& p_mesh) -> double {
+      const auto& mesh = *p_mesh;
 
-  CellValue<double> local_dt{mesh.connectivity()};
-  parallel_for(
-    mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); });
+      using MeshType = decltype(mesh);
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        const auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj();
+        const auto Sj = MeshDataManager::instance().getMeshData(mesh).sumOverRLjr();
 
-  return min(local_dt);
-}
+        CellValue<double> local_dt{mesh.connectivity()};
+        parallel_for(
+          mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); });
 
-double
-acoustic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c)
-{
-  std::shared_ptr mesh = getCommonMesh({c});
-
-  switch (mesh->dimension()) {
-  case 1: {
-    return acoustic_dt(c->get<DiscreteFunctionP0<1, const double>>());
-  }
-  case 2: {
-    return acoustic_dt(c->get<DiscreteFunctionP0<2, const double>>());
-  }
-  case 3: {
-    return acoustic_dt(c->get<DiscreteFunctionP0<3, const double>>());
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+        return min(local_dt);
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    c.meshVariant()->variant());
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler::IAcousticSolver
 {
  private:
+  static constexpr size_t Dimension = MeshType::Dimension;
+
   using Rdxd = TinyMatrix<Dimension>;
   using Rd   = TinyVector<Dimension>;
 
-  using MeshType     = Mesh<Connectivity<Dimension>>;
-  using MeshDataType = MeshData<Dimension>;
+  using MeshDataType = MeshData<MeshType>;
 
-  using DiscreteScalarFunction = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteVectorFunction = DiscreteFunctionP0<Dimension, const Rd>;
+  using DiscreteScalarFunction = DiscreteFunctionP0<const double>;
+  using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>;
 
   class FixedBoundaryCondition;
   class PressureBoundaryCondition;
@@ -200,7 +192,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   }
 
   NodeValue<Rd>
-  _computeBr(const Mesh<Connectivity<Dimension>>& mesh,
+  _computeBr(const Mesh<Dimension>& mesh,
              const NodeValuePerCell<const Rdxd>& Ajr,
              const DiscreteVectorFunction& u,
              const DiscreteScalarFunction& p) const
@@ -267,8 +259,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
         const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
           dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
         if (dirichlet_bc_descriptor.name() == "velocity") {
-          MeshNodeBoundary<Dimension> mesh_node_boundary =
-            getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor());
+          MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor());
 
           Array<const Rd> value_list =
             InterpolateItemValue<Rd(Rd)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(),
@@ -280,17 +271,15 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
           const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId();
 
           if constexpr (Dimension == 1) {
-            MeshNodeBoundary<Dimension> mesh_node_boundary =
-              getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             Array<const double> node_values =
               InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh.xr(),
                                                                                      mesh_node_boundary.nodeList());
-
-            bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary, node_values});
+            PressureBoundaryCondition p(mesh_node_boundary, node_values);
+            bc_list.emplace_back(p);
           } else {
-            MeshFaceBoundary<Dimension> mesh_face_boundary =
-              getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
             Array<const double> face_values =
@@ -391,7 +380,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
       throw NormalError("acoustic solver expects P0 functions");
     }
 
-    const MeshType& mesh              = dynamic_cast<const MeshType&>(*i_mesh);
+    const MeshType& mesh              = *i_mesh->get<MeshType>();
     const DiscreteScalarFunction& rho = rho_v->get<DiscreteScalarFunction>();
     const DiscreteScalarFunction& c   = c_v->get<DiscreteScalarFunction>();
     const DiscreteVectorFunction& u   = u_v->get<DiscreteVectorFunction>();
@@ -416,7 +405,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
                            std::make_shared<const SubItemValuePerItemVariant>(Fjr));
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>>
@@ -468,12 +457,13 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
     parallel_for(
       mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; });
 
-    return {new_mesh, std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)),
+    return {std::make_shared<MeshVariant>(new_mesh),
+            std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)),
             std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)),
             std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E))};
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>>
@@ -493,16 +483,16 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
       throw NormalError("acoustic solver expects P0 functions");
     }
 
-    return this->apply_fluxes(dt,                                       //
-                              dynamic_cast<const MeshType&>(*i_mesh),   //
-                              rho_v->get<DiscreteScalarFunction>(),     //
-                              u_v->get<DiscreteVectorFunction>(),       //
-                              E_v->get<DiscreteScalarFunction>(),       //
-                              ur->get<NodeValue<const Rd>>(),           //
+    return this->apply_fluxes(dt,                                     //
+                              *i_mesh->get<MeshType>(),               //
+                              rho_v->get<DiscreteScalarFunction>(),   //
+                              u_v->get<DiscreteVectorFunction>(),     //
+                              E_v->get<DiscreteScalarFunction>(),     //
+                              ur->get<NodeValue<const Rd>>(),         //
                               Fjr->get<NodeValuePerCell<const Rd>>());
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>>
@@ -524,18 +514,18 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   ~AcousticSolver()                = default;
 };
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-AcousticSolverHandler::AcousticSolver<Dimension>::_applyPressureBC(const BoundaryConditionList& bc_list,
-                                                                   const MeshType& mesh,
-                                                                   NodeValue<Rd>& br) const
+AcousticSolverHandler::AcousticSolver<MeshType>::_applyPressureBC(const BoundaryConditionList& bc_list,
+                                                                  const MeshType& mesh,
+                                                                  NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<PressureBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();
 
@@ -589,11 +579,11 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyPressureBC(const Boundar
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-AcousticSolverHandler::AcousticSolver<Dimension>::_applySymmetryBC(const BoundaryConditionList& bc_list,
-                                                                   NodeValue<Rdxd>& Ar,
-                                                                   NodeValue<Rd>& br) const
+AcousticSolverHandler::AcousticSolver<MeshType>::_applySymmetryBC(const BoundaryConditionList& bc_list,
+                                                                  NodeValue<Rdxd>& Ar,
+                                                                  NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
@@ -620,11 +610,11 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applySymmetryBC(const Boundar
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-AcousticSolverHandler::AcousticSolver<Dimension>::_applyVelocityBC(const BoundaryConditionList& bc_list,
-                                                                   NodeValue<Rdxd>& Ar,
-                                                                   NodeValue<Rd>& br) const
+AcousticSolverHandler::AcousticSolver<MeshType>::_applyVelocityBC(const BoundaryConditionList& bc_list,
+                                                                  NodeValue<Rdxd>& Ar,
+                                                                  NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
@@ -657,12 +647,12 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyVelocityBC(const Boundar
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-AcousticSolverHandler::AcousticSolver<Dimension>::_applyExternalVelocityBC(const BoundaryConditionList& bc_list,
-                                                                           const DiscreteScalarFunction& p,
-                                                                           NodeValue<Rdxd>& Ar,
-                                                                           NodeValue<Rd>& br) const
+AcousticSolverHandler::AcousticSolver<MeshType>::_applyExternalVelocityBC(const BoundaryConditionList& bc_list,
+                                                                          const DiscreteScalarFunction& p,
+                                                                          NodeValue<Rdxd>& Ar,
+                                                                          NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
@@ -686,11 +676,11 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyExternalVelocityBC(const
   }
 }
 
-template <size_t Dimension>
-class AcousticSolverHandler::AcousticSolver<Dimension>::FixedBoundaryCondition
+template <MeshConcept MeshType>
+class AcousticSolverHandler::AcousticSolver<MeshType>::FixedBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
  public:
   const Array<const NodeId>&
@@ -699,18 +689,16 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::FixedBoundaryCondition
     return m_mesh_node_boundary.nodeList();
   }
 
-  FixedBoundaryCondition(const MeshNodeBoundary<Dimension> mesh_node_boundary)
-    : m_mesh_node_boundary{mesh_node_boundary}
-  {}
+  FixedBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
 
   ~FixedBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class AcousticSolverHandler::AcousticSolver<Dimension>::PressureBoundaryCondition
+template <MeshConcept MeshType>
+class AcousticSolverHandler::AcousticSolver<MeshType>::PressureBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
   const Array<const double> m_value_list;
 
  public:
@@ -726,8 +714,7 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::PressureBoundaryConditio
     return m_value_list;
   }
 
-  PressureBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary,
-                            const Array<const double>& value_list)
+  PressureBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const double>& value_list)
     : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list}
   {}
 
@@ -735,10 +722,10 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::PressureBoundaryConditio
 };
 
 template <>
-class AcousticSolverHandler::AcousticSolver<1>::PressureBoundaryCondition
+class AcousticSolverHandler::AcousticSolver<Mesh<1>>::PressureBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<1> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
   const Array<const double> m_value_list;
 
  public:
@@ -754,19 +741,19 @@ class AcousticSolverHandler::AcousticSolver<1>::PressureBoundaryCondition
     return m_value_list;
   }
 
-  PressureBoundaryCondition(const MeshNodeBoundary<1>& mesh_node_boundary, const Array<const double>& value_list)
+  PressureBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const double>& value_list)
     : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list}
   {}
 
   ~PressureBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBoundaryCondition
+template <MeshConcept MeshType>
+class AcousticSolverHandler::AcousticSolver<MeshType>::ExternalFSIVelocityBoundaryCondition
 {
  private:
   const ItemToItemMatrix<ItemType::node, ItemType::cell> m_node_to_cell_matrix;
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
   const Array<TinyVector<Dimension>> m_value_list;
   const std::shared_ptr<const Socket> m_socket;
 
@@ -794,8 +781,8 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBound
     return m_value_list;
   }
 
-  ExternalFSIVelocityBoundaryCondition(const Mesh<Connectivity<Dimension>>& mesh,
-                                       const MeshNodeBoundary<Dimension>& mesh_node_boundary,
+  ExternalFSIVelocityBoundaryCondition(const Mesh<Dimension>& mesh,
+                                       const MeshNodeBoundary& mesh_node_boundary,
                                        const std::shared_ptr<const Socket>& socket)
     : m_node_to_cell_matrix{mesh.connectivity().nodeToCellMatrix()},
       m_mesh_node_boundary{mesh_node_boundary},
@@ -810,11 +797,11 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBound
   ~ExternalFSIVelocityBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryCondition
+template <MeshConcept MeshType>
+class AcousticSolverHandler::AcousticSolver<MeshType>::VelocityBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
   const Array<const TinyVector<Dimension>> m_value_list;
 
@@ -831,7 +818,7 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryConditio
     return m_value_list;
   }
 
-  VelocityBoundaryCondition(const MeshNodeBoundary<Dimension>& mesh_node_boundary,
+  VelocityBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary,
                             const Array<const TinyVector<Dimension>>& value_list)
     : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list}
   {}
@@ -839,14 +826,14 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryConditio
   ~VelocityBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class AcousticSolverHandler::AcousticSolver<Dimension>::SymmetryBoundaryCondition
+template <MeshConcept MeshType>
+class AcousticSolverHandler::AcousticSolver<MeshType>::SymmetryBoundaryCondition
 {
  public:
   using Rd = TinyVector<Dimension, double>;
 
  private:
-  const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+  const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary;
 
  public:
   const Rd&
@@ -867,7 +854,7 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::SymmetryBoundaryConditio
     return m_mesh_flat_node_boundary.nodeList();
   }
 
-  SymmetryBoundaryCondition(const MeshFlatNodeBoundary<Dimension>& mesh_flat_node_boundary)
+  SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary)
     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
   {
     ;
@@ -876,27 +863,20 @@ class AcousticSolverHandler::AcousticSolver<Dimension>::SymmetryBoundaryConditio
   ~SymmetryBoundaryCondition() = default;
 };
 
-AcousticSolverHandler::AcousticSolverHandler(const std::shared_ptr<const IMesh>& i_mesh)
+AcousticSolverHandler::AcousticSolverHandler(const std::shared_ptr<const MeshVariant>& i_mesh)
 {
   if (not i_mesh) {
     throw NormalError("discrete functions are not defined on the same mesh");
   }
 
-  switch (i_mesh->dimension()) {
-  case 1: {
-    m_acoustic_solver = std::make_unique<AcousticSolver<1>>();
-    break;
-  }
-  case 2: {
-    m_acoustic_solver = std::make_unique<AcousticSolver<2>>();
-    break;
-  }
-  case 3: {
-    m_acoustic_solver = std::make_unique<AcousticSolver<3>>();
-    break;
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        m_acoustic_solver = std::make_unique<AcousticSolver<MeshType>>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    i_mesh->variant());
 }
diff --git a/src/scheme/AcousticSolver.hpp b/src/scheme/AcousticSolver.hpp
index 36b62a32a1442b18cbe014a9e90ee28ffad08c8a..73f9522ba12ebb5f2570486a1bcb3a108b6b4270 100644
--- a/src/scheme/AcousticSolver.hpp
+++ b/src/scheme/AcousticSolver.hpp
@@ -1,13 +1,15 @@
 #ifndef ACOUSTIC_SOLVER_HPP
 #define ACOUSTIC_SOLVER_HPP
 
+#include <mesh/MeshTraits.hpp>
+
 #include <memory>
 #include <tuple>
 #include <vector>
 
 class DiscreteFunctionVariant;
 class IBoundaryConditionDescriptor;
-class IMesh;
+class MeshVariant;
 class ItemValueVariant;
 class SubItemValuePerItemVariant;
 
@@ -35,7 +37,7 @@ class AcousticSolverHandler
       const std::shared_ptr<const DiscreteFunctionVariant>& p,
       const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0;
 
-    virtual std::tuple<std::shared_ptr<const IMesh>,
+    virtual std::tuple<std::shared_ptr<const MeshVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>>
@@ -46,7 +48,7 @@ class AcousticSolverHandler
                  const std::shared_ptr<const ItemValueVariant>& ur,
                  const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr) const = 0;
 
-    virtual std::tuple<std::shared_ptr<const IMesh>,
+    virtual std::tuple<std::shared_ptr<const MeshVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>>
@@ -66,7 +68,7 @@ class AcousticSolverHandler
     virtual ~IAcousticSolver() = default;
   };
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   class AcousticSolver;
 
   std::unique_ptr<IAcousticSolver> m_acoustic_solver;
@@ -78,7 +80,7 @@ class AcousticSolverHandler
     return *m_acoustic_solver;
   }
 
-  AcousticSolverHandler(const std::shared_ptr<const IMesh>& mesh);
+  AcousticSolverHandler(const std::shared_ptr<const MeshVariant>& mesh_v);
 };
 
 #endif   // ACOUSTIC_SOLVER_HPP
diff --git a/src/scheme/CellIntegrator.hpp b/src/scheme/CellIntegrator.hpp
index 26544b7c4f742dee16295922d9a9206320847844..33241f9b8683700d24507000a50ed0f0ae507517 100644
--- a/src/scheme/CellIntegrator.hpp
+++ b/src/scheme/CellIntegrator.hpp
@@ -75,7 +75,7 @@ class CellIntegrator
     CellList(const ArrayT<CellId>& cell_list) : m_cell_list{cell_list} {}
   };
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   _tensorialIntegrateTo(std::function<OutputType(InputType)> function,
                         const IQuadratureDescriptor& quadrature_descriptor,
@@ -303,7 +303,7 @@ class CellIntegrator
     // LCOV_EXCL_STOP
   }
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   _directIntegrateTo(std::function<OutputType(InputType)> function,
                      const IQuadratureDescriptor& quadrature_descriptor,
@@ -514,7 +514,7 @@ class CellIntegrator
   }
 
  public:
-  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   integrateTo(const std::function<OutputType(InputType)>& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -532,7 +532,7 @@ class CellIntegrator
     }
   }
 
-  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename FunctionType>
   static PUGS_INLINE void
   integrateTo(const FunctionType& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -542,7 +542,7 @@ class CellIntegrator
     integrateTo(std::function(function), quadrature_descriptor, mesh, value);
   }
 
-  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE ArrayT<OutputType>
   integrate(const std::function<OutputType(InputType)>& function,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -561,7 +561,7 @@ class CellIntegrator
     return value;
   }
 
-  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE auto
   integrate(const FunctionType& function,
             const IQuadratureDescriptor& quadrature_descriptor,
diff --git a/src/scheme/DiscreteFunctionIntegrator.cpp b/src/scheme/DiscreteFunctionIntegrator.cpp
index c77f7a2c87ece74da4f48902855e9f3a5f6742bc..5caf8e5012a835417a99017c3625e9934092e748 100644
--- a/src/scheme/DiscreteFunctionIntegrator.cpp
+++ b/src/scheme/DiscreteFunctionIntegrator.cpp
@@ -2,20 +2,22 @@
 
 #include <language/utils/IntegrateCellValue.hpp>
 #include <mesh/MeshCellZone.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::_integrateOnZoneList() const
 {
   static_assert(std::is_convertible_v<DataType, ValueType>);
   Assert(m_zone_list.size() > 0, "no zone list provided");
 
-  using MeshType = Mesh<Connectivity<Dimension>>;
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(m_mesh);
+  std::shared_ptr p_mesh = m_mesh->get<MeshType>();
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -62,37 +64,36 @@ DiscreteFunctionIntegrator::_integrateOnZoneList() const
   parallel_for(
     zone_cell_list.size(), PUGS_LAMBDA(const size_t i_cell) { cell_value[zone_cell_list[i_cell]] = array[i_cell]; });
 
-  return DiscreteFunctionP0<Dimension, ValueType>(p_mesh, cell_value);
+  return DiscreteFunctionP0<ValueType>(m_mesh, cell_value);
 }
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::_integrateGlobally() const
 {
   Assert(m_zone_list.size() == 0, "invalid call when zones are defined");
 
-  using MeshType       = Mesh<Connectivity<Dimension>>;
-  std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(m_mesh);
+  std::shared_ptr mesh = m_mesh->get<MeshType>();
 
   static_assert(std::is_convertible_v<DataType, ValueType>);
 
-  return DiscreteFunctionP0<Dimension,
-                            ValueType>(mesh, IntegrateCellValue<ValueType(TinyVector<Dimension>)>::template integrate<
-                                               MeshType>(m_function_id, *m_quadrature_descriptor, *mesh));
+  return DiscreteFunctionP0<ValueType>(m_mesh,
+                                       IntegrateCellValue<ValueType(TinyVector<MeshType::Dimension>)>::
+                                         template integrate<MeshType>(m_function_id, *m_quadrature_descriptor, *mesh));
 }
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::_integrate() const
 {
   if (m_zone_list.size() == 0) {
-    return this->_integrateGlobally<Dimension, DataType, ValueType>();
+    return this->_integrateGlobally<MeshType, DataType, ValueType>();
   } else {
-    return this->_integrateOnZoneList<Dimension, DataType, ValueType>();
+    return this->_integrateOnZoneList<MeshType, DataType, ValueType>();
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::_integrate() const
 {
@@ -103,27 +104,27 @@ DiscreteFunctionIntegrator::_integrate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_integrate<Dimension, bool, double>();
+    return this->_integrate<MeshType, bool, double>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_integrate<Dimension, uint64_t, double>();
+    return this->_integrate<MeshType, uint64_t, double>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_integrate<Dimension, int64_t, double>();
+    return this->_integrate<MeshType, int64_t, double>();
   }
   case ASTNodeDataType::double_t: {
-    return this->_integrate<Dimension, double>();
+    return this->_integrate<MeshType, double>();
   }
   case ASTNodeDataType::vector_t: {
     switch (data_type.dimension()) {
     case 1: {
-      return this->_integrate<Dimension, TinyVector<1>>();
+      return this->_integrate<MeshType, TinyVector<1>>();
     }
     case 2: {
-      return this->_integrate<Dimension, TinyVector<2>>();
+      return this->_integrate<MeshType, TinyVector<2>>();
     }
     case 3: {
-      return this->_integrate<Dimension, TinyVector<3>>();
+      return this->_integrate<MeshType, TinyVector<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -139,13 +140,13 @@ DiscreteFunctionIntegrator::_integrate() const
     Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
     switch (data_type.numberOfColumns()) {
     case 1: {
-      return this->_integrate<Dimension, TinyMatrix<1>>();
+      return this->_integrate<MeshType, TinyMatrix<1>>();
     }
     case 2: {
-      return this->_integrate<Dimension, TinyMatrix<2>>();
+      return this->_integrate<MeshType, TinyMatrix<2>>();
     }
     case 3: {
-      return this->_integrate<Dimension, TinyMatrix<3>>();
+      return this->_integrate<MeshType, TinyMatrix<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -171,20 +172,14 @@ DiscreteFunctionIntegrator::_integrate() const
 DiscreteFunctionVariant
 DiscreteFunctionIntegrator::integrate() const
 {
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_integrate<1>();
-  }
-  case 2: {
-    return this->_integrate<2>();
-  }
-  case 3: {
-    return this->_integrate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_integrate<MeshType>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionIntegrator.hpp b/src/scheme/DiscreteFunctionIntegrator.hpp
index e21beabf704b6ed1a7cd3ef4277f751036d274da..f294d39e1c75cd10090cde9414bed8288c9fee48 100644
--- a/src/scheme/DiscreteFunctionIntegrator.hpp
+++ b/src/scheme/DiscreteFunctionIntegrator.hpp
@@ -3,43 +3,44 @@
 
 #include <analysis/IQuadratureDescriptor.hpp>
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 
 #include <memory>
 
 class DiscreteFunctionVariant;
+class MeshVariant;
 
 class DiscreteFunctionIntegrator
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
   const std::vector<std::shared_ptr<const IZoneDescriptor>> m_zone_list;
   std::shared_ptr<const IQuadratureDescriptor> m_quadrature_descriptor;
   const FunctionSymbolId m_function_id;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _integrateOnZoneList() const;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _integrateGlobally() const;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _integrate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   DiscreteFunctionVariant _integrate() const;
 
  public:
   DiscreteFunctionVariant integrate() const;
 
-  DiscreteFunctionIntegrator(const std::shared_ptr<const IMesh>& mesh,
+  DiscreteFunctionIntegrator(const std::shared_ptr<const MeshVariant>& mesh,
                              const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
                              const FunctionSymbolId& function_id)
     : m_mesh{mesh}, m_quadrature_descriptor{quadrature_descriptor}, m_function_id{function_id}
   {}
 
-  DiscreteFunctionIntegrator(const std::shared_ptr<const IMesh>& mesh,
+  DiscreteFunctionIntegrator(const std::shared_ptr<const MeshVariant>& mesh,
                              const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_list,
                              const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
                              const FunctionSymbolId& function_id)
diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp
index c570e67e5ae9b417a60096e5ccb34a025e7a94a4..8f2e0eef738402c67275fdc0a66ff96fa1de744e 100644
--- a/src/scheme/DiscreteFunctionInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionInterpoler.cpp
@@ -2,20 +2,23 @@
 
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/MeshCellZone.hpp>
+#include <mesh/MeshTraits.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionInterpoler::_interpolateOnZoneList() const
 {
   static_assert(std::is_convertible_v<DataType, ValueType>);
   Assert(m_zone_list.size() > 0, "no zone list provided");
 
-  std::shared_ptr p_mesh  = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  constexpr size_t Dimension = MeshType::Dimension;
+
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -62,23 +65,23 @@ DiscreteFunctionInterpoler::_interpolateOnZoneList() const
   parallel_for(
     zone_cell_list.size(), PUGS_LAMBDA(const size_t i_cell) { cell_value[zone_cell_list[i_cell]] = array[i_cell]; });
 
-  return DiscreteFunctionP0<Dimension, ValueType>(p_mesh, cell_value);
+  return DiscreteFunctionP0<ValueType>{m_mesh, cell_value};
 }
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionInterpoler::_interpolateGlobally() const
 {
   Assert(m_zone_list.size() == 0, "invalid call when zones are defined");
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh  = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   if constexpr (std::is_same_v<DataType, ValueType>) {
-    return DiscreteFunctionP0<Dimension, ValueType>(p_mesh, InterpolateItemValue<DataType(TinyVector<Dimension>)>::
-                                                              template interpolate<ItemType::cell>(m_function_id,
-                                                                                                   mesh_data.xj()));
+    return DiscreteFunctionP0<ValueType>(m_mesh,
+                                         InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<
+                                           ItemType::cell>(m_function_id, mesh_data.xj()));
   } else {
     static_assert(std::is_convertible_v<DataType, ValueType>);
 
@@ -91,22 +94,22 @@ DiscreteFunctionInterpoler::_interpolateGlobally() const
     parallel_for(
       p_mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_data[cell_id]; });
 
-    return DiscreteFunctionP0<Dimension, ValueType>(p_mesh, cell_value);
+    return DiscreteFunctionP0<ValueType>(m_mesh, cell_value);
   }
 }
 
-template <size_t Dimension, typename DataType, typename ValueType>
+template <MeshConcept MeshType, typename DataType, typename ValueType>
 DiscreteFunctionVariant
 DiscreteFunctionInterpoler::_interpolate() const
 {
   if (m_zone_list.size() == 0) {
-    return this->_interpolateGlobally<Dimension, DataType, ValueType>();
+    return this->_interpolateGlobally<MeshType, DataType, ValueType>();
   } else {
-    return this->_interpolateOnZoneList<Dimension, DataType, ValueType>();
+    return this->_interpolateOnZoneList<MeshType, DataType, ValueType>();
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 DiscreteFunctionVariant
 DiscreteFunctionInterpoler::_interpolate() const
 {
@@ -117,27 +120,27 @@ DiscreteFunctionInterpoler::_interpolate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_interpolate<Dimension, bool, double>();
+    return this->_interpolate<MeshType, bool, double>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_interpolate<Dimension, uint64_t, double>();
+    return this->_interpolate<MeshType, uint64_t, double>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_interpolate<Dimension, int64_t, double>();
+    return this->_interpolate<MeshType, int64_t, double>();
   }
   case ASTNodeDataType::double_t: {
-    return this->_interpolate<Dimension, double>();
+    return this->_interpolate<MeshType, double>();
   }
   case ASTNodeDataType::vector_t: {
     switch (data_type.dimension()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyVector<1>>();
+      return this->_interpolate<MeshType, TinyVector<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyVector<2>>();
+      return this->_interpolate<MeshType, TinyVector<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyVector<3>>();
+      return this->_interpolate<MeshType, TinyVector<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -153,13 +156,13 @@ DiscreteFunctionInterpoler::_interpolate() const
     Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
     switch (data_type.numberOfColumns()) {
     case 1: {
-      return this->_interpolate<Dimension, TinyMatrix<1>>();
+      return this->_interpolate<MeshType, TinyMatrix<1>>();
     }
     case 2: {
-      return this->_interpolate<Dimension, TinyMatrix<2>>();
+      return this->_interpolate<MeshType, TinyMatrix<2>>();
     }
     case 3: {
-      return this->_interpolate<Dimension, TinyMatrix<3>>();
+      return this->_interpolate<MeshType, TinyMatrix<3>>();
     }
       // LCOV_EXCL_START
     default: {
@@ -185,20 +188,15 @@ DiscreteFunctionInterpoler::_interpolate() const
 DiscreteFunctionVariant
 DiscreteFunctionInterpoler::interpolate() const
 {
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_interpolate<1>();
-  }
-  case 2: {
-    return this->_interpolate<2>();
-  }
-  case 3: {
-    return this->_interpolate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_interpolate<MeshType>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionInterpoler.hpp b/src/scheme/DiscreteFunctionInterpoler.hpp
index 0f436d43cf2c347fc437e05016d798bc28aeed77..95350e34c42f851fedb2c3bbf7420140522d36ef 100644
--- a/src/scheme/DiscreteFunctionInterpoler.hpp
+++ b/src/scheme/DiscreteFunctionInterpoler.hpp
@@ -2,44 +2,45 @@
 #define DISCRETE_FUNCTION_INTERPOLER_HPP
 
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 
 class DiscreteFunctionVariant;
+class MeshVariant;
 
 #include <memory>
 
 class DiscreteFunctionInterpoler
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
   const std::vector<std::shared_ptr<const IZoneDescriptor>> m_zone_list;
   std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor;
   const FunctionSymbolId m_function_id;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _interpolateOnZoneList() const;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _interpolateGlobally() const;
 
-  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  template <MeshConcept MeshType, typename DataType, typename ValueType = DataType>
   DiscreteFunctionVariant _interpolate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   DiscreteFunctionVariant _interpolate() const;
 
  public:
   DiscreteFunctionVariant interpolate() const;
 
-  DiscreteFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh,
+  DiscreteFunctionInterpoler(const std::shared_ptr<const MeshVariant>& mesh,
                              const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
                              const FunctionSymbolId& function_id)
     : m_mesh{mesh}, m_discrete_function_descriptor{discrete_function_descriptor}, m_function_id{function_id}
   {}
 
-  DiscreteFunctionInterpoler(const std::shared_ptr<const IMesh>& mesh,
+  DiscreteFunctionInterpoler(const std::shared_ptr<const MeshVariant>& mesh,
                              const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_list,
                              const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
                              const FunctionSymbolId& function_id)
diff --git a/src/scheme/DiscreteFunctionP0.hpp b/src/scheme/DiscreteFunctionP0.hpp
index 5a49b9db3d7f6ecd6b589f8a58703ec02d311f6b..df43f52905837036d0ef843fbc5635f0d1274c6d 100644
--- a/src/scheme/DiscreteFunctionP0.hpp
+++ b/src/scheme/DiscreteFunctionP0.hpp
@@ -3,25 +3,23 @@
 
 #include <language/utils/ASTNodeDataTypeTraits.hpp>
 
-#include <mesh/Connectivity.hpp>
 #include <mesh/ItemValueUtils.hpp>
-#include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/DiscreteFunctionDescriptorP0.hpp>
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 class DiscreteFunctionP0
 {
  public:
   using data_type = DataType;
-  using MeshType  = Mesh<Connectivity<Dimension>>;
 
-  friend class DiscreteFunctionP0<Dimension, std::add_const_t<DataType>>;
-  friend class DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>;
+  friend class DiscreteFunctionP0<std::add_const_t<DataType>>;
+  friend class DiscreteFunctionP0<std::remove_const_t<DataType>>;
 
  private:
-  std::shared_ptr<const MeshType> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh_v;
   CellValue<DataType> m_cell_values;
 
   DiscreteFunctionDescriptorP0 m_discrete_function_descriptor;
@@ -41,11 +39,10 @@ class DiscreteFunctionP0
     return m_cell_values;
   }
 
-  PUGS_INLINE
-  std::shared_ptr<const IMesh>
-  mesh() const
+  PUGS_INLINE std::shared_ptr<const MeshVariant>
+  meshVariant() const
   {
-    return m_mesh;
+    return m_mesh_v;
   }
 
   PUGS_INLINE
@@ -56,9 +53,9 @@ class DiscreteFunctionP0
   }
 
   PUGS_FORCEINLINE
-  operator DiscreteFunctionP0<Dimension, const DataType>() const
+  operator DiscreteFunctionP0<const DataType>() const
   {
-    return DiscreteFunctionP0<Dimension, const DataType>(m_mesh, m_cell_values);
+    return DiscreteFunctionP0<const DataType>(m_mesh_v, m_cell_values);
   }
 
   PUGS_INLINE
@@ -72,22 +69,21 @@ class DiscreteFunctionP0
   PUGS_INLINE DiscreteFunctionP0
   operator=(const DiscreteFunctionP0& f)
   {
-    Assert(m_mesh == f.m_mesh);
+    Assert(m_mesh_v->id() == f.m_mesh_v->id());
     m_cell_values = f.m_cell_values;
     return *this;
   }
 
-  friend PUGS_INLINE DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  friend PUGS_INLINE DiscreteFunctionP0<std::remove_const_t<DataType>>
   copy(const DiscreteFunctionP0& source)
   {
-    return DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>{source.m_mesh, copy(source.cellValues())};
+    return DiscreteFunctionP0<std::remove_const_t<DataType>>{source.m_mesh_v, copy(source.cellValues())};
   }
 
   friend PUGS_INLINE void
-  copy_to(const DiscreteFunctionP0<Dimension, DataType>& source,
-          DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>& destination)
+  copy_to(const DiscreteFunctionP0<DataType>& source, DiscreteFunctionP0<std::remove_const_t<DataType>>& destination)
   {
-    Assert(source.m_mesh == destination.m_mesh);
+    Assert(source.m_mesh_v == destination.m_mesh_v);
     copy_to(source.m_cell_values, destination.m_cell_values);
   }
 
@@ -97,514 +93,515 @@ class DiscreteFunctionP0
     return m_cell_values[cell_id];
   }
 
-  PUGS_INLINE DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE DiscreteFunctionP0<std::remove_const_t<DataType>>
   operator-() const
   {
     Assert(m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> opposite = copy(*this);
+    DiscreteFunctionP0<std::remove_const_t<DataType>> opposite = copy(*this);
     parallel_for(
-      m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { opposite[cell_id] = -opposite[cell_id]; });
+      m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { opposite[cell_id] = -opposite[cell_id]; });
 
     return opposite;
   }
 
   template <typename DataType2T>
-  PUGS_INLINE DiscreteFunctionP0<Dimension, decltype(DataType{} + DataType2T{})>
-  operator+(const DiscreteFunctionP0<Dimension, DataType2T>& g) const
+  PUGS_INLINE DiscreteFunctionP0<decltype(DataType{} + DataType2T{})>
+  operator+(const DiscreteFunctionP0<DataType2T>& g) const
   {
     const DiscreteFunctionP0& f = *this;
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.mesh() == g.mesh(), "functions are not defined on the same mesh");
-    DiscreteFunctionP0<Dimension, decltype(DataType{} + DataType2T{})> sum(f.m_mesh);
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are not defined on the same mesh");
+    DiscreteFunctionP0<decltype(DataType{} + DataType2T{})> sum(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = f[cell_id] + g[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = f[cell_id] + g[cell_id]; });
     return sum;
   }
 
   template <typename LHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(LHSDataType{} + DataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(LHSDataType{} + DataType{})>
   operator+(const LHSDataType& a, const DiscreteFunctionP0& g)
   {
     using SumDataType = decltype(LHSDataType{} + DataType{});
     Assert(g.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, SumDataType> sum(g.m_mesh);
+    DiscreteFunctionP0<SumDataType> sum(g.m_mesh_v);
     parallel_for(
-      g.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = a + g[cell_id]; });
+      g.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = a + g[cell_id]; });
     return sum;
   }
 
   template <typename RHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(DataType{} + RHSDataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(DataType{} + RHSDataType{})>
   operator+(const DiscreteFunctionP0& f, const RHSDataType& b)
   {
     using SumDataType = decltype(DataType{} + RHSDataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, SumDataType> sum(f.m_mesh);
+    DiscreteFunctionP0<SumDataType> sum(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = f[cell_id] + b; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum[cell_id] = f[cell_id] + b; });
     return sum;
   }
 
   template <typename DataType2T>
-  PUGS_INLINE DiscreteFunctionP0<Dimension, decltype(DataType{} - DataType2T{})>
-  operator-(const DiscreteFunctionP0<Dimension, DataType2T>& g) const
+  PUGS_INLINE DiscreteFunctionP0<decltype(DataType{} - DataType2T{})>
+  operator-(const DiscreteFunctionP0<DataType2T>& g) const
   {
     const DiscreteFunctionP0& f = *this;
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.mesh() == g.mesh(), "functions are not defined on the same mesh");
-    DiscreteFunctionP0<Dimension, decltype(DataType{} - DataType2T{})> difference(f.m_mesh);
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are not defined on the same mesh");
+    DiscreteFunctionP0<decltype(DataType{} - DataType2T{})> difference(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = f[cell_id] - g[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = f[cell_id] - g[cell_id]; });
     return difference;
   }
 
   template <typename LHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(LHSDataType{} - DataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(LHSDataType{} - DataType{})>
   operator-(const LHSDataType& a, const DiscreteFunctionP0& g)
   {
     using DifferenceDataType = decltype(LHSDataType{} - DataType{});
     Assert(g.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, DifferenceDataType> difference(g.m_mesh);
+    DiscreteFunctionP0<DifferenceDataType> difference(g.m_mesh_v);
     parallel_for(
-      g.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = a - g[cell_id]; });
+      g.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = a - g[cell_id]; });
     return difference;
   }
 
   template <typename RHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(DataType{} - RHSDataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(DataType{} - RHSDataType{})>
   operator-(const DiscreteFunctionP0& f, const RHSDataType& b)
   {
     using DifferenceDataType = decltype(DataType{} - RHSDataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, DifferenceDataType> difference(f.m_mesh);
+    DiscreteFunctionP0<DifferenceDataType> difference(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = f[cell_id] - b; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference[cell_id] = f[cell_id] - b; });
     return difference;
   }
 
   template <typename DataType2T>
-  PUGS_INLINE DiscreteFunctionP0<Dimension, decltype(DataType{} * DataType2T{})>
-  operator*(const DiscreteFunctionP0<Dimension, DataType2T>& g) const
+  PUGS_INLINE DiscreteFunctionP0<decltype(DataType{} * DataType2T{})>
+  operator*(const DiscreteFunctionP0<DataType2T>& g) const
   {
     const DiscreteFunctionP0& f = *this;
     Assert(f.m_cell_values.isBuilt());
-    Assert(f.mesh() == g.mesh(), "functions are not defined on the same mesh");
-    DiscreteFunctionP0<Dimension, decltype(DataType{} * DataType2T{})> product(f.m_mesh);
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are not defined on the same mesh");
+    DiscreteFunctionP0<decltype(DataType{} * DataType2T{})> product(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = f[cell_id] * g[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = f[cell_id] * g[cell_id]; });
     return product;
   }
 
   template <typename LHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(LHSDataType{} * DataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(LHSDataType{} * DataType{})>
   operator*(const LHSDataType& a, const DiscreteFunctionP0& f)
   {
     using ProductDataType = decltype(LHSDataType{} * DataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, ProductDataType> product(f.m_mesh);
+    DiscreteFunctionP0<ProductDataType> product(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = a * f[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = a * f[cell_id]; });
     return product;
   }
 
   template <typename RHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(DataType{} * RHSDataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(DataType{} * RHSDataType{})>
   operator*(const DiscreteFunctionP0& f, const RHSDataType& b)
   {
     using ProductDataType = decltype(DataType{} * RHSDataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, ProductDataType> product(f.m_mesh);
+    DiscreteFunctionP0<ProductDataType> product(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = f[cell_id] * b; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product[cell_id] = f[cell_id] * b; });
     return product;
   }
 
   template <typename DataType2T>
-  PUGS_INLINE DiscreteFunctionP0<Dimension, decltype(DataType{} / DataType2T{})>
-  operator/(const DiscreteFunctionP0<Dimension, DataType2T>& g) const
+  PUGS_INLINE DiscreteFunctionP0<decltype(DataType{} / DataType2T{})>
+  operator/(const DiscreteFunctionP0<DataType2T>& g) const
   {
     const DiscreteFunctionP0& f = *this;
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.mesh() == g.mesh(), "functions are not defined on the same mesh");
-    DiscreteFunctionP0<Dimension, decltype(DataType{} / DataType2T{})> ratio(f.m_mesh);
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are not defined on the same mesh");
+    DiscreteFunctionP0<decltype(DataType{} / DataType2T{})> ratio(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = f[cell_id] / g[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = f[cell_id] / g[cell_id]; });
     return ratio;
   }
 
   template <typename LHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(LHSDataType{} / DataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(LHSDataType{} / DataType{})>
   operator/(const LHSDataType& a, const DiscreteFunctionP0& f)
   {
     using RatioDataType = decltype(LHSDataType{} / DataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, RatioDataType> ratio(f.m_mesh);
+    DiscreteFunctionP0<RatioDataType> ratio(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = a / f[cell_id]; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = a / f[cell_id]; });
     return ratio;
   }
 
   template <typename RHSDataType>
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, decltype(DataType{} / RHSDataType{})>
+  PUGS_INLINE friend DiscreteFunctionP0<decltype(DataType{} / RHSDataType{})>
   operator/(const DiscreteFunctionP0& f, const RHSDataType& b)
   {
     using RatioDataType = decltype(DataType{} / RHSDataType{});
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, RatioDataType> ratio(f.m_mesh);
+    DiscreteFunctionP0<RatioDataType> ratio(f.m_mesh_v);
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = f[cell_id] / b; });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio[cell_id] = f[cell_id] / b; });
     return ratio;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   sqrt(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sqrt(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sqrt(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   abs(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::abs(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::abs(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   sin(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sin(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sin(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   cos(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::cos(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::cos(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   tan(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::tan(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::tan(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   asin(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::asin(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::asin(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   acos(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::acos(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::acos(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   atan(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   sinh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sinh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::sinh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   cosh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::cosh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::cosh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   tanh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::tanh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::tanh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   asinh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::asinh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::asinh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   acosh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::acosh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::acosh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   atanh(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atanh(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atanh(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   exp(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::exp(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::exp(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   log(const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::log(f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::log(f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   atan2(const DiscreteFunctionP0& f, const DiscreteFunctionP0& g)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.m_mesh == g.m_mesh);
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    Assert(f.m_mesh_v->id() == g.m_mesh_v->id());
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(f[cell_id], g[cell_id]); });
+      f.m_mesh_v->numberOfCells(),
+      PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(f[cell_id], g[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   atan2(const double a, const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(a, f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(a, f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   atan2(const DiscreteFunctionP0& f, const double a)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(f[cell_id], a); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::atan2(f[cell_id], a); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   pow(const DiscreteFunctionP0& f, const DiscreteFunctionP0& g)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.m_mesh == g.m_mesh);
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    Assert(f.m_mesh_v->id() == g.m_mesh_v->id());
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(f[cell_id], g[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(f[cell_id], g[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   pow(const double a, const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(a, f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(a, f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   pow(const DiscreteFunctionP0& f, const double a)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(f[cell_id], a); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::pow(f[cell_id], a); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   det(const DiscreteFunctionP0& A)
   {
     static_assert(is_tiny_matrix_v<std::decay_t<DataType>>);
     Assert(A.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, double> result{A.m_mesh};
+    DiscreteFunctionP0<double> result{A.m_mesh_v};
     parallel_for(
-      A.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = det(A[cell_id]); });
+      A.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = det(A[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   trace(const DiscreteFunctionP0& A)
   {
     static_assert(is_tiny_matrix_v<std::decay_t<DataType>>);
     Assert(A.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, double> result{A.m_mesh};
+    DiscreteFunctionP0<double> result{A.m_mesh_v};
     parallel_for(
-      A.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = trace(A[cell_id]); });
+      A.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = trace(A[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   inverse(const DiscreteFunctionP0& A)
   {
     static_assert(is_tiny_matrix_v<std::decay_t<DataType>>);
     static_assert(DataType::NumberOfRows == DataType::NumberOfColumns, "cannot compute inverse of non square matrices");
     Assert(A.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{A.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{A.m_mesh_v};
     parallel_for(
-      A.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = inverse(A[cell_id]); });
+      A.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = inverse(A[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   transpose(const DiscreteFunctionP0& A)
   {
     static_assert(is_tiny_matrix_v<std::decay_t<DataType>>);
     static_assert(DataType::NumberOfRows == DataType::NumberOfColumns, "cannot compute inverse of non square matrices");
     Assert(A.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{A.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{A.m_mesh_v};
     parallel_for(
-      A.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = transpose(A[cell_id]); });
+      A.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = transpose(A[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   dot(const DiscreteFunctionP0& f, const DiscreteFunctionP0& g)
   {
     static_assert(is_tiny_vector_v<std::decay_t<DataType>>);
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.m_mesh == g.m_mesh);
-    DiscreteFunctionP0<Dimension, double> result{f.m_mesh};
+    Assert(f.m_mesh_v->id() == g.m_mesh_v->id());
+    DiscreteFunctionP0<double> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(f[cell_id], g[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(f[cell_id], g[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   dot(const DiscreteFunctionP0& f, const DataType& a)
   {
     static_assert(is_tiny_vector_v<std::decay_t<DataType>>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, double> result{f.m_mesh};
+    DiscreteFunctionP0<double> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(f[cell_id], a); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(f[cell_id], a); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   dot(const DataType& a, const DiscreteFunctionP0& f)
   {
     static_assert(is_tiny_vector_v<std::decay_t<DataType>>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, double> result{f.m_mesh};
+    DiscreteFunctionP0<double> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(a, f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = dot(a, f[cell_id]); });
 
     return result;
   }
@@ -618,39 +615,39 @@ class DiscreteFunctionP0
     return min(f.m_cell_values);
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   min(const DiscreteFunctionP0& f, const DiscreteFunctionP0& g)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.m_mesh == g.m_mesh);
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    Assert(f.m_mesh_v->id() == g.m_mesh_v->id());
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(f[cell_id], g[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(f[cell_id], g[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   min(const double a, const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(a, f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(a, f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   min(const DiscreteFunctionP0& f, const double a)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(f[cell_id], a); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::min(f[cell_id], a); });
 
     return result;
   }
@@ -664,39 +661,39 @@ class DiscreteFunctionP0
     return max(f.m_cell_values);
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   max(const DiscreteFunctionP0& f, const DiscreteFunctionP0& g)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt() and g.m_cell_values.isBuilt());
-    Assert(f.m_mesh == g.m_mesh);
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    Assert(f.m_mesh_v->id() == g.m_mesh_v->id());
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(f[cell_id], g[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(f[cell_id], g[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   max(const double a, const DiscreteFunctionP0& f)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(a, f[cell_id]); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(a, f[cell_id]); });
 
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE friend DiscreteFunctionP0<std::remove_const_t<DataType>>
   max(const DiscreteFunctionP0& f, const double a)
   {
     static_assert(std::is_arithmetic_v<DataType>);
     Assert(f.m_cell_values.isBuilt());
-    DiscreteFunctionP0<Dimension, std::remove_const_t<DataType>> result{f.m_mesh};
+    DiscreteFunctionP0<std::remove_const_t<DataType>> result{f.m_mesh_v};
     parallel_for(
-      f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(f[cell_id], a); });
+      f.m_mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { result[cell_id] = std::max(f[cell_id], a); });
 
     return result;
   }
@@ -713,22 +710,41 @@ class DiscreteFunctionP0
   {
     Assert(f.m_cell_values.isBuilt());
 
-    const Mesh<Connectivity<Dimension>>& mesh = *f.m_mesh;
-    CellValue<const double> cell_volume       = MeshDataManager::instance().getMeshData(mesh).Vj();
+    return std::visit(
+      [&](auto&& p_mesh) {
+        const auto& mesh = *p_mesh;
 
-    CellValue<std::remove_const_t<DataType>> f_v{mesh.connectivity()};
-    parallel_for(
-      mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { f_v[cell_id] = cell_volume[cell_id] * f[cell_id]; });
+        CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(mesh).Vj();
 
-    return sum(f_v);
+        CellValue<std::remove_const_t<DataType>> f_v{mesh.connectivity()};
+        parallel_for(
+          mesh.numberOfCells(), PUGS_LAMBDA(CellId cell_id) { f_v[cell_id] = cell_volume[cell_id] * f[cell_id]; });
+
+        return sum(f_v);
+      },
+      f.m_mesh_v->variant());
+  }
+
+  DiscreteFunctionP0(const std::shared_ptr<const MeshVariant>& mesh_v)
+    : m_mesh_v{mesh_v}, m_cell_values{mesh_v->connectivity()}
+  {}
+
+  DiscreteFunctionP0(const std::shared_ptr<const MeshVariant>& mesh_v, const CellValue<DataType>& cell_value)
+    : m_mesh_v{mesh_v}, m_cell_values{cell_value}
+  {
+    Assert(mesh_v->connectivity().id() == cell_value.connectivity_ptr()->id());
   }
 
-  DiscreteFunctionP0(const std::shared_ptr<const MeshType>& mesh) : m_mesh{mesh}, m_cell_values{mesh->connectivity()} {}
+  template <MeshConcept MeshType>
+  DiscreteFunctionP0(const std::shared_ptr<const MeshType>& p_mesh)
+    : m_mesh_v{p_mesh->meshVariant()}, m_cell_values{m_mesh_v->connectivity()}
+  {}
 
-  DiscreteFunctionP0(const std::shared_ptr<const MeshType>& mesh, const CellValue<DataType>& cell_value)
-    : m_mesh{mesh}, m_cell_values{cell_value}
+  template <MeshConcept MeshType>
+  DiscreteFunctionP0(const std::shared_ptr<const MeshType>& p_mesh, const CellValue<DataType>& cell_value)
+    : m_mesh_v{p_mesh->meshVariant()}, m_cell_values{cell_value}
   {
-    Assert(mesh->shared_connectivity() == cell_value.connectivity_ptr());
+    Assert(m_mesh_v->connectivity().id() == cell_value.connectivity_ptr()->id());
   }
 
   DiscreteFunctionP0() noexcept = delete;
diff --git a/src/scheme/DiscreteFunctionP0Vector.hpp b/src/scheme/DiscreteFunctionP0Vector.hpp
index 7bd833f344ffd29f6f47f6fa0d0bff13a2407339..da3904b2ad9853a05a521920c3a3a9434aac14a9 100644
--- a/src/scheme/DiscreteFunctionP0Vector.hpp
+++ b/src/scheme/DiscreteFunctionP0Vector.hpp
@@ -2,7 +2,6 @@
 #define DISCRETE_FUNCTION_P0_VECTOR_HPP
 
 #include <algebra/Vector.hpp>
-#include <mesh/Connectivity.hpp>
 #include <mesh/ItemArray.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
@@ -11,20 +10,19 @@
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 class DiscreteFunctionP0Vector
 {
  public:
   using data_type = DataType;
-  using MeshType  = Mesh<Connectivity<Dimension>>;
 
-  friend class DiscreteFunctionP0Vector<Dimension, std::add_const_t<DataType>>;
-  friend class DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>;
+  friend class DiscreteFunctionP0Vector<std::add_const_t<DataType>>;
+  friend class DiscreteFunctionP0Vector<std::remove_const_t<DataType>>;
 
   static_assert(std::is_arithmetic_v<DataType>, "DiscreteFunctionP0Vector are only defined for arithmetic data type");
 
  private:
-  std::shared_ptr<const MeshType> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
   CellArray<DataType> m_cell_arrays;
 
   DiscreteFunctionDescriptorP0Vector m_discrete_function_descriptor;
@@ -51,9 +49,8 @@ class DiscreteFunctionP0Vector
     return m_cell_arrays;
   }
 
-  PUGS_INLINE
-  std::shared_ptr<const IMesh>
-  mesh() const
+  PUGS_INLINE std::shared_ptr<const MeshVariant>
+  meshVariant() const
   {
     return m_mesh;
   }
@@ -66,9 +63,9 @@ class DiscreteFunctionP0Vector
   }
 
   PUGS_FORCEINLINE
-  operator DiscreteFunctionP0Vector<Dimension, const DataType>() const
+  operator DiscreteFunctionP0Vector<const DataType>() const
   {
-    return DiscreteFunctionP0Vector<Dimension, const DataType>(m_mesh, m_cell_arrays);
+    return DiscreteFunctionP0Vector<const DataType>(m_mesh, m_cell_arrays);
   }
 
   PUGS_INLINE
@@ -82,23 +79,23 @@ class DiscreteFunctionP0Vector
   PUGS_INLINE DiscreteFunctionP0Vector
   operator=(const DiscreteFunctionP0Vector& f)
   {
-    Assert(m_mesh == f.m_mesh);
+    Assert(m_mesh->id() == f.m_mesh->id());
     Assert(this->size() == f.size());
     m_cell_arrays = f.m_cell_arrays;
     return *this;
   }
 
-  friend PUGS_INLINE DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
+  friend PUGS_INLINE DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
   copy(const DiscreteFunctionP0Vector& source)
   {
-    return DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>{source.m_mesh, copy(source.cellArrays())};
+    return DiscreteFunctionP0Vector<std::remove_const_t<DataType>>{source.m_mesh, copy(source.cellArrays())};
   }
 
   friend PUGS_INLINE void
-  copy_to(const DiscreteFunctionP0Vector<Dimension, DataType>& source,
-          DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>& destination)
+  copy_to(const DiscreteFunctionP0Vector<DataType>& source,
+          DiscreteFunctionP0Vector<std::remove_const_t<DataType>>& destination)
   {
-    Assert(source.m_mesh == destination.m_mesh);
+    Assert(source.m_mesh->id() == destination.m_mesh->id());
     copy_to(source.m_cell_arrays, destination.m_cell_arrays);
   }
 
@@ -109,11 +106,11 @@ class DiscreteFunctionP0Vector
     return m_cell_arrays[cell_id];
   }
 
-  PUGS_INLINE DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
+  PUGS_INLINE DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
   operator-() const
   {
     Assert(m_cell_arrays.isBuilt());
-    DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>> opposite = copy(*this);
+    DiscreteFunctionP0Vector<std::remove_const_t<DataType>> opposite = copy(*this);
 
     const size_t size_of_arrays = this->size();
     parallel_for(
@@ -127,12 +124,12 @@ class DiscreteFunctionP0Vector
     return opposite;
   }
 
-  friend DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
+  friend DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
   operator+(const DiscreteFunctionP0Vector& f, const DiscreteFunctionP0Vector& g)
   {
-    Assert(f.m_mesh == g.m_mesh, "functions are nor defined on the same mesh");
+    Assert(f.m_mesh->id() == g.m_mesh->id(), "functions are nor defined on the same mesh");
     Assert(f.size() == g.size(), "P0 vectors have different sizes");
-    DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>> sum(f.m_mesh, f.size());
+    DiscreteFunctionP0Vector<std::remove_const_t<DataType>> sum(f.m_mesh, f.size());
     parallel_for(
       f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
         for (size_t i = 0; i < f.size(); ++i) {
@@ -142,12 +139,12 @@ class DiscreteFunctionP0Vector
     return sum;
   }
 
-  friend DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
+  friend DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
   operator-(const DiscreteFunctionP0Vector& f, const DiscreteFunctionP0Vector& g)
   {
-    Assert(f.mesh() == g.mesh(), "functions are nor defined on the same mesh");
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are nor defined on the same mesh");
     Assert(f.size() == g.size(), "P0 vectors have different sizes");
-    DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>> difference(f.m_mesh, f.size());
+    DiscreteFunctionP0Vector<std::remove_const_t<DataType>> difference(f.m_mesh, f.size());
     parallel_for(
       f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
         for (size_t i = 0; i < f.size(); ++i) {
@@ -158,13 +155,13 @@ class DiscreteFunctionP0Vector
   }
 
   template <typename DataType2>
-  friend DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
-  operator*(const DiscreteFunctionP0<Dimension, DataType2>& f, const DiscreteFunctionP0Vector& g)
+  friend DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
+  operator*(const DiscreteFunctionP0<DataType2>& f, const DiscreteFunctionP0Vector& g)
   {
     static_assert(std::is_arithmetic_v<DataType2>, "left operand must be arithmetic");
 
-    Assert(f.mesh() == g.mesh(), "functions are nor defined on the same mesh");
-    DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>> product(g.m_mesh, g.size());
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are nor defined on the same mesh");
+    DiscreteFunctionP0Vector<std::remove_const_t<DataType>> product(g.m_mesh, g.size());
     const size_t size_of_arrays = g.size();
     parallel_for(
       g.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
@@ -177,10 +174,10 @@ class DiscreteFunctionP0Vector
   }
 
   template <typename DataType2>
-  friend DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>>
+  friend DiscreteFunctionP0Vector<std::remove_const_t<DataType>>
   operator*(const DataType2& a, const DiscreteFunctionP0Vector& f)
   {
-    DiscreteFunctionP0Vector<Dimension, std::remove_const_t<DataType>> product(f.m_mesh, f.size());
+    DiscreteFunctionP0Vector<std::remove_const_t<DataType>> product(f.m_mesh, f.size());
     const size_t size_of_arrays = f.size();
     parallel_for(
       f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
@@ -191,10 +188,10 @@ class DiscreteFunctionP0Vector
     return product;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   sumOfComponents(const DiscreteFunctionP0Vector& f)
   {
-    DiscreteFunctionP0<Dimension, double> result{f.m_mesh};
+    DiscreteFunctionP0<double> result{f.m_mesh};
 
     parallel_for(
       f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
@@ -211,12 +208,12 @@ class DiscreteFunctionP0Vector
     return result;
   }
 
-  PUGS_INLINE friend DiscreteFunctionP0<Dimension, double>
+  PUGS_INLINE friend DiscreteFunctionP0<double>
   dot(const DiscreteFunctionP0Vector& f, const DiscreteFunctionP0Vector& g)
   {
-    Assert(f.mesh() == g.mesh(), "functions are nor defined on the same mesh");
+    Assert(f.meshVariant()->id() == g.meshVariant()->id(), "functions are nor defined on the same mesh");
     Assert(f.size() == g.size());
-    DiscreteFunctionP0<Dimension, double> result{f.m_mesh};
+    DiscreteFunctionP0<double> result{f.m_mesh};
     parallel_for(
       f.m_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
         const auto& f_cell_id = f[cell_id];
@@ -233,14 +230,26 @@ class DiscreteFunctionP0Vector
     return result;
   }
 
-  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshType>& mesh, size_t size)
+  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshVariant>& mesh, size_t size)
     : m_mesh{mesh}, m_cell_arrays{mesh->connectivity(), size}
   {}
 
-  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshType>& mesh, const CellArray<DataType>& cell_arrays)
+  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshVariant>& mesh, const CellArray<DataType>& cell_arrays)
     : m_mesh{mesh}, m_cell_arrays{cell_arrays}
   {
-    Assert(mesh->shared_connectivity() == cell_arrays.connectivity_ptr());
+    Assert(m_mesh->connectivity().id() == cell_arrays.connectivity_ptr()->id());
+  }
+
+  template <MeshConcept MeshType>
+  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshType>& p_mesh, size_t size)
+    : m_mesh{p_mesh->meshVariant()}, m_cell_arrays{m_mesh->connectivity(), size}
+  {}
+
+  template <MeshConcept MeshType>
+  DiscreteFunctionP0Vector(const std::shared_ptr<const MeshType>& p_mesh, const CellArray<DataType>& cell_arrays)
+    : m_mesh{p_mesh->meshVariant()}, m_cell_arrays{cell_arrays}
+  {
+    Assert(m_mesh->connectivity().id() == cell_arrays.connectivity_ptr()->id());
   }
 
   DiscreteFunctionP0Vector(const DiscreteFunctionP0Vector&) noexcept = default;
diff --git a/src/scheme/DiscreteFunctionUtils.cpp b/src/scheme/DiscreteFunctionUtils.cpp
index ba2416a9ab08e03e75bb0824f32930c105cd366c..9824b138cd909892e8b54d773462694070ca78d1 100644
--- a/src/scheme/DiscreteFunctionUtils.cpp
+++ b/src/scheme/DiscreteFunctionUtils.cpp
@@ -1,109 +1,115 @@
 #include <scheme/DiscreteFunctionUtils.hpp>
 
 #include <mesh/Connectivity.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <utils/Stringify.hpp>
 
-std::shared_ptr<const IMesh>
+std::shared_ptr<const MeshVariant>
 getCommonMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list)
 {
-  std::shared_ptr<const IMesh> i_mesh;
+  std::optional<size_t> mesh_id;
+  std::shared_ptr<const MeshVariant> mesh_v;
   bool is_same_mesh = true;
   for (const auto& discrete_function_variant : discrete_function_variant_list) {
     std::visit(
       [&](auto&& discrete_function) {
-        if (not i_mesh.use_count()) {
-          i_mesh = discrete_function.mesh();
+        if (not mesh_id.has_value()) {
+          mesh_v  = discrete_function.meshVariant();
+          mesh_id = discrete_function.meshVariant()->id();
         } else {
-          if (i_mesh != discrete_function.mesh()) {
+          if (mesh_id != discrete_function.meshVariant()->id()) {
             is_same_mesh = false;
+            mesh_v.reset();
           }
         }
       },
       discrete_function_variant->discreteFunction());
   }
-  if (not is_same_mesh) {
-    i_mesh.reset();
-  }
-  return i_mesh;
+
+  return mesh_v;
 }
 
 bool
 hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list)
 {
-  std::shared_ptr<const IMesh> i_mesh;
-  bool is_same_mesh = true;
+  std::optional<size_t> mesh_id;
+
+  bool same_mesh = true;
   for (const auto& discrete_function_variant : discrete_function_variant_list) {
     std::visit(
       [&](auto&& discrete_function) {
-        if (not i_mesh.use_count()) {
-          i_mesh = discrete_function.mesh();
+        if (not mesh_id.has_value()) {
+          mesh_id = discrete_function.meshVariant()->id();
         } else {
-          if (i_mesh != discrete_function.mesh()) {
-            is_same_mesh = false;
+          if (mesh_id != discrete_function.meshVariant()->id()) {
+            same_mesh = false;
           }
         }
       },
       discrete_function_variant->discreteFunction());
   }
 
-  return is_same_mesh;
+  return same_mesh;
 }
 
-template <typename MeshType, typename DiscreteFunctionT>
+bool
+hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list,
+            const std::shared_ptr<const MeshVariant>& mesh_v)
+{
+  const size_t mesh_id = mesh_v->id();
+
+  bool same_mesh = true;
+  for (const auto& discrete_function_variant : discrete_function_variant_list) {
+    std::visit(
+      [&](auto&& discrete_function) {
+        if (mesh_id != discrete_function.meshVariant()->id()) {
+          same_mesh = false;
+        }
+      },
+      discrete_function_variant->discreteFunction());
+  }
+
+  return same_mesh;
+}
+
+template <typename DiscreteFunctionT>
 std::shared_ptr<const DiscreteFunctionVariant>
-shallowCopy(const std::shared_ptr<const MeshType>& mesh, const DiscreteFunctionT& f)
+shallowCopy(const std::shared_ptr<const MeshVariant>& mesh_v, const DiscreteFunctionT& f)
 {
-  const std::shared_ptr function_mesh = std::dynamic_pointer_cast<const MeshType>(f.mesh());
+  const size_t function_connectivity_id = f.meshVariant()->shared_connectivity()->id();
 
-  if (mesh->shared_connectivity() != function_mesh->shared_connectivity()) {
+  if (mesh_v->shared_connectivity()->id() != function_connectivity_id) {
     throw NormalError("cannot shallow copy when connectivity changes");
   }
 
-  if constexpr (std::is_same_v<MeshType, typename DiscreteFunctionT::MeshType>) {
-    if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
-      return std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionT(mesh, f.cellValues()));
-    } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
-      return std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionT(mesh, f.cellArrays()));
-    } else {
-      throw UnexpectedError("invalid discrete function type");
-    }
+  if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
+    return std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionT(mesh_v, f.cellValues()));
+  } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
+    return std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionT(mesh_v, f.cellArrays()));
   } else {
-    throw UnexpectedError("invalid mesh types");
+    throw UnexpectedError("invalid discrete function type");
   }
 }
 
 std::shared_ptr<const DiscreteFunctionVariant>
-shallowCopy(const std::shared_ptr<const IMesh>& mesh,
+shallowCopy(const std::shared_ptr<const MeshVariant>& mesh_v,
             const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function_variant)
 {
   return std::visit(
     [&](auto&& f) {
-      if (mesh == f.mesh()) {
+      const size_t mesh_dimension = std::visit([](auto&& mesh) { return mesh->dimension(); }, mesh_v->variant());
+      const size_t f_mesh_dimension =
+        std::visit([](auto&& mesh) { return mesh->dimension(); }, f.meshVariant()->variant());
+      if (mesh_v->id() == f.meshVariant()->id()) {
         return discrete_function_variant;
-      } else if (mesh->dimension() != f.mesh()->dimension()) {
+      } else if (mesh_dimension != f_mesh_dimension) {
         throw NormalError("incompatible mesh dimensions");
       }
 
-      switch (mesh->dimension()) {
-      case 1: {
-        return shallowCopy(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(mesh), f);
-      }
-      case 2: {
-        return shallowCopy(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(mesh), f);
-      }
-      case 3: {
-        return shallowCopy(std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(mesh), f);
-      }
-        // LCOV_EXCL_START
-      default: {
-        throw UnexpectedError("invalid mesh dimension");
-      }
-        // LCOV_EXCL_STOP
-      }
+      return shallowCopy(mesh_v, f);
     },
     discrete_function_variant->discreteFunction());
 }
diff --git a/src/scheme/DiscreteFunctionUtils.hpp b/src/scheme/DiscreteFunctionUtils.hpp
index 91acbccb7c1444e2adfbc55333b1ee2a0e89d3b9..865eaa213199a39284b76c07754100ce6e112c29 100644
--- a/src/scheme/DiscreteFunctionUtils.hpp
+++ b/src/scheme/DiscreteFunctionUtils.hpp
@@ -21,13 +21,16 @@ checkDiscretizationType(const std::vector<std::shared_ptr<const DiscreteFunction
   return true;
 }
 
-std::shared_ptr<const IMesh> getCommonMesh(
+std::shared_ptr<const MeshVariant> getCommonMesh(
   const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list);
 
 bool hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list);
 
+bool hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list,
+                 const std::shared_ptr<const MeshVariant>& mesh_v);
+
 std::shared_ptr<const DiscreteFunctionVariant> shallowCopy(
-  const std::shared_ptr<const IMesh>& mesh,
+  const std::shared_ptr<const MeshVariant>& mesh_v,
   const std::shared_ptr<const DiscreteFunctionVariant>& discrete_function);
 
 #endif   // DISCRETE_FUNCTION_UTILS_HPP
diff --git a/src/scheme/DiscreteFunctionVariant.hpp b/src/scheme/DiscreteFunctionVariant.hpp
index af1b1cc3757ad38a5f5561c41705301c2f086ebe..901b7989084249ca0e437f9eef24e0371dd723a8 100644
--- a/src/scheme/DiscreteFunctionVariant.hpp
+++ b/src/scheme/DiscreteFunctionVariant.hpp
@@ -11,33 +11,15 @@
 class DiscreteFunctionVariant
 {
  private:
-  using Variant = std::variant<DiscreteFunctionP0<1, const double>,
-                               DiscreteFunctionP0<1, const TinyVector<1>>,
-                               DiscreteFunctionP0<1, const TinyVector<2>>,
-                               DiscreteFunctionP0<1, const TinyVector<3>>,
-                               DiscreteFunctionP0<1, const TinyMatrix<1>>,
-                               DiscreteFunctionP0<1, const TinyMatrix<2>>,
-                               DiscreteFunctionP0<1, const TinyMatrix<3>>,
+  using Variant = std::variant<DiscreteFunctionP0<const double>,
+                               DiscreteFunctionP0<const TinyVector<1>>,
+                               DiscreteFunctionP0<const TinyVector<2>>,
+                               DiscreteFunctionP0<const TinyVector<3>>,
+                               DiscreteFunctionP0<const TinyMatrix<1>>,
+                               DiscreteFunctionP0<const TinyMatrix<2>>,
+                               DiscreteFunctionP0<const TinyMatrix<3>>,
 
-                               DiscreteFunctionP0<2, const double>,
-                               DiscreteFunctionP0<2, const TinyVector<1>>,
-                               DiscreteFunctionP0<2, const TinyVector<2>>,
-                               DiscreteFunctionP0<2, const TinyVector<3>>,
-                               DiscreteFunctionP0<2, const TinyMatrix<1>>,
-                               DiscreteFunctionP0<2, const TinyMatrix<2>>,
-                               DiscreteFunctionP0<2, const TinyMatrix<3>>,
-
-                               DiscreteFunctionP0<3, const double>,
-                               DiscreteFunctionP0<3, const TinyVector<1>>,
-                               DiscreteFunctionP0<3, const TinyVector<2>>,
-                               DiscreteFunctionP0<3, const TinyVector<3>>,
-                               DiscreteFunctionP0<3, const TinyMatrix<1>>,
-                               DiscreteFunctionP0<3, const TinyMatrix<2>>,
-                               DiscreteFunctionP0<3, const TinyMatrix<3>>,
-
-                               DiscreteFunctionP0Vector<1, const double>,
-                               DiscreteFunctionP0Vector<2, const double>,
-                               DiscreteFunctionP0Vector<3, const double>>;
+                               DiscreteFunctionP0Vector<const double>>;
 
   Variant m_discrete_function;
 
@@ -70,9 +52,9 @@ class DiscreteFunctionVariant
     return std::get<DiscreteFunctionT>(this->discreteFunction());
   }
 
-  template <size_t Dimension, typename DataType>
-  DiscreteFunctionVariant(const DiscreteFunctionP0<Dimension, DataType>& discrete_function)
-    : m_discrete_function{DiscreteFunctionP0<Dimension, const DataType>{discrete_function}}
+  template <typename DataType>
+  DiscreteFunctionVariant(const DiscreteFunctionP0<DataType>& discrete_function)
+    : m_discrete_function{DiscreteFunctionP0<const DataType>{discrete_function}}
   {
     static_assert(std::is_same_v<std::remove_const_t<DataType>, double> or                       //
                     std::is_same_v<std::remove_const_t<DataType>, TinyVector<1, double>> or      //
@@ -84,9 +66,9 @@ class DiscreteFunctionVariant
                   "DiscreteFunctionP0 with this DataType is not allowed in variant");
   }
 
-  template <size_t Dimension, typename DataType>
-  DiscreteFunctionVariant(const DiscreteFunctionP0Vector<Dimension, DataType>& discrete_function)
-    : m_discrete_function{DiscreteFunctionP0Vector<Dimension, const DataType>{discrete_function}}
+  template <typename DataType>
+  DiscreteFunctionVariant(const DiscreteFunctionP0Vector<DataType>& discrete_function)
+    : m_discrete_function{DiscreteFunctionP0Vector<const DataType>{discrete_function}}
   {
     static_assert(std::is_same_v<std::remove_const_t<DataType>, double>,
                   "DiscreteFunctionP0Vector with this DataType is not allowed in variant");
diff --git a/src/scheme/DiscreteFunctionVectorIntegrator.cpp b/src/scheme/DiscreteFunctionVectorIntegrator.cpp
index 80b9e711ebfe9e45297e1a01a1d63ad37da72914..fccc6f79db41dd61cd6feea80baae7c8021487d3 100644
--- a/src/scheme/DiscreteFunctionVectorIntegrator.cpp
+++ b/src/scheme/DiscreteFunctionVectorIntegrator.cpp
@@ -2,17 +2,19 @@
 
 #include <language/utils/IntegrateCellArray.hpp>
 #include <mesh/MeshCellZone.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/DiscreteFunctionP0Vector.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorIntegrator::_integrateOnZoneList() const
 {
   Assert(m_zone_list.size() > 0, "no zone list provided");
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
+  std::shared_ptr p_mesh = m_mesh->get<MeshType>();
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -63,34 +65,36 @@ DiscreteFunctionVectorIntegrator::_integrateOnZoneList() const
       }
     });
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(p_mesh, cell_array);
+  return DiscreteFunctionP0Vector<DataType>(m_mesh, cell_array);
 }
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorIntegrator::_integrateGlobally() const
 {
   Assert(m_zone_list.size() == 0, "invalid call when zones are defined");
 
-  std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
+  std::shared_ptr mesh       = m_mesh->get<MeshType>();
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(mesh, IntegrateCellArray<DataType(TinyVector<Dimension>)>::
-                                                               template integrate(m_function_id_list,
-                                                                                  *m_quadrature_descriptor, *mesh));
+  return DiscreteFunctionP0Vector<
+    DataType>(m_mesh,
+              IntegrateCellArray<DataType(TinyVector<Dimension>)>::template integrate(m_function_id_list,
+                                                                                      *m_quadrature_descriptor, *mesh));
 }
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorIntegrator::_integrate() const
 {
   if (m_zone_list.size() == 0) {
-    return this->_integrateGlobally<Dimension, DataType>();
+    return this->_integrateGlobally<MeshType, DataType>();
   } else {
-    return this->_integrateOnZoneList<Dimension, DataType>();
+    return this->_integrateOnZoneList<MeshType, DataType>();
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorIntegrator::_integrate() const
 {
@@ -114,7 +118,7 @@ DiscreteFunctionVectorIntegrator::_integrate() const
     }
     }
   }
-  return this->_integrate<Dimension, double>();
+  return this->_integrate<MeshType, double>();
 }
 
 DiscreteFunctionVariant
@@ -124,20 +128,15 @@ DiscreteFunctionVectorIntegrator::integrate() const
     throw NormalError("invalid discrete function type for vector integration");
   }
 
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_integrate<1>();
-  }
-  case 2: {
-    return this->_integrate<2>();
-  }
-  case 3: {
-    return this->_integrate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_integrate<MeshType>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionVectorIntegrator.hpp b/src/scheme/DiscreteFunctionVectorIntegrator.hpp
index 44b192bc985888325a691db1433737d5c5a03ca0..b9b78525fda0d3ea12c71270aed85e8371c9a3a8 100644
--- a/src/scheme/DiscreteFunctionVectorIntegrator.hpp
+++ b/src/scheme/DiscreteFunctionVectorIntegrator.hpp
@@ -3,41 +3,42 @@
 
 #include <analysis/IQuadratureDescriptor.hpp>
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 
 #include <memory>
 #include <vector>
 
 class DiscreteFunctionVariant;
+class MeshVariant;
 
 class DiscreteFunctionVectorIntegrator
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
   const std::vector<std::shared_ptr<const IZoneDescriptor>> m_zone_list;
   std::shared_ptr<const IQuadratureDescriptor> m_quadrature_descriptor;
   std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor;
   const std::vector<FunctionSymbolId> m_function_id_list;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _integrateOnZoneList() const;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _integrateGlobally() const;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _integrate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   DiscreteFunctionVariant _integrate() const;
 
  public:
   DiscreteFunctionVariant integrate() const;
 
   DiscreteFunctionVectorIntegrator(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh,
     const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
     const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
     const std::vector<FunctionSymbolId>& function_id_list)
@@ -48,7 +49,7 @@ class DiscreteFunctionVectorIntegrator
   {}
 
   DiscreteFunctionVectorIntegrator(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh,
     const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_list,
     const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
     const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
index 57724b7722df4a8410993afee713c35f8d1c73e1..661eb483c2da2bb7a30ea1e7adb02d770482fa32 100644
--- a/src/scheme/DiscreteFunctionVectorInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
@@ -2,19 +2,20 @@
 
 #include <language/utils/InterpolateItemArray.hpp>
 #include <mesh/MeshCellZone.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/DiscreteFunctionP0Vector.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorInterpoler::_interpolateOnZoneList() const
 {
   Assert(m_zone_list.size() > 0, "no zone list provided");
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh  = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  std::shared_ptr p_mesh        = m_mesh->get<MeshType>();
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
   CellValue<bool> is_in_zone{p_mesh->connectivity()};
   is_in_zone.fill(false);
@@ -61,38 +62,37 @@ DiscreteFunctionVectorInterpoler::_interpolateOnZoneList() const
       }
     });
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(p_mesh, cell_array);
+  return DiscreteFunctionP0Vector<DataType>(p_mesh, cell_array);
 }
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorInterpoler::_interpolateGlobally() const
 {
   Assert(m_zone_list.size() == 0, "invalid call when zones are defined");
+  constexpr size_t Dimension = MeshType::Dimension;
 
-  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
+  std::shared_ptr p_mesh = m_mesh->get<MeshType>();
 
-  using MeshDataType      = MeshData<Dimension>;
-  MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
+  MeshData<MeshType>& mesh_data = MeshDataManager::instance().getMeshData(*p_mesh);
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(p_mesh,
-                                                       InterpolateItemArray<DataType(TinyVector<Dimension>)>::
-                                                         template interpolate<ItemType::cell>(m_function_id_list,
-                                                                                              mesh_data.xj()));
+  return DiscreteFunctionP0Vector<DataType>(m_mesh,
+                                            InterpolateItemArray<DataType(TinyVector<Dimension>)>::template interpolate<
+                                              ItemType::cell>(m_function_id_list, mesh_data.xj()));
 }
 
-template <size_t Dimension, typename DataType>
+template <MeshConcept MeshType, typename DataType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorInterpoler::_interpolate() const
 {
   if (m_zone_list.size() == 0) {
-    return this->_interpolateGlobally<Dimension, DataType>();
+    return this->_interpolateGlobally<MeshType, DataType>();
   } else {
-    return this->_interpolateOnZoneList<Dimension, DataType>();
+    return this->_interpolateOnZoneList<MeshType, DataType>();
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 DiscreteFunctionVariant
 DiscreteFunctionVectorInterpoler::_interpolate() const
 {
@@ -137,7 +137,7 @@ DiscreteFunctionVectorInterpoler::_interpolate() const
     }
   }
 
-  return this->_interpolate<Dimension, double>();
+  return this->_interpolate<MeshType, double>();
 }
 
 DiscreteFunctionVariant
@@ -147,20 +147,15 @@ DiscreteFunctionVectorInterpoler::interpolate() const
     throw NormalError("invalid discrete function type for vector interpolation");
   }
 
-  switch (m_mesh->dimension()) {
-  case 1: {
-    return this->_interpolate<1>();
-  }
-  case 2: {
-    return this->_interpolate<2>();
-  }
-  case 3: {
-    return this->_interpolate<3>();
-  }
-    // LCOV_EXCL_START
-  default: {
-    throw UnexpectedError("invalid dimension");
-  }
-    // LCOV_EXCL_STOP
-  }
+  return std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        return this->_interpolate<MeshType>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.hpp b/src/scheme/DiscreteFunctionVectorInterpoler.hpp
index 8cec9d09e7f9ce0fd03e98dbdaed0edf6afee900..ee8536d2d3655548b92d95c45e5d247390eaf9d9 100644
--- a/src/scheme/DiscreteFunctionVectorInterpoler.hpp
+++ b/src/scheme/DiscreteFunctionVectorInterpoler.hpp
@@ -2,11 +2,12 @@
 #define DISCRETE_FUNCTION_VECTOR_INTERPOLER_HPP
 
 #include <language/utils/FunctionSymbolId.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/IZoneDescriptor.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 
 class DiscreteFunctionVariant;
+class MeshVariant;
 
 #include <memory>
 #include <vector>
@@ -14,35 +15,35 @@ class DiscreteFunctionVariant;
 class DiscreteFunctionVectorInterpoler
 {
  private:
-  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const MeshVariant> m_mesh;
   const std::vector<std::shared_ptr<const IZoneDescriptor>> m_zone_list;
   std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor;
   const std::vector<FunctionSymbolId> m_function_id_list;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _interpolateOnZoneList() const;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _interpolateGlobally() const;
 
-  template <size_t Dimension, typename DataType>
+  template <MeshConcept MeshType, typename DataType>
   DiscreteFunctionVariant _interpolate() const;
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   DiscreteFunctionVariant _interpolate() const;
 
  public:
   DiscreteFunctionVariant interpolate() const;
 
   DiscreteFunctionVectorInterpoler(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh,
     const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
     const std::vector<FunctionSymbolId>& function_id_list)
     : m_mesh{mesh}, m_discrete_function_descriptor{discrete_function_descriptor}, m_function_id_list{function_id_list}
   {}
 
   DiscreteFunctionVectorInterpoler(
-    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const MeshVariant>& mesh,
     const std::vector<std::shared_ptr<const IZoneDescriptor>>& zone_list,
     const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
     const std::vector<FunctionSymbolId>& function_id_list)
diff --git a/src/scheme/EdgeIntegrator.hpp b/src/scheme/EdgeIntegrator.hpp
index 2bf287c3d6f61cb83ef3619124334a2124898f8c..7557a7ed851fa999e129e99f68bdd7812dbafd3d 100644
--- a/src/scheme/EdgeIntegrator.hpp
+++ b/src/scheme/EdgeIntegrator.hpp
@@ -67,7 +67,7 @@ class EdgeIntegrator
     EdgeList(const ArrayT<EdgeId>& edge_list) : m_edge_list{edge_list} {}
   };
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   _integrateTo(std::function<OutputType(InputType)> function,
                const IQuadratureDescriptor& quadrature_descriptor,
@@ -116,7 +116,7 @@ class EdgeIntegrator
   }
 
  public:
-  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   integrateTo(const std::function<OutputType(InputType)>& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -129,7 +129,7 @@ class EdgeIntegrator
                                          AllEdges<Dimension>{mesh.connectivity()}, value);
   }
 
-  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename FunctionType>
   static PUGS_INLINE void
   integrateTo(const FunctionType& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -139,7 +139,7 @@ class EdgeIntegrator
     integrateTo(std::function(function), quadrature_descriptor, mesh, value);
   }
 
-  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE ArrayT<OutputType>
   integrate(const std::function<OutputType(InputType)>& function,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -152,7 +152,7 @@ class EdgeIntegrator
     return value;
   }
 
-  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE auto
   integrate(const FunctionType& function,
             const IQuadratureDescriptor& quadrature_descriptor,
diff --git a/src/scheme/FaceIntegrator.hpp b/src/scheme/FaceIntegrator.hpp
index 657ca7a88a3a959f2856bc34ae8938ab91561785..3fc6b25f7b1bf99419e99e22c3873083832b8a7c 100644
--- a/src/scheme/FaceIntegrator.hpp
+++ b/src/scheme/FaceIntegrator.hpp
@@ -69,7 +69,7 @@ class FaceIntegrator
     FaceList(const ArrayT<FaceId>& face_list) : m_face_list{face_list} {}
   };
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   _tensorialIntegrateTo(std::function<OutputType(InputType)> function,
                         const IQuadratureDescriptor& quadrature_descriptor,
@@ -167,7 +167,7 @@ class FaceIntegrator
     }
   }
 
-  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   _directIntegrateTo(std::function<OutputType(InputType)> function,
                      const IQuadratureDescriptor& quadrature_descriptor,
@@ -265,7 +265,7 @@ class FaceIntegrator
   }
 
  public:
-  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename OutputType, typename InputType>
   static PUGS_INLINE void
   integrateTo(const std::function<OutputType(InputType)>& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -283,7 +283,7 @@ class FaceIntegrator
     }
   }
 
-  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  template <MeshConcept MeshType, typename OutputArrayT, typename FunctionType>
   static PUGS_INLINE void
   integrateTo(const FunctionType& function,
               const IQuadratureDescriptor& quadrature_descriptor,
@@ -293,7 +293,7 @@ class FaceIntegrator
     integrateTo(std::function(function), quadrature_descriptor, mesh, value);
   }
 
-  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE ArrayT<OutputType>
   integrate(const std::function<OutputType(InputType)>& function,
             const IQuadratureDescriptor& quadrature_descriptor,
@@ -312,7 +312,7 @@ class FaceIntegrator
     return value;
   }
 
-  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  template <MeshConcept MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
   static PUGS_INLINE auto
   integrate(const FunctionType& function,
             const IQuadratureDescriptor& quadrature_descriptor,
diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp
index 754c6e3ac6448a96e5e05c0a7f60967243b4798f..a2ebb2b256a3957169cf52457fd87a3d3236e8f3 100644
--- a/src/scheme/FluxingAdvectionSolver.cpp
+++ b/src/scheme/FluxingAdvectionSolver.cpp
@@ -7,7 +7,6 @@
 #include <language/utils/InterpolateItemArray.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/IMesh.hpp>
 #include <mesh/ItemArrayUtils.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
@@ -16,6 +15,7 @@
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshFlatFaceBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
@@ -27,14 +27,13 @@
 #include <variant>
 #include <vector>
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class FluxingAdvectionSolver
 {
  private:
-  using Rd = TinyVector<Dimension>;
+  static constexpr size_t Dimension = MeshType::Dimension;
 
-  using MeshType     = Mesh<Connectivity<Dimension>>;
-  using MeshDataType = MeshData<Dimension>;
+  using Rd = TinyVector<Dimension>;
 
   const std::shared_ptr<const MeshType> m_old_mesh;
   const std::shared_ptr<const MeshType> m_new_mesh;
@@ -81,13 +80,13 @@ class FluxingAdvectionSolver
 
   template <typename DataType>
   void
-  _storeValues(const DiscreteFunctionP0<Dimension, const DataType>& old_q)
+  _storeValues(const DiscreteFunctionP0<const DataType>& old_q)
   {
     m_remapped_list.emplace_back(copy(old_q.cellValues()));
   }
 
   void
-  _storeValues(const DiscreteFunctionP0Vector<Dimension, const double>& old_q)
+  _storeValues(const DiscreteFunctionP0Vector<const double>& old_q)
   {
     m_remapped_list.emplace_back(copy(old_q.cellArrays()));
   }
@@ -97,7 +96,7 @@ class FluxingAdvectionSolver
   _storeValueBCList(const size_t i_quantity,
                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_list)
   {
-    const Mesh<Connectivity<Dimension>>& mesh = *m_old_mesh;
+    const Mesh<Dimension>& mesh = *m_old_mesh;
 
     for (auto& i_bc : bc_list) {
       switch (i_bc->type()) {
@@ -115,8 +114,7 @@ class FluxingAdvectionSolver
         const InflowBoundaryConditionDescriptor& inflow_bc_descriptor =
           dynamic_cast<const InflowBoundaryConditionDescriptor&>(*i_bc);
 
-        MeshFaceBoundary<Dimension> mesh_face_boundary =
-          getMeshFaceBoundary(mesh, inflow_bc_descriptor.boundaryDescriptor());
+        MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, inflow_bc_descriptor.boundaryDescriptor());
 
         FaceValue<const Rd> xl = MeshDataManager::instance().getMeshData(*m_old_mesh).xl();
         Array<const DataType> value_list =
@@ -141,7 +139,7 @@ class FluxingAdvectionSolver
   _storeArrayBCList(const size_t i_quantity,
                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_list)
   {
-    const Mesh<Connectivity<Dimension>>& mesh = *m_old_mesh;
+    const Mesh<Dimension>& mesh = *m_old_mesh;
 
     for (auto& i_bc : bc_list) {
       switch (i_bc->type()) {
@@ -159,8 +157,7 @@ class FluxingAdvectionSolver
         const InflowBoundaryConditionDescriptor& inflow_bc_descriptor =
           dynamic_cast<const InflowBoundaryConditionDescriptor&>(*i_bc);
 
-        MeshFaceBoundary<Dimension> mesh_face_boundary =
-          getMeshFaceBoundary(mesh, inflow_bc_descriptor.boundaryDescriptor());
+        MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, inflow_bc_descriptor.boundaryDescriptor());
 
         FaceValue<const Rd> xl = MeshDataManager::instance().getMeshData(*m_old_mesh).xl();
         Table<const double> array_list =
@@ -192,15 +189,15 @@ class FluxingAdvectionSolver
   std::vector<std::shared_ptr<const DiscreteFunctionVariant>>   //
   remap(const std::vector<std::shared_ptr<const VariableBCDescriptor>>& quantity_list_with_bc);
 
-  FluxingAdvectionSolver(const std::shared_ptr<const IMesh> i_old_mesh, const std::shared_ptr<const IMesh> i_new_mesh)
-    : m_old_mesh{std::dynamic_pointer_cast<const MeshType>(i_old_mesh)},
-      m_new_mesh{std::dynamic_pointer_cast<const MeshType>(i_new_mesh)}
+  FluxingAdvectionSolver(const std::shared_ptr<const MeshType>& i_old_mesh,
+                         const std::shared_ptr<const MeshType>& i_new_mesh)
+    : m_old_mesh{i_old_mesh}, m_new_mesh{i_new_mesh}
   {
     if ((m_old_mesh.use_count() == 0) or (m_new_mesh.use_count() == 0)) {
       throw NormalError("old and new meshes must be of same type");
     }
 
-    if (m_new_mesh->shared_connectivity() != m_old_mesh->shared_connectivity()) {
+    if (m_new_mesh->connectivity().id() != m_old_mesh->connectivity().id()) {
       throw NormalError("old and new meshes must share the same connectivity");
     }
 
@@ -210,9 +207,9 @@ class FluxingAdvectionSolver
   ~FluxingAdvectionSolver() = default;
 };
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-FluxingAdvectionSolver<Dimension>::_computeDonorCells(FaceValue<const double> algebraic_fluxing_volumes)
+FluxingAdvectionSolver<MeshType>::_computeDonorCells(FaceValue<const double> algebraic_fluxing_volumes)
 {
   m_donnor_cell = [&] {
     const FaceValuePerCell<const bool> cell_face_is_reversed = m_new_mesh->connectivity().cellFaceIsReversed();
@@ -243,7 +240,7 @@ FluxingAdvectionSolver<Dimension>::_computeDonorCells(FaceValue<const double> al
 
 template <>
 void
-FluxingAdvectionSolver<1>::_computeDonorCells(FaceValue<const double> algebraic_fluxing_volumes)
+FluxingAdvectionSolver<Mesh<1>>::_computeDonorCells(FaceValue<const double> algebraic_fluxing_volumes)
 {
   m_donnor_cell = [&] {
     const auto face_to_cell_matrix = m_new_mesh->connectivity().faceToCellMatrix();
@@ -277,7 +274,7 @@ FluxingAdvectionSolver<1>::_computeDonorCells(FaceValue<const double> algebraic_
 
 template <>
 FaceValue<double>
-FluxingAdvectionSolver<1>::_computeAlgebraicFluxingVolume()
+FluxingAdvectionSolver<Mesh<1>>::_computeAlgebraicFluxingVolume()
 {
   Array<double> fluxing_volumes{m_new_mesh->numberOfNodes()};
   NodeValue<double> nodal_fluxing_volume(m_new_mesh->connectivity(), fluxing_volumes);
@@ -296,7 +293,7 @@ FluxingAdvectionSolver<1>::_computeAlgebraicFluxingVolume()
 
 template <>
 FaceValue<double>
-FluxingAdvectionSolver<2>::_computeAlgebraicFluxingVolume()
+FluxingAdvectionSolver<Mesh<2>>::_computeAlgebraicFluxingVolume()
 {
   const auto face_to_node_matrix = m_old_mesh->connectivity().faceToNodeMatrix();
   FaceValue<double> algebraic_fluxing_volume(m_new_mesh->connectivity());
@@ -323,7 +320,7 @@ FluxingAdvectionSolver<2>::_computeAlgebraicFluxingVolume()
 
 template <>
 FaceValue<double>
-FluxingAdvectionSolver<3>::_computeAlgebraicFluxingVolume()
+FluxingAdvectionSolver<Mesh<3>>::_computeAlgebraicFluxingVolume()
 {
   // due to the z component of the jacobian determinant, degree 3
   // polynomials must be exactly integrated
@@ -388,9 +385,9 @@ FluxingAdvectionSolver<3>::_computeAlgebraicFluxingVolume()
   return algebraic_fluxing_volume;
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 FaceValue<double>
-FluxingAdvectionSolver<Dimension>::_computeFluxingVolume(FaceValue<double> algebraic_fluxing_volumes)
+FluxingAdvectionSolver<MeshType>::_computeFluxingVolume(FaceValue<double> algebraic_fluxing_volumes)
 {
   Assert(m_donnor_cell.isBuilt());
   // Now that donnor cells are clearly defined, we consider the
@@ -403,9 +400,9 @@ FluxingAdvectionSolver<Dimension>::_computeFluxingVolume(FaceValue<double> algeb
   return algebraic_fluxing_volumes;
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-FluxingAdvectionSolver<Dimension>::_computeCycleNumber(FaceValue<double> fluxing_volumes)
+FluxingAdvectionSolver<MeshType>::_computeCycleNumber(FaceValue<double> fluxing_volumes)
 {
   const auto cell_to_face_matrix = m_old_mesh->connectivity().cellToFaceMatrix();
 
@@ -423,7 +420,7 @@ FluxingAdvectionSolver<Dimension>::_computeCycleNumber(FaceValue<double> fluxing
       }
     });
 
-  MeshData<Dimension>& mesh_data   = MeshDataManager::instance().getMeshData(*m_old_mesh);
+  MeshData<MeshType>& mesh_data    = MeshDataManager::instance().getMeshData(*m_old_mesh);
   const CellValue<const double> Vj = mesh_data.Vj();
   CellValue<size_t> ratio(m_old_mesh->connectivity());
 
@@ -446,9 +443,9 @@ FluxingAdvectionSolver<Dimension>::_computeCycleNumber(FaceValue<double> fluxing
   m_cycle_fluxing_volume = fluxing_volumes;
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-FluxingAdvectionSolver<Dimension>::_computeGeometricalData()
+FluxingAdvectionSolver<MeshType>::_computeGeometricalData()
 {
   auto fluxing_volumes = this->_computeAlgebraicFluxingVolume();
   this->_computeDonorCells(fluxing_volumes);
@@ -456,12 +453,12 @@ FluxingAdvectionSolver<Dimension>::_computeGeometricalData()
   this->_computeCycleNumber(fluxing_volumes);
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 template <typename CellDataType>
 void
-FluxingAdvectionSolver<Dimension>::_remapOne(const CellValue<const double>& step_Vj,
-                                             const std::vector<BoundaryConditionVariant>& q_bc_list,
-                                             CellDataType& old_q)
+FluxingAdvectionSolver<MeshType>::_remapOne(const CellValue<const double>& step_Vj,
+                                            const std::vector<BoundaryConditionVariant>& q_bc_list,
+                                            CellDataType& old_q)
 {
   using DataType = std::decay_t<typename CellDataType::data_type>;
 
@@ -636,14 +633,14 @@ FluxingAdvectionSolver<Dimension>::_remapOne(const CellValue<const double>& step
   old_q = new_q;
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-FluxingAdvectionSolver<Dimension>::_remapAllQuantities()
+FluxingAdvectionSolver<MeshType>::_remapAllQuantities()
 {
   const auto cell_to_face_matrix              = m_new_mesh->connectivity().cellToFaceMatrix();
   const auto face_local_number_in_their_cells = m_new_mesh->connectivity().faceLocalNumbersInTheirCells();
 
-  MeshData<Dimension>& old_mesh_data = MeshDataManager::instance().getMeshData(*m_old_mesh);
+  MeshData<MeshType>& old_mesh_data = MeshDataManager::instance().getMeshData(*m_old_mesh);
 
   const CellValue<const double> old_Vj = old_mesh_data.Vj();
   const CellValue<double> step_Vj      = copy(old_Vj);
@@ -699,9 +696,9 @@ FluxingAdvectionSolver<Dimension>::_remapAllQuantities()
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 std::vector<std::shared_ptr<const DiscreteFunctionVariant>>
-FluxingAdvectionSolver<Dimension>::remap(
+FluxingAdvectionSolver<MeshType>::remap(
   const std::vector<std::shared_ptr<const VariableBCDescriptor>>& quantity_list_with_bc)
 {
   m_boundary_condition_list.resize(quantity_list_with_bc.size());
@@ -712,18 +709,14 @@ FluxingAdvectionSolver<Dimension>::remap(
     std::visit(
       [&](auto&& variable) {
         using DiscreteFunctionT = std::decay_t<decltype(variable)>;
-        if constexpr (std::is_same_v<MeshType, typename DiscreteFunctionT::MeshType>) {
-          this->_storeValues(variable);
-          if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
-            using DataType = std::decay_t<typename DiscreteFunctionT::data_type>;
-            this->_storeValueBCList<DataType>(i, bc_list);
-          } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
-            this->_storeArrayBCList(i, bc_list);
-          } else {
-            throw UnexpectedError("invalid discrete function type");
-          }
+        this->_storeValues(variable);
+        if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
+          using DataType = std::decay_t<typename DiscreteFunctionT::data_type>;
+          this->_storeValueBCList<DataType>(i, bc_list);
+        } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
+          this->_storeArrayBCList(i, bc_list);
         } else {
-          throw UnexpectedError("incompatible mesh types");
+          throw UnexpectedError("invalid discrete function type");
         }
       },
       quantity_v->discreteFunction());
@@ -739,18 +732,14 @@ FluxingAdvectionSolver<Dimension>::remap(
         using DiscreteFunctionT = std::decay_t<decltype(variable)>;
         using DataType          = std::decay_t<typename DiscreteFunctionT::data_type>;
 
-        if constexpr (std::is_same_v<MeshType, typename DiscreteFunctionT::MeshType>) {
-          if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
-            new_variables.push_back(std::make_shared<DiscreteFunctionVariant>(
-              DiscreteFunctionT(m_new_mesh, std::get<CellValue<DataType>>(m_remapped_list[i]))));
-          } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
-            new_variables.push_back(std::make_shared<DiscreteFunctionVariant>(
-              DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i]))));
-          } else {
-            throw UnexpectedError("invalid discrete function type");
-          }
+        if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
+          new_variables.push_back(std::make_shared<DiscreteFunctionVariant>(
+            DiscreteFunctionT(m_new_mesh, std::get<CellValue<DataType>>(m_remapped_list[i]))));
+        } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
+          new_variables.push_back(std::make_shared<DiscreteFunctionVariant>(
+            DiscreteFunctionT(m_new_mesh, std::get<CellArray<DataType>>(m_remapped_list[i]))));
         } else {
-          throw UnexpectedError("incompatible mesh types");
+          throw UnexpectedError("invalid discrete function type");
         }
       },
       quantity_list_with_bc[i]->discreteFunctionVariant()->discreteFunction());
@@ -759,12 +748,12 @@ FluxingAdvectionSolver<Dimension>::remap(
   return new_variables;
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 template <typename DataType>
-class FluxingAdvectionSolver<Dimension>::InflowValueBoundaryCondition
+class FluxingAdvectionSolver<MeshType>::InflowValueBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
 
   Array<const DataType> m_value_list;
 
@@ -781,7 +770,7 @@ class FluxingAdvectionSolver<Dimension>::InflowValueBoundaryCondition
     return m_value_list;
   }
 
-  InflowValueBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary, Array<const DataType> value_list)
+  InflowValueBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, Array<const DataType> value_list)
     : m_mesh_face_boundary(mesh_face_boundary), m_value_list(value_list)
   {
     ;
@@ -790,11 +779,11 @@ class FluxingAdvectionSolver<Dimension>::InflowValueBoundaryCondition
   ~InflowValueBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class FluxingAdvectionSolver<Dimension>::InflowArrayBoundaryCondition
+template <MeshConcept MeshType>
+class FluxingAdvectionSolver<MeshType>::InflowArrayBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
 
   Table<const double> m_array_list;
 
@@ -811,7 +800,7 @@ class FluxingAdvectionSolver<Dimension>::InflowArrayBoundaryCondition
     return m_array_list;
   }
 
-  InflowArrayBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary, Table<const double> array_list)
+  InflowArrayBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, Table<const double> array_list)
     : m_mesh_face_boundary(mesh_face_boundary), m_array_list(array_list)
   {
     ;
@@ -820,11 +809,11 @@ class FluxingAdvectionSolver<Dimension>::InflowArrayBoundaryCondition
   ~InflowArrayBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class FluxingAdvectionSolver<Dimension>::OutflowBoundaryCondition
+template <MeshConcept MeshType>
+class FluxingAdvectionSolver<MeshType>::OutflowBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
 
  public:
   const Array<const FaceId>&
@@ -833,8 +822,7 @@ class FluxingAdvectionSolver<Dimension>::OutflowBoundaryCondition
     return m_mesh_face_boundary.faceList();
   }
 
-  OutflowBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary)
-    : m_mesh_face_boundary(mesh_face_boundary)
+  OutflowBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary) : m_mesh_face_boundary(mesh_face_boundary)
   {
     ;
   }
@@ -842,11 +830,11 @@ class FluxingAdvectionSolver<Dimension>::OutflowBoundaryCondition
   ~OutflowBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class FluxingAdvectionSolver<Dimension>::SymmetryBoundaryCondition
+template <MeshConcept MeshType>
+class FluxingAdvectionSolver<MeshType>::SymmetryBoundaryCondition
 {
  private:
-  const MeshFlatFaceBoundary<Dimension> m_mesh_flat_face_boundary;
+  const MeshFlatFaceBoundary<MeshType> m_mesh_flat_face_boundary;
 
  public:
   const Array<const FaceId>&
@@ -855,7 +843,7 @@ class FluxingAdvectionSolver<Dimension>::SymmetryBoundaryCondition
     return m_mesh_flat_face_boundary.faceList();
   }
 
-  SymmetryBoundaryCondition(const MeshFlatFaceBoundary<Dimension>& mesh_flat_face_boundary)
+  SymmetryBoundaryCondition(const MeshFlatFaceBoundary<MeshType>& mesh_flat_face_boundary)
     : m_mesh_flat_face_boundary(mesh_flat_face_boundary)
   {
     ;
@@ -865,7 +853,7 @@ class FluxingAdvectionSolver<Dimension>::SymmetryBoundaryCondition
 };
 
 std::vector<std::shared_ptr<const DiscreteFunctionVariant>>
-advectByFluxing(const std::shared_ptr<const IMesh> i_new_mesh,
+advectByFluxing(const std::shared_ptr<const MeshVariant> new_mesh_v,
                 const std::vector<std::shared_ptr<const VariableBCDescriptor>>& remapped_variables_with_bc)
 {
   std::vector<std::shared_ptr<const DiscreteFunctionVariant>> remapped_variables;
@@ -877,20 +865,18 @@ advectByFluxing(const std::shared_ptr<const IMesh> i_new_mesh,
     throw NormalError("remapped quantities are not defined on the same mesh");
   }
 
-  const std::shared_ptr<const IMesh> i_old_mesh = getCommonMesh(remapped_variables);
+  const std::shared_ptr<const MeshVariant> old_mesh_v = getCommonMesh(remapped_variables);
 
-  switch (i_old_mesh->dimension()) {
-  case 1: {
-    return FluxingAdvectionSolver<1>{i_old_mesh, i_new_mesh}.remap(remapped_variables_with_bc);
-  }
-  case 2: {
-    return FluxingAdvectionSolver<2>{i_old_mesh, i_new_mesh}.remap(remapped_variables_with_bc);
-  }
-  case 3: {
-    return FluxingAdvectionSolver<3>{i_old_mesh, i_new_mesh}.remap(remapped_variables_with_bc);
-  }
-  default: {
-    throw UnexpectedError("Invalid mesh dimension");
-  }
-  }
+  return std::visit(
+    [&](auto&& old_mesh, auto&& new_mesh) -> std::vector<std::shared_ptr<const DiscreteFunctionVariant>> {
+      using OldMeshType = mesh_type_t<decltype(old_mesh)>;
+      using NewMeshType = mesh_type_t<decltype(new_mesh)>;
+
+      if constexpr (std::is_same_v<OldMeshType, NewMeshType>) {
+        return FluxingAdvectionSolver<OldMeshType>{old_mesh, new_mesh}.remap(remapped_variables_with_bc);
+      } else {
+        throw NormalError("incompatible mesh types");
+      }
+    },
+    old_mesh_v->variant(), new_mesh_v->variant());
 }
diff --git a/src/scheme/FluxingAdvectionSolver.hpp b/src/scheme/FluxingAdvectionSolver.hpp
index 2cbb653cbeed76000d8eb1e3ba01ed61c35bc8a3..5cbb6c76d5604466db4440acc0d4453dd31d1398 100644
--- a/src/scheme/FluxingAdvectionSolver.hpp
+++ b/src/scheme/FluxingAdvectionSolver.hpp
@@ -1,18 +1,19 @@
 #ifndef FLUXING_ADVECION_SOLVER_HPP
 #define FLUXING_ADVECION_SOLVER_HPP
 
-#include <language/utils/FunctionSymbolId.hpp>
 #include <scheme/DiscreteFunctionVariant.hpp>
 #include <scheme/VariableBCDescriptor.hpp>
 
 #include <vector>
 
+class MeshVariant;
+
 std::vector<std::shared_ptr<const DiscreteFunctionVariant>> advectByFluxing(
-  const std::shared_ptr<const IMesh> new_mesh,
+  const std::shared_ptr<const MeshVariant> new_mesh,
   const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& remapped_variables);
 
 std::vector<std::shared_ptr<const DiscreteFunctionVariant>> advectByFluxing(
-  const std::shared_ptr<const IMesh> new_mesh,
+  const std::shared_ptr<const MeshVariant> new_mesh,
   const std::vector<std::shared_ptr<const VariableBCDescriptor>>& remapped_variables_with_bc);
 
 #endif   // FLUXING_ADVECION_SOLVER_HPP
diff --git a/src/scheme/HyperelasticSolver.cpp b/src/scheme/HyperelasticSolver.cpp
index 2f683fa805aa2741b003acff8cb5f39040dafd29..d88067f982e244a53cb6f87d062e404392188a69 100644
--- a/src/scheme/HyperelasticSolver.cpp
+++ b/src/scheme/HyperelasticSolver.cpp
@@ -6,6 +6,7 @@
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshFlatNodeBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/MeshTraits.hpp>
 #include <mesh/SubItemValuePerItemVariant.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
@@ -21,56 +22,46 @@
 #include <variant>
 #include <vector>
 
-template <size_t Dimension>
 double
-hyperelastic_dt(const DiscreteFunctionP0<Dimension, const double>& c)
+hyperelastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c_v)
 {
-  const Mesh<Connectivity<Dimension>>& mesh = dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*c.mesh());
+  const auto& c = c_v->get<DiscreteFunctionP0<const double>>();
 
-  const auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj();
-  const auto Sj = MeshDataManager::instance().getMeshData(mesh).sumOverRLjr();
+  return std::visit(
+    [&](auto&& p_mesh) -> double {
+      const auto& mesh = *p_mesh;
 
-  CellValue<double> local_dt{mesh.connectivity()};
-  parallel_for(
-    mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); });
+      using MeshType = decltype(mesh);
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        const auto Vj = MeshDataManager::instance().getMeshData(mesh).Vj();
+        const auto Sj = MeshDataManager::instance().getMeshData(mesh).sumOverRLjr();
 
-  return min(local_dt);
-}
+        CellValue<double> local_dt{mesh.connectivity()};
+        parallel_for(
+          mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); });
 
-double
-hyperelastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c)
-{
-  std::shared_ptr mesh = getCommonMesh({c});
-
-  switch (mesh->dimension()) {
-  case 1: {
-    return hyperelastic_dt(c->get<DiscreteFunctionP0<1, const double>>());
-  }
-  case 2: {
-    return hyperelastic_dt(c->get<DiscreteFunctionP0<2, const double>>());
-  }
-  case 3: {
-    return hyperelastic_dt(c->get<DiscreteFunctionP0<3, const double>>());
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+        return min(local_dt);
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    c.meshVariant()->variant());
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticSolverHandler::IHyperelasticSolver
 {
  private:
+  static constexpr size_t Dimension = MeshType::Dimension;
+
   using Rdxd = TinyMatrix<Dimension>;
   using Rd   = TinyVector<Dimension>;
 
-  using MeshType     = Mesh<Connectivity<Dimension>>;
-  using MeshDataType = MeshData<Dimension>;
+  using MeshDataType = MeshData<MeshType>;
 
-  using DiscreteScalarFunction = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteVectorFunction = DiscreteFunctionP0<Dimension, const Rd>;
-  using DiscreteTensorFunction = DiscreteFunctionP0<Dimension, const Rdxd>;
+  using DiscreteScalarFunction = DiscreteFunctionP0<const double>;
+  using DiscreteVectorFunction = DiscreteFunctionP0<const Rd>;
+  using DiscreteTensorFunction = DiscreteFunctionP0<const Rdxd>;
 
   class FixedBoundaryCondition;
   class PressureBoundaryCondition;
@@ -211,7 +202,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
   }
 
   NodeValue<Rd>
-  _computeBr(const Mesh<Connectivity<Dimension>>& mesh,
+  _computeBr(const Mesh<Dimension>& mesh,
              const NodeValuePerCell<const Rdxd>& Ajr,
              const DiscreteVectorFunction& u,
              const DiscreteTensorFunction& sigma) const
@@ -266,8 +257,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
         const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
           dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
         if (dirichlet_bc_descriptor.name() == "velocity") {
-          MeshNodeBoundary<Dimension> mesh_node_boundary =
-            getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor());
+          MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, dirichlet_bc_descriptor.boundaryDescriptor());
 
           Array<const Rd> value_list =
             InterpolateItemValue<Rd(Rd)>::template interpolate<ItemType::node>(dirichlet_bc_descriptor.rhsSymbolId(),
@@ -279,8 +269,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
           const FunctionSymbolId pressure_id = dirichlet_bc_descriptor.rhsSymbolId();
 
           if constexpr (Dimension == 1) {
-            MeshNodeBoundary<Dimension> mesh_node_boundary =
-              getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             Array<const double> node_values =
               InterpolateItemValue<double(Rd)>::template interpolate<ItemType::node>(pressure_id, mesh.xr(),
@@ -288,8 +277,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
 
             bc_list.emplace_back(PressureBoundaryCondition{mesh_node_boundary, node_values});
           } else {
-            MeshFaceBoundary<Dimension> mesh_face_boundary =
-              getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
             Array<const double> face_values =
@@ -302,8 +290,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
           const FunctionSymbolId normal_stress_id = dirichlet_bc_descriptor.rhsSymbolId();
 
           if constexpr (Dimension == 1) {
-            MeshNodeBoundary<Dimension> mesh_node_boundary =
-              getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshNodeBoundary mesh_node_boundary = getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             Array<const Rdxd> node_values =
               InterpolateItemValue<Rdxd(Rd)>::template interpolate<ItemType::node>(normal_stress_id, mesh.xr(),
@@ -311,8 +298,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
 
             bc_list.emplace_back(NormalStressBoundaryCondition{mesh_node_boundary, node_values});
           } else {
-            MeshFaceBoundary<Dimension> mesh_face_boundary =
-              getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
+            MeshFaceBoundary mesh_face_boundary = getMeshFaceBoundary(mesh, bc_descriptor->boundaryDescriptor());
 
             MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
             Array<const Rdxd> face_values =
@@ -411,7 +397,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
       throw NormalError("hyperelastic solver expects P0 functions");
     }
 
-    const MeshType& mesh                = dynamic_cast<const MeshType&>(*i_mesh);
+    const MeshType& mesh                = *i_mesh->get<MeshType>();
     const DiscreteScalarFunction& rho   = rho_v->get<DiscreteScalarFunction>();
     const DiscreteVectorFunction& u     = u_v->get<DiscreteVectorFunction>();
     const DiscreteScalarFunction& aL    = aL_v->get<DiscreteScalarFunction>();
@@ -436,7 +422,7 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
                            std::make_shared<const SubItemValuePerItemVariant>(Fjr));
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
@@ -499,13 +485,14 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
     parallel_for(
       mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { new_rho[j] *= Vj[j] / new_Vj[j]; });
 
-    return {new_mesh, std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)),
+    return {std::make_shared<MeshVariant>(new_mesh),
+            std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_rho)),
             std::make_shared<DiscreteFunctionVariant>(DiscreteVectorFunction(new_mesh, new_u)),
             std::make_shared<DiscreteFunctionVariant>(DiscreteScalarFunction(new_mesh, new_E)),
             std::make_shared<DiscreteFunctionVariant>(DiscreteTensorFunction(new_mesh, new_CG))};
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
@@ -527,17 +514,17 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
       throw NormalError("hyperelastic solver expects P0 functions");
     }
 
-    return this->apply_fluxes(dt,                                       //
-                              dynamic_cast<const MeshType&>(*i_mesh),   //
-                              rho->get<DiscreteScalarFunction>(),       //
-                              u->get<DiscreteVectorFunction>(),         //
-                              E->get<DiscreteScalarFunction>(),         //
-                              CG->get<DiscreteTensorFunction>(),        //
-                              ur->get<NodeValue<const Rd>>(),           //
+    return this->apply_fluxes(dt,                                   //
+                              *i_mesh->get<MeshType>(),             //
+                              rho->get<DiscreteScalarFunction>(),   //
+                              u->get<DiscreteVectorFunction>(),     //
+                              E->get<DiscreteScalarFunction>(),     //
+                              CG->get<DiscreteTensorFunction>(),    //
+                              ur->get<NodeValue<const Rd>>(),       //
                               Fjr->get<NodeValuePerCell<const Rd>>());
   }
 
-  std::tuple<std::shared_ptr<const IMesh>,
+  std::tuple<std::shared_ptr<const MeshVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
              std::shared_ptr<const DiscreteFunctionVariant>,
@@ -562,18 +549,18 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
   ~HyperelasticSolver()                    = default;
 };
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyPressureBC(const BoundaryConditionList& bc_list,
-                                                                           const MeshType& mesh,
-                                                                           NodeValue<Rd>& br) const
+HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyPressureBC(const BoundaryConditionList& bc_list,
+                                                                          const MeshType& mesh,
+                                                                          NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<PressureBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();
 
@@ -627,18 +614,18 @@ HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyPressureBC(const
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyNormalStressBC(const BoundaryConditionList& bc_list,
-                                                                               const MeshType& mesh,
-                                                                               NodeValue<Rd>& br) const
+HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyNormalStressBC(const BoundaryConditionList& bc_list,
+                                                                              const MeshType& mesh,
+                                                                              NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
       [&](auto&& bc) {
         using T = std::decay_t<decltype(bc)>;
         if constexpr (std::is_same_v<NormalStressBoundaryCondition, T>) {
-          MeshData<Dimension>& mesh_data = MeshDataManager::instance().getMeshData(mesh);
+          MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(mesh);
           if constexpr (Dimension == 1) {
             const NodeValuePerCell<const Rd> Cjr = mesh_data.Cjr();
 
@@ -692,11 +679,11 @@ HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyNormalStressBC(c
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applySymmetryBC(const BoundaryConditionList& bc_list,
-                                                                           NodeValue<Rdxd>& Ar,
-                                                                           NodeValue<Rd>& br) const
+HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applySymmetryBC(const BoundaryConditionList& bc_list,
+                                                                          NodeValue<Rdxd>& Ar,
+                                                                          NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
@@ -723,11 +710,11 @@ HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applySymmetryBC(const
   }
 }
 
-template <size_t Dimension>
+template <MeshConcept MeshType>
 void
-HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyVelocityBC(const BoundaryConditionList& bc_list,
-                                                                           NodeValue<Rdxd>& Ar,
-                                                                           NodeValue<Rd>& br) const
+HyperelasticSolverHandler::HyperelasticSolver<MeshType>::_applyVelocityBC(const BoundaryConditionList& bc_list,
+                                                                          NodeValue<Rdxd>& Ar,
+                                                                          NodeValue<Rd>& br) const
 {
   for (const auto& boundary_condition : bc_list) {
     std::visit(
@@ -760,11 +747,11 @@ HyperelasticSolverHandler::HyperelasticSolver<Dimension>::_applyVelocityBC(const
   }
 }
 
-template <size_t Dimension>
-class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::FixedBoundaryCondition
+template <MeshConcept MeshType>
+class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::FixedBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
  public:
   const Array<const NodeId>&
@@ -773,18 +760,16 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::FixedBoundaryCon
     return m_mesh_node_boundary.nodeList();
   }
 
-  FixedBoundaryCondition(const MeshNodeBoundary<Dimension> mesh_node_boundary)
-    : m_mesh_node_boundary{mesh_node_boundary}
-  {}
+  FixedBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
 
   ~FixedBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::PressureBoundaryCondition
+template <MeshConcept MeshType>
+class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::PressureBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
   const Array<const double> m_value_list;
 
  public:
@@ -800,8 +785,7 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::PressureBoundary
     return m_value_list;
   }
 
-  PressureBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary,
-                            const Array<const double>& value_list)
+  PressureBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const double>& value_list)
     : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list}
   {}
 
@@ -809,10 +793,10 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::PressureBoundary
 };
 
 template <>
-class HyperelasticSolverHandler::HyperelasticSolver<1>::PressureBoundaryCondition
+class HyperelasticSolverHandler::HyperelasticSolver<Mesh<1>>::PressureBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<1> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
   const Array<const double> m_value_list;
 
  public:
@@ -828,18 +812,18 @@ class HyperelasticSolverHandler::HyperelasticSolver<1>::PressureBoundaryConditio
     return m_value_list;
   }
 
-  PressureBoundaryCondition(const MeshNodeBoundary<1>& mesh_node_boundary, const Array<const double>& value_list)
+  PressureBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const double>& value_list)
     : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list}
   {}
 
   ~PressureBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::NormalStressBoundaryCondition
+template <MeshConcept MeshType>
+class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::NormalStressBoundaryCondition
 {
  private:
-  const MeshFaceBoundary<Dimension> m_mesh_face_boundary;
+  const MeshFaceBoundary m_mesh_face_boundary;
   const Array<const Rdxd> m_value_list;
 
  public:
@@ -855,8 +839,7 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::NormalStressBoun
     return m_value_list;
   }
 
-  NormalStressBoundaryCondition(const MeshFaceBoundary<Dimension>& mesh_face_boundary,
-                                const Array<const Rdxd>& value_list)
+  NormalStressBoundaryCondition(const MeshFaceBoundary& mesh_face_boundary, const Array<const Rdxd>& value_list)
     : m_mesh_face_boundary{mesh_face_boundary}, m_value_list{value_list}
   {}
 
@@ -864,10 +847,10 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::NormalStressBoun
 };
 
 template <>
-class HyperelasticSolverHandler::HyperelasticSolver<1>::NormalStressBoundaryCondition
+class HyperelasticSolverHandler::HyperelasticSolver<Mesh<1>>::NormalStressBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<1> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
   const Array<const Rdxd> m_value_list;
 
  public:
@@ -883,18 +866,18 @@ class HyperelasticSolverHandler::HyperelasticSolver<1>::NormalStressBoundaryCond
     return m_value_list;
   }
 
-  NormalStressBoundaryCondition(const MeshNodeBoundary<1>& mesh_node_boundary, const Array<const Rdxd>& value_list)
+  NormalStressBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary, const Array<const Rdxd>& value_list)
     : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list}
   {}
 
   ~NormalStressBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::VelocityBoundaryCondition
+template <MeshConcept MeshType>
+class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::VelocityBoundaryCondition
 {
  private:
-  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const MeshNodeBoundary m_mesh_node_boundary;
 
   const Array<const TinyVector<Dimension>> m_value_list;
 
@@ -911,7 +894,7 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::VelocityBoundary
     return m_value_list;
   }
 
-  VelocityBoundaryCondition(const MeshNodeBoundary<Dimension>& mesh_node_boundary,
+  VelocityBoundaryCondition(const MeshNodeBoundary& mesh_node_boundary,
                             const Array<const TinyVector<Dimension>>& value_list)
     : m_mesh_node_boundary{mesh_node_boundary}, m_value_list{value_list}
   {}
@@ -919,14 +902,14 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::VelocityBoundary
   ~VelocityBoundaryCondition() = default;
 };
 
-template <size_t Dimension>
-class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::SymmetryBoundaryCondition
+template <MeshConcept MeshType>
+class HyperelasticSolverHandler::HyperelasticSolver<MeshType>::SymmetryBoundaryCondition
 {
  public:
   using Rd = TinyVector<Dimension, double>;
 
  private:
-  const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+  const MeshFlatNodeBoundary<MeshType> m_mesh_flat_node_boundary;
 
  public:
   const Rd&
@@ -947,7 +930,7 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::SymmetryBoundary
     return m_mesh_flat_node_boundary.nodeList();
   }
 
-  SymmetryBoundaryCondition(const MeshFlatNodeBoundary<Dimension>& mesh_flat_node_boundary)
+  SymmetryBoundaryCondition(const MeshFlatNodeBoundary<MeshType>& mesh_flat_node_boundary)
     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
   {
     ;
@@ -956,27 +939,20 @@ class HyperelasticSolverHandler::HyperelasticSolver<Dimension>::SymmetryBoundary
   ~SymmetryBoundaryCondition() = default;
 };
 
-HyperelasticSolverHandler::HyperelasticSolverHandler(const std::shared_ptr<const IMesh>& i_mesh)
+HyperelasticSolverHandler::HyperelasticSolverHandler(const std::shared_ptr<const MeshVariant>& i_mesh)
 {
   if (not i_mesh) {
     throw NormalError("discrete functions are not defined on the same mesh");
   }
 
-  switch (i_mesh->dimension()) {
-  case 1: {
-    m_hyperelastic_solver = std::make_unique<HyperelasticSolver<1>>();
-    break;
-  }
-  case 2: {
-    m_hyperelastic_solver = std::make_unique<HyperelasticSolver<2>>();
-    break;
-  }
-  case 3: {
-    m_hyperelastic_solver = std::make_unique<HyperelasticSolver<3>>();
-    break;
-  }
-  default: {
-    throw UnexpectedError("invalid mesh dimension");
-  }
-  }
+  std::visit(
+    [&](auto&& mesh) {
+      using MeshType = mesh_type_t<decltype(mesh)>;
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
+        m_hyperelastic_solver = std::make_unique<HyperelasticSolver<MeshType>>();
+      } else {
+        throw NormalError("unexpected mesh type");
+      }
+    },
+    i_mesh->variant());
 }
diff --git a/src/scheme/HyperelasticSolver.hpp b/src/scheme/HyperelasticSolver.hpp
index f82cbe361051efc64a3e413d60c830febd62d907..0fe58e8572aac1a4dabc66da20f2634211f500de 100644
--- a/src/scheme/HyperelasticSolver.hpp
+++ b/src/scheme/HyperelasticSolver.hpp
@@ -1,12 +1,14 @@
 #ifndef HYPERELASTIC_SOLVER_HPP
 #define HYPERELASTIC_SOLVER_HPP
 
+#include <mesh/MeshTraits.hpp>
+
 #include <memory>
 #include <tuple>
 #include <vector>
 
 class IBoundaryConditionDescriptor;
-class IMesh;
+class MeshVariant;
 class ItemValueVariant;
 class SubItemValuePerItemVariant;
 class DiscreteFunctionVariant;
@@ -36,7 +38,7 @@ class HyperelasticSolverHandler
       const std::shared_ptr<const DiscreteFunctionVariant>& sigma,
       const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0;
 
-    virtual std::tuple<std::shared_ptr<const IMesh>,
+    virtual std::tuple<std::shared_ptr<const MeshVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
@@ -49,7 +51,7 @@ class HyperelasticSolverHandler
                  const std::shared_ptr<const ItemValueVariant>& ur,
                  const std::shared_ptr<const SubItemValuePerItemVariant>& Fjr) const = 0;
 
-    virtual std::tuple<std::shared_ptr<const IMesh>,
+    virtual std::tuple<std::shared_ptr<const MeshVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
                        std::shared_ptr<const DiscreteFunctionVariant>,
@@ -65,14 +67,14 @@ class HyperelasticSolverHandler
           const std::shared_ptr<const DiscreteFunctionVariant>& p,
           const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const = 0;
 
-    IHyperelasticSolver()                                 = default;
-    IHyperelasticSolver(IHyperelasticSolver&&)            = default;
+    IHyperelasticSolver()                      = default;
+    IHyperelasticSolver(IHyperelasticSolver&&) = default;
     IHyperelasticSolver& operator=(IHyperelasticSolver&&) = default;
 
     virtual ~IHyperelasticSolver() = default;
   };
 
-  template <size_t Dimension>
+  template <MeshConcept MeshType>
   class HyperelasticSolver;
 
   std::unique_ptr<IHyperelasticSolver> m_hyperelastic_solver;
@@ -84,7 +86,7 @@ class HyperelasticSolverHandler
     return *m_hyperelastic_solver;
   }
 
-  HyperelasticSolverHandler(const std::shared_ptr<const IMesh>& mesh);
+  HyperelasticSolverHandler(const std::shared_ptr<const MeshVariant>& mesh);
 };
 
 #endif   // HYPERELASTIC_SOLVER_HPP
diff --git a/src/utils/GlobalVariableManager.hpp b/src/utils/GlobalVariableManager.hpp
index 9df677fac507545ca7559ecca4b6f55197e35c48..f720252fdd7c04b1ddd8e234d890bf5b1956c899 100644
--- a/src/utils/GlobalVariableManager.hpp
+++ b/src/utils/GlobalVariableManager.hpp
@@ -8,6 +8,7 @@ class GlobalVariableManager
 {
  private:
   size_t m_connectivity_id = 0;
+  size_t m_mesh_id         = 0;
 
   static GlobalVariableManager* m_instance;
 
@@ -24,6 +25,13 @@ class GlobalVariableManager
     return m_connectivity_id++;
   }
 
+  PUGS_INLINE
+  size_t
+  getAndIncrementMeshId()
+  {
+    return m_mesh_id++;
+  }
+
   PUGS_INLINE
   static GlobalVariableManager&
   instance()
diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp
index 285d7978d8a8f05e784534d0fb475bc01da75042..2e1d409578c9295a1ad032466842c2f336ba272f 100644
--- a/src/utils/PugsTraits.hpp
+++ b/src/utils/PugsTraits.hpp
@@ -20,10 +20,10 @@ class ItemValue;
 template <typename DataType, ItemType item_type, typename ConnectivityPtr>
 class ItemArray;
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 class DiscreteFunctionP0;
 
-template <size_t Dimension, typename DataType>
+template <typename DataType>
 class DiscreteFunctionP0Vector;
 
 // Traits is_trivially_castable
@@ -135,16 +135,16 @@ constexpr inline bool is_item_array_v<ItemArray<DataType, item_type, Connectivit
 template <typename T>
 constexpr inline bool is_discrete_function_P0_v = false;
 
-template <size_t Dimension, typename DataType>
-constexpr inline bool is_discrete_function_P0_v<DiscreteFunctionP0<Dimension, DataType>> = true;
+template <typename DataType>
+constexpr inline bool is_discrete_function_P0_v<DiscreteFunctionP0<DataType>> = true;
 
 // Trais is DiscreteFunctionP0Vector
 
 template <typename T>
 constexpr inline bool is_discrete_function_P0_vector_v = false;
 
-template <size_t Dimension, typename DataType>
-constexpr inline bool is_discrete_function_P0_vector_v<DiscreteFunctionP0Vector<Dimension, DataType>> = true;
+template <typename DataType>
+constexpr inline bool is_discrete_function_P0_vector_v<DiscreteFunctionP0Vector<DataType>> = true;
 
 // Trais is DiscreteFunction
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index aaace485deb7ed83e66d80c2e75d9bce2cc9bc21..e7665f8944202ff0c139ca1ccce1403026891f50 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -197,6 +197,7 @@ add_executable (mpi_unit_tests
   test_MeshLineNodeBoundary.cpp
   test_MeshNodeBoundary.cpp
   test_MeshNodeInterface.cpp
+  test_MeshVariant.cpp
   test_Messenger.cpp
   test_OFStream.cpp
   test_ParallelChecker_read.cpp
diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp
index ab9d957cfe8dc0f1d178643884369c150ad7e239..1f2b485edcb7f7b3fadf35d845b59f23c1a03274 100644
--- a/tests/MeshDataBaseForTests.cpp
+++ b/tests/MeshDataBaseForTests.cpp
@@ -2,6 +2,7 @@
 #include <mesh/CartesianMeshBuilder.hpp>
 #include <mesh/Connectivity.hpp>
 #include <mesh/GmshReader.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Messenger.hpp>
 #include <utils/PugsAssert.hpp>
 
@@ -12,14 +13,13 @@ const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr;
 
 MeshDataBaseForTests::MeshDataBaseForTests()
 {
-  m_cartesian_1d_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(
-    CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh());
+  m_cartesian_1d_mesh = CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{23}}.mesh();
 
-  m_cartesian_2d_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(
-    CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh());
+  m_cartesian_2d_mesh =
+    CartesianMeshBuilder{TinyVector<2>{0, -1}, TinyVector<2>{3, 2}, TinyVector<2, size_t>{6, 7}}.mesh();
 
-  m_cartesian_3d_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(
-    CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh());
+  m_cartesian_3d_mesh =
+    CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh();
 
   m_unordered_1d_mesh = _buildUnordered1dMesh();
   m_hybrid_2d_mesh    = _buildHybrid2dMesh();
@@ -47,43 +47,43 @@ MeshDataBaseForTests::destroy()
   m_instance = nullptr;
 }
 
-std::shared_ptr<const Mesh<Connectivity<1>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::cartesian1DMesh() const
 {
   return m_cartesian_1d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<2>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::cartesian2DMesh() const
 {
   return m_cartesian_2d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<3>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::cartesian3DMesh() const
 {
   return m_cartesian_3d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<1>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::unordered1DMesh() const
 {
   return m_unordered_1d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<2>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::hybrid2DMesh() const
 {
   return m_hybrid_2d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<3>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::hybrid3DMesh() const
 {
   return m_hybrid_3d_mesh;
 }
 
-std::shared_ptr<const Mesh<Connectivity<1>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::_buildUnordered1dMesh()
 {
   const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("unordered-1d.msh");
@@ -181,10 +181,10 @@ $EndElements
 )";
   }
 
-  return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(GmshReader{filename}.mesh());
+  return GmshReader{filename}.mesh();
 }
 
-std::shared_ptr<const Mesh<Connectivity<2>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::_buildHybrid2dMesh()
 {
   const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-2d.msh");
@@ -358,10 +358,10 @@ $Elements
 $EndElements
 )";
   }
-  return std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(GmshReader{filename}.mesh());
+  return GmshReader{filename}.mesh();
 }
 
-std::shared_ptr<const Mesh<Connectivity<3>>>
+std::shared_ptr<const MeshVariant>
 MeshDataBaseForTests::_buildHybrid3dMesh()
 {
   const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-3d.msh");
@@ -986,5 +986,5 @@ $Periodic
 $EndPeriodic
 )";
   }
-  return std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(GmshReader{filename}.mesh());
+  return GmshReader{filename}.mesh();
 }
diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp
index 79f3379f0cbfd51416594aa6015d66aa0b9f861c..2b092d544271fbd1b1d0488ed73c9c5a401d548b 100644
--- a/tests/MeshDataBaseForTests.hpp
+++ b/tests/MeshDataBaseForTests.hpp
@@ -1,32 +1,23 @@
 #ifndef MESH_DATA_BASE_FOR_TESTS_HPP
 #define MESH_DATA_BASE_FOR_TESTS_HPP
 
-#include <mesh/IMesh.hpp>
-
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityT>
-class Mesh;
-
 #include <array>
 #include <memory>
 #include <string>
 
+class MeshVariant;
+
 class MeshDataBaseForTests
 {
  public:
-  template <size_t Dimension>
   class NamedMesh
   {
    private:
     const std::string m_name;
-    const std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh;
+    const std::shared_ptr<const MeshVariant> m_mesh_v;
 
    public:
-    NamedMesh(const std::string& name, const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh)
-      : m_name(name), m_mesh(mesh)
-    {}
+    NamedMesh(const std::string& name, const std::shared_ptr<const MeshVariant>& mesh) : m_name(name), m_mesh_v(mesh) {}
 
     const std::string&
     name() const
@@ -37,7 +28,7 @@ class MeshDataBaseForTests
     auto
     mesh() const
     {
-      return m_mesh;
+      return m_mesh_v;
     }
   };
 
@@ -46,27 +37,27 @@ class MeshDataBaseForTests
 
   static const MeshDataBaseForTests* m_instance;
 
-  std::shared_ptr<const Mesh<Connectivity<1>>> m_cartesian_1d_mesh;
-  std::shared_ptr<const Mesh<Connectivity<2>>> m_cartesian_2d_mesh;
-  std::shared_ptr<const Mesh<Connectivity<3>>> m_cartesian_3d_mesh;
+  std::shared_ptr<const MeshVariant> m_cartesian_1d_mesh;
+  std::shared_ptr<const MeshVariant> m_cartesian_2d_mesh;
+  std::shared_ptr<const MeshVariant> m_cartesian_3d_mesh;
 
-  std::shared_ptr<const Mesh<Connectivity<1>>> m_unordered_1d_mesh;
-  std::shared_ptr<const Mesh<Connectivity<2>>> m_hybrid_2d_mesh;
-  std::shared_ptr<const Mesh<Connectivity<3>>> m_hybrid_3d_mesh;
+  std::shared_ptr<const MeshVariant> m_unordered_1d_mesh;
+  std::shared_ptr<const MeshVariant> m_hybrid_2d_mesh;
+  std::shared_ptr<const MeshVariant> m_hybrid_3d_mesh;
 
-  std::shared_ptr<const Mesh<Connectivity<1>>> _buildUnordered1dMesh();
-  std::shared_ptr<const Mesh<Connectivity<2>>> _buildHybrid2dMesh();
-  std::shared_ptr<const Mesh<Connectivity<3>>> _buildHybrid3dMesh();
+  std::shared_ptr<const MeshVariant> _buildUnordered1dMesh();
+  std::shared_ptr<const MeshVariant> _buildHybrid2dMesh();
+  std::shared_ptr<const MeshVariant> _buildHybrid3dMesh();
 
  public:
-  std::shared_ptr<const Mesh<Connectivity<1>>> cartesian1DMesh() const;
-  std::shared_ptr<const Mesh<Connectivity<1>>> unordered1DMesh() const;
+  std::shared_ptr<const MeshVariant> cartesian1DMesh() const;
+  std::shared_ptr<const MeshVariant> unordered1DMesh() const;
 
-  std::shared_ptr<const Mesh<Connectivity<2>>> cartesian2DMesh() const;
-  std::shared_ptr<const Mesh<Connectivity<2>>> hybrid2DMesh() const;
+  std::shared_ptr<const MeshVariant> cartesian2DMesh() const;
+  std::shared_ptr<const MeshVariant> hybrid2DMesh() const;
 
-  std::shared_ptr<const Mesh<Connectivity<3>>> cartesian3DMesh() const;
-  std::shared_ptr<const Mesh<Connectivity<3>>> hybrid3DMesh() const;
+  std::shared_ptr<const MeshVariant> cartesian3DMesh() const;
+  std::shared_ptr<const MeshVariant> hybrid3DMesh() const;
 
   static const MeshDataBaseForTests& get();
 
diff --git a/tests/test_CellIntegrator.cpp b/tests/test_CellIntegrator.cpp
index 9b286a44508726c5a286964a89e8acec80ef12ec..d5e7dddece7d2e644194ac5c6cb4e62bcd3dea07 100644
--- a/tests/test_CellIntegrator.cpp
+++ b/tests/test_CellIntegrator.cpp
@@ -21,7 +21,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
     {
       using R1 = TinyVector<1>;
 
-      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       auto f          = [](const R1& x) -> double { return x[0] * x[0] + 1; };
 
       Array<const double> int_f_per_cell = [=] {
@@ -236,7 +236,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
     {
       using R2 = TinyVector<2>;
 
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> double {
         const double x = X[0];
@@ -495,11 +495,11 @@ TEST_CASE("CellIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         SECTION(mesh_name)
         {
@@ -943,7 +943,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
     {
       using R1 = TinyVector<1>;
 
-      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       auto f          = [](const R1& x) -> R2 { return R2{x[0] * x[0] + 1, 2 * x[0]}; };
 
       Array<const R2> int_f_per_cell = [=] {
@@ -1156,7 +1156,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
 
     SECTION("2D")
     {
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> R2 {
         const double x = X[0];
@@ -1414,11 +1414,11 @@ TEST_CASE("CellIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         SECTION(mesh_name)
         {
@@ -1863,7 +1863,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
     {
       using R1 = TinyVector<1>;
 
-      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       auto f          = [](const R1& x) -> R2x2 { return R2x2{x[0] * x[0] + 1, 2 * x[0], 3 - x[0], 3 * x[0] - 1}; };
 
       Array<const R2x2> int_f_per_cell = [=] {
@@ -2081,7 +2081,7 @@ TEST_CASE("CellIntegrator", "[scheme]")
     {
       using R2 = TinyVector<2>;
 
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> R2x2 {
         const double x = X[0];
@@ -2341,11 +2341,11 @@ TEST_CASE("CellIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         SECTION(mesh_name)
         {
diff --git a/tests/test_Connectivity.cpp b/tests/test_Connectivity.cpp
index b0465d08489c644685500d59b145e12594966522..c64988ec2ffb34c273290d65709a0056e392222f 100644
--- a/tests/test_Connectivity.cpp
+++ b/tests/test_Connectivity.cpp
@@ -7,6 +7,7 @@
 #include <mesh/ItemValue.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
 #include <utils/Messenger.hpp>
 
 // clazy:excludeall=non-pod-global-static
@@ -19,7 +20,7 @@ TEST_CASE("Connectivity", "[mesh]")
     {
       SECTION("unordered 1D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 35);
@@ -36,7 +37,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
       SECTION("cartesian 1D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 24);
@@ -56,7 +57,7 @@ TEST_CASE("Connectivity", "[mesh]")
     {
       SECTION("hybrid 2D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 53);
@@ -73,7 +74,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
       SECTION("cartesian 2D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 56);
@@ -93,7 +94,7 @@ TEST_CASE("Connectivity", "[mesh]")
     {
       SECTION("hybrid 3D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 132);
@@ -110,7 +111,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
       SECTION("cartesian 3D mesh")
       {
-        const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh();
+        const auto& mesh = *MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>();
 
         if (parallel::size() == 1) {
           REQUIRE(mesh.numberOfNodes() == 280);
@@ -136,7 +137,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d                        = named_mesh.mesh();
+          auto mesh_1d                        = named_mesh.mesh()->get<Mesh<1>>();
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
           SECTION("nodes/edges/faces")
@@ -227,7 +228,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d                        = named_mesh.mesh();
+          auto mesh_2d                        = named_mesh.mesh()->get<Mesh<2>>();
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
           SECTION("nodes")
@@ -330,7 +331,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -447,7 +448,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d                        = named_mesh.mesh();
+          auto mesh_1d                        = named_mesh.mesh()->get<Mesh<1>>();
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
           auto is_boundary_node = connectivity.isBoundaryNode();
@@ -496,7 +497,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d                        = named_mesh.mesh();
+          auto mesh_2d                        = named_mesh.mesh()->get<Mesh<2>>();
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
           SECTION("faces/edges")
@@ -574,7 +575,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -682,7 +683,7 @@ TEST_CASE("Connectivity", "[mesh]")
         {
           SECTION("cell -> nodes")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<1>>();
             auto xr   = mesh->xr();
 
             const Connectivity<1>& connectivity = mesh->connectivity();
@@ -701,7 +702,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<1>>();
 
             const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -730,7 +731,7 @@ TEST_CASE("Connectivity", "[mesh]")
         {
           SECTION("face -> nodes")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
             const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -750,7 +751,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> faces")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
             const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -769,7 +770,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
             const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -788,7 +789,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("face -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
             const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -817,7 +818,7 @@ TEST_CASE("Connectivity", "[mesh]")
         {
           SECTION("edge -> nodes")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -837,7 +838,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("face -> nodes")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -864,7 +865,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> edges")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -886,7 +887,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> faces")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -905,7 +906,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("node -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -924,7 +925,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("edge -> faces")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -943,7 +944,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("edge -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -962,7 +963,7 @@ TEST_CASE("Connectivity", "[mesh]")
 
           SECTION("face -> cells")
           {
-            auto mesh = named_mesh.mesh();
+            auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
             const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -1019,7 +1020,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh                           = named_mesh.mesh();
+          auto mesh                           = named_mesh.mesh()->get<Mesh<1>>();
           const Connectivity<1>& connectivity = mesh->connectivity();
 
           SECTION("node <-> cell")
@@ -1091,7 +1092,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh                           = named_mesh.mesh();
+          auto mesh                           = named_mesh.mesh()->get<Mesh<2>>();
           const Connectivity<2>& connectivity = mesh->connectivity();
 
           SECTION("node <-> cell")
@@ -1193,7 +1194,7 @@ TEST_CASE("Connectivity", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh                           = named_mesh.mesh();
+          auto mesh                           = named_mesh.mesh()->get<Mesh<3>>();
           const Connectivity<3>& connectivity = mesh->connectivity();
 
           SECTION("node <-> cell")
diff --git a/tests/test_DiamondDualConnectivityBuilder.cpp b/tests/test_DiamondDualConnectivityBuilder.cpp
index 45d9cd700ca7b5ae73e6c6fd22db024e7de3126c..2a5d1d4b6f72ff39666536eaa0146c5e80a3029b 100644
--- a/tests/test_DiamondDualConnectivityBuilder.cpp
+++ b/tests/test_DiamondDualConnectivityBuilder.cpp
@@ -37,10 +37,10 @@ TEST_CASE("DiamondDualConnectivityBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     REQUIRE(primal_connectivity.numberOfNodes() == 53);
@@ -147,10 +147,10 @@ TEST_CASE("DiamondDualConnectivityBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     REQUIRE(primal_connectivity.numberOfNodes() == 132);
diff --git a/tests/test_DiamondDualMeshBuilder.cpp b/tests/test_DiamondDualMeshBuilder.cpp
index 3b9b2564eadd3ca97aee05651a2bd769b28bb1c0..c1f95caab35d9ffa2944f8effb5f9ab7fd598770 100644
--- a/tests/test_DiamondDualMeshBuilder.cpp
+++ b/tests/test_DiamondDualMeshBuilder.cpp
@@ -18,18 +18,17 @@ TEST_CASE("DiamondDualMeshBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType = Mesh<Dimension>;
 
-    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid2DMesh();
-    const MeshType& primal_mesh = *p_mesh;
+    std::shared_ptr primal_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    const MeshType& primal_mesh   = *primal_mesh_v->get<Mesh<Dimension>>();
 
     REQUIRE(primal_mesh.numberOfNodes() == 53);
     REQUIRE(primal_mesh.numberOfFaces() == 110);
     REQUIRE(primal_mesh.numberOfCells() == 58);
 
-    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(primal_mesh);
-    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+    std::shared_ptr diamond_dual_mesh_v = DualMeshManager::instance().getDiamondDualMesh(primal_mesh_v);
+    const MeshType& dual_mesh           = *diamond_dual_mesh_v->get<Mesh<Dimension>>();
 
     REQUIRE(dual_mesh.numberOfNodes() == 111);
     REQUIRE(dual_mesh.numberOfFaces() == 220);
@@ -85,19 +84,18 @@ TEST_CASE("DiamondDualMeshBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType = Mesh<Dimension>;
 
-    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid3DMesh();
-    const MeshType& primal_mesh = *p_mesh;
+    std::shared_ptr primal_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    const MeshType& primal_mesh   = *primal_mesh_v->get<Mesh<Dimension>>();
 
     REQUIRE(primal_mesh.numberOfNodes() == 132);
     REQUIRE(primal_mesh.numberOfEdges() == 452);
     REQUIRE(primal_mesh.numberOfFaces() == 520);
     REQUIRE(primal_mesh.numberOfCells() == 199);
 
-    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(primal_mesh);
-    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+    std::shared_ptr diamond_dual_mesh_v = DualMeshManager::instance().getDiamondDualMesh(primal_mesh_v);
+    const MeshType& dual_mesh           = *diamond_dual_mesh_v->get<Mesh<Dimension>>();
 
     REQUIRE(dual_mesh.numberOfNodes() == 331);
     REQUIRE(dual_mesh.numberOfEdges() == 1461);
diff --git a/tests/test_DiscreteFunctionIntegrator.cpp b/tests/test_DiscreteFunctionIntegrator.cpp
index 8970a360d6c0bf96feae69f17df05ba806cc44ef..1ab77c2e34478946bdffb81f7e522c7a1fd6d544 100644
--- a/tests/test_DiscreteFunctionIntegrator.cpp
+++ b/tests/test_DiscreteFunctionIntegrator.cpp
@@ -47,8 +47,6 @@ TEST_CASE("DiscreteFunctionIntegrator", "[scheme]")
 
   SECTION("1D")
   {
-    constexpr size_t Dimension = 1;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
     std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
@@ -56,7 +54,7 @@ TEST_CASE("DiscreteFunctionIntegrator", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
 
         std::string_view data = R"(
 import math;
@@ -99,12 +97,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_1d);
+            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_1d")
@@ -116,12 +114,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_1d);
+            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_1d")
@@ -133,12 +131,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_1d);
+            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_1d")
@@ -150,12 +148,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_1d);
+            IntegrateCellValue<double(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_1d")
@@ -170,12 +168,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_1d")
@@ -190,12 +188,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_1d")
@@ -210,12 +208,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_1d")
@@ -230,12 +228,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_1d")
@@ -250,12 +248,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_1d")
@@ -270,12 +268,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<1>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_1d);
+                                                                   mesh_1d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_1d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_1d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
@@ -283,8 +281,6 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
   SECTION("2D")
   {
-    constexpr size_t Dimension = 2;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
     std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
@@ -292,7 +288,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
 
         std::string_view data = R"(
 import math;
@@ -335,12 +331,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_2d);
+            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_2d")
@@ -352,12 +348,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_2d);
+            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_2d")
@@ -369,12 +365,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_2d);
+            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_2d")
@@ -386,12 +382,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_2d);
+            IntegrateCellValue<double(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_2d")
@@ -406,12 +402,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_2d")
@@ -426,12 +422,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_2d")
@@ -446,12 +442,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_2d")
@@ -466,12 +462,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_2d")
@@ -486,12 +482,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_2d")
@@ -506,12 +502,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<2>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_2d);
+                                                                   mesh_2d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_2d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_2d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
@@ -519,8 +515,6 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
   SECTION("3D")
   {
-    constexpr size_t Dimension = 3;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
     std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
@@ -528,7 +522,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
 
         std::string_view data = R"(
 import math;
@@ -571,12 +565,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_3d);
+            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_3d")
@@ -588,12 +582,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_3d);
+            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_3d")
@@ -605,12 +599,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_3d);
+            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_3d")
@@ -622,12 +616,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
           CellValue<double> cell_value =
-            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, *mesh_3d);
+            IntegrateCellValue<double(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor, mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_3d")
@@ -642,12 +636,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_3d")
@@ -662,12 +656,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_3d")
@@ -682,12 +676,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_3d")
@@ -702,12 +696,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_3d")
@@ -722,12 +716,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_3d")
@@ -742,12 +736,12 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
 
           CellValue<DataType> cell_value =
             IntegrateCellValue<DataType(TinyVector<3>)>::integrate(function_symbol_id, *quadrature_descriptor,
-                                                                   *mesh_3d);
+                                                                   mesh_3d_v);
 
-          DiscreteFunctionIntegrator integrator(mesh_3d, quadrature_descriptor, function_symbol_id);
+          DiscreteFunctionIntegrator integrator(mesh_3d_v, quadrature_descriptor, function_symbol_id);
           DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
diff --git a/tests/test_DiscreteFunctionIntegratorByZone.cpp b/tests/test_DiscreteFunctionIntegratorByZone.cpp
index 4027507e4c06ab7b97acec7a42707a03536f79c8..608a523b40b8e169bfe8e27c833214f8281c593a 100644
--- a/tests/test_DiscreteFunctionIntegratorByZone.cpp
+++ b/tests/test_DiscreteFunctionIntegratorByZone.cpp
@@ -49,11 +49,10 @@ TEST_CASE("DiscreteFunctionIntegratorByZone", "[scheme]")
 
   SECTION("1D")
   {
-    constexpr size_t Dimension = 1;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -114,10 +113,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_1d")
@@ -141,10 +140,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_1d")
@@ -168,10 +167,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_1d")
@@ -195,10 +194,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_1d")
@@ -224,10 +223,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_1d")
@@ -253,10 +252,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_1d")
@@ -282,10 +281,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_1d")
@@ -311,10 +310,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_1d")
@@ -340,10 +339,10 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_1d")
@@ -369,20 +368,19 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
   SECTION("2D")
   {
-    constexpr size_t Dimension = 2;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -443,10 +441,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_2d")
@@ -470,10 +468,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_2d")
@@ -497,10 +495,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_2d")
@@ -524,10 +522,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_2d")
@@ -553,10 +551,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_2d")
@@ -582,10 +580,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_2d")
@@ -611,10 +609,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_2d")
@@ -640,10 +638,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_2d")
@@ -669,10 +667,10 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_2d")
@@ -698,20 +696,19 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
   SECTION("3D")
   {
-    constexpr size_t Dimension = 3;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -772,10 +769,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_3d")
@@ -799,10 +796,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_3d")
@@ -826,10 +823,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_3d")
@@ -853,10 +850,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_3d")
@@ -882,10 +879,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_3d")
@@ -911,10 +908,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_3d")
@@ -940,10 +937,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_3d")
@@ -969,10 +966,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_3d")
@@ -998,10 +995,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_3d")
@@ -1027,10 +1024,10 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           cell_value[cell_id]  = array[i];
         });
 
-      DiscreteFunctionIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor, function_symbol_id);
+      DiscreteFunctionIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor, function_symbol_id);
       DiscreteFunctionVariant discrete_function = integrator.integrate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
diff --git a/tests/test_DiscreteFunctionInterpoler.cpp b/tests/test_DiscreteFunctionInterpoler.cpp
index ed28e8f929d3b640395a17981c3b86abb57018bd..bb27d85aa962fef7061cad81750a4e808dfe92a7 100644
--- a/tests/test_DiscreteFunctionInterpoler.cpp
+++ b/tests/test_DiscreteFunctionInterpoler.cpp
@@ -52,7 +52,8 @@ TEST_CASE("DiscreteFunctionInterpoler", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -103,11 +104,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_1d")
@@ -125,11 +126,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_1d")
@@ -147,11 +148,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_1d")
@@ -169,11 +170,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_1d")
@@ -193,11 +194,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_1d")
@@ -217,11 +218,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[0]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_1d")
@@ -241,11 +242,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_1d")
@@ -265,11 +266,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_1d")
@@ -290,11 +291,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_1d")
@@ -322,11 +323,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                              std::exp(x[0])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
@@ -341,7 +342,8 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -392,11 +394,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::exp(2 * x[0]) < 2 * x[1];
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_2d")
@@ -414,11 +416,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + 2);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_2d")
@@ -436,11 +438,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 3 * x[1]);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_2d")
@@ -458,11 +460,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = 2 * std::exp(x[0]) + 3 * x[1];
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_2d")
@@ -482,11 +484,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_2d")
@@ -506,11 +508,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[1]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_2d")
@@ -530,11 +532,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_2d")
@@ -554,11 +556,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_2d")
@@ -579,11 +581,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[1] - 2 * x[0]), 3, x[1] * x[0]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_2d")
@@ -612,11 +614,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                              std::exp(x[1])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
@@ -631,7 +633,8 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -682,11 +685,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::exp(2 * x[0]) < 2 * x[1] + x[2];
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("N_scalar_non_linear_3d")
@@ -704,11 +707,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + x[2] * x[2]);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("Z_scalar_non_linear_3d")
@@ -726,11 +729,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 3 * x[1] + x[2]);
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R_scalar_non_linear_3d")
@@ -748,11 +751,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = 2 * std::exp(x[0] + x[2]) + 3 * x[1];
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
         }
 
         SECTION("R1_non_linear_3d")
@@ -772,11 +775,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + std::sin(x[1] + x[2])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2_non_linear_3d")
@@ -796,11 +799,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[1] * x[2]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3_non_linear_3d")
@@ -820,11 +823,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R1x1_non_linear_3d")
@@ -844,11 +847,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3 * x[2]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R2x2_non_linear_3d")
@@ -869,11 +872,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
 
         SECTION("R3x3_non_linear_3d")
@@ -902,11 +905,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                              std::exp(x[1] + x[2])};
             });
 
-          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+          DiscreteFunctionInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                                 function_symbol_id);
           DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+          REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
         }
       }
     }
diff --git a/tests/test_DiscreteFunctionInterpolerByZone.cpp b/tests/test_DiscreteFunctionInterpolerByZone.cpp
index dd242220e3eb17b8d45c8cbf8a83ada5b10acd83..9a228fa7c26099be940ac45c1bbb361277cc9fd7 100644
--- a/tests/test_DiscreteFunctionInterpolerByZone.cpp
+++ b/tests/test_DiscreteFunctionInterpolerByZone.cpp
@@ -49,7 +49,8 @@ TEST_CASE("DiscreteFunctionInterpolerByZone", "[scheme]")
   {
     constexpr size_t Dimension = 1;
 
-    auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -115,11 +116,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_1d")
@@ -141,11 +142,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_1d")
@@ -167,11 +168,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_1d")
@@ -193,11 +194,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_1d")
@@ -221,11 +222,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_1d")
@@ -249,11 +250,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_1d")
@@ -277,11 +278,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_1d")
@@ -305,11 +306,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_1d")
@@ -334,11 +335,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_1d")
@@ -370,11 +371,11 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_1d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
@@ -382,7 +383,8 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
   {
     constexpr size_t Dimension = 2;
 
-    auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -448,11 +450,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_2d")
@@ -474,11 +476,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_2d")
@@ -500,11 +502,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_2d")
@@ -526,11 +528,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_2d")
@@ -554,11 +556,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_2d")
@@ -582,11 +584,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_2d")
@@ -610,11 +612,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_2d")
@@ -638,11 +640,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_2d")
@@ -667,11 +669,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_2d")
@@ -704,11 +706,11 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_2d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_2d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
@@ -716,7 +718,8 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
   {
     constexpr size_t Dimension = 3;
 
-    auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -782,11 +785,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("N_scalar_non_linear_3d")
@@ -808,11 +811,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("Z_scalar_non_linear_3d")
@@ -834,11 +837,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R_scalar_non_linear_3d")
@@ -860,11 +863,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const double>>()));
     }
 
     SECTION("R1_non_linear_3d")
@@ -888,11 +891,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2_non_linear_3d")
@@ -916,11 +919,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3_non_linear_3d")
@@ -944,11 +947,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R1x1_non_linear_3d")
@@ -972,11 +975,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R2x2_non_linear_3d")
@@ -1001,11 +1004,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
 
     SECTION("R3x3_non_linear_3d")
@@ -1038,11 +1041,11 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           }
         });
 
-      DiscreteFunctionInterpoler interpoler(mesh_3d, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
+      DiscreteFunctionInterpoler interpoler(mesh_3d_v, zone_list, std::make_shared<DiscreteFunctionDescriptorP0>(),
                                             function_symbol_id);
       DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
-      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<Dimension, const DataType>>()));
+      REQUIRE(same_cell_value(cell_value, discrete_function.get<DiscreteFunctionP0<const DataType>>()));
     }
   }
 
diff --git a/tests/test_DiscreteFunctionP0.cpp b/tests/test_DiscreteFunctionP0.cpp
index d7d4dff52124e734b1d0d5f6bce1d402fa0f680d..5b1133b4e6c60290b06aa90e5c68b64a1c1b4973 100644
--- a/tests/test_DiscreteFunctionP0.cpp
+++ b/tests/test_DiscreteFunctionP0.cpp
@@ -2,6 +2,7 @@
 #include <catch2/matchers/catch_matchers_all.hpp>
 
 #include <MeshDataBaseForTests.hpp>
+#include <mesh/Mesh.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 
 #ifdef __clang__
@@ -33,31 +34,32 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          auto mesh_v = named_mesh.mesh();
+          auto mesh   = mesh_v->get<Mesh<1>>();
+          DiscreteFunctionP0<double> f{mesh};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh_v->id());
 
           DiscreteFunctionP0 g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
           REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          CellValue<TinyVector<Dimension>> h_values{mesh_v->connectivity()};
           h_values.fill(ZeroType{});
 
-          DiscreteFunctionP0 zero_function{mesh, [&] {
+          DiscreteFunctionP0 zero_function{mesh_v, [&] {
                                              CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
                                              cell_value.fill(ZeroType{});
                                              return cell_value;
                                            }()};
 
-          DiscreteFunctionP0 h{mesh, h_values};
+          DiscreteFunctionP0 h{mesh_v, h_values};
           REQUIRE(same_values(h, zero_function));
           REQUIRE(same_values(h, h_values));
 
-          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          DiscreteFunctionP0<TinyVector<Dimension>> shallow_h{mesh};
           shallow_h = h;
 
           copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
@@ -81,12 +83,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          auto mesh_v = named_mesh.mesh();
+          auto mesh   = mesh_v->get<Mesh<2>>();
+
+          DiscreteFunctionP0<double> f{mesh_v};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh->id());
 
           DiscreteFunctionP0 g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
@@ -105,7 +109,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           REQUIRE(same_values(h, zero_function));
           REQUIRE(same_values(h, h_values));
 
-          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          DiscreteFunctionP0<TinyVector<Dimension>> shallow_h{mesh};
           shallow_h = h;
 
           copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
@@ -129,13 +133,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh_v = named_mesh.mesh();
+          auto mesh   = mesh_v->get<Mesh<3>>();
 
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          DiscreteFunctionP0<double> f{mesh_v};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh_v->id());
 
           DiscreteFunctionP0 g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
@@ -144,7 +149,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
           h_values.fill(ZeroType{});
 
-          DiscreteFunctionP0 zero_function{mesh, [&] {
+          DiscreteFunctionP0 zero_function{mesh_v, [&] {
                                              CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
                                              cell_value.fill(ZeroType{});
                                              return cell_value;
@@ -154,7 +159,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           REQUIRE(same_values(h, zero_function));
           REQUIRE(same_values(h, h_values));
 
-          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          DiscreteFunctionP0<TinyVector<Dimension>> shallow_h{mesh};
           shallow_h = h;
 
           copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
@@ -184,26 +189,24 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      constexpr size_t Dimension = 1;
-
       std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<1>>();
 
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          DiscreteFunctionP0<double> f{mesh};
           f.fill(3);
 
           REQUIRE(all_values_equal(f, 3));
 
-          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          DiscreteFunctionP0<TinyVector<3>> v{mesh};
           v.fill(TinyVector<3>{1, 2, 3});
 
           REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          DiscreteFunctionP0<TinyMatrix<3>> A{mesh};
           A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
           REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
@@ -213,26 +216,24 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("2D")
     {
-      constexpr size_t Dimension = 2;
-
       std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh_v = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          DiscreteFunctionP0<double> f{mesh_v};
           f.fill(3);
 
           REQUIRE(all_values_equal(f, 3));
 
-          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          DiscreteFunctionP0<TinyVector<3>> v{mesh_v};
           v.fill(TinyVector<3>{1, 2, 3});
 
           REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          DiscreteFunctionP0<TinyMatrix<3>> A{mesh_v};
           A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
           REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
@@ -242,26 +243,24 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("3D")
     {
-      constexpr size_t Dimension = 3;
-
       std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
-          DiscreteFunctionP0<Dimension, double> f{mesh};
+          DiscreteFunctionP0<double> f{mesh};
           f.fill(3);
 
           REQUIRE(all_values_equal(f, 3));
 
-          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          DiscreteFunctionP0<TinyVector<3>> v{mesh};
           v.fill(TinyVector<3>{1, 2, 3});
 
           REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          DiscreteFunctionP0<TinyMatrix<3>> A{mesh};
           A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
           REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
@@ -284,21 +283,19 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      constexpr size_t Dimension = 1;
-
       std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh_v = named_mesh.mesh();
 
           SECTION("scalar")
           {
             const size_t value      = parallel::rank() + 1;
             const size_t zero_value = 0;
 
-            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            DiscreteFunctionP0<size_t> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -312,7 +309,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_value);
 
-            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            DiscreteFunctionP0<const size_t> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_value));
@@ -327,7 +324,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
             const TinyVector<2, size_t> zero_vector{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyVector<2, size_t>> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -341,7 +338,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_vector);
 
-            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyVector<2, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_vector));
@@ -356,7 +353,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
             const TinyMatrix<3, 3, size_t> zero_matrix{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyMatrix<3, 3, size_t>> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -370,7 +367,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_matrix);
 
-            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyMatrix<3, 3, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_matrix));
@@ -386,21 +383,19 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("2D")
     {
-      constexpr size_t Dimension = 2;
-
       std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
           SECTION("scalar")
           {
             const size_t value      = parallel::rank() + 1;
             const size_t zero_value = 0;
 
-            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            DiscreteFunctionP0<size_t> f{mesh};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -414,7 +409,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_value);
 
-            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            DiscreteFunctionP0<const size_t> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_value));
@@ -429,7 +424,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
             const TinyVector<2, size_t> zero_vector{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyVector<2, size_t>> f{mesh};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -443,7 +438,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_vector);
 
-            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyVector<2, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_vector));
@@ -458,7 +453,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
             const TinyMatrix<3, 3, size_t> zero_vector{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyMatrix<3, 3, size_t>> f{mesh->meshVariant()};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -472,7 +467,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_vector);
 
-            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyMatrix<3, 3, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_vector));
@@ -488,21 +483,19 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("3D")
     {
-      constexpr size_t Dimension = 3;
-
       std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh_v = named_mesh.mesh();
 
           SECTION("scalar")
           {
             const size_t value      = parallel::rank() + 1;
             const size_t zero_value = 0;
 
-            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            DiscreteFunctionP0<size_t> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -516,7 +509,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_value);
 
-            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            DiscreteFunctionP0<const size_t> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_value));
@@ -531,7 +524,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
             const TinyVector<2, size_t> zero_vector{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyVector<2, size_t>> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -545,7 +538,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_vector);
 
-            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyVector<2, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_vector));
@@ -560,7 +553,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
             const TinyMatrix<3, 3, size_t> zero_matrix{ZeroType{}};
-            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            DiscreteFunctionP0<TinyMatrix<3, 3, size_t>> f{mesh_v};
             f.fill(value);
 
             REQUIRE(all_values_equal(f, value));
@@ -574,7 +567,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             copy_to(g, f);
             g.fill(zero_matrix);
 
-            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<const TinyMatrix<3, 3, size_t>> h = copy(f);
 
             REQUIRE(all_values_equal(f, value));
             REQUIRE(all_values_equal(g, zero_matrix));
@@ -593,13 +586,12 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      constexpr size_t Dimension = 1;
-      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<1>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -607,14 +599,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   f[cell_id]     = 2 * x + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<const double> const_f = f;
 
               Array<double> minus_values{mesh->numberOfCells()};
               parallel_for(
@@ -628,7 +620,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -636,7 +628,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
 
               Array<TinyVector<VectorDimension>> minus_values{mesh->numberOfCells()};
               parallel_for(
@@ -650,7 +642,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -658,7 +650,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
 
               Array<TinyMatrix<MatrixDimension>> minus_values{mesh->numberOfCells()};
               parallel_for(
@@ -677,14 +669,12 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      constexpr size_t Dimension = 1;
-
       std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<1>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -692,22 +682,22 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   f[cell_id]     = 2 * x + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, double> g{mesh};
+              DiscreteFunctionP0<double> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   g[cell_id]     = std::abs((x + 1) * (x - 2)) + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
-              DiscreteFunctionP0<Dimension, const double> const_g{g};
+              DiscreteFunctionP0<const double> const_f = f;
+              DiscreteFunctionP0<const double> const_g{g};
 
               SECTION("sum")
               {
@@ -766,7 +756,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -774,7 +764,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -782,8 +772,8 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   g[cell_id] = X;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_g{g};
 
               SECTION("sum")
               {
@@ -816,7 +806,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -824,7 +814,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -832,8 +822,8 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   g[cell_id] = A;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_g{g};
 
               SECTION("sum")
               {
@@ -880,7 +870,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -889,7 +879,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               const double a = 3;
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<const double> const_f = f;
 
               SECTION("sum")
               {
@@ -963,7 +953,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  DiscreteFunctionP0<TinyVector<3>> v{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -990,7 +980,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<2>> M{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -1031,7 +1021,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1039,7 +1029,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -1098,7 +1088,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -1126,7 +1116,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<VectorDimension>> M{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -1147,7 +1137,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1155,7 +1145,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -1214,7 +1204,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -1258,45 +1248,43 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("2D")
     {
-      constexpr size_t Dimension = 2;
-
       std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-
-          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          auto mesh_v = named_mesh.mesh();
+          auto mesh   = mesh_v->get<Mesh<2>>();
+          auto xj     = MeshDataManager::instance().getMeshData(*mesh).xj();
 
           SECTION("inner operators")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const double y = xj[cell_id][1];
                   f[cell_id]     = 2 * x + y + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, double> g{mesh};
+              DiscreteFunctionP0<double> g{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const double y = xj[cell_id][1];
                   g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y)) + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
-              DiscreteFunctionP0<Dimension, const double> const_g{g};
+              DiscreteFunctionP0<const double> const_f = f;
+              DiscreteFunctionP0<const double> const_g{g};
 
               SECTION("sum")
               {
-                Array<double> sum_values{mesh->numberOfCells()};
+                Array<double> sum_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
                 REQUIRE(same_values(f + g, sum_values));
@@ -1307,9 +1295,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("difference")
               {
-                Array<double> difference_values{mesh->numberOfCells()};
+                Array<double> difference_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
                 REQUIRE(same_values(f - g, difference_values));
@@ -1320,9 +1308,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("product")
               {
-                Array<double> product_values{mesh->numberOfCells()};
+                Array<double> product_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
 
                 REQUIRE(same_values(f * g, product_values));
@@ -1333,9 +1321,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("ratio")
               {
-                Array<double> ratio_values{mesh->numberOfCells()};
+                Array<double> ratio_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
 
                 REQUIRE(same_values(f / g, ratio_values));
@@ -1349,30 +1337,30 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const TinyVector<VectorDimension> X{x, 2 - x};
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> g{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
                   g[cell_id] = X;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_g{g};
 
               SECTION("sum")
               {
-                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                Array<TinyVector<VectorDimension>> sum_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
                 REQUIRE(same_values(f + g, sum_values));
@@ -1383,9 +1371,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("difference")
               {
-                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                Array<TinyVector<VectorDimension>> difference_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
                 REQUIRE(same_values(f - g, difference_values));
@@ -1399,30 +1387,30 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
                   f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> g{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
                   g[cell_id] = A;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_g{g};
 
               SECTION("sum")
               {
-                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
                 REQUIRE(same_values(f + g, sum_values));
@@ -1433,9 +1421,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("difference")
               {
-                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
                 REQUIRE(same_values(f - g, difference_values));
@@ -1446,9 +1434,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               SECTION("product")
               {
-                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh_v->numberOfCells()};
                 parallel_for(
-                  mesh->numberOfCells(),
+                  mesh_v->numberOfCells(),
                   PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
 
                 REQUIRE(same_values(f * g, product_values));
@@ -1463,9 +1451,9 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh_v};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const double y = xj[cell_id][1];
                   f[cell_id]     = std::abs(2 * x + y) + 1;
@@ -1473,22 +1461,22 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               const double a = 3;
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<const double> const_f = f;
 
               SECTION("sum")
               {
                 {
-                  Array<double> sum_values{mesh->numberOfCells()};
+                  Array<double> sum_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
 
                   REQUIRE(same_values(a + f, sum_values));
                   REQUIRE(same_values(a + const_f, sum_values));
                 }
                 {
-                  Array<double> sum_values{mesh->numberOfCells()};
+                  Array<double> sum_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
 
                   REQUIRE(same_values(f + a, sum_values));
                   REQUIRE(same_values(const_f + a, sum_values));
@@ -1498,18 +1486,18 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               SECTION("difference")
               {
                 {
-                  Array<double> difference_values{mesh->numberOfCells()};
+                  Array<double> difference_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
                   REQUIRE(same_values(a - f, difference_values));
                   REQUIRE(same_values(a - const_f, difference_values));
                 }
 
                 {
-                  Array<double> difference_values{mesh->numberOfCells()};
+                  Array<double> difference_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
                   REQUIRE(same_values(f - a, difference_values));
                   REQUIRE(same_values(const_f - a, difference_values));
@@ -1519,43 +1507,43 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               SECTION("product")
               {
                 {
-                  Array<double> product_values{mesh->numberOfCells()};
+                  Array<double> product_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
 
                   REQUIRE(same_values(a * f, product_values));
                   REQUIRE(same_values(a * const_f, product_values));
                 }
                 {
-                  Array<double> product_values{mesh->numberOfCells()};
+                  Array<double> product_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
 
                   REQUIRE(same_values(f * a, product_values));
                   REQUIRE(same_values(const_f * a, product_values));
                 }
 
                 {
-                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  Array<TinyVector<3>> product_values{mesh_v->numberOfCells()};
                   const TinyVector<3> v{1, 2, 3};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
 
                   REQUIRE(same_values(f * v, product_values));
                   REQUIRE(same_values(const_f * v, product_values));
                 }
 
                 {
-                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  Array<TinyVector<3>> product_values{mesh_v->numberOfCells()};
+                  DiscreteFunctionP0<TinyVector<3>> v{mesh};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
                       v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
                     });
 
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
 
                   REQUIRE(same_values(f * v, product_values));
@@ -1566,7 +1554,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
                   const TinyMatrix<2> A{1, 2, 3, 4};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
 
                   REQUIRE(same_values(f * A, product_values));
                   REQUIRE(same_values(const_f * A, product_values));
@@ -1574,15 +1562,15 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<2>> M{mesh};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
                       M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
                     });
 
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
 
                   REQUIRE(same_values(f * M, product_values));
@@ -1593,17 +1581,17 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
               SECTION("ratio")
               {
                 {
-                  Array<double> ratio_values{mesh->numberOfCells()};
+                  Array<double> ratio_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
 
                   REQUIRE(same_values(a / f, ratio_values));
                   REQUIRE(same_values(a / const_f, ratio_values));
                 }
                 {
-                  Array<double> ratio_values{mesh->numberOfCells()};
+                  Array<double> ratio_values{mesh_v->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
 
                   REQUIRE(same_values(f / a, ratio_values));
                   REQUIRE(same_values(const_f / a, ratio_values));
@@ -1615,16 +1603,16 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const double y = xj[cell_id][1];
                   const TinyVector<VectorDimension> X{x + y, 2 - x * y};
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -1632,7 +1620,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
 
                   REQUIRE(same_values(v + f, sum_values));
                   REQUIRE(same_values(v + const_f, sum_values));
@@ -1640,7 +1628,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
 
                   REQUIRE(same_values(f + v, sum_values));
                   REQUIRE(same_values(const_f + v, sum_values));
@@ -1653,7 +1641,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
 
                   REQUIRE(same_values(v - f, difference_values));
@@ -1662,7 +1650,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
 
                   REQUIRE(same_values(f - v, difference_values));
@@ -1676,23 +1664,23 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   const double a = 2.3;
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
 
                   REQUIRE(same_values(a * f, product_values));
                   REQUIRE(same_values(a * const_f, product_values));
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
                       a[cell_id]     = 2 * x * x - 1;
                     });
 
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
 
                   REQUIRE(same_values(a * f, product_values));
@@ -1703,7 +1691,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
 
                   REQUIRE(same_values(A * f, product_values));
                   REQUIRE(same_values(A * const_f, product_values));
@@ -1711,15 +1699,15 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<VectorDimension>> M{mesh};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
                       M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
                     });
 
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
 
                   REQUIRE(same_values(M * f, product_values));
@@ -1732,16 +1720,16 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
                   const double y = xj[cell_id][1];
                   const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * 3};
                   f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -1749,7 +1737,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
 
                   REQUIRE(same_values(A + f, sum_values));
                   REQUIRE(same_values(A + const_f, sum_values));
@@ -1757,7 +1745,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
 
                   REQUIRE(same_values(f + A, sum_values));
                   REQUIRE(same_values(const_f + A, sum_values));
@@ -1770,7 +1758,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
 
                   REQUIRE(same_values(A - f, difference_values));
@@ -1779,7 +1767,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 {
                   Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
 
                   REQUIRE(same_values(f - A, difference_values));
@@ -1793,23 +1781,23 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   const double a = 2.3;
                   Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
 
                   REQUIRE(same_values(a * f, product_values));
                   REQUIRE(same_values(a * const_f, product_values));
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
                       a[cell_id]     = 2 * x * x - 1;
                     });
 
                   Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(),
+                    mesh_v->numberOfCells(),
                     PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
 
                   REQUIRE(same_values(a * f, product_values));
@@ -1820,7 +1808,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
                   Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
 
                   REQUIRE(same_values(A * f, product_values));
                   REQUIRE(same_values(A * const_f, product_values));
@@ -1830,7 +1818,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
                   Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
                   parallel_for(
-                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+                    mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
 
                   REQUIRE(same_values(f * A, product_values));
                   REQUIRE(same_values(const_f * A, product_values));
@@ -1844,14 +1832,12 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("3D")
     {
-      constexpr size_t Dimension = 3;
-
       std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -1859,7 +1845,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1868,7 +1854,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id]     = 2 * x + y - z;
                 });
 
-              DiscreteFunctionP0<Dimension, double> g{mesh};
+              DiscreteFunctionP0<double> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1877,8 +1863,8 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y) + 2 * z) + 1;
                 });
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
-              DiscreteFunctionP0<Dimension, const double> const_g{g};
+              DiscreteFunctionP0<const double> const_f = f;
+              DiscreteFunctionP0<const double> const_g{g};
 
               SECTION("sum")
               {
@@ -1937,7 +1923,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1945,7 +1931,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1953,8 +1939,8 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   g[cell_id] = X;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_g{g};
 
               SECTION("sum")
               {
@@ -1987,7 +1973,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1995,7 +1981,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -2003,8 +1989,8 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   g[cell_id] = A;
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_g{g};
 
               SECTION("sum")
               {
@@ -2051,7 +2037,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0<Dimension, double> f{mesh};
+              DiscreteFunctionP0<double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -2062,7 +2048,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
               const double a = 3;
 
-              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<const double> const_f = f;
 
               SECTION("sum")
               {
@@ -2136,7 +2122,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  DiscreteFunctionP0<TinyVector<3>> v{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -2163,7 +2149,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<2>> M{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -2204,7 +2190,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t VectorDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              DiscreteFunctionP0<TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -2214,7 +2200,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyVector<VectorDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -2273,7 +2259,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -2301,7 +2287,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
                 {
                   Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  DiscreteFunctionP0<TinyMatrix<VectorDimension>> M{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -2322,7 +2308,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             {
               constexpr std::uint64_t MatrixDimension = 2;
 
-              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              DiscreteFunctionP0<TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -2332,7 +2318,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                   f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<const TinyMatrix<MatrixDimension>> const_f = f;
 
               SECTION("sum")
               {
@@ -2391,7 +2377,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
                 }
 
                 {
-                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  DiscreteFunctionP0<double> a{mesh};
                   parallel_for(
                     mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                       const double x = xj[cell_id][0];
@@ -2494,17 +2480,16 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      constexpr size_t Dimension = 1;
-      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<1>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
+          DiscreteFunctionP0<double> positive_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(),
@@ -2559,7 +2544,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             CHECK_STD_MATH_FUNCTION(positive_function, tan);
           }
 
-          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
+          DiscreteFunctionP0<double> unit_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
@@ -2699,14 +2684,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,hv)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
               });
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2718,7 +2703,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,v)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2734,7 +2719,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2> u{3, -2};
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2753,7 +2738,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("vector sum")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2766,7 +2751,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("matrix sum")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2792,7 +2777,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate vector")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2812,7 +2797,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate matrix")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -2837,18 +2822,16 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("2D")
     {
-      constexpr size_t Dimension = 2;
-
       std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<2>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
+          DiscreteFunctionP0<double> positive_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(),
@@ -2903,7 +2886,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             CHECK_STD_MATH_FUNCTION(positive_function, tan);
           }
 
-          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
+          DiscreteFunctionP0<double> unit_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
@@ -3043,14 +3026,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,hv)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
               });
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3062,7 +3045,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,v)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3078,7 +3061,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2> u{3, -2};
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3097,7 +3080,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("vector sum")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3110,7 +3093,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("matrix sum")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3136,7 +3119,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate vector")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3156,7 +3139,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate matrix")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3181,18 +3164,16 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("3D")
     {
-      constexpr size_t Dimension = 3;
-
       std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<3>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
+          DiscreteFunctionP0<double> positive_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(),
@@ -3247,7 +3228,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             CHECK_STD_MATH_FUNCTION(positive_function, tan);
           }
 
-          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
+          DiscreteFunctionP0<double> unit_function{mesh};
 
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
@@ -3387,14 +3368,14 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,hv)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
                 uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
               });
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3406,7 +3387,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("dot(uh,v)")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3422,7 +3403,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             const TinyVector<2> u{3, -2};
 
-            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> vh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3434,7 +3415,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("det(Ah)")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> Ah{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> Ah{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3456,7 +3437,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("trace(Ah)")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> Ah{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> Ah{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3478,7 +3459,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("inverse(Ah)")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> Ah{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> Ah{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3500,7 +3481,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("transpose(Ah)")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> Ah{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> Ah{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3529,7 +3510,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("vector sum")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3542,7 +3523,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("matrix sum")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3568,7 +3549,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate vector")
           {
-            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            DiscreteFunctionP0<TinyVector<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3588,7 +3569,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           SECTION("integrate matrix")
           {
-            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            DiscreteFunctionP0<TinyMatrix<2>> uh{mesh};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -3626,13 +3607,13 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1 = named_mesh.mesh();
+            auto mesh_1 = named_mesh.mesh()->get<Mesh<1>>();
 
             std::shared_ptr mesh_2 =
-              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+              std::make_shared<const Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<double> f1{mesh_1};
+            DiscreteFunctionP0<double> f2{mesh_2};
 
             REQUIRE_THROWS_AS(f1 = f2, AssertError);
             REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
@@ -3653,13 +3634,13 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1 = named_mesh.mesh();
+            auto mesh_1 = named_mesh.mesh()->get<Mesh<2>>();
 
             std::shared_ptr mesh_2 =
-              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+              std::make_shared<const Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<double> f1{mesh_1};
+            DiscreteFunctionP0<double> f2{mesh_2};
 
             REQUIRE_THROWS_AS(f1 = f2, AssertError);
             REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
@@ -3680,13 +3661,13 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1 = named_mesh.mesh();
+            auto mesh_1 = named_mesh.mesh()->get<Mesh<3>>();
 
             std::shared_ptr mesh_2 =
-              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+              std::make_shared<const Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<double> f1{mesh_1};
+            DiscreteFunctionP0<double> f2{mesh_2};
 
             REQUIRE_THROWS_AS(f1 = f2, AssertError);
             REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
diff --git a/tests/test_DiscreteFunctionP0Vector.cpp b/tests/test_DiscreteFunctionP0Vector.cpp
index 2ab61b2405ba5eb098663db5fa6a3d26b6fe75b9..78a3f6d5de0207f2e0e4857ad6381625798008cb 100644
--- a/tests/test_DiscreteFunctionP0Vector.cpp
+++ b/tests/test_DiscreteFunctionP0Vector.cpp
@@ -34,13 +34,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
           REQUIRE(f.size() == size);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh->id());
 
           DiscreteFunctionP0Vector g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
@@ -82,13 +82,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
           REQUIRE(f.size() == size);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh->id());
 
           DiscreteFunctionP0Vector g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
@@ -130,13 +130,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           REQUIRE(f.dataType() == ASTNodeDataType::double_t);
           REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
           REQUIRE(f.size() == size);
 
-          REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.meshVariant()->id() == mesh->id());
 
           DiscreteFunctionP0Vector g{f};
           REQUIRE(g.dataType() == ASTNodeDataType::double_t);
@@ -192,10 +192,10 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh                  = named_mesh.mesh();
           constexpr size_t Dimension = 1;
+          auto mesh                  = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           f.fill(3);
 
           REQUIRE(all_values_equal(f, 3));
@@ -214,8 +214,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           f.fill(2.3);
 
           REQUIRE(all_values_equal(f, 2.3));
@@ -227,15 +227,12 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
     {
       const size_t size = 2;
 
-      constexpr size_t Dimension = 3;
-
       std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{named_mesh.mesh(), size};
           f.fill(3.2);
 
           REQUIRE(all_values_equal(f, 3.2));
@@ -269,13 +266,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           const size_t size       = 3;
           const size_t value      = parallel::rank() + 1;
           const size_t zero_value = 0;
 
-          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          DiscreteFunctionP0Vector<size_t> f{mesh, size};
           f.fill(value);
 
           REQUIRE(all_values_equal(f, value));
@@ -289,9 +286,9 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           copy_to(g, f);
           g.fill(zero_value);
 
-          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<const size_t> h = copy(f);
 
-          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          DiscreteFunctionP0Vector<size_t> shallow_g{mesh, size};
           shallow_g = g;
 
           REQUIRE(all_values_equal(f, value));
@@ -315,13 +312,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           const size_t size       = 3;
           const size_t value      = parallel::rank() + 1;
           const size_t zero_value = 0;
 
-          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          DiscreteFunctionP0Vector<size_t> f{mesh, size};
           f.fill(value);
 
           REQUIRE(all_values_equal(f, value));
@@ -335,9 +332,9 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           copy_to(g, f);
           g.fill(zero_value);
 
-          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<const size_t> h = copy(f);
 
-          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          DiscreteFunctionP0Vector<size_t> shallow_g{mesh, size};
           shallow_g = g;
 
           REQUIRE(all_values_equal(f, value));
@@ -362,13 +359,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           const size_t size       = 3;
           const size_t value      = parallel::rank() + 1;
           const size_t zero_value = 0;
 
-          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          DiscreteFunctionP0Vector<size_t> f{mesh, size};
           f.fill(value);
 
           REQUIRE(all_values_equal(f, value));
@@ -382,9 +379,9 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           copy_to(g, f);
           g.fill(zero_value);
 
-          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<const size_t> h = copy(f);
 
-          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          DiscreteFunctionP0Vector<size_t> shallow_g{mesh, size};
           shallow_g = g;
 
           REQUIRE(all_values_equal(f, value));
@@ -413,13 +410,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
           SECTION("unary minus")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -428,7 +425,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
             Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
@@ -456,13 +453,13 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
           SECTION("unary minus")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -472,7 +469,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
             Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
@@ -500,15 +497,15 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
-
-          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          auto mesh_v = named_mesh.mesh();
+          auto mesh   = mesh_v->get<Mesh<Dimension>>();
+          auto xj     = MeshDataManager::instance().getMeshData(*mesh).xj();
 
           SECTION("unary minus")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh_v, size};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
                 const double y = xj[cell_id][1];
                 const double z = xj[cell_id][2];
@@ -517,11 +514,11 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
-            Table<double> minus_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh_v->numberOfCells(), size};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              mesh_v->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
                   minus_values[cell_id][i] = -f[cell_id][i];
                 }
@@ -548,11 +545,11 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -561,7 +558,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               f[cell_id][2]  = 2 + x;
             });
 
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+          DiscreteFunctionP0Vector<double> g{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -570,8 +567,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               g[cell_id][2]  = (x + 3) * 5;
             });
 
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+          DiscreteFunctionP0Vector<const double> const_f = f;
+          DiscreteFunctionP0Vector<const double> const_g{g};
 
           auto same_data = [](const auto& f0, const auto& f1) {
             const size_t number_of_cells = f1.size();
@@ -631,11 +628,11 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -644,7 +641,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               f[cell_id][2]  = 2 + x;
             });
 
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+          DiscreteFunctionP0Vector<double> g{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -653,8 +650,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               g[cell_id][2]  = (x + 3) * 5;
             });
 
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+          DiscreteFunctionP0Vector<const double> const_f = f;
+          DiscreteFunctionP0Vector<const double> const_g{g};
 
           auto same_data = [](const auto& f1, const auto& f2) {
             const size_t number_of_cells = f2.size();
@@ -714,11 +711,11 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -727,7 +724,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               f[cell_id][2]  = 2 + x;
             });
 
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+          DiscreteFunctionP0Vector<double> g{mesh, size};
           parallel_for(
             mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
               const double x = xj[cell_id][0];
@@ -736,8 +733,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
               g[cell_id][2]  = (x + 3) * 5;
             });
 
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+          DiscreteFunctionP0Vector<const double> const_f = f;
+          DiscreteFunctionP0Vector<const double> const_g{g};
 
           auto same_data = [](const auto& f1, const auto& f2) {
             const size_t number_of_cells = f2.size();
@@ -800,7 +797,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -808,7 +805,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              DiscreteFunctionP0Vector<double> f{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -817,7 +814,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   f[cell_id][2]  = 2 + x;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              DiscreteFunctionP0Vector<double> g{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -826,8 +823,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   g[cell_id][2]  = (x + 3) * 5;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+              DiscreteFunctionP0Vector<const double> const_f = f;
+              DiscreteFunctionP0Vector<const double> const_g{g};
 
               SECTION("sum")
               {
@@ -865,7 +862,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           SECTION("external operators")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -874,7 +871,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
             SECTION("product")
             {
@@ -895,7 +892,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
               SECTION("DiscreteFunctionP0 lhs")
               {
-                DiscreteFunctionP0<Dimension, double> a{mesh};
+                DiscreteFunctionP0<double> a{mesh};
                 parallel_for(
                   mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                     const double x = xj[cell_id][0];
@@ -913,7 +910,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 REQUIRE(same_values(a * f, product_values));
                 REQUIRE(same_values(a * const_f, product_values));
 
-                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                DiscreteFunctionP0<const double> const_a = a;
                 REQUIRE(same_values(const_a * f, product_values));
                 REQUIRE(same_values(const_a * const_f, product_values));
               }
@@ -934,7 +931,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -942,7 +939,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              DiscreteFunctionP0Vector<double> f{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -952,7 +949,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   f[cell_id][2]  = 2 + x * y;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              DiscreteFunctionP0Vector<double> g{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -962,8 +959,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   g[cell_id][2]  = (x + 3) + 5 * y;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+              DiscreteFunctionP0Vector<const double> const_f = f;
+              DiscreteFunctionP0Vector<const double> const_g{g};
 
               SECTION("sum")
               {
@@ -1001,7 +998,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           SECTION("external operators")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -1011,7 +1008,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
             SECTION("product")
             {
@@ -1032,7 +1029,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
               SECTION("DiscreteFunctionP0 lhs")
               {
-                DiscreteFunctionP0<Dimension, double> a{mesh};
+                DiscreteFunctionP0<double> a{mesh};
                 parallel_for(
                   mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                     const double x = xj[cell_id][0];
@@ -1051,7 +1048,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 REQUIRE(same_values(a * f, product_values));
                 REQUIRE(same_values(a * const_f, product_values));
 
-                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                DiscreteFunctionP0<const double> const_a = a;
                 REQUIRE(same_values(const_a * f, product_values));
                 REQUIRE(same_values(const_a * const_f, product_values));
               }
@@ -1072,7 +1069,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -1080,7 +1077,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           {
             SECTION("scalar functions")
             {
-              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              DiscreteFunctionP0Vector<double> f{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1090,7 +1087,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   f[cell_id][1]  = x * z - y;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              DiscreteFunctionP0Vector<double> g{mesh, size};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
@@ -1100,8 +1097,8 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                   g[cell_id][1]  = 3 * (x + 2) - y * z;
                 });
 
-              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+              DiscreteFunctionP0Vector<const double> const_f = f;
+              DiscreteFunctionP0Vector<const double> const_g{g};
 
               SECTION("sum")
               {
@@ -1139,7 +1136,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           SECTION("external operators")
           {
-            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+            DiscreteFunctionP0Vector<double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
@@ -1150,7 +1147,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 }
               });
 
-            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+            DiscreteFunctionP0Vector<const double> const_f = f;
 
             SECTION("product")
             {
@@ -1171,7 +1168,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
               SECTION("DiscreteFunctionP0 lhs")
               {
-                DiscreteFunctionP0<Dimension, double> a{mesh};
+                DiscreteFunctionP0<double> a{mesh};
                 parallel_for(
                   mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                     const double x = xj[cell_id][0];
@@ -1191,7 +1188,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
                 REQUIRE(same_values(a * f, product_values));
                 REQUIRE(same_values(a * const_f, product_values));
 
-                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                DiscreteFunctionP0<const double> const_a = a;
                 REQUIRE(same_values(const_a * f, product_values));
                 REQUIRE(same_values(const_a * const_f, product_values));
               }
diff --git a/tests/test_DiscreteFunctionUtils.cpp b/tests/test_DiscreteFunctionUtils.cpp
index 85c916c4cf559ca4c2ceef612d1da03db779715e..daeaf70f0643f415da40589d190d09511efc4c97 100644
--- a/tests/test_DiscreteFunctionUtils.cpp
+++ b/tests/test_DiscreteFunctionUtils.cpp
@@ -21,36 +21,38 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh = named_mesh.mesh();
+        auto mesh_v = named_mesh.mesh();
+        auto mesh   = mesh_v->get<Mesh<Dimension>>();
 
         std::shared_ptr mesh_copy =
-          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+          std::make_shared<const std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+        std::shared_ptr mesh_copy_v = std::make_shared<const MeshVariant>(mesh_copy);
 
         SECTION("common mesh")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<TinyVector<2>> wh(mesh);
 
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
           std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
           std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
           std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v})->id() == mesh->id());
           REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
-          DiscreteFunctionP0Vector<Dimension, double> Uh(mesh, 3);
-          DiscreteFunctionP0Vector<Dimension, double> Vh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Uh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Vh(mesh, 3);
 
           auto uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           auto vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
@@ -70,13 +72,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("scalar function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const double>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, double>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const double>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<double>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -86,13 +88,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1 function shallow copy")
         {
           using DataType          = TinyVector<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -102,13 +104,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2 function shallow copy")
         {
           using DataType          = TinyVector<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -118,13 +120,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3 function shallow copy")
         {
           using DataType          = TinyVector<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -134,13 +136,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1x1 function shallow copy")
         {
           using DataType          = TinyMatrix<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -150,13 +152,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2x2 function shallow copy")
         {
           using DataType          = TinyMatrix<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -166,13 +168,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3x3 function shallow copy")
         {
           using DataType          = TinyMatrix<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -181,14 +183,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("P0Vector function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0Vector<Dimension, const double>;
-          std::shared_ptr uh =
-            std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<Dimension, double>(mesh, 2));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0Vector<const double>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<double>(mesh, 2));
+          std::shared_ptr vh = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellArrays()[CellId{0}][0]) ==
@@ -207,36 +208,37 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh = named_mesh.mesh();
-
+        auto mesh_v = named_mesh.mesh();
+        auto mesh   = mesh_v->get<Mesh<Dimension>>();
         std::shared_ptr mesh_copy =
-          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+          std::make_shared<const std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+        std::shared_ptr mesh_copy_v = std::make_shared<const MeshVariant>(mesh_copy);
 
         SECTION("common mesh")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<TinyVector<2>> wh(mesh);
 
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
           std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
           std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
           std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v})->id() == mesh->id());
           REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
-          DiscreteFunctionP0Vector<Dimension, double> Uh(mesh, 3);
-          DiscreteFunctionP0Vector<Dimension, double> Vh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Uh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Vh(mesh, 3);
 
           auto uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           auto vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
@@ -256,13 +258,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("scalar function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const double>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, double>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const double>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<double>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -272,13 +274,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1 function shallow copy")
         {
           using DataType          = TinyVector<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -288,13 +290,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2 function shallow copy")
         {
           using DataType          = TinyVector<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -304,13 +306,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3 function shallow copy")
         {
           using DataType          = TinyVector<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -320,13 +322,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1x1 function shallow copy")
         {
           using DataType          = TinyMatrix<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -336,13 +338,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2x2 function shallow copy")
         {
           using DataType          = TinyMatrix<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -352,13 +354,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3x3 function shallow copy")
         {
           using DataType          = TinyMatrix<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -367,14 +369,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("P0Vector function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0Vector<Dimension, const double>;
-          std::shared_ptr uh =
-            std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<Dimension, double>(mesh, 2));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0Vector<const double>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<double>(mesh, 2));
+          std::shared_ptr vh = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellArrays()[CellId{0}][0]) ==
@@ -386,43 +387,43 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
   SECTION("3D")
   {
-    constexpr size_t Dimension = 3;
-
     std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh = named_mesh.mesh();
+        auto mesh_v = named_mesh.mesh();
+        auto mesh   = mesh_v->get<Mesh<3>>();
 
         std::shared_ptr mesh_copy =
-          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+          std::make_shared<const std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+        std::shared_ptr mesh_copy_v = std::make_shared<const MeshVariant>(mesh_copy);
 
         SECTION("common mesh")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, TinyVector<2>> wh(mesh);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<TinyVector<2>> wh(mesh);
 
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
           std::shared_ptr uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           std::shared_ptr vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
           std::shared_ptr wh_v = std::make_shared<DiscreteFunctionVariant>(wh);
           std::shared_ptr qh_v = std::make_shared<DiscreteFunctionVariant>(qh);
 
-          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh_v, vh_v, wh_v})->id() == mesh->id());
           REQUIRE(getCommonMesh({uh_v, vh_v, wh_v, qh_v}).use_count() == 0);
         }
 
         SECTION("check discretization type")
         {
-          DiscreteFunctionP0<Dimension, double> uh(mesh);
-          DiscreteFunctionP0<Dimension, double> vh(mesh);
-          DiscreteFunctionP0<Dimension, double> qh(mesh_copy);
+          DiscreteFunctionP0<double> uh(mesh);
+          DiscreteFunctionP0<double> vh(mesh);
+          DiscreteFunctionP0<double> qh(mesh_copy);
 
-          DiscreteFunctionP0Vector<Dimension, double> Uh(mesh, 3);
-          DiscreteFunctionP0Vector<Dimension, double> Vh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Uh(mesh, 3);
+          DiscreteFunctionP0Vector<double> Vh(mesh, 3);
 
           auto uh_v = std::make_shared<DiscreteFunctionVariant>(uh);
           auto vh_v = std::make_shared<DiscreteFunctionVariant>(vh);
@@ -442,13 +443,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("scalar function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const double>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, double>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const double>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<double>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -458,13 +459,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1 function shallow copy")
         {
           using DataType          = TinyVector<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -474,13 +475,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2 function shallow copy")
         {
           using DataType          = TinyVector<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -490,13 +491,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3 function shallow copy")
         {
           using DataType          = TinyVector<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -506,13 +507,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^1x1 function shallow copy")
         {
           using DataType          = TinyMatrix<1>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -522,13 +523,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^2x2 function shallow copy")
         {
           using DataType          = TinyMatrix<2>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -538,13 +539,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
         SECTION("R^3x3 function shallow copy")
         {
           using DataType          = TinyMatrix<3>;
-          using DiscreteFunctionT = DiscreteFunctionP0<Dimension, const DataType>;
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, DataType>(mesh));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0<const DataType>;
+          std::shared_ptr uh      = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<DataType>(mesh));
+          std::shared_ptr vh      = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellValues()[CellId{0}]) ==
@@ -553,14 +554,13 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         SECTION("P0Vector function shallow copy")
         {
-          using DiscreteFunctionT = DiscreteFunctionP0Vector<Dimension, const double>;
-          std::shared_ptr uh =
-            std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<Dimension, double>(mesh, 2));
-          std::shared_ptr vh = shallowCopy(mesh, uh);
+          using DiscreteFunctionT = DiscreteFunctionP0Vector<const double>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0Vector<double>(mesh, 2));
+          std::shared_ptr vh = shallowCopy(mesh_v, uh);
 
           REQUIRE(uh == vh);
 
-          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy_v, uh);
 
           REQUIRE(uh != wh);
           REQUIRE(&(uh->get<DiscreteFunctionT>().cellArrays()[CellId{0}][0]) ==
@@ -581,28 +581,28 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh = named_mesh.mesh();
+          auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
           std::shared_ptr other_mesh =
-            CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh();
+            CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh()->get<Mesh<1>>();
+          std::shared_ptr other_mesh_v = std::make_shared<const MeshVariant>(other_mesh);
 
-          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, double>(mesh));
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<double>(mesh));
 
-          REQUIRE_THROWS_WITH(shallowCopy(other_mesh, uh), "error: cannot shallow copy when connectivity changes");
+          REQUIRE_THROWS_WITH(shallowCopy(other_mesh_v, uh), "error: cannot shallow copy when connectivity changes");
         }
       }
     }
 
     SECTION("incompatible mesh dimension")
     {
-      constexpr size_t Dimension = 1;
-
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-      std::shared_ptr mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
+      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
+      std::shared_ptr mesh_2d_v =
+        std::make_shared<const MeshVariant>(MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>());
 
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<Dimension, double>(mesh_1d));
+      std::shared_ptr uh = std::make_shared<DiscreteFunctionVariant>(DiscreteFunctionP0<double>(mesh_1d));
 
-      REQUIRE_THROWS_WITH(shallowCopy(mesh_2d, uh), "error: incompatible mesh dimensions");
+      REQUIRE_THROWS_WITH(shallowCopy(mesh_2d_v, uh), "error: incompatible mesh dimensions");
     }
   }
 }
diff --git a/tests/test_DiscreteFunctionVectorIntegrator.cpp b/tests/test_DiscreteFunctionVectorIntegrator.cpp
index e5712786254c42723778e05cc37e2a3158f90bda..c176efb6535ae8b50438c31358b63b8f88d647c2 100644
--- a/tests/test_DiscreteFunctionVectorIntegrator.cpp
+++ b/tests/test_DiscreteFunctionVectorIntegrator.cpp
@@ -67,7 +67,8 @@ TEST_CASE("DiscreteFunctionVectorIntegrator", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
         std::string_view data = R"(
 import math;
@@ -101,7 +102,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
         register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
 
-        DiscreteFunctionVectorIntegrator integrator(mesh_1d, quadrature_descriptor,
+        DiscreteFunctionVectorIntegrator integrator(mesh_1d_v, quadrature_descriptor,
                                                     std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -112,32 +113,28 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<1>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_1d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<1>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_1d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<1>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_1d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<1>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_1d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -156,7 +153,8 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
         std::string_view data = R"(
 import math;
@@ -190,7 +188,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
         register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
 
-        DiscreteFunctionVectorIntegrator integrator(mesh_2d, quadrature_descriptor,
+        DiscreteFunctionVectorIntegrator integrator(mesh_2d_v, quadrature_descriptor,
                                                     std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -201,32 +199,28 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<2>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_2d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<2>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_2d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<2>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_2d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<2>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_2d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -236,8 +230,6 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
 
   SECTION("3D")
   {
-    constexpr size_t Dimension = 3;
-
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
     std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
@@ -245,7 +237,8 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         std::string_view data = R"(
 import math;
@@ -279,7 +272,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
         register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-        DiscreteFunctionVectorIntegrator integrator(mesh_3d, quadrature_descriptor,
+        DiscreteFunctionVectorIntegrator integrator(mesh_3d_v, quadrature_descriptor,
                                                     std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -290,32 +283,28 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<3>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_3d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<3>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_3d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<3>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_3d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
           CellValue<double> cell_value =
             IntegrateCellValue<double(TinyVector<3>)>::integrate(function_id_list[i], *quadrature_descriptor, *mesh_3d);
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -332,7 +321,8 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         std::string_view data = R"(
 import math;
@@ -369,7 +359,7 @@ let R2_scalar_non_linear_3d: R^3 -> R^2, x -> [2 * exp(x[0] + x[1]) + 3 * x[2],
           register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorIntegrator integrator(mesh_3d, quadrature_descriptor,
+          DiscreteFunctionVectorIntegrator integrator(mesh_3d_v, quadrature_descriptor,
                                                       std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
@@ -387,7 +377,7 @@ note: provided function B_scalar_non_linear_2d: R^2 -> B)";
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorIntegrator integrator(mesh_3d, quadrature_descriptor,
+          DiscreteFunctionVectorIntegrator integrator(mesh_3d_v, quadrature_descriptor,
                                                       std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
diff --git a/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp b/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
index c6526f0c14880cfe7e918053ded8acbb48b92eeb..180bc3b2af49cc49aa0f2e36a23af5c1e8e5c62c 100644
--- a/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
+++ b/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
@@ -64,7 +64,8 @@ TEST_CASE("DiscreteFunctionVectorIntegratorByZone", "[scheme]")
 
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -104,7 +105,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
     register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
 
-    DiscreteFunctionVectorIntegrator integrator(mesh_1d, zone_list, quadrature_descriptor,
+    DiscreteFunctionVectorIntegrator integrator(mesh_1d_v, zone_list, quadrature_descriptor,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -125,8 +126,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -143,8 +143,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -161,8 +160,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -179,8 +177,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
@@ -192,7 +189,8 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
 
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -232,7 +230,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
     register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
 
-    DiscreteFunctionVectorIntegrator integrator(mesh_2d, zone_list, quadrature_descriptor,
+    DiscreteFunctionVectorIntegrator integrator(mesh_2d_v, zone_list, quadrature_descriptor,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -253,8 +251,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -271,8 +268,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -289,8 +285,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -307,8 +302,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
@@ -320,7 +314,8 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
 
     std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor = std::make_shared<GaussQuadratureDescriptor>(3);
 
-    auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -360,7 +355,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
     register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-    DiscreteFunctionVectorIntegrator integrator(mesh_3d, zone_list, quadrature_descriptor,
+    DiscreteFunctionVectorIntegrator integrator(mesh_3d_v, zone_list, quadrature_descriptor,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = integrator.integrate();
@@ -381,8 +376,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -399,8 +393,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -417,8 +410,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -435,8 +427,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           cell_value[cell_id]  = array[j];
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
diff --git a/tests/test_DiscreteFunctionVectorInterpoler.cpp b/tests/test_DiscreteFunctionVectorInterpoler.cpp
index 2069db16cbd638dadc093fa02bde505f342db768..466e88bb5e2af9f69caedd7a487aa80e0b960560 100644
--- a/tests/test_DiscreteFunctionVectorInterpoler.cpp
+++ b/tests/test_DiscreteFunctionVectorInterpoler.cpp
@@ -62,7 +62,8 @@ TEST_CASE("DiscreteFunctionVectorInterpoler", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -98,7 +99,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
         register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
 
-        DiscreteFunctionVectorInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+        DiscreteFunctionVectorInterpoler interpoler(mesh_1d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
@@ -112,8 +113,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
               cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -124,8 +124,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
               cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -136,8 +135,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
               cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -148,8 +146,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
               cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -166,7 +163,8 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -202,7 +200,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
         register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
 
-        DiscreteFunctionVectorInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+        DiscreteFunctionVectorInterpoler interpoler(mesh_2d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
@@ -216,8 +214,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
               cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -228,8 +225,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
               cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -240,8 +236,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
               cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - 1);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -252,8 +247,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
               cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3;
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -270,7 +264,8 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -306,7 +301,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
         register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
         register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-        DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+        DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                     function_id_list);
         DiscreteFunctionVariant discrete_function = interpoler.interpolate();
 
@@ -320,8 +315,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
               cell_value[cell_id]            = std::exp(2 * x[0] + x[2]) + 3 > 4;
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -332,8 +326,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
               cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -344,8 +337,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
               cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - x[2]);
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         {
@@ -356,8 +348,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
               cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3 * x[2];
             });
 
-          REQUIRE(same_cell_value(cell_value, i++,
-                                  discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+          REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
         }
 
         REQUIRE(i == function_id_list.size());
@@ -372,7 +363,8 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -411,7 +403,7 @@ let R2_scalar_non_linear_3d: R^3 -> R^2, x -> [2 * exp(x[0] + x[1]) + 3 * x[2],
           register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
           const std::string error_msg = R"(error: invalid function type
@@ -428,7 +420,7 @@ note: provided function B_scalar_non_linear_2d: R^2 -> B)";
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
           const std::string error_msg = R"(error: vector functions require scalar value type.
@@ -441,7 +433,7 @@ Invalid interpolation value type: R^2)";
         {
           const std::string error_msg = "error: invalid discrete function type for vector interpolation";
 
-          DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
+          DiscreteFunctionVectorInterpoler interpoler{mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
           REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
         }
       }
diff --git a/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp b/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
index b486cd528743a337c7a127499c62c7bd71748db4..e5e99b8542a0aad471434bc805dc00d437f74978 100644
--- a/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
+++ b/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
@@ -59,7 +59,8 @@ TEST_CASE("DiscreteFunctionVectorInterpolerByZone", "[scheme]")
   {
     constexpr size_t Dimension = 1;
 
-    auto mesh_1d = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -106,7 +107,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
     register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
 
-    DiscreteFunctionVectorInterpoler interpoler(mesh_1d, zone_list,
+    DiscreteFunctionVectorInterpoler interpoler(mesh_1d_v, zone_list,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = interpoler.interpolate();
@@ -125,8 +126,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -141,8 +141,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -157,8 +156,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -173,8 +171,7 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
@@ -184,7 +181,8 @@ let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
   {
     constexpr size_t Dimension = 2;
 
-    auto mesh_2d = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -231,7 +229,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
     register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
 
-    DiscreteFunctionVectorInterpoler interpoler(mesh_2d, zone_list,
+    DiscreteFunctionVectorInterpoler interpoler(mesh_2d_v, zone_list,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = interpoler.interpolate();
@@ -250,8 +248,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -266,8 +263,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -282,8 +278,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -298,8 +293,7 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
@@ -309,7 +303,8 @@ let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
   {
     constexpr size_t Dimension = 3;
 
-    auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
     std::vector<std::shared_ptr<const IZoneDescriptor>> zone_list;
     zone_list.push_back(std::make_shared<NamedZoneDescriptor>("LEFT"));
@@ -356,7 +351,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
     register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
     register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-    DiscreteFunctionVectorInterpoler interpoler(mesh_3d, zone_list,
+    DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, zone_list,
                                                 std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                 function_id_list);
     DiscreteFunctionVariant discrete_function = interpoler.interpolate();
@@ -375,8 +370,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -391,8 +385,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -407,8 +400,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     {
@@ -423,8 +415,7 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
           }
         });
 
-      REQUIRE(
-        same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<Dimension, const double>>()));
+      REQUIRE(same_cell_value(cell_value, i++, discrete_function.get<DiscreteFunctionP0Vector<const double>>()));
     }
 
     REQUIRE(i == function_id_list.size());
@@ -437,7 +428,8 @@ let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -476,7 +468,7 @@ let R2_scalar_non_linear_3d: R^3 -> R^2, x -> [2 * exp(x[0] + x[1]) + 3 * x[2],
           register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
           const std::string error_msg = R"(error: invalid function type
@@ -493,7 +485,7 @@ note: provided function B_scalar_non_linear_2d: R^2 -> B)";
           register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
           register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
 
-          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
                                                       function_id_list);
 
           const std::string error_msg = R"(error: vector functions require scalar value type.
@@ -506,7 +498,7 @@ Invalid interpolation value type: R^2)";
         {
           const std::string error_msg = "error: invalid discrete function type for vector interpolation";
 
-          DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
+          DiscreteFunctionVectorInterpoler interpoler{mesh_3d_v, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
           REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
         }
       }
diff --git a/tests/test_Dual1DConnectivityBuilder.cpp b/tests/test_Dual1DConnectivityBuilder.cpp
index b7a35abfc577d93f729a0befc166c16c04f47fd3..d08d53b2295ec74b793159771132542bebdfef1f 100644
--- a/tests/test_Dual1DConnectivityBuilder.cpp
+++ b/tests/test_Dual1DConnectivityBuilder.cpp
@@ -36,10 +36,10 @@ TEST_CASE("Dual1DConnectivityBuilder", "[mesh]")
 {
   constexpr static size_t Dimension = 1;
 
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType         = Mesh<Dimension>;
+  using ConnectivityType = typename MeshType::Connectivity;
 
-  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh()->get<MeshType>();
   const ConnectivityType& primal_connectivity = mesh->connectivity();
 
   REQUIRE(primal_connectivity.numberOfNodes() == 35);
diff --git a/tests/test_Dual1DMeshBuilder.cpp b/tests/test_Dual1DMeshBuilder.cpp
index 9471fa8816b3a6f1e3a5f2f5b737d52320006bcf..88ac2de4f0858fbc97ea81b59f4025732e8590fe 100644
--- a/tests/test_Dual1DMeshBuilder.cpp
+++ b/tests/test_Dual1DMeshBuilder.cpp
@@ -18,18 +18,17 @@ TEST_CASE("Dual1DMeshBuilder", "[mesh]")
 {
   constexpr static size_t Dimension = 1;
 
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType = Mesh<Dimension>;
 
-  std::shared_ptr<const MeshType> p_primal_mesh = MeshDataBaseForTests::get().unordered1DMesh();
+  std::shared_ptr primal_mesh_v = MeshDataBaseForTests::get().unordered1DMesh();
 
-  const MeshType& primal_mesh = *p_primal_mesh;
+  const MeshType& primal_mesh = *primal_mesh_v->get<MeshType>();
 
   REQUIRE(primal_mesh.numberOfNodes() == 35);
   REQUIRE(primal_mesh.numberOfCells() == 34);
 
-  std::shared_ptr p_dual_1d_mesh = DualMeshManager::instance().getDual1DMesh(primal_mesh);
-  const MeshType& dual_mesh      = *p_dual_1d_mesh;
+  std::shared_ptr dual_1d_mesh_v = DualMeshManager::instance().getDual1DMesh(primal_mesh_v);
+  const MeshType& dual_mesh      = *dual_1d_mesh_v->get<MeshType>();
 
   REQUIRE(dual_mesh.numberOfNodes() == 36);
   REQUIRE(dual_mesh.numberOfCells() == 35);
diff --git a/tests/test_DualConnectivityManager.cpp b/tests/test_DualConnectivityManager.cpp
index 00e42e44fa592b4f1c1cbede7fb37b4b5ad64dfc..8c24eb0798631cbf3b4f365a6e955706fe00bab5 100644
--- a/tests/test_DualConnectivityManager.cpp
+++ b/tests/test_DualConnectivityManager.cpp
@@ -11,10 +11,12 @@
 
 TEST_CASE("DualConnectivityManager", "[mesh]")
 {
-  using ConnectivityType = Connectivity<2>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType         = Mesh<2>;
+  using ConnectivityType = typename MeshType::Connectivity;
+
+  std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+  std::shared_ptr mesh   = mesh_v->get<MeshType>();
 
-  std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid2DMesh();
   const ConnectivityType& connectivity = mesh->connectivity();
 
   SECTION("diamond dual connectivity access")
diff --git a/tests/test_DualMeshManager.cpp b/tests/test_DualMeshManager.cpp
index 1d05b996ae569ea30e995e5b1c8422b1edb9aeef..9c60d8a27eda913ded141a8dc0147c4ccdf0f733 100644
--- a/tests/test_DualMeshManager.cpp
+++ b/tests/test_DualMeshManager.cpp
@@ -13,35 +13,31 @@ TEST_CASE("DualMeshManager", "[mesh]")
 {
   SECTION("same 1D dual connectivities ")
   {
-    using ConnectivityType = Connectivity<1>;
-    using MeshType         = Mesh<ConnectivityType>;
+    std::shared_ptr mesh_v = MeshDataBaseForTests::get().unordered1DMesh();
 
-    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().unordered1DMesh();
-
-    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
-    std::shared_ptr p_median_dual_mesh  = DualMeshManager::instance().getMedianDualMesh(*mesh);
-    std::shared_ptr p_dual1d_mesh       = DualMeshManager::instance().getDual1DMesh(*mesh);
+    std::shared_ptr diamond_dual_mesh_v = DualMeshManager::instance().getDiamondDualMesh(mesh_v);
+    std::shared_ptr median_dual_mesh_v  = DualMeshManager::instance().getMedianDualMesh(mesh_v);
+    std::shared_ptr dual1d_mesh_v       = DualMeshManager::instance().getDual1DMesh(mesh_v);
 
     // In 1d all these dual meshes are the same
-    REQUIRE(p_dual1d_mesh.get() == p_diamond_dual_mesh.get());
-    REQUIRE(p_dual1d_mesh.get() == p_median_dual_mesh.get());
+    REQUIRE(dual1d_mesh_v->id() == diamond_dual_mesh_v->id());
+    REQUIRE(dual1d_mesh_v->id() == median_dual_mesh_v->id());
   }
 
   SECTION("2D")
   {
-    using ConnectivityType = Connectivity<2>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType = Mesh<2>;
 
-    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+    std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
 
     SECTION("diamond dual mesh access")
     {
-      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
 
       const auto ref_counter = p_diamond_dual_mesh.use_count();
 
       {
-        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
 
         REQUIRE(p_diamond_dual_mesh == p_diamond_dual_mesh2);
         REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter + 1);
@@ -49,18 +45,19 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter);
 
-      DualMeshManager::instance().deleteMesh(mesh.get());
+      DualMeshManager::instance().deleteMesh(mesh_v->id());
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
 
       // Can delete mesh from the list again. This means that no
       // dual mesh associated with it is managed.
-      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh_v->id()));
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
 
       // A new dual mesh is built
-      std::shared_ptr p_diamond_dual_mesh_rebuilt = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      std::shared_ptr p_diamond_dual_mesh_rebuilt =
+        DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
       REQUIRE(p_diamond_dual_mesh != p_diamond_dual_mesh_rebuilt);
-      REQUIRE(p_diamond_dual_mesh.get() != p_diamond_dual_mesh_rebuilt.get());
+      REQUIRE(p_diamond_dual_mesh->id() != p_diamond_dual_mesh_rebuilt->id());
 
       // Exactly two references to the dual mesh. One here and
       // one in the manager.
@@ -69,12 +66,12 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
     SECTION("median dual mesh access")
     {
-      std::shared_ptr p_median_dual_mesh = DualMeshManager::instance().getMedianDualMesh(*mesh);
+      std::shared_ptr p_median_dual_mesh = DualMeshManager::instance().getMedianDualMesh(mesh_v)->get<MeshType>();
 
       const auto ref_counter = p_median_dual_mesh.use_count();
 
       {
-        std::shared_ptr p_median_dual_mesh2 = DualMeshManager::instance().getMedianDualMesh(*mesh);
+        std::shared_ptr p_median_dual_mesh2 = DualMeshManager::instance().getMedianDualMesh(mesh_v)->get<MeshType>();
 
         REQUIRE(p_median_dual_mesh == p_median_dual_mesh2);
         REQUIRE(p_median_dual_mesh.use_count() == ref_counter + 1);
@@ -82,18 +79,19 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
       REQUIRE(p_median_dual_mesh.use_count() == ref_counter);
 
-      DualMeshManager::instance().deleteMesh(mesh.get());
+      DualMeshManager::instance().deleteMesh(mesh_v->id());
       REQUIRE(p_median_dual_mesh.use_count() == ref_counter - 1);
 
       // Can delete mesh from the list again. This means that no
       // dual mesh associated with it is managed.
-      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh_v->id()));
       REQUIRE(p_median_dual_mesh.use_count() == ref_counter - 1);
 
       // A new dual mesh is built
-      std::shared_ptr p_median_dual_mesh_rebuilt = DualMeshManager::instance().getMedianDualMesh(*mesh);
+      std::shared_ptr p_median_dual_mesh_rebuilt =
+        DualMeshManager::instance().getMedianDualMesh(mesh_v)->get<MeshType>();
       REQUIRE(p_median_dual_mesh != p_median_dual_mesh_rebuilt);
-      REQUIRE(p_median_dual_mesh.get() != p_median_dual_mesh_rebuilt.get());
+      REQUIRE(p_median_dual_mesh->id() != p_median_dual_mesh_rebuilt->id());
 
       // Exactly two references to the dual mesh. One here and
       // one in the manager.
@@ -102,15 +100,15 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
     SECTION("check multiple dual mesh using/freeing")
     {
-      std::shared_ptr p_median_dual_mesh  = DualMeshManager::instance().getMedianDualMesh(*mesh);
-      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      std::shared_ptr p_median_dual_mesh  = DualMeshManager::instance().getMedianDualMesh(mesh_v)->get<MeshType>();
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
 
       const auto median_ref_counter  = p_median_dual_mesh.use_count();
       const auto diamond_ref_counter = p_diamond_dual_mesh.use_count();
 
       REQUIRE(p_median_dual_mesh != p_diamond_dual_mesh);
 
-      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh_v->id()));
       REQUIRE(p_median_dual_mesh.use_count() == median_ref_counter - 1);
       REQUIRE(p_diamond_dual_mesh.use_count() == diamond_ref_counter - 1);
     }
@@ -118,19 +116,19 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
   SECTION("3D")
   {
-    using ConnectivityType = Connectivity<3>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType = Mesh<3>;
 
-    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+    std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    std::shared_ptr mesh   = mesh_v->get<MeshType>();
 
     SECTION("diamond dual mesh access")
     {
-      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
 
       const auto ref_counter = p_diamond_dual_mesh.use_count();
 
       {
-        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
 
         REQUIRE(p_diamond_dual_mesh == p_diamond_dual_mesh2);
         REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter + 1);
@@ -138,16 +136,17 @@ TEST_CASE("DualMeshManager", "[mesh]")
 
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter);
 
-      DualMeshManager::instance().deleteMesh(mesh.get());
+      DualMeshManager::instance().deleteMesh(mesh->id());
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
 
       // Can delete mesh from the list again. This means that no
       // dual mesh associated with it is managed.
-      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh->id()));
       REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
 
       // A new dual mesh is built
-      std::shared_ptr p_diamond_dual_mesh_rebuilt = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      std::shared_ptr p_diamond_dual_mesh_rebuilt =
+        DualMeshManager::instance().getDiamondDualMesh(mesh_v)->get<MeshType>();
       REQUIRE(p_diamond_dual_mesh != p_diamond_dual_mesh_rebuilt);
       REQUIRE(p_diamond_dual_mesh.get() != p_diamond_dual_mesh_rebuilt.get());
 
diff --git a/tests/test_EdgeIntegrator.cpp b/tests/test_EdgeIntegrator.cpp
index 46f4d423d933a97faeffcaa762cdd210e36a74d0..5415a03f10f16b43574f258233586ec3b0dc3312 100644
--- a/tests/test_EdgeIntegrator.cpp
+++ b/tests/test_EdgeIntegrator.cpp
@@ -22,7 +22,7 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
     {
       using R3 = TinyVector<3>;
 
-      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto hybrid_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
 
       auto f = [](const R3& X) -> double {
         const double x = X[0];
@@ -31,13 +31,14 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
         return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
       };
 
-      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
-      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      std::vector<std::pair<std::string, decltype(hybrid_mesh_v)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh_v));
+      mesh_list.push_back(
+        std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh_v)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const double> int_f_per_edge = [=] {
           Array<double> int_f(mesh->numberOfEdges());
@@ -271,7 +272,7 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
     {
       using R3 = TinyVector<3>;
 
-      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto hybrid_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
 
       auto f = [](const R3& X) -> R2 {
         const double x = X[0];
@@ -280,13 +281,14 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
         return R2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x + 3 * y - 1};
       };
 
-      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
-      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      std::vector<std::pair<std::string, decltype(hybrid_mesh_v)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh_v));
+      mesh_list.push_back(
+        std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh_v)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const R2> int_f_per_edge = [=] {
           Array<R2> int_f(mesh->numberOfEdges());
@@ -521,7 +523,7 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
     {
       using R3 = TinyVector<3>;
 
-      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto hybrid_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
 
       auto f = [](const R3& X) -> R2x2 {
         const double x = X[0];
@@ -531,13 +533,14 @@ TEST_CASE("EdgeIntegrator", "[scheme]")
                     3 - 2 * x + 3 * y};
       };
 
-      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
-      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      std::vector<std::pair<std::string, decltype(hybrid_mesh_v)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh_v));
+      mesh_list.push_back(
+        std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh_v)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const R2x2> int_f_per_edge = [=] {
           Array<R2x2> int_f(mesh->numberOfEdges());
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
index 887d0785da4ce3b5b02ce9b9e7e8eec7dd841159..5843a8ae766549e0828551bae8d33ff8c595935d 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
@@ -21,23 +21,22 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
 
   std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-  using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-  using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-  using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-  using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-  using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-  using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+  using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+  using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+  using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+  using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+  using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+  using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+  using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-  using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+  using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
   for (const auto& named_mesh : mesh_list) {
     SECTION(named_mesh.name())
     {
-      auto mesh = named_mesh.mesh();
+      auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+      std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
       CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -583,7 +582,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions1D", "[scheme]")
 
           const DiscreteFunctionR& sum_components = p_sum_components->get<DiscreteFunctionR>();
           const DiscreteFunctionVector& vector3_u = p_Vector3_u->get<DiscreteFunctionVector>();
-          DiscreteFunctionP0<Dimension, double> direct_sum(mesh);
+          DiscreteFunctionP0<double> direct_sum(mesh);
           for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
             double sum = 0;
             for (size_t i = 0; i < vector3_u.size(); ++i) {
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
index ddbf9266412e1395b922d70d3f83c6ca3ad977a5..11582b9820d9e2f6d910773b2084517a47069b37 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
@@ -23,23 +23,22 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
 
     std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-    using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-    using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-    using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-    using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-    using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-    using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+    using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+    using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+    using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+    using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+    using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+    using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+    using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-    using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+    using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh = named_mesh.mesh();
+        auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-        std::shared_ptr other_mesh =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+        std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
         CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -585,7 +584,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions2D", "[scheme]")
 
             const DiscreteFunctionR& sum_components = p_sum_components->get<DiscreteFunctionR>();
             const DiscreteFunctionVector& vector3_u = p_Vector3_u->get<DiscreteFunctionVector>();
-            DiscreteFunctionP0<Dimension, double> direct_sum(mesh);
+            DiscreteFunctionP0<double> direct_sum(mesh);
             for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
               double sum = 0;
               for (size_t i = 0; i < vector3_u.size(); ++i) {
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
index efb77fdde37448c15de9b95499cd28d01805e63f..edca0a4eb9c9e015a7e0ed31fd74ca068d4258c5 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
@@ -21,23 +21,22 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
 
   std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-  using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-  using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-  using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-  using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-  using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-  using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+  using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+  using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+  using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+  using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+  using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+  using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+  using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-  using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+  using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
   for (const auto& named_mesh : mesh_list) {
     SECTION(named_mesh.name())
     {
-      auto mesh = named_mesh.mesh();
+      auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+      std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
       CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
@@ -583,7 +582,7 @@ TEST_CASE("EmbeddedDiscreteFunctionVariantMathFunctions3D", "[scheme]")
 
           const DiscreteFunctionR& sum_components = p_sum_components->get<DiscreteFunctionR>();
           const DiscreteFunctionVector& vector3_u = p_Vector3_u->get<DiscreteFunctionVector>();
-          DiscreteFunctionP0<Dimension, double> direct_sum(mesh);
+          DiscreteFunctionP0<double> direct_sum(mesh);
           for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
             double sum = 0;
             for (size_t i = 0; i < vector3_u.size(); ++i) {
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
index 47ff54931e0f569dedda032a1b99608ad0b22bd0..2703b7ace512b300cc62ca35a1fb3eae43dcefa1 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
@@ -12,23 +12,22 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators1D", "[scheme]")
 
   std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-  using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-  using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-  using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-  using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-  using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-  using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+  using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+  using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+  using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+  using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+  using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+  using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+  using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-  using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+  using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
   for (const auto& named_mesh : mesh_list) {
     SECTION(named_mesh.name())
     {
-      auto mesh = named_mesh.mesh();
+      auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+      std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
       CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
index f6885617190de4a928626105697eaeabab641b40..54263a0a0d8b4efd9554c95a83f732b7519b52cd 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
@@ -12,23 +12,22 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators2D", "[scheme]")
 
   std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-  using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-  using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-  using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-  using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-  using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-  using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+  using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+  using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+  using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+  using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+  using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+  using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+  using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-  using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+  using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
   for (const auto& named_mesh : mesh_list) {
     SECTION(named_mesh.name())
     {
-      auto mesh = named_mesh.mesh();
+      auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+      std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
       CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
diff --git a/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp b/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
index 60b68b473def74a34e839a0d558a3e08f21eb58f..2d2cf930dd5f0fe2d76b8731e282da96d44577ae 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
@@ -12,23 +12,22 @@ TEST_CASE("EmbeddedDiscreteFunctionOperators3D", "[scheme]")
 
   std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-  using DiscreteFunctionR    = DiscreteFunctionP0<Dimension, const double>;
-  using DiscreteFunctionR1   = DiscreteFunctionP0<Dimension, const TinyVector<1>>;
-  using DiscreteFunctionR2   = DiscreteFunctionP0<Dimension, const TinyVector<2>>;
-  using DiscreteFunctionR3   = DiscreteFunctionP0<Dimension, const TinyVector<3>>;
-  using DiscreteFunctionR1x1 = DiscreteFunctionP0<Dimension, const TinyMatrix<1>>;
-  using DiscreteFunctionR2x2 = DiscreteFunctionP0<Dimension, const TinyMatrix<2>>;
-  using DiscreteFunctionR3x3 = DiscreteFunctionP0<Dimension, const TinyMatrix<3>>;
+  using DiscreteFunctionR    = DiscreteFunctionP0<const double>;
+  using DiscreteFunctionR1   = DiscreteFunctionP0<const TinyVector<1>>;
+  using DiscreteFunctionR2   = DiscreteFunctionP0<const TinyVector<2>>;
+  using DiscreteFunctionR3   = DiscreteFunctionP0<const TinyVector<3>>;
+  using DiscreteFunctionR1x1 = DiscreteFunctionP0<const TinyMatrix<1>>;
+  using DiscreteFunctionR2x2 = DiscreteFunctionP0<const TinyMatrix<2>>;
+  using DiscreteFunctionR3x3 = DiscreteFunctionP0<const TinyMatrix<3>>;
 
-  using DiscreteFunctionVector = DiscreteFunctionP0Vector<Dimension, const double>;
+  using DiscreteFunctionVector = DiscreteFunctionP0Vector<const double>;
 
   for (const auto& named_mesh : mesh_list) {
     SECTION(named_mesh.name())
     {
-      auto mesh = named_mesh.mesh();
+      auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+      std::shared_ptr other_mesh = std::make_shared<const Mesh<Dimension>>(mesh->shared_connectivity(), mesh->xr());
 
       CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
diff --git a/tests/test_EmbeddedDiscreteFunctionUtils.cpp b/tests/test_EmbeddedDiscreteFunctionUtils.cpp
index dd2fe97b69541189d1eb859bad6d952e9d705605..20ae4d80f659a3e7fc819c15f543c250b9ff9d7e 100644
--- a/tests/test_EmbeddedDiscreteFunctionUtils.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionUtils.cpp
@@ -34,23 +34,19 @@ TEST_CASE("EmbeddedDiscreteFunctionUtils", "[language]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, double>{mesh_1d}) ==
-                  "Vh(P0:R)");
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<double>{mesh_1d}) == "Vh(P0:R)");
 
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1>{mesh_1d}) ==
-                  "Vh(P0:R^1)");
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2>{mesh_1d}) ==
-                  "Vh(P0:R^2)");
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3>{mesh_1d}) ==
-                  "Vh(P0:R^3)");
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R1>{mesh_1d}) == "Vh(P0:R^1)");
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R2>{mesh_1d}) == "Vh(P0:R^2)");
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R3>{mesh_1d}) == "Vh(P0:R^3)");
 
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1x1>{mesh_1d}) ==
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R1x1>{mesh_1d}) ==
                   "Vh(P0:R^1x1)");
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2x2>{mesh_1d}) ==
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R2x2>{mesh_1d}) ==
                   "Vh(P0:R^2x2)");
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3x3>{mesh_1d}) ==
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<R3x3>{mesh_1d}) ==
                   "Vh(P0:R^3x3)");
         }
       }
@@ -63,9 +59,9 @@ TEST_CASE("EmbeddedDiscreteFunctionUtils", "[language]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
-          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<1, double>{mesh_1d, 2}) ==
+          REQUIRE(EmbeddedDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<double>{mesh_1d, 2}) ==
                   "Vh(P0Vector:R)");
         }
       }
diff --git a/tests/test_FaceIntegrator.cpp b/tests/test_FaceIntegrator.cpp
index 916dcf497b7dfdd9dba2200092286b2a6c6c2522..73e6ecad94efed88c5c4c7b50ad535a573851beb 100644
--- a/tests/test_FaceIntegrator.cpp
+++ b/tests/test_FaceIntegrator.cpp
@@ -21,7 +21,8 @@ TEST_CASE("FaceIntegrator", "[scheme]")
     {
       using R2 = TinyVector<2>;
 
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh   = mesh_v->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> double {
         const double x = X[0];
@@ -260,11 +261,11 @@ TEST_CASE("FaceIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const double> int_f_per_face = [=] {
           Array<double> int_f(mesh->numberOfFaces());
@@ -513,7 +514,7 @@ TEST_CASE("FaceIntegrator", "[scheme]")
     {
       using R2 = TinyVector<2>;
 
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> R2 {
         const double x = X[0];
@@ -752,11 +753,11 @@ TEST_CASE("FaceIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const R2> int_f_per_face = [=] {
           Array<R2> int_f(mesh->numberOfFaces());
@@ -1008,7 +1009,7 @@ TEST_CASE("FaceIntegrator", "[scheme]")
     {
       using R2 = TinyVector<2>;
 
-      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       auto f = [](const R2& X) -> R2x2 {
         const double x = X[0];
@@ -1247,11 +1248,11 @@ TEST_CASE("FaceIntegrator", "[scheme]")
 
       std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
       mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(hybrid_mesh)));
 
       for (const auto& mesh_info : mesh_list) {
         auto mesh_name = mesh_info.first;
-        auto mesh      = mesh_info.second;
+        auto mesh      = mesh_info.second->get<Mesh<3>>();
 
         Array<const R2x2> int_f_per_face = [=] {
           Array<R2x2> int_f(mesh->numberOfFaces());
diff --git a/tests/test_IntegrateCellArray.cpp b/tests/test_IntegrateCellArray.cpp
index 8a3692ca56bb0c04bbbb4417e4d6ccac6a57b707..738fad05fa74ff45820a5f4071dff9a91abbc42b 100644
--- a/tests/test_IntegrateCellArray.cpp
+++ b/tests/test_IntegrateCellArray.cpp
@@ -54,7 +54,8 @@ TEST_CASE("IntegrateCellArray", "[language]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           std::string_view data = R"(
 import math;
@@ -140,7 +141,8 @@ let g: R^1 -> R, x -> 2 * exp(x[0]) + 3;
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           std::string_view data = R"(
 import math;
@@ -221,7 +223,7 @@ let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
       constexpr size_t Dimension = 3;
       auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
 
-      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+      using NamedMesh = MeshDataBaseForTests::NamedMesh;
 
       std::vector<NamedMesh> mesh_list = [] {
         std::vector<NamedMesh> extended_mesh_list;
@@ -230,14 +232,15 @@ let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
           extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
         }
         extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                 MeshDataBaseForTests::get().hybrid3DMesh())));
         return extended_mesh_list;
       }();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           std::string_view data = R"(
 import math;
@@ -339,7 +342,9 @@ let g: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
+
           Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
 
           {
@@ -432,7 +437,8 @@ let g: R^1 -> R, x -> 2 * exp(x[0]) + 3;
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
 
@@ -522,7 +528,7 @@ let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
       constexpr size_t Dimension = 3;
       auto quadrature_descriptor = GaussQuadratureDescriptor(3);
 
-      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+      using NamedMesh = MeshDataBaseForTests::NamedMesh;
 
       std::vector<NamedMesh> mesh_list = [] {
         std::vector<NamedMesh> extended_mesh_list;
@@ -531,14 +537,15 @@ let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
           extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
         }
         extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                 MeshDataBaseForTests::get().hybrid3DMesh())));
         return extended_mesh_list;
       }();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
 
diff --git a/tests/test_IntegrateCellValue.cpp b/tests/test_IntegrateCellValue.cpp
index a230b9530056f764f0061abaa8dcd151087d6f44..ab2c86f82c7b2b10db0a54555d70a06558460468 100644
--- a/tests/test_IntegrateCellValue.cpp
+++ b/tests/test_IntegrateCellValue.cpp
@@ -52,7 +52,8 @@ TEST_CASE("IntegrateCellValue", "[language]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           std::string_view data = R"(
 import math;
@@ -109,7 +110,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           std::string_view data = R"(
 import math;
@@ -162,7 +164,7 @@ let R3_2d: R^2 -> R^3, x -> [2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3];
       constexpr size_t Dimension = 3;
       auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
 
-      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+      using NamedMesh = MeshDataBaseForTests::NamedMesh;
 
       std::vector<NamedMesh> mesh_list = [] {
         std::vector<NamedMesh> extended_mesh_list;
@@ -171,14 +173,15 @@ let R3_2d: R^2 -> R^3, x -> [2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3];
           extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
         }
         extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                 MeshDataBaseForTests::get().hybrid3DMesh())));
         return extended_mesh_list;
       }();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           std::string_view data = R"(
 import math;
@@ -247,7 +250,9 @@ let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
+
           Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
 
           {
@@ -310,7 +315,8 @@ let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
 
@@ -373,7 +379,7 @@ let R3_2d: R^2 -> R^3, x -> [2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3];
       constexpr size_t Dimension = 3;
       auto quadrature_descriptor = GaussQuadratureDescriptor(3);
 
-      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+      using NamedMesh = MeshDataBaseForTests::NamedMesh;
 
       std::vector<NamedMesh> mesh_list = [] {
         std::vector<NamedMesh> extended_mesh_list;
@@ -382,14 +388,15 @@ let R3_2d: R^2 -> R^3, x -> [2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3];
           extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
         }
         extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                 MeshDataBaseForTests::get().hybrid3DMesh())));
         return extended_mesh_list;
       }();
 
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
 
diff --git a/tests/test_IntegrateOnCells.cpp b/tests/test_IntegrateOnCells.cpp
index 2104bcbf364b8aa039bbb896e56e958188c5f96d..93d1c6806dda456aa17b7f138c6d1f23d8e33315 100644
--- a/tests/test_IntegrateOnCells.cpp
+++ b/tests/test_IntegrateOnCells.cpp
@@ -54,7 +54,8 @@ TEST_CASE("IntegrateOnCells", "[language]")
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
             std::string_view data = R"(
 import math;
@@ -154,7 +155,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             std::string_view data = R"(
 import math;
@@ -251,7 +253,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -260,14 +262,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             std::string_view data = R"(
 import math;
@@ -385,7 +388,9 @@ let R2x2_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
+
             Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
 
             {
@@ -494,7 +499,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
 
@@ -601,7 +607,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -610,14 +616,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
 
@@ -750,7 +757,8 @@ let R2x2_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
             std::string_view data = R"(
 import math;
@@ -850,7 +858,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             std::string_view data = R"(
 import math;
@@ -947,7 +956,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -956,14 +965,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             std::string_view data = R"(
 import math;
@@ -1081,7 +1091,9 @@ let R2x2_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
+
             Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
 
             {
@@ -1190,7 +1202,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
 
@@ -1297,7 +1310,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -1306,14 +1319,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
 
@@ -1446,7 +1460,8 @@ let R2x2_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
             std::string_view data = R"(
 import math;
@@ -1546,7 +1561,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             std::string_view data = R"(
 import math;
@@ -1643,7 +1659,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -1652,14 +1668,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             std::string_view data = R"(
 import math;
@@ -1777,7 +1794,9 @@ let R2x2_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
+
             Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
 
             {
@@ -1886,7 +1905,8 @@ let R2x2_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
             Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
 
@@ -1993,7 +2013,7 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
-        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh;
 
         std::vector<NamedMesh> mesh_list = [] {
           std::vector<NamedMesh> extended_mesh_list;
@@ -2002,14 +2022,15 @@ let R2x2_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1])], [3
             extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
           }
           extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
-                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+                                                                   MeshDataBaseForTests::get().hybrid3DMesh())));
           return extended_mesh_list;
         }();
 
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
             Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
 
diff --git a/tests/test_InterpolateItemArray.cpp b/tests/test_InterpolateItemArray.cpp
index 01720b617888bda30674993a42a0dc78ef951cc6..cab00ae0a3540baba764495e13ce89be9511923c 100644
--- a/tests/test_InterpolateItemArray.cpp
+++ b/tests/test_InterpolateItemArray.cpp
@@ -56,7 +56,8 @@ TEST_CASE("InterpolateItemArray", "[language]")
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -125,7 +126,8 @@ let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -184,7 +186,8 @@ let f_1d: R^1 -> (R), x -> (2*x[0] + 2, 2 * exp(x[0]) + 3);
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -252,7 +255,8 @@ let f_1d: R^1 -> (R^1), x -> (2*x[0] + 2, [2 * exp(x[0]) + 3]);
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -321,7 +325,8 @@ let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -387,7 +392,8 @@ let f_2d: R^2 -> (R), x -> (2*x[0] + 3*x[1] + 2, 2*exp(x[0])*sin(x[1])+3);
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -456,7 +462,8 @@ let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -536,7 +543,8 @@ let f_3d: R^3 -> (R), x -> (2 * x[0] + 3 * x[1] + 2 * x[2] - 1, 2 * exp(x[0]) *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -614,7 +622,8 @@ let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_1d = named_mesh.mesh();
+            auto mesh_1d_v = named_mesh.mesh();
+            auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -689,7 +698,8 @@ let f_1d: R^1 -> (R), x -> (2*x[0] + 2, 2 * exp(x[0]) + 3);
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -764,7 +774,8 @@ let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -829,7 +840,8 @@ let f_2d: R^2 -> (R), x -> (2*x[0] + 3*x[1] + 2, 2*exp(x[0])*sin(x[1])+3);
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_2d = named_mesh.mesh();
+            auto mesh_2d_v = named_mesh.mesh();
+            auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -906,7 +918,8 @@ let f_2d: R^2 -> (R^2x2), x -> ([[x[0],0],[2-x[1], x[0]*x[1]]], [[2*x[0], x[1]],
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -981,7 +994,8 @@ let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -1046,7 +1060,8 @@ let f_3d: R^3 -> (R), x -> (2 * x[0] + 3 * x[1] + 2 * x[2] - 1, 2 * exp(x[0]) *
         for (const auto& named_mesh : mesh_list) {
           SECTION(named_mesh.name())
           {
-            auto mesh_3d = named_mesh.mesh();
+            auto mesh_3d_v = named_mesh.mesh();
+            auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
             auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
diff --git a/tests/test_InterpolateItemValue.cpp b/tests/test_InterpolateItemValue.cpp
index a06eee8ca72584903c73b28defdba220ca7dadf7..370b8b4015122ce3227c3ee99da177b1b7e20978 100644
--- a/tests/test_InterpolateItemValue.cpp
+++ b/tests/test_InterpolateItemValue.cpp
@@ -51,7 +51,8 @@ TEST_CASE("InterpolateItemValue", "[language]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -222,7 +223,8 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -387,7 +389,8 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
@@ -566,7 +569,8 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3 * cos
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
@@ -748,7 +752,8 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
@@ -922,7 +927,8 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> [[2*exp(x[0])*sin(x[1])+3, sin(x[0]-2
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<Dimension>>();
 
           auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
diff --git a/tests/test_ItemArray.cpp b/tests/test_ItemArray.cpp
index 644f348c6ae2df4dcf56c87816d1b3b9c323a1a1..212e1214113e929ecf730cfe603f7d1da21963c1 100644
--- a/tests/test_ItemArray.cpp
+++ b/tests/test_ItemArray.cpp
@@ -33,7 +33,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
         const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -71,7 +72,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -107,7 +109,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -141,7 +144,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -192,7 +196,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -227,7 +232,8 @@ TEST_CASE("ItemArray", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -273,7 +279,8 @@ TEST_CASE("ItemArray", "[mesh]")
 
   SECTION("output")
   {
-    auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh   = mesh_v->get<Mesh<1>>();
 
     Table<int> table{mesh->numberOfCells(), 3};
     for (size_t i = 0; i < table.numberOfRows(); ++i) {
@@ -316,7 +323,8 @@ TEST_CASE("ItemArray", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -346,7 +354,8 @@ TEST_CASE("ItemArray", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -363,7 +372,8 @@ TEST_CASE("ItemArray", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity_2d = mesh_2d->connectivity();
 
@@ -372,7 +382,8 @@ TEST_CASE("ItemArray", "[mesh]")
           for (const auto& named_mesh_3d : mesh_3d_list) {
             SECTION(named_mesh_3d.name())
             {
-              auto mesh_3d = named_mesh_3d.mesh();
+              auto mesh_3d_v = named_mesh_3d.mesh();
+              auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
               const Connectivity<3>& connectivity_3d = mesh_3d->connectivity();
 
diff --git a/tests/test_ItemArrayUtils.cpp b/tests/test_ItemArrayUtils.cpp
index 346345eae1134da16e7e75b7c1d6f553ac14ef6d..8d04842849ed469e1cf0c1e6e217d8e1da4f01f8 100644
--- a/tests/test_ItemArrayUtils.cpp
+++ b/tests/test_ItemArrayUtils.cpp
@@ -25,7 +25,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -299,7 +300,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -573,7 +575,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -850,7 +853,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -879,7 +883,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -908,7 +913,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -940,7 +946,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -969,7 +976,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -998,7 +1006,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1030,7 +1039,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -1059,7 +1069,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for double data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -1086,7 +1097,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^2 data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -1118,7 +1130,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1140,7 +1153,8 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^3x2 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_ItemArrayVariant.cpp b/tests/test_ItemArrayVariant.cpp
index f3b529126f6552cff212790f024728b1d3a2cda9..1f0d1d822c594d223cb94075b6be1a87fddb730a 100644
--- a/tests/test_ItemArrayVariant.cpp
+++ b/tests/test_ItemArrayVariant.cpp
@@ -11,7 +11,8 @@
 
 TEST_CASE("ItemArrayVariant", "[mesh]")
 {
-  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+  std::shared_ptr mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+  std::shared_ptr mesh   = mesh_v->get<Mesh<2>>();
 
   const Connectivity<2>& connectivity = *mesh->shared_connectivity();
 
diff --git a/tests/test_ItemArrayVariantFunctionInterpoler.cpp b/tests/test_ItemArrayVariantFunctionInterpoler.cpp
index 05d0ab6a7a0d96d7913b8949f2256fb494b465b4..95f9d9b5c45c0ff66d0c908e5831877f99ef2e29 100644
--- a/tests/test_ItemArrayVariantFunctionInterpoler.cpp
+++ b/tests/test_ItemArrayVariantFunctionInterpoler.cpp
@@ -55,7 +55,8 @@ TEST_CASE("ItemArrayVariantFunctionInterpoler", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
         auto xr = mesh_1d->xr();
@@ -129,7 +130,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][1]         = std::exp(2 * x[0]) + 3 < 4;
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_data_variant = interpoler.interpolate();
 
@@ -158,7 +159,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               node_array[node_id][1]         = std::floor(2 * x[0] * x[0]);
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::node,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::node,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_data_variant = interpoler.interpolate();
 
@@ -187,7 +188,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][1]         = std::floor(cos(2 * x[0]) + 0.5);
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
@@ -224,7 +225,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][2] = x[0] * std::sin(x[0]);
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell,
                                                         {function1_symbol_id, function2_symbol_id,
                                                          function3_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
@@ -257,7 +258,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][1] = DataType{2 * std::exp(x[0]) * x[0]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
@@ -282,7 +283,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][0] = DataType{2 * std::exp(x[0]), -3 * x[0]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
@@ -305,7 +306,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][0]         = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
@@ -328,7 +329,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_array[cell_id][0]         = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
@@ -352,7 +353,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
@@ -384,7 +385,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 std::exp(x[0])};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(cell_array, item_array_variant->get<CellArray<DataType>>()));
@@ -410,7 +411,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
 
           FunctionSymbolId function3_symbol_id(std::get<uint64_t>(i_symbol_f3->attributes().value()), symbol_table);
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell,
                                                         {function1_symbol_id, function2_symbol_id,
                                                          function3_symbol_id});
 
@@ -429,7 +430,8 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         auto xl = MeshDataManager::instance().getMeshData(*mesh_2d).xl();
 
@@ -487,7 +489,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               face_array[face_id][1]         = std::sin(2 * x[0]) < x[1];
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
@@ -512,7 +514,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               face_array[face_id][0] = DataType{2 * std::exp(x[0]), -3 * x[1]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(face_array, item_array_variant->get<FaceArray<DataType>>()));
@@ -544,7 +546,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 std::exp(x[1])};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(face_array, item_array_variant->get<FaceArray<DataType>>()));
@@ -562,7 +564,8 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         auto xe = MeshDataManager::instance().getMeshData(*mesh_3d).xe();
 
@@ -618,7 +621,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2,
               edge_array[edge_id][1] = 3 * std::sin(x[0] + x[2]) + 2 * x[1];
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge,
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge,
                                                         {function1_symbol_id, function2_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
@@ -643,7 +646,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2,
               edge_array[edge_id][0] = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(edge_array, item_array_variant->get<EdgeArray<DataType>>()));
@@ -667,7 +670,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2,
                 DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]};
             });
 
-          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, {function_symbol_id});
+          ItemArrayVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge, {function_symbol_id});
           std::shared_ptr item_array_variant = interpoler.interpolate();
 
           REQUIRE(same_item_array(edge_array, item_array_variant->get<EdgeArray<DataType>>()));
diff --git a/tests/test_ItemValue.cpp b/tests/test_ItemValue.cpp
index e5643274772843499826d487279f1003be2f1c4a..43a8dd459b59a438b4a3b072ff60b9b543dcf8f2 100644
--- a/tests/test_ItemValue.cpp
+++ b/tests/test_ItemValue.cpp
@@ -31,7 +31,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
         const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -64,7 +65,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -93,7 +95,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -117,7 +120,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -147,7 +151,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -185,7 +190,8 @@ TEST_CASE("ItemValue", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -219,7 +225,8 @@ TEST_CASE("ItemValue", "[mesh]")
 
   SECTION("output")
   {
-    auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh   = mesh_v->get<Mesh<1>>();
 
     Array<int> array{mesh->numberOfCells()};
     for (size_t i = 0; i < array.size(); ++i) {
@@ -260,7 +267,8 @@ TEST_CASE("ItemValue", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -290,7 +298,8 @@ TEST_CASE("ItemValue", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -307,7 +316,8 @@ TEST_CASE("ItemValue", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity_2d = mesh_2d->connectivity();
 
@@ -316,7 +326,8 @@ TEST_CASE("ItemValue", "[mesh]")
           for (const auto& named_mesh_3d : mesh_3d_list) {
             SECTION(named_mesh_3d.name())
             {
-              auto mesh_3d = named_mesh_3d.mesh();
+              auto mesh_3d_v = named_mesh_3d.mesh();
+              auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
               const Connectivity<3>& connectivity_3d = mesh_3d->connectivity();
 
diff --git a/tests/test_ItemValueUtils.cpp b/tests/test_ItemValueUtils.cpp
index e9df51a05bc9b5a411a186ad742c2309c93ad9d4..f80819f2c619709584c6574dd185d5a0e2f512fb 100644
--- a/tests/test_ItemValueUtils.cpp
+++ b/tests/test_ItemValueUtils.cpp
@@ -23,7 +23,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -76,7 +77,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -103,7 +105,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -130,7 +133,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -160,7 +164,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -187,7 +192,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -214,7 +220,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -244,7 +251,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -271,7 +279,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for double data")
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d_v = named_mesh.mesh();
+          auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -307,7 +316,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + "for size_t data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -334,7 +344,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + "for double data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -363,7 +374,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + "for N^3 data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -394,7 +406,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + "for R2 data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d_v = named_mesh.mesh();
+          auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -438,7 +451,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -460,7 +474,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^3x2 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -486,7 +501,8 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 
         SECTION(named_mesh.name() + "for R^2x3 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d_v = named_mesh.mesh();
+          auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_ItemValueVariant.cpp b/tests/test_ItemValueVariant.cpp
index 4d8fca46dac3d6ccd0c3e88a5d6fa5fc9c065fc4..6eed537b91a0ef1a9009c79653ce89b141e57d27 100644
--- a/tests/test_ItemValueVariant.cpp
+++ b/tests/test_ItemValueVariant.cpp
@@ -11,9 +11,10 @@
 
 TEST_CASE("ItemValueVariant", "[mesh]")
 {
-  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+  auto mesh_2d_v = MeshDataBaseForTests::get().hybrid2DMesh();
+  auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
-  const Connectivity<2>& connectivity = *mesh->shared_connectivity();
+  const Connectivity<2>& connectivity = *mesh_2d->shared_connectivity();
 
   using R1 = TinyVector<1>;
   using R2 = TinyVector<2>;
diff --git a/tests/test_ItemValueVariantFunctionInterpoler.cpp b/tests/test_ItemValueVariantFunctionInterpoler.cpp
index bd05f94e9ade781d2e36fdc075a977166597ffaf..4bc251aba0b6b75d7c65d87474e72010f0cecf1b 100644
--- a/tests/test_ItemValueVariantFunctionInterpoler.cpp
+++ b/tests/test_ItemValueVariantFunctionInterpoler.cpp
@@ -49,7 +49,8 @@ TEST_CASE("ItemValueVariantFunctionInterpoler", "[scheme]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_1d = named_mesh.mesh();
+        auto mesh_1d_v = named_mesh.mesh();
+        auto mesh_1d   = mesh_1d_v->get<Mesh<1>>();
 
         auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
         auto xr = mesh_1d->xr();
@@ -101,7 +102,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_data_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_data_variant->get<CellValue<bool>>()));
@@ -122,7 +123,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               node_value[node_id]            = std::floor(3 * x[0] * x[0] + 2);
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::node, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::node, function_symbol_id);
           std::shared_ptr item_data_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(node_value, item_data_variant->get<NodeValue<uint64_t>>()));
@@ -143,7 +144,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<int64_t>>()));
@@ -164,7 +165,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<double>>()));
@@ -187,7 +188,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -210,7 +211,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[0]};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -233,7 +234,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -256,7 +257,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
               cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -280,7 +281,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -311,7 +312,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                              std::exp(x[0])};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_1d, ItemType::cell, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_1d_v, ItemType::cell, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(cell_value, item_value_variant->get<CellValue<DataType>>()));
@@ -329,7 +330,8 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d_v = named_mesh.mesh();
+        auto mesh_2d   = mesh_2d_v->get<Mesh<2>>();
 
         auto xl = MeshDataManager::instance().getMeshData(*mesh_2d).xl();
 
@@ -375,7 +377,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               face_value[face_id]            = std::exp(2 * x[0]) < 2 * x[1];
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(face_value, item_value_variant->get<FaceValue<DataType>>()));
@@ -398,7 +400,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               face_value[face_id]            = DataType{2 * std::exp(x[0]), -3 * x[1]};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(face_value, item_value_variant->get<FaceValue<DataType>>()));
@@ -430,7 +432,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                              std::exp(x[1])};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_2d, ItemType::face, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_2d_v, ItemType::face, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(face_value, item_value_variant->get<FaceValue<DataType>>()));
@@ -448,7 +450,8 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d_v = named_mesh.mesh();
+        auto mesh_3d   = mesh_3d_v->get<Mesh<3>>();
 
         auto xe = MeshDataManager::instance().getMeshData(*mesh_3d).xe();
 
@@ -492,7 +495,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               edge_value[edge_id]            = 2 * std::exp(x[0] + x[2]) + 3 * x[1];
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(edge_value, item_value_variant->get<EdgeValue<double>>()));
@@ -515,7 +518,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
               edge_value[edge_id]            = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(edge_value, item_value_variant->get<EdgeValue<DataType>>()));
@@ -539,7 +542,7 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                 DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]};
             });
 
-          ItemValueVariantFunctionInterpoler interpoler(mesh_3d, ItemType::edge, function_symbol_id);
+          ItemValueVariantFunctionInterpoler interpoler(mesh_3d_v, ItemType::edge, function_symbol_id);
           std::shared_ptr item_value_variant = interpoler.interpolate();
 
           REQUIRE(same_item_value(edge_value, item_value_variant->get<EdgeValue<DataType>>()));
diff --git a/tests/test_MedianDualConnectivityBuilder.cpp b/tests/test_MedianDualConnectivityBuilder.cpp
index 6d091bd037e4ac57635757b98671eff9fe6eb643..ab418f24a8cd59fab535cf5b4997f3e82f1e8972 100644
--- a/tests/test_MedianDualConnectivityBuilder.cpp
+++ b/tests/test_MedianDualConnectivityBuilder.cpp
@@ -37,10 +37,10 @@ TEST_CASE("MedianDualConnectivityBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     REQUIRE(primal_connectivity.numberOfNodes() == 53);
diff --git a/tests/test_MedianDualMeshBuilder.cpp b/tests/test_MedianDualMeshBuilder.cpp
index 062c48264f4a766d9a9af64da42417746e10636b..f4f96d240dcab0f9dbe09cb32c5a25366402574c 100644
--- a/tests/test_MedianDualMeshBuilder.cpp
+++ b/tests/test_MedianDualMeshBuilder.cpp
@@ -18,18 +18,17 @@ TEST_CASE("MedianDualMeshBuilder", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType = Mesh<Dimension>;
 
-    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid2DMesh();
-    const MeshType& primal_mesh = *p_mesh;
+    std::shared_ptr primal_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    const MeshType& primal_mesh   = *primal_mesh_v->get<MeshType>();
 
     REQUIRE(primal_mesh.numberOfNodes() == 53);
     REQUIRE(primal_mesh.numberOfFaces() == 110);
     REQUIRE(primal_mesh.numberOfCells() == 58);
 
-    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getMedianDualMesh(primal_mesh);
-    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getMedianDualMesh(primal_mesh_v);
+    const MeshType& dual_mesh           = *p_diamond_dual_mesh->get<MeshType>();
 
     REQUIRE(dual_mesh.numberOfNodes() == 192);
     REQUIRE(dual_mesh.numberOfFaces() == 244);
diff --git a/tests/test_MeshEdgeBoundary.cpp b/tests/test_MeshEdgeBoundary.cpp
index 74facdecf52bd9dce75e8ebff557415d4cf8f691..307d73fdcccdc990a1b8cfa740493dfb21d154d3 100644
--- a/tests/test_MeshEdgeBoundary.cpp
+++ b/tests/test_MeshEdgeBoundary.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -86,7 +86,7 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -120,13 +120,13 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -158,7 +158,7 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -192,13 +192,13 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -230,7 +230,7 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -266,11 +266,10 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary");
 
@@ -284,11 +283,10 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 1;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -301,11 +299,10 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 2;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -318,11 +315,10 @@ TEST_CASE("MeshEdgeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 3;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1");
 
diff --git a/tests/test_MeshEdgeInterface.cpp b/tests/test_MeshEdgeInterface.cpp
index a42258e45510177e5192a39712cad9323658ce38..328a1ce637f34969994766961f69be5e186ae6a9 100644
--- a/tests/test_MeshEdgeInterface.cpp
+++ b/tests/test_MeshEdgeInterface.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshEdgeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -87,13 +87,13 @@ TEST_CASE("MeshEdgeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -127,13 +127,13 @@ TEST_CASE("MeshEdgeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -169,11 +169,10 @@ TEST_CASE("MeshEdgeInterface", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedInterfaceDescriptor named_interface_descriptor("invalid_interface");
 
diff --git a/tests/test_MeshFaceBoundary.cpp b/tests/test_MeshFaceBoundary.cpp
index aa4142d6b5fc9ca64c3f64f5cec9ed3a2b1fc993..40de5b4e1594c454529730cf2fdfb322bf570814 100644
--- a/tests/test_MeshFaceBoundary.cpp
+++ b/tests/test_MeshFaceBoundary.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -86,7 +86,7 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -120,13 +120,13 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -158,7 +158,7 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -192,13 +192,13 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -230,7 +230,7 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -266,11 +266,10 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary");
 
@@ -284,11 +283,10 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 1;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -301,11 +299,10 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 2;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -318,11 +315,10 @@ TEST_CASE("MeshFaceBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 3;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1");
 
diff --git a/tests/test_MeshFaceInterface.cpp b/tests/test_MeshFaceInterface.cpp
index 503ae0a1fe159fa4c55b8760abdc05a19c2e2370..b213ffd48d67677700943549b436a4fefbdb9471 100644
--- a/tests/test_MeshFaceInterface.cpp
+++ b/tests/test_MeshFaceInterface.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -87,13 +87,13 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -127,13 +127,13 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -169,11 +169,10 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedInterfaceDescriptor named_interface_descriptor("invalid_interface");
 
@@ -187,11 +186,10 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 1;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("XMIN");
 
@@ -204,11 +202,10 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 2;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("XMIN");
 
@@ -221,11 +218,10 @@ TEST_CASE("MeshFaceInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 3;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("XMIN");
 
diff --git a/tests/test_MeshFlatEdgeBoundary.cpp b/tests/test_MeshFlatEdgeBoundary.cpp
index 16d843ade242d9105530fb04cfdbf4094085955e..f717565461f9ae77ee8424108013293ec20060f9 100644
--- a/tests/test_MeshFlatEdgeBoundary.cpp
+++ b/tests/test_MeshFlatEdgeBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 1;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R1 = TinyVector<1>;
 
       SECTION("cartesian 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -121,7 +121,7 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
       SECTION("unordered 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -186,15 +186,15 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -290,7 +290,7 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -389,15 +389,15 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -572,7 +572,7 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
       SECTION("hybrid 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -752,8 +752,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -763,7 +763,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -845,7 +846,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -930,8 +932,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -943,7 +945,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1038,7 +1041,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1139,8 +1143,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -1148,7 +1152,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1239,14 +1244,15 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1358,7 +1364,8 @@ TEST_CASE("MeshFlatEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshFlatFaceBoundary.cpp b/tests/test_MeshFlatFaceBoundary.cpp
index 08e1779ce777bb4c6c3600bfd744c063f3359df5..f5630ee1d2b60858525c7b17a60312fcfe55f524 100644
--- a/tests/test_MeshFlatFaceBoundary.cpp
+++ b/tests/test_MeshFlatFaceBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 1;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R1 = TinyVector<1>;
 
       SECTION("cartesian 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -121,7 +121,7 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
       SECTION("unordered 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -186,15 +186,15 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -268,7 +268,7 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -345,15 +345,15 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -440,7 +440,7 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
       SECTION("hybrid 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -532,8 +532,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -543,7 +543,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -625,7 +626,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -710,8 +712,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -723,7 +725,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -818,7 +821,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -919,8 +923,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -928,7 +932,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1019,14 +1024,15 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1138,7 +1144,8 @@ TEST_CASE("MeshFlatFaceBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshFlatNodeBoundary.cpp b/tests/test_MeshFlatNodeBoundary.cpp
index 760f5564226f77fa53ec8de15f3f85552f02cbc4..60348c53b204289a0e0a3745d42dc70af132a3a5 100644
--- a/tests/test_MeshFlatNodeBoundary.cpp
+++ b/tests/test_MeshFlatNodeBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 1;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R1 = TinyVector<1>;
 
       SECTION("cartesian 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -121,7 +121,7 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
       SECTION("unordered 1d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -186,15 +186,15 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -290,7 +290,7 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -389,15 +389,15 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -572,7 +572,7 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
       SECTION("hybrid 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -752,8 +752,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -763,7 +763,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -845,7 +846,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -930,8 +932,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -943,7 +945,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1038,7 +1041,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1139,8 +1143,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -1148,7 +1152,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1232,8 +1237,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -1243,7 +1248,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1344,8 +1350,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -1353,7 +1359,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1444,14 +1451,15 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1563,7 +1571,8 @@ TEST_CASE("MeshFlatNodeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshLineEdgeBoundary.cpp b/tests/test_MeshLineEdgeBoundary.cpp
index 31d9a578b956279ce52fdea6d165de1f13250a6d..39a93e8880b5f616003a0c5d1c28860af6363124 100644
--- a/tests/test_MeshLineEdgeBoundary.cpp
+++ b/tests/test_MeshLineEdgeBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -154,7 +154,7 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -253,15 +253,15 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -403,7 +403,7 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
       SECTION("hybrid 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -552,8 +552,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -563,7 +563,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -645,7 +646,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -730,8 +732,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -743,7 +745,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -831,7 +834,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -925,8 +929,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -934,7 +938,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1025,14 +1030,15 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1143,7 +1149,8 @@ TEST_CASE("MeshLineEdgeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshLineFaceBoundary.cpp b/tests/test_MeshLineFaceBoundary.cpp
index 86150aa40cb5fb82fd8718b56551dc7b52017c1d..5599862251d7e9c3e8f8d759e350e2776d0fa013 100644
--- a/tests/test_MeshLineFaceBoundary.cpp
+++ b/tests/test_MeshLineFaceBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -154,7 +154,7 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -256,8 +256,8 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -267,7 +267,8 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -349,7 +350,8 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -437,8 +439,8 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -446,7 +448,8 @@ TEST_CASE("MeshLineFaceBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshLineNodeBoundary.cpp b/tests/test_MeshLineNodeBoundary.cpp
index f82f69b142df991b8c757ba2707be0bc045610d3..748c4533528fcbd137a4c88b83601e17ea48793a 100644
--- a/tests/test_MeshLineNodeBoundary.cpp
+++ b/tests/test_MeshLineNodeBoundary.cpp
@@ -50,15 +50,15 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
       SECTION("cartesian 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -154,7 +154,7 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
       SECTION("hybrid 2d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -253,15 +253,15 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -403,7 +403,7 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
       SECTION("hybrid 3d")
       {
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -552,8 +552,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -563,7 +563,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("cartesian 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -645,7 +646,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -730,8 +732,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
@@ -743,7 +745,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -831,7 +834,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -925,8 +929,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 2;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R2 = TinyVector<2>;
 
@@ -934,7 +938,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("hybrid 2d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1025,14 +1030,15 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType         = Mesh<Dimension>;
+      using ConnectivityType = typename MeshType::Connectivity;
 
       using R3 = TinyVector<3>;
 
       SECTION("cartesian 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().cartesian3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
@@ -1143,7 +1149,8 @@ TEST_CASE("MeshLineNodeBoundary", "[mesh]")
 
       SECTION("hybrid 3d")
       {
-        std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+        std::shared_ptr p_mesh   = p_mesh_v->get<MeshType>();
 
         const ConnectivityType& connectivity = p_mesh->connectivity();
 
diff --git a/tests/test_MeshNodeBoundary.cpp b/tests/test_MeshNodeBoundary.cpp
index aab1cfd500a78b0835ce6a59c62da6f27b1b2cd6..68d398095ae3764c30a8fe03c365236453b00dfb 100644
--- a/tests/test_MeshNodeBoundary.cpp
+++ b/tests/test_MeshNodeBoundary.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -86,7 +86,7 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -120,13 +120,13 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -159,7 +159,7 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -194,13 +194,13 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("cartesian 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -240,7 +240,7 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -284,11 +284,10 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedBoundaryDescriptor named_boundary_descriptor("invalid_boundary");
 
@@ -302,11 +301,10 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 1;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -319,11 +317,10 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 2;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE");
 
@@ -336,11 +333,10 @@ TEST_CASE("MeshNodeBoundary", "[mesh]")
       {
         static constexpr size_t Dimension = 3;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedBoundaryDescriptor named_boundary_descriptor("INTERFACE1");
 
diff --git a/tests/test_MeshNodeInterface.cpp b/tests/test_MeshNodeInterface.cpp
index d74080e3fa14541e7debccc4c0ee1fd2c8db76eb..f8ce8a5fb61c5166db9520868e0d999a0f9e7d0c 100644
--- a/tests/test_MeshNodeInterface.cpp
+++ b/tests/test_MeshNodeInterface.cpp
@@ -47,13 +47,13 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("unordered 1d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -87,13 +87,13 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 2d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -127,13 +127,13 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
   {
     static constexpr size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
     SECTION("hybrid 3d")
     {
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       const ConnectivityType& connectivity = mesh.connectivity();
 
@@ -169,11 +169,10 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
     {
       static constexpr size_t Dimension = 3;
 
-      using ConnectivityType = Connectivity<Dimension>;
-      using MeshType         = Mesh<ConnectivityType>;
+      using MeshType = Mesh<Dimension>;
 
       std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-      const MeshType& mesh   = *p_mesh;
+      const MeshType& mesh   = *p_mesh->get<MeshType>();
 
       NamedInterfaceDescriptor named_interface_descriptor("invalid_interface");
 
@@ -187,11 +186,10 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 1;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().unordered1DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("XMIN");
 
@@ -204,11 +202,10 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 2;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid2DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("XMIN");
 
@@ -221,11 +218,10 @@ TEST_CASE("MeshNodeInterface", "[mesh]")
       {
         static constexpr size_t Dimension = 3;
 
-        using ConnectivityType = Connectivity<Dimension>;
-        using MeshType         = Mesh<ConnectivityType>;
+        using MeshType = Mesh<Dimension>;
 
         std::shared_ptr p_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
-        const MeshType& mesh   = *p_mesh;
+        const MeshType& mesh   = *p_mesh->get<MeshType>();
 
         NamedInterfaceDescriptor named_interface_descriptor("ZMAX");
 
diff --git a/tests/test_MeshVariant.cpp b/tests/test_MeshVariant.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..212bcecd003f1842dd6e769451172b70d0871d36
--- /dev/null
+++ b/tests/test_MeshVariant.cpp
@@ -0,0 +1,104 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshVariant.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("MeshVariant", "[mesh]")
+{
+  SECTION("Polygonal 1D")
+  {
+    auto mesh_v = MeshDataBaseForTests::get().unordered1DMesh();
+    auto mesh   = mesh_v->get<Mesh<1>>();
+
+    const std::string error_msg =
+      R"(error: invalid mesh type type
+- required Mesh<3ul>
+- contains Mesh<1ul>)";
+
+    REQUIRE_THROWS_WITH(mesh_v->get<Mesh<3>>(), error_msg);
+
+    REQUIRE(mesh->id() == mesh_v->id());
+    REQUIRE(mesh->numberOfCells() == mesh_v->numberOfCells());
+    REQUIRE(mesh->numberOfFaces() == mesh_v->numberOfFaces());
+    REQUIRE(mesh->numberOfEdges() == mesh_v->numberOfEdges());
+    REQUIRE(mesh->numberOfNodes() == mesh_v->numberOfNodes());
+
+    REQUIRE(mesh->connectivity().id() == mesh_v->connectivity().id());
+
+    {
+      std::ostringstream os_v;
+      os_v << *mesh_v;
+
+      std::ostringstream os;
+      os << *mesh;
+
+      REQUIRE(os_v.str() == os.str());
+    }
+  }
+
+  SECTION("Polygonal 2D")
+  {
+    auto mesh_v = MeshDataBaseForTests::get().hybrid2DMesh();
+    auto mesh   = mesh_v->get<Mesh<2>>();
+
+    const std::string error_msg =
+      R"(error: invalid mesh type type
+- required Mesh<1ul>
+- contains Mesh<2ul>)";
+
+    REQUIRE_THROWS_WITH(mesh_v->get<Mesh<1>>(), error_msg);
+
+    REQUIRE(mesh->id() == mesh_v->id());
+    REQUIRE(mesh->numberOfCells() == mesh_v->numberOfCells());
+    REQUIRE(mesh->numberOfFaces() == mesh_v->numberOfFaces());
+    REQUIRE(mesh->numberOfEdges() == mesh_v->numberOfEdges());
+    REQUIRE(mesh->numberOfNodes() == mesh_v->numberOfNodes());
+
+    REQUIRE(mesh->connectivity().id() == mesh_v->connectivity().id());
+
+    {
+      std::ostringstream os_v;
+      os_v << *mesh_v;
+
+      std::ostringstream os;
+      os << *mesh;
+
+      REQUIRE(os_v.str() == os.str());
+    }
+  }
+
+  SECTION("Polygonal 3D")
+  {
+    auto mesh_v = MeshDataBaseForTests::get().hybrid3DMesh();
+    auto mesh   = mesh_v->get<Mesh<3>>();
+
+    const std::string error_msg =
+      R"(error: invalid mesh type type
+- required Mesh<2ul>
+- contains Mesh<3ul>)";
+
+    REQUIRE_THROWS_WITH(mesh_v->get<Mesh<2>>(), error_msg);
+
+    REQUIRE(mesh->id() == mesh_v->id());
+    REQUIRE(mesh->numberOfCells() == mesh_v->numberOfCells());
+    REQUIRE(mesh->numberOfFaces() == mesh_v->numberOfFaces());
+    REQUIRE(mesh->numberOfEdges() == mesh_v->numberOfEdges());
+    REQUIRE(mesh->numberOfNodes() == mesh_v->numberOfNodes());
+
+    REQUIRE(mesh->connectivity().id() == mesh_v->connectivity().id());
+
+    {
+      std::ostringstream os_v;
+      os_v << *mesh_v;
+
+      std::ostringstream os;
+      os << *mesh;
+
+      REQUIRE(os_v.str() == os.str());
+    }
+  }
+}
diff --git a/tests/test_ParallelChecker_read.cpp b/tests/test_ParallelChecker_read.cpp
index 233d37e34d1b8b9069d918ade3657f07c3b3ba38..026cd4063fc0837dcec9a83bac312d4d58619561 100644
--- a/tests/test_ParallelChecker_read.cpp
+++ b/tests/test_ParallelChecker_read.cpp
@@ -199,7 +199,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
   {
     // ItemValues
     {   // 1d
-      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -363,7 +363,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
         const Connectivity<1>& other_connectivity = other_mesh->connectivity();
 
         CellValue<double> other_shape{other_connectivity};
@@ -377,7 +377,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // ItemArray
     {   // 1d
-      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -503,7 +503,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
         const Connectivity<1>& other_connectivity = other_mesh->connectivity();
 
         CellArray<double> other_shape{other_connectivity, 2};
@@ -517,7 +517,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // ItemValues
     {   // 2d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -610,7 +610,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // ItemArray
     {   // 2d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -747,7 +747,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
         const Connectivity<2>& other_connectivity = other_mesh->connectivity();
 
         FaceArray<DataType> other_shape{other_connectivity, 2};
@@ -761,7 +761,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // ItemValues
     {   // 3d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -858,7 +858,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // ItemArray
     {   // 3d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -991,7 +991,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
         const Connectivity<2>& other_connectivity = other_mesh->connectivity();
 
         FaceArray<DataType> other_shape{other_connectivity, 2};
@@ -1007,7 +1007,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
   {
     // SubItemValuePerItem
     {   // 1d
-      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -1198,7 +1198,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
         const Connectivity<1>& other_connectivity = other_mesh->connectivity();
 
         CellValuePerNode<double> other_shape{other_connectivity};
@@ -1212,7 +1212,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // SubItemArrayPerItem
     {   // 1d
-      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -1352,7 +1352,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian1DMesh()->get<Mesh<1>>();
         const Connectivity<1>& other_connectivity = other_mesh->connectivity();
 
         CellArray<double> other_shape{other_connectivity, 2};
@@ -1366,7 +1366,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // SubItemValuePerItem
     {   // 2d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -1503,7 +1503,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
         const Connectivity<2>& other_connectivity = other_mesh->connectivity();
 
         FaceArray<DataType> other_shape{other_connectivity, 2};
@@ -1517,7 +1517,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // SubItemArrayPerItem
     {   // 2d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -1708,7 +1708,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
         const Connectivity<2>& other_connectivity = other_mesh->connectivity();
 
         CellValuePerNode<double> other_shape{other_connectivity};
@@ -1722,7 +1722,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // SubItemValuePerItem
     {   // 3d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -1855,7 +1855,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian2DMesh()->get<Mesh<2>>();
         const Connectivity<2>& other_connectivity = other_mesh->connectivity();
 
         FaceArray<DataType> other_shape{other_connectivity, 2};
@@ -1868,7 +1868,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
 
     // SubItemArrayPerItem
     {   // 3d
-      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh                           = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
       std::string filename                = ParallelChecker::instance().filename();
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -2008,7 +2008,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
       }
 
       {
-        auto other_mesh                           = MeshDataBaseForTests::get().cartesian3DMesh();
+        auto other_mesh                           = MeshDataBaseForTests::get().cartesian3DMesh()->get<Mesh<3>>();
         const Connectivity<3>& other_connectivity = other_mesh->connectivity();
 
         CellArray<double> other_shape{other_connectivity, 2};
@@ -2046,7 +2046,7 @@ TEST_CASE("ParallelChecker_read", "[dev]")
   REQUIRE_NOTHROW(ParallelChecker::instance().setMode(ParallelChecker::Mode::read));
   REQUIRE_NOTHROW(not ParallelChecker::instance().isWriting());
 
-  auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+  auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
   const Connectivity<1>& connectivity = mesh->connectivity();
 
diff --git a/tests/test_ParallelChecker_write.cpp b/tests/test_ParallelChecker_write.cpp
index 2854852373f5449c19403f4dd89b5811d02224bc..8ca7ceb50cdd2d2e8c1d4f3f5a2fd2669d9d9806 100644
--- a/tests/test_ParallelChecker_write.cpp
+++ b/tests/test_ParallelChecker_write.cpp
@@ -131,7 +131,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
 
     // ItemValues
     {   // 1d
-      auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -148,7 +148,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
         check(var, var_name, source_location, tag);
       }
       {
-        DiscreteFunctionP0<1, double> var{mesh};
+        DiscreteFunctionP0<double> var{mesh};
         var.fill(1);
 
         const SourceLocation source_location;
@@ -172,7 +172,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 2d
-      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -202,7 +202,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 3d
-      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -230,7 +230,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
         check(var, var_name, source_location, tag);
       }
       {
-        DiscreteFunctionP0<3, TinyVector<3>> var{mesh};
+        DiscreteFunctionP0<TinyVector<3>> var{mesh};
         var.fill(zero);
 
         const SourceLocation source_location;
@@ -244,7 +244,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
 
     // ItemArrays
     {   // 1d
-      auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -261,7 +261,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
         check(var, var_name, source_location, tag);
       }
       {
-        DiscreteFunctionP0Vector<1, double> var{mesh, 2};
+        DiscreteFunctionP0Vector<double> var{mesh, 2};
         var.fill(1);
 
         const SourceLocation source_location;
@@ -285,7 +285,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 2d
-      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -315,7 +315,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 3d
-      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -343,7 +343,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
         check(var, var_name, source_location, tag);
       }
       {
-        DiscreteFunctionP0Vector<3, double> var{mesh, 3};
+        DiscreteFunctionP0Vector<double> var{mesh, 3};
         var.fill(0);
 
         const SourceLocation source_location;
@@ -394,7 +394,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
 
     // ItemValues
     {   // 1d
-      auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -424,7 +424,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 2d
-      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -454,7 +454,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 3d
-      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -485,7 +485,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
 
     // ItemArrays
     {   // 1d
-      auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
       const Connectivity<1>& connectivity = mesh->connectivity();
 
@@ -515,7 +515,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 2d
-      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
 
       const Connectivity<2>& connectivity = mesh->connectivity();
 
@@ -545,7 +545,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
     }
 
     {   // 3d
-      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh->connectivity();
 
@@ -620,7 +620,7 @@ TEST_CASE("ParallelChecker_write", "[dev]")
   REQUIRE_NOTHROW(ParallelChecker::instance().setMode(ParallelChecker::Mode::automatic));
   REQUIRE_NOTHROW(ParallelChecker::instance().isWriting() == (parallel::size() == 1));
 
-  auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+  auto mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
 
   const Connectivity<1>& connectivity = mesh->connectivity();
 
diff --git a/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp b/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp
index f503eaac5865ac213c3140cae9b1afb9b30a33b7..68a2301df4dd28e3cc847f065c014bfc44ded6cc 100644
--- a/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp
+++ b/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp
@@ -17,10 +17,10 @@ TEST_CASE("PrimalToDiamondDualConnectivityDataMapper", "[mesh]")
   {
     constexpr static size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     std::shared_ptr p_diamond_dual_connectivity =
@@ -44,10 +44,10 @@ TEST_CASE("PrimalToDiamondDualConnectivityDataMapper", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     std::shared_ptr p_diamond_dual_connectivity =
@@ -223,10 +223,10 @@ TEST_CASE("PrimalToDiamondDualConnectivityDataMapper", "[mesh]")
   {
     constexpr static size_t Dimension = 3;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     std::shared_ptr p_diamond_dual_connectivity =
diff --git a/tests/test_PrimalToDual1DConnectivityDataMapper.cpp b/tests/test_PrimalToDual1DConnectivityDataMapper.cpp
index 6bf365ec4371e26163c7003261fa125fb851ff3f..10a6fc4b7a2691e356de6e9b146447296c1e7e6f 100644
--- a/tests/test_PrimalToDual1DConnectivityDataMapper.cpp
+++ b/tests/test_PrimalToDual1DConnectivityDataMapper.cpp
@@ -14,10 +14,10 @@ TEST_CASE("PrimalToDual1DConnectivityDataMapper", "[mesh]")
 {
   constexpr static size_t Dimension = 1;
 
-  using ConnectivityType = Connectivity<Dimension>;
-  using MeshType         = Mesh<ConnectivityType>;
+  using MeshType         = Mesh<Dimension>;
+  using ConnectivityType = typename MeshType::Connectivity;
 
-  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh()->get<MeshType>();
   const ConnectivityType& primal_connectivity = mesh->connectivity();
 
   std::shared_ptr p_dual_1d_connectivity =
diff --git a/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp b/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp
index 6a753b49fecf2b89f420b28f8c61411b01fe14df..7fb1e02a06c57b8657570a6f51e4312b40ca9fe2 100644
--- a/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp
+++ b/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp
@@ -17,10 +17,10 @@ TEST_CASE("PrimalToMedianDualConnectivityDataMapper", "[mesh]")
   {
     constexpr static size_t Dimension = 1;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     std::shared_ptr p_median_dual_connectivity =
@@ -44,10 +44,10 @@ TEST_CASE("PrimalToMedianDualConnectivityDataMapper", "[mesh]")
   {
     constexpr static size_t Dimension = 2;
 
-    using ConnectivityType = Connectivity<Dimension>;
-    using MeshType         = Mesh<ConnectivityType>;
+    using MeshType         = Mesh<Dimension>;
+    using ConnectivityType = typename MeshType::Connectivity;
 
-    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh()->get<MeshType>();
     const ConnectivityType& primal_connectivity = mesh->connectivity();
 
     std::shared_ptr p_median_dual_connectivity =
diff --git a/tests/test_SubItemArrayPerItem.cpp b/tests/test_SubItemArrayPerItem.cpp
index ec4e06e0c31da2906333bd8a37ea390e7ee8d9c3..23f32a41bc9cfd6a503a7e78e726106a8c79fdf9 100644
--- a/tests/test_SubItemArrayPerItem.cpp
+++ b/tests/test_SubItemArrayPerItem.cpp
@@ -69,7 +69,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -192,7 +192,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -358,7 +358,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -555,7 +555,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -628,7 +628,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -700,7 +700,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -772,7 +772,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -883,7 +883,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -994,7 +994,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -1068,7 +1068,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
 
     SECTION("checking invalid table size in constructor")
     {
-      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1090,7 +1090,7 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_SubItemArrayPerItemUtils.cpp b/tests/test_SubItemArrayPerItemUtils.cpp
index d36829250ce4a5e9b88c7015504c1ffb68362f96..1ff8bb17f7bf170c748cfa15aa3fc82f25f4a879 100644
--- a/tests/test_SubItemArrayPerItemUtils.cpp
+++ b/tests/test_SubItemArrayPerItemUtils.cpp
@@ -23,7 +23,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -95,7 +95,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -127,7 +127,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -159,7 +159,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -194,7 +194,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -226,7 +226,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -258,7 +258,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -294,7 +294,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -324,7 +324,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -347,7 +347,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^2 data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -373,7 +373,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for float data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -417,7 +417,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -440,7 +440,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^2x3 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -466,7 +466,7 @@ TEST_CASE("SubItemArrayPerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for R^2x3 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_SubItemArrayPerItemVariant.cpp b/tests/test_SubItemArrayPerItemVariant.cpp
index 6e3413aec062f9c785592884650a706706f21088..7a224e10aa03cdd58ed184b1b92d13d470151259 100644
--- a/tests/test_SubItemArrayPerItemVariant.cpp
+++ b/tests/test_SubItemArrayPerItemVariant.cpp
@@ -11,9 +11,9 @@
 
 TEST_CASE("SubItemArrayPerItemVariant", "[mesh]")
 {
-  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
-  const Connectivity<3>& connectivity = *mesh->shared_connectivity();
+  const Connectivity<3>& connectivity = mesh->connectivity();
 
   using R1   = TinyVector<1>;
   using R2   = TinyVector<2>;
diff --git a/tests/test_SubItemValuePerItem.cpp b/tests/test_SubItemValuePerItem.cpp
index 349de725023c5156bb05f75265d61d589cc48799..72b794bb1e8b5e0fdc3b07bdb6b63da9a4782683 100644
--- a/tests/test_SubItemValuePerItem.cpp
+++ b/tests/test_SubItemValuePerItem.cpp
@@ -79,7 +79,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -215,7 +215,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -391,7 +391,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -596,7 +596,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -712,7 +712,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -898,7 +898,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1119,7 +1119,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -1164,7 +1164,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -1208,7 +1208,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1254,7 +1254,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_3d = named_mesh.mesh();
+        auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
         const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1353,7 +1353,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -1420,7 +1420,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
 
     SECTION("checking invalid array size in constructor")
     {
-      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh();
+      auto mesh_3d = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
       const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -1442,7 +1442,7 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_SubItemValuePerItemUtils.cpp b/tests/test_SubItemValuePerItemUtils.cpp
index 353d7a9500a2cba3384ae22b8863f020d1c07361..69214c1c933ba6abb0c93e094317901263e70eed 100644
--- a/tests/test_SubItemValuePerItemUtils.cpp
+++ b/tests/test_SubItemValuePerItemUtils.cpp
@@ -23,7 +23,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
     for (const auto& named_mesh : mesh_list) {
       SECTION(named_mesh.name())
       {
-        auto mesh_2d = named_mesh.mesh();
+        auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
         const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -90,7 +90,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -120,7 +120,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -150,7 +150,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -183,7 +183,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -213,7 +213,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -243,7 +243,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -278,7 +278,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name())
         {
-          auto mesh_1d = named_mesh.mesh();
+          auto mesh_1d = named_mesh.mesh()->get<Mesh<1>>();
 
           const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
@@ -307,7 +307,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -329,7 +329,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for R^2 data")
         {
-          auto mesh_2d = named_mesh.mesh();
+          auto mesh_2d = named_mesh.mesh()->get<Mesh<2>>();
 
           const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
@@ -366,7 +366,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
       for (const auto& named_mesh : mesh_list) {
         SECTION(named_mesh.name() + " for size_t data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -388,7 +388,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for N^2x3 data")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
@@ -413,7 +413,7 @@ TEST_CASE("SubItemValuePerItemUtils", "[mesh]")
 
         SECTION(named_mesh.name() + " for R^3x2 float")
         {
-          auto mesh_3d = named_mesh.mesh();
+          auto mesh_3d = named_mesh.mesh()->get<Mesh<3>>();
 
           const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
diff --git a/tests/test_SubItemValuePerItemVariant.cpp b/tests/test_SubItemValuePerItemVariant.cpp
index d36be5e42b7644f4bfd1abdcfb0c763d8e24029c..28f62ace44001ac1a95c0f81e59b9bd1bf3e1215 100644
--- a/tests/test_SubItemValuePerItemVariant.cpp
+++ b/tests/test_SubItemValuePerItemVariant.cpp
@@ -11,9 +11,9 @@
 
 TEST_CASE("SubItemValuePerItemVariant", "[mesh]")
 {
-  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+  std::shared_ptr mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
 
-  const Connectivity<3>& connectivity = *mesh->shared_connectivity();
+  const Connectivity<3>& connectivity = mesh->connectivity();
 
   using R1   = TinyVector<1>;
   using R2   = TinyVector<2>;
diff --git a/tests/test_Synchronizer.cpp b/tests/test_Synchronizer.cpp
index fcad9e414d4f5d3c88a6c50e41c1108cf996a13b..b54f9321aa3a01a9ce6114ef9d0194e01d4ee367 100644
--- a/tests/test_Synchronizer.cpp
+++ b/tests/test_Synchronizer.cpp
@@ -28,7 +28,8 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 1;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity();
+      const ConnectivityType& connectivity =
+        MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>()->connectivity();
 
       SECTION("synchonize NodeValue")
       {
@@ -136,7 +137,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 2;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>()->connectivity();
 
       SECTION("synchonize NodeValue")
       {
@@ -244,7 +245,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 3;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>()->connectivity();
 
       SECTION("synchonize NodeValue")
       {
@@ -366,7 +367,8 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 1;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity();
+      const ConnectivityType& connectivity =
+        MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>()->connectivity();
 
       SECTION("synchonize NodeArray")
       {
@@ -498,7 +500,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 2;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>()->connectivity();
 
       SECTION("synchonize NodeArray")
       {
@@ -630,7 +632,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 3;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>()->connectivity();
 
       SECTION("synchonize NodeArray")
       {
@@ -789,7 +791,8 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 1;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity();
+      const ConnectivityType& connectivity =
+        MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>()->connectivity();
 
       SECTION("synchonize NodeValuePerCell")
       {
@@ -1064,7 +1067,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 2;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>()->connectivity();
 
       SECTION("synchonize NodeValuePerCell")
       {
@@ -1517,7 +1520,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 3;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>()->connectivity();
 
       SECTION("synchonize NodeValuePerCell")
       {
@@ -2093,7 +2096,8 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 1;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity();
+      const ConnectivityType& connectivity =
+        MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>()->connectivity();
 
       SECTION("synchonize NodeArrayPerCell")
       {
@@ -2398,7 +2402,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 2;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>()->connectivity();
 
       SECTION("synchonize NodeArrayPerCell")
       {
@@ -2901,7 +2905,7 @@ TEST_CASE("Synchronizer", "[mesh]")
       constexpr size_t Dimension = 3;
       using ConnectivityType     = Connectivity<Dimension>;
 
-      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity();
+      const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>()->connectivity();
 
       SECTION("synchonize NodeArrayPerCell")
       {