From 067d0fa4624b166c6c2eeff3b2048e590cb4c952 Mon Sep 17 00:00:00 2001
From: Stephane Del Pino <stephane.delpino44@gmail.com>
Date: Fri, 1 Mar 2024 09:09:09 +0100
Subject: [PATCH] Remove "Dimension" template argument for DiscreteFunctionP0*

On the way:
- add bunch of helpers to MeshVariant
- add MeshConcept
- add is_mesh_v
- rename is_polygonal_mesh -> is_polygonal_mesh_v
---
 src/dev/ParallelChecker.hpp                   |   8 +-
 src/language/modules/SchemeModule.cpp         |  11 +-
 .../EmbeddedDiscreteFunctionMathFunctions.cpp | 101 ++--
 .../EmbeddedDiscreteFunctionOperators.cpp     |   8 +-
 src/language/utils/IntegrateCellValue.hpp     |  18 +-
 .../ItemArrayVariantFunctionInterpoler.cpp    |   4 +-
 .../ItemValueVariantFunctionInterpoler.cpp    |   4 +-
 src/mesh/DiamondDualMeshBuilder.cpp           |   4 +-
 src/mesh/Dual1DMeshBuilder.cpp                |   2 +-
 src/mesh/DualMeshManager.cpp                  |   4 +-
 src/mesh/GmshReader.cpp                       |   2 +-
 src/mesh/IConnectivity.hpp                    |   2 +
 src/mesh/MedianDualMeshBuilder.cpp            |   4 +-
 src/mesh/Mesh.hpp                             |   5 +-
 src/mesh/MeshNodeInterface.cpp                |   4 +-
 src/mesh/MeshRandomizer.cpp                   |   8 +-
 src/mesh/MeshRelaxer.cpp                      |   4 +-
 src/mesh/MeshSmoother.cpp                     |  10 +-
 src/mesh/MeshTraits.hpp                       |  28 +-
 src/mesh/MeshTransformer.cpp                  |   2 +-
 src/mesh/MeshUtils.cpp                        |   2 +-
 src/mesh/MeshVariant.cpp                      |  41 +-
 src/mesh/MeshVariant.hpp                      |  20 +-
 src/output/GnuplotWriter.cpp                  |   6 +-
 src/output/GnuplotWriter1D.cpp                |   4 +-
 src/output/VTKWriter.cpp                      |   6 +-
 src/output/WriterBase.cpp                     |  12 +-
 src/scheme/AcousticSolver.cpp                 |  57 +-
 src/scheme/DiscreteFunctionIntegrator.cpp     |  13 +-
 src/scheme/DiscreteFunctionInterpoler.cpp     |  15 +-
 src/scheme/DiscreteFunctionP0.hpp             | 386 ++++++-------
 src/scheme/DiscreteFunctionP0Vector.hpp       |  86 +--
 src/scheme/DiscreteFunctionUtils.cpp          |  45 +-
 src/scheme/DiscreteFunctionVariant.hpp        |  46 +-
 .../DiscreteFunctionVectorIntegrator.cpp      |  13 +-
 .../DiscreteFunctionVectorInterpoler.cpp      |  13 +-
 src/scheme/FluxingAdvectionSolver.cpp         |  44 +-
 src/scheme/HyperelasticSolver.cpp             |  45 +-
 src/utils/PugsTraits.hpp                      |  12 +-
 tests/test_DiscreteFunctionIntegrator.cpp     |  66 ++-
 .../test_DiscreteFunctionIntegratorByZone.cpp |  66 ++-
 tests/test_DiscreteFunctionInterpoler.cpp     |  60 +--
 .../test_DiscreteFunctionInterpolerByZone.cpp |  60 +--
 tests/test_DiscreteFunctionP0.cpp             | 510 +++++++++---------
 tests/test_DiscreteFunctionP0Vector.cpp       | 135 +++--
 tests/test_DiscreteFunctionUtils.cpp          | 203 ++++---
 .../test_DiscreteFunctionVectorIntegrator.cpp |  38 +-
 ...DiscreteFunctionVectorIntegratorByZone.cpp |  36 +-
 .../test_DiscreteFunctionVectorInterpoler.cpp |  36 +-
 ...DiscreteFunctionVectorInterpolerByZone.cpp |  36 +-
 ...mbeddedDiscreteFunctionMathFunctions1D.cpp |  20 +-
 ...mbeddedDiscreteFunctionMathFunctions2D.cpp |  20 +-
 ...mbeddedDiscreteFunctionMathFunctions3D.cpp |  20 +-
 ...st_EmbeddedDiscreteFunctionOperators1D.cpp |  18 +-
 ...st_EmbeddedDiscreteFunctionOperators2D.cpp |  18 +-
 ...st_EmbeddedDiscreteFunctionOperators3D.cpp |  18 +-
 tests/test_EmbeddedDiscreteFunctionUtils.cpp  |  20 +-
 tests/test_ParallelChecker_write.cpp          |   8 +-
 58 files changed, 1201 insertions(+), 1286 deletions(-)

diff --git a/src/dev/ParallelChecker.hpp b/src/dev/ParallelChecker.hpp
index 97d82ee4b..91b4bc1ab 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/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 3736e8599..e8d4bb390 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -633,19 +633,20 @@ SchemeModule::SchemeModule()
   this->_addBuiltinFunction("cell_volume",
                             std::function(
 
-                              [](const std::shared_ptr<const MeshVariant>& i_mesh)
+                              [](const std::shared_ptr<const MeshVariant>& mesh_v)
                                 -> std::shared_ptr<const DiscreteFunctionVariant> {
                                 return std::visit(
-                                  [](auto&& mesh) {
+                                  [&](auto&& mesh) {
                                     using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-                                    if constexpr (is_polygonal_mesh<MeshType>) {
+                                    if constexpr (is_polygonal_mesh_v<MeshType>) {
                                       return std::make_shared<DiscreteFunctionVariant>(
-                                        DiscreteFunctionP0(mesh, MeshDataManager::instance().getMeshData(*mesh).Vj()));
+                                        DiscreteFunctionP0(mesh_v,
+                                                           MeshDataManager::instance().getMeshData(*mesh).Vj()));
                                     } else {
                                       throw NormalError("unexpected mesh type");
                                     }
                                   },
-                                  i_mesh->meshPointer());
+                                  mesh_v->variant());
                               }
 
                               ));
diff --git a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
index b53e19ac6..715de1be2 100644
--- a/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
+++ b/src/language/utils/EmbeddedDiscreteFunctionMathFunctions.cpp
@@ -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<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,33 +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 mesh_v = getCommonMesh(discrete_function_list);
-    Assert(mesh_v.use_count() > 0);
-
-    return std::visit(
-      [&](auto&& mesh) {
-        using MeshType                   = typename std::decay_t<decltype(mesh)>::element_type;
-        constexpr size_t Dimension       = MeshType::Dimension;
-        using DiscreteFunctionVectorType = DiscreteFunctionP0Vector<Dimension, double>;
-
-        DiscreteFunctionVectorType vector_function(mesh, discrete_function_list.size());
-        vectorize_to(discrete_function_list, *mesh, vector_function);
-
-        return std::make_shared<DiscreteFunctionVariant>(vector_function);
-      },
-      mesh_v->meshPointer());
-
+    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 0b47153cf..d339a4a79 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/IntegrateCellValue.hpp b/src/language/utils/IntegrateCellValue.hpp
index 5c2e357aa..ecd304ca7 100644
--- a/src/language/utils/IntegrateCellValue.hpp
+++ b/src/language/utils/IntegrateCellValue.hpp
@@ -21,7 +21,7 @@ class IntegrateCellValue<OutputType(InputType)>
             const IQuadratureDescriptor& quadrature_descriptor,
             const MeshType& mesh)
   {
-    static_assert(is_polygonal_mesh<MeshType>);
+    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);
@@ -37,13 +37,13 @@ class IntegrateCellValue<OutputType(InputType)>
     return std::visit(
       [&](auto&& p_mesh) -> CellValue<OutputType> {
         using MeshType = typename std::decay_t<decltype(p_mesh)>::element_type;
-        if constexpr ((is_polygonal_mesh<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+        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->meshPointer());
+      mesh_v->variant());
   }
 
   template <typename MeshType>
@@ -53,7 +53,7 @@ class IntegrateCellValue<OutputType(InputType)>
             const MeshType& mesh,
             const Array<const CellId>& list_of_cells)
   {
-    static_assert(is_polygonal_mesh<MeshType>);
+    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});
   }
@@ -67,13 +67,13 @@ class IntegrateCellValue<OutputType(InputType)>
     return std::visit(
       [&](auto&& p_mesh) -> Array<OutputType> {
         using MeshType = typename std::decay_t<decltype(p_mesh)>::element_type;
-        if constexpr ((is_polygonal_mesh<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+        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->meshPointer());
+      mesh_v->variant());
   }
 
   template <typename MeshType>
@@ -83,7 +83,7 @@ class IntegrateCellValue<OutputType(InputType)>
             const MeshType& mesh,
             const Array<CellId>& list_of_cells)
   {
-    static_assert(is_polygonal_mesh<MeshType>);
+    static_assert(is_polygonal_mesh_v<MeshType>);
     return integrate(function_symbol_id, quadrature_descriptor, mesh, Array<const CellId>{list_of_cells});
   }
 
@@ -96,13 +96,13 @@ class IntegrateCellValue<OutputType(InputType)>
     return std::visit(
       [&](auto&& p_mesh) -> Array<OutputType> {
         using MeshType = typename std::decay_t<decltype(p_mesh)>::element_type;
-        if constexpr ((is_polygonal_mesh<MeshType>)and(MeshType::Dimension == InputType::Dimension)) {
+        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->meshPointer());
+      mesh_v->variant());
   }
 };
 
diff --git a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
index 86bbf0851..a288765cc 100644
--- a/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemArrayVariantFunctionInterpoler.cpp
@@ -148,11 +148,11 @@ ItemArrayVariantFunctionInterpoler::interpolate() const
   return std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_interpolate<MeshType>();
       } else {
         throw UnexpectedError("invalid mesh type");
       }
     },
-    m_mesh_v->meshPointer());
+    m_mesh_v->variant());
 }
diff --git a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
index bb3d5ad2b..1273a8da4 100644
--- a/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
+++ b/src/language/utils/ItemValueVariantFunctionInterpoler.cpp
@@ -134,11 +134,11 @@ ItemValueVariantFunctionInterpoler::interpolate() const
   return std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_interpolate<MeshType>();
       } else {
         throw UnexpectedError("invalid mesh type");
       }
     },
-    m_mesh_v->meshPointer());
+    m_mesh_v->variant());
 }
diff --git a/src/mesh/DiamondDualMeshBuilder.cpp b/src/mesh/DiamondDualMeshBuilder.cpp
index 6b834679a..36cb042d3 100644
--- a/src/mesh/DiamondDualMeshBuilder.cpp
+++ b/src/mesh/DiamondDualMeshBuilder.cpp
@@ -46,7 +46,7 @@ DiamondDualMeshBuilder::DiamondDualMeshBuilder(const std::shared_ptr<const MeshV
   std::visit(
     [&](auto&& p_mesh) {
       using MeshType = typename std::decay_t<decltype(p_mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         if constexpr (MeshType::Dimension > 1) {
           this->_buildDualDiamondMeshFrom(*p_mesh);
         } else {
@@ -56,5 +56,5 @@ DiamondDualMeshBuilder::DiamondDualMeshBuilder(const std::shared_ptr<const MeshV
         throw UnexpectedError("invalid mesh type");
       }
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/Dual1DMeshBuilder.cpp b/src/mesh/Dual1DMeshBuilder.cpp
index 495bbbe86..c861a8bf9 100644
--- a/src/mesh/Dual1DMeshBuilder.cpp
+++ b/src/mesh/Dual1DMeshBuilder.cpp
@@ -15,7 +15,7 @@ template <typename MeshType>
 void
 Dual1DMeshBuilder::_buildDual1DMeshFrom(const MeshType& primal_mesh)
 {
-  static_assert(is_polygonal_mesh<MeshType>);
+  static_assert(is_polygonal_mesh_v<MeshType>);
   static_assert(MeshType::Dimension == 1);
 
   using ConnectivityType = typename MeshType::Connectivity;
diff --git a/src/mesh/DualMeshManager.cpp b/src/mesh/DualMeshManager.cpp
index 99e4c7aa8..846033489 100644
--- a/src/mesh/DualMeshManager.cpp
+++ b/src/mesh/DualMeshManager.cpp
@@ -92,7 +92,7 @@ DualMeshManager::getMedianDualMesh(const std::shared_ptr<const MeshVariant>& mes
         }
       }
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
 
 std::shared_ptr<const MeshVariant>
@@ -115,5 +115,5 @@ DualMeshManager::getDiamondDualMesh(const std::shared_ptr<const MeshVariant>& me
         }
       }
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index 50ecb1af1..bb409dd28 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -1066,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 = std::visit([](auto&& mesh) { return mesh->dimension(); }, m_mesh->meshPointer());
+        mutable_mesh_dimension = std::visit([](auto&& mesh) { return mesh->dimension(); }, m_mesh->variant());
       }
 
       Array<int> dimensions = parallel::allGather(mutable_mesh_dimension);
diff --git a/src/mesh/IConnectivity.hpp b/src/mesh/IConnectivity.hpp
index 3ea34ab6e..dca704333 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/MedianDualMeshBuilder.cpp b/src/mesh/MedianDualMeshBuilder.cpp
index 0dd61cfa7..a895dd088 100644
--- a/src/mesh/MedianDualMeshBuilder.cpp
+++ b/src/mesh/MedianDualMeshBuilder.cpp
@@ -47,7 +47,7 @@ MedianDualMeshBuilder::MedianDualMeshBuilder(const std::shared_ptr<const MeshVar
   std::visit(
     [&](auto&& p_mesh) {
       using MeshType = typename std::decay_t<decltype(p_mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         if constexpr (MeshType::Dimension > 1) {
           this->_buildMedianDualMeshFrom(*p_mesh);
         } else {
@@ -57,5 +57,5 @@ MedianDualMeshBuilder::MedianDualMeshBuilder(const std::shared_ptr<const MeshVar
         throw UnexpectedError("invalid mesh type");
       }
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/Mesh.hpp b/src/mesh/Mesh.hpp
index 997984e7e..60e3656bc 100644
--- a/src/mesh/Mesh.hpp
+++ b/src/mesh/Mesh.hpp
@@ -52,9 +52,10 @@ class Mesh : public std::enable_shared_from_this<Mesh<MeshDimension>>
   }
 
   PUGS_INLINE
-  operator std::shared_ptr<MeshVariant>() const
+  std::shared_ptr<const MeshVariant>
+  meshVariant() const
   {
-    return std::make_shared<MeshVariant>(this->shared_ptr());
+    return std::make_shared<const MeshVariant>(this->shared_ptr());
   }
 
   PUGS_INLINE
diff --git a/src/mesh/MeshNodeInterface.cpp b/src/mesh/MeshNodeInterface.cpp
index 1703f36a0..54da43007 100644
--- a/src/mesh/MeshNodeInterface.cpp
+++ b/src/mesh/MeshNodeInterface.cpp
@@ -8,7 +8,7 @@
 template <typename MeshType>
 MeshNodeInterface::MeshNodeInterface(const MeshType& mesh, const RefFaceList& ref_face_list)
 {
-  static_assert(is_polygonal_mesh<MeshType>);
+  static_assert(is_polygonal_mesh_v<MeshType>);
   constexpr size_t Dimension = MeshType::Dimension;
 
   const Array<const FaceId>& face_list = ref_face_list.list();
@@ -56,7 +56,7 @@ MeshNodeInterface::MeshNodeInterface(const MeshType& mesh, const RefFaceList& re
 template <typename MeshType>
 MeshNodeInterface::MeshNodeInterface(const MeshType& mesh, const RefEdgeList& ref_edge_list)
 {
-  static_assert(is_polygonal_mesh<MeshType>);
+  static_assert(is_polygonal_mesh_v<MeshType>);
   constexpr size_t Dimension = MeshType::Dimension;
 
   const Array<const EdgeId>& edge_list = ref_edge_list.list();
diff --git a/src/mesh/MeshRandomizer.cpp b/src/mesh/MeshRandomizer.cpp
index a5b0e0351..6b7d6f4e4 100644
--- a/src/mesh/MeshRandomizer.cpp
+++ b/src/mesh/MeshRandomizer.cpp
@@ -370,14 +370,14 @@ MeshRandomizerHandler::getRandomizedMesh(
   return std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         MeshRandomizer randomizer(*mesh, bc_descriptor_list);
         return randomizer.getRandomizedMesh();
       } else {
         throw UnexpectedError("invalid mesh type");
       }
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
 
 std::shared_ptr<const MeshVariant>
@@ -389,12 +389,12 @@ MeshRandomizerHandler::getRandomizedMesh(
   return std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      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->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshRelaxer.cpp b/src/mesh/MeshRelaxer.cpp
index 16cbaa3ba..1192c2aed 100644
--- a/src/mesh/MeshRelaxer.cpp
+++ b/src/mesh/MeshRelaxer.cpp
@@ -9,7 +9,7 @@ template <typename MeshType>
 std::shared_ptr<const MeshVariant>
 MeshRelaxer::_relax(const MeshType& source_mesh, const MeshType& destination_mesh, const double& theta) const
 {
-  static_assert(is_polygonal_mesh<MeshType>);
+  static_assert(is_polygonal_mesh_v<MeshType>);
   if (source_mesh.shared_connectivity() == destination_mesh.shared_connectivity()) {
     using ConnectivityType               = typename MeshType::Connectivity;
     constexpr size_t Dimension           = MeshType::Dimension;
@@ -46,5 +46,5 @@ MeshRelaxer::relax(const std::shared_ptr<const MeshVariant>& p_source_mesh,
         throw UnexpectedError("invalid mesh dimension");
       }
     },
-    p_source_mesh->meshPointer(), p_destination_mesh->meshPointer());
+    p_source_mesh->variant(), p_destination_mesh->variant());
 }
diff --git a/src/mesh/MeshSmoother.cpp b/src/mesh/MeshSmoother.cpp
index 5eee712d8..22c8b41b6 100644
--- a/src/mesh/MeshSmoother.cpp
+++ b/src/mesh/MeshSmoother.cpp
@@ -285,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) {
@@ -419,7 +419,7 @@ MeshSmootherHandler::getSmoothedMesh(
       MeshSmoother smoother(*mesh, bc_descriptor_list);
       return smoother.getSmoothedMesh();
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
 
 std::shared_ptr<const MeshVariant>
@@ -433,7 +433,7 @@ MeshSmootherHandler::getSmoothedMesh(
       MeshSmoother smoother(*mesh, bc_descriptor_list);
       return smoother.getSmoothedMesh(function_symbol_id);
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
 
 std::shared_ptr<const MeshVariant>
@@ -447,7 +447,7 @@ MeshSmootherHandler::getSmoothedMesh(
       MeshSmoother smoother(*mesh, bc_descriptor_list);
       return smoother.getSmoothedMesh(smoothing_zone_list);
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
 
 std::shared_ptr<const MeshVariant>
@@ -465,5 +465,5 @@ MeshSmootherHandler::getSmoothedMesh(
       MeshSmoother smoother(*mesh, bc_descriptor_list);
       return smoother.getSmoothedMesh(discrete_function_variant_list);
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshTraits.hpp b/src/mesh/MeshTraits.hpp
index bf1a8a8f9..976954ed1 100644
--- a/src/mesh/MeshTraits.hpp
+++ b/src/mesh/MeshTraits.hpp
@@ -2,17 +2,37 @@
 #define MESH_TRAITS_HPP
 
 #include <cstddef>
+#include <type_traits>
 
 template <size_t Dimension>
 class Mesh;
 
-template <typename MeshType>
-constexpr inline bool is_polygonal_mesh = false;
+template <typename T>
+constexpr inline bool is_mesh_v = false;
 
 template <size_t Dimension>
-constexpr inline bool is_polygonal_mesh<Mesh<Dimension>> = true;
+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>;
+
+// Check mesh kind
+template <MeshConcept MeshType>
+constexpr inline bool is_polygonal_mesh_v = false;
 
 template <size_t Dimension>
-constexpr inline bool is_polygonal_mesh<const Mesh<Dimension>> = true;
+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 5fc2e3e94..74911b312 100644
--- a/src/mesh/MeshTransformer.cpp
+++ b/src/mesh/MeshTransformer.cpp
@@ -36,5 +36,5 @@ MeshTransformer::transform(const FunctionSymbolId& function_id, std::shared_ptr<
 
       return std::make_shared<MeshVariant>(MeshTransformation<TransformT>::transform(function_id, *mesh));
     },
-    mesh_v->meshPointer());
+    mesh_v->variant());
 }
diff --git a/src/mesh/MeshUtils.cpp b/src/mesh/MeshUtils.cpp
index 3b8be38fa..73fabd86f 100644
--- a/src/mesh/MeshUtils.cpp
+++ b/src/mesh/MeshUtils.cpp
@@ -8,5 +8,5 @@
 bool
 checkConnectivityOrdering(const std::shared_ptr<const MeshVariant>& mesh_v)
 {
-  return std::visit([](auto&& mesh) { return checkConnectivityOrdering(mesh->connectivity()); }, mesh_v->meshPointer());
+  return std::visit([](auto&& mesh) { return checkConnectivityOrdering(mesh->connectivity()); }, mesh_v->variant());
 }
diff --git a/src/mesh/MeshVariant.cpp b/src/mesh/MeshVariant.cpp
index 7b054297a..2232e4f73 100644
--- a/src/mesh/MeshVariant.cpp
+++ b/src/mesh/MeshVariant.cpp
@@ -6,12 +6,49 @@
 size_t
 MeshVariant::id() const
 {
-  return std::visit([](auto&& mesh) { return mesh->id(); }, m_p_mesh);
+  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.meshPointer());
+  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
index 53e432795..bf3851a00 100644
--- a/src/mesh/MeshVariant.hpp
+++ b/src/mesh/MeshVariant.hpp
@@ -7,6 +7,8 @@
 #include <memory>
 #include <variant>
 
+class IConnectivity;
+
 template <size_t Dimension>
 class Mesh;
 
@@ -17,31 +19,39 @@ class MeshVariant
                                std::shared_ptr<const Mesh<2>>,   //
                                std::shared_ptr<const Mesh<3>>>;
 
-  Variant m_p_mesh;
+  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 <typename MeshType>
   PUGS_INLINE std::shared_ptr<const MeshType>
   get() const
   {
-    return std::get<std::shared_ptr<const MeshType>>(m_p_mesh);
+    return std::get<std::shared_ptr<const MeshType>>(m_p_mesh_variant);
   }
 
   PUGS_INLINE
   Variant
-  meshPointer() const
+  variant() const
   {
-    return m_p_mesh;
+    return m_p_mesh_variant;
   }
 
   MeshVariant() = delete;
 
   template <typename MeshType>
-  MeshVariant(const std::shared_ptr<const MeshType>& p_mesh) : m_p_mesh{p_mesh}
+  MeshVariant(const std::shared_ptr<const MeshType>& p_mesh) : m_p_mesh_variant{p_mesh}
   {}
 
   MeshVariant(const MeshVariant&) = default;
diff --git a/src/output/GnuplotWriter.cpp b/src/output/GnuplotWriter.cpp
index 8d93fb117..f846dd2eb 100644
--- a/src/output/GnuplotWriter.cpp
+++ b/src/output/GnuplotWriter.cpp
@@ -327,7 +327,7 @@ GnuplotWriter::_writeAtTime(const MeshVariant& mesh_v,
         throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
       }
     },
-    mesh_v.meshPointer());
+    mesh_v.variant());
 }
 
 void
@@ -344,7 +344,7 @@ GnuplotWriter::_writeMesh(const MeshVariant& mesh_v) const
         throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
       }
     },
-    mesh_v.meshPointer());
+    mesh_v.variant());
 }
 
 void
@@ -362,5 +362,5 @@ GnuplotWriter::_write(const MeshVariant& mesh_v,
         throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
       }
     },
-    mesh_v.meshPointer());
+    mesh_v.variant());
 }
diff --git a/src/output/GnuplotWriter1D.cpp b/src/output/GnuplotWriter1D.cpp
index c839f9000..745903222 100644
--- a/src/output/GnuplotWriter1D.cpp
+++ b/src/output/GnuplotWriter1D.cpp
@@ -380,7 +380,7 @@ GnuplotWriter1D::_writeAtTime(const MeshVariant& mesh_v,
         throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
       }
     },
-    mesh_v.meshPointer());
+    mesh_v.variant());
 }
 
 void
@@ -404,5 +404,5 @@ GnuplotWriter1D::_write(const MeshVariant& mesh_v,
         throw NormalError("gnuplot format is not available in dimension " + stringify(MeshType::Dimension));
       }
     },
-    mesh_v.meshPointer());
+    mesh_v.variant());
 }
diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp
index be606c829..889b6b554 100644
--- a/src/output/VTKWriter.cpp
+++ b/src/output/VTKWriter.cpp
@@ -687,7 +687,7 @@ VTKWriter::_writeMesh(const MeshVariant& mesh_v) const
 {
   OutputNamedItemDataSet output_named_item_data_set;
 
-  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.meshPointer());
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.variant());
 }
 
 void
@@ -697,7 +697,7 @@ VTKWriter::_writeAtTime(const MeshVariant& mesh_v,
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, time); }, mesh_v.meshPointer());
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, time); }, mesh_v.variant());
 }
 
 void
@@ -706,5 +706,5 @@ VTKWriter::_write(const MeshVariant& mesh_v,
 {
   OutputNamedItemDataSet output_named_item_data_set = this->_getOutputNamedItemDataSet(named_discrete_data_list);
 
-  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.meshPointer());
+  std::visit([&](auto&& p_mesh) { this->_write(*p_mesh, output_named_item_data_set, {}); }, mesh_v.variant());
 }
diff --git a/src/output/WriterBase.cpp b/src/output/WriterBase.cpp
index 11d37def8..bc7f90be2 100644
--- a/src/output/WriterBase.cpp
+++ b/src/output/WriterBase.cpp
@@ -37,7 +37,7 @@ WriterBase::_checkConnectivity(
 
   std::shared_ptr<const IConnectivity> connectivity =
     std::visit([&](auto&& p_mesh) -> std::shared_ptr<const IConnectivity> { return p_mesh->shared_connectivity(); },
-               mesh_v->meshPointer());
+               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];
@@ -135,7 +135,7 @@ WriterBase::_checkMesh(const std::shared_ptr<const MeshVariant>& mesh_v,
 {
   Assert(named_discrete_data_list.size() > 0);
 
-  const size_t mesh_id = std::visit([](auto&& p_mesh) { return p_mesh->id(); }, mesh_v->meshPointer());
+  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];
@@ -145,7 +145,7 @@ WriterBase::_checkMesh(const std::shared_ptr<const MeshVariant>& mesh_v,
         dynamic_cast<const NamedDiscreteFunction&>(*named_discrete_data);
 
       const size_t discrete_function_mesh_id =
-        std::visit([](auto&& f) { return f.mesh()->id(); },
+        std::visit([](auto&& f) { return f.meshVariant()->id(); },
                    named_discrete_function.discreteFunctionVariant()->discreteFunction());
 
       if (mesh_id != discrete_function_mesh_id) {
@@ -178,11 +178,11 @@ WriterBase::_getMesh(const std::vector<std::shared_ptr<const INamedDiscreteData>
 
       std::visit(
         [&](auto&& f) {
-          mesh_id_to_function_name_map[f.mesh()->id()] = named_discrete_function.name();
+          mesh_id_to_function_name_map[f.meshVariant()->id()] = named_discrete_function.name();
           if (not mesh_v) {
-            mesh_v = std::make_shared<MeshVariant>(f.mesh());
+            mesh_v = f.meshVariant();
           }
-          connectivity_set[f.mesh()->shared_connectivity()] = named_discrete_function.name();
+          connectivity_set[f.meshVariant()->shared_connectivity()] = named_discrete_function.name();
         },
         named_discrete_function.discreteFunctionVariant()->discreteFunction());
 
diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp
index 3cd71fe5d..131854ec8 100644
--- a/src/scheme/AcousticSolver.cpp
+++ b/src/scheme/AcousticSolver.cpp
@@ -22,43 +22,30 @@
 #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<Dimension>& mesh = dynamic_cast<const Mesh<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);
-}
-
-double
-acoustic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c)
-{
-  std::shared_ptr mesh_v = getCommonMesh({c});
+        CellValue<double> local_dt{mesh.connectivity()};
+        parallel_for(
+          mesh.numberOfCells(), PUGS_LAMBDA(CellId j) { local_dt[j] = 2 * Vj[j] / (Sj[j] * c[j]); });
 
-  const size_t mesh_dimension = std::visit([](auto&& mesh) { return mesh->dimension(); }, mesh_v->meshPointer());
-
-  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 <typename MeshType>
@@ -72,8 +59,8 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
 
   using MeshDataType = MeshData<Dimension>;
 
-  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;
@@ -885,11 +872,11 @@ AcousticSolverHandler::AcousticSolverHandler(const std::shared_ptr<const MeshVar
   std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         m_acoustic_solver = std::make_unique<AcousticSolver<MeshType>>();
       } else {
         throw NormalError("unexpected mesh type");
       }
     },
-    i_mesh->meshPointer());
+    i_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionIntegrator.cpp b/src/scheme/DiscreteFunctionIntegrator.cpp
index 5fd91f84a..8b2913fe2 100644
--- a/src/scheme/DiscreteFunctionIntegrator.cpp
+++ b/src/scheme/DiscreteFunctionIntegrator.cpp
@@ -64,7 +64,7 @@ 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 <typename MeshType, typename DataType, typename ValueType>
@@ -77,10 +77,9 @@ DiscreteFunctionIntegrator::_integrateGlobally() const
 
   static_assert(std::is_convertible_v<DataType, ValueType>);
 
-  return DiscreteFunctionP0<
-    MeshType::Dimension, ValueType>(mesh,
-                                    IntegrateCellValue<ValueType(TinyVector<MeshType::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 <typename MeshType, typename DataType, typename ValueType>
@@ -177,9 +176,9 @@ DiscreteFunctionIntegrator::integrate() const
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
 
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_integrate<MeshType>();
       }
     },
-    m_mesh->meshPointer());
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp
index 00e6ebabe..3894eaed8 100644
--- a/src/scheme/DiscreteFunctionInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionInterpoler.cpp
@@ -3,6 +3,7 @@
 #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>
@@ -65,7 +66,7 @@ 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 <typename MeshType, typename DataType, typename ValueType>
@@ -80,9 +81,9 @@ DiscreteFunctionInterpoler::_interpolateGlobally() const
   MeshDataType& 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>);
 
@@ -95,7 +96,7 @@ 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);
   }
 }
 
@@ -193,9 +194,9 @@ DiscreteFunctionInterpoler::interpolate() const
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
 
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_interpolate<MeshType>();
       }
     },
-    m_mesh->meshPointer());
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionP0.hpp b/src/scheme/DiscreteFunctionP0.hpp
index 9f7fc39c5..56642754b 100644
--- a/src/scheme/DiscreteFunctionP0.hpp
+++ b/src/scheme/DiscreteFunctionP0.hpp
@@ -4,24 +4,22 @@
 #include <language/utils/ASTNodeDataTypeTraits.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<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 MeshType>
-  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<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 <typename 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 <typename 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 ada6df3ad..aaa549173 100644
--- a/src/scheme/DiscreteFunctionP0Vector.hpp
+++ b/src/scheme/DiscreteFunctionP0Vector.hpp
@@ -10,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<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;
@@ -50,9 +49,8 @@ class DiscreteFunctionP0Vector
     return m_cell_arrays;
   }
 
-  PUGS_INLINE
-  std::shared_ptr<const MeshType>
-  mesh() const
+  PUGS_INLINE std::shared_ptr<const MeshVariant>
+  meshVariant() const
   {
     return m_mesh;
   }
@@ -65,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
@@ -81,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);
   }
 
@@ -108,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(
@@ -126,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) {
@@ -141,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) {
@@ -157,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) {
@@ -176,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) {
@@ -190,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) {
@@ -210,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];
@@ -232,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 <typename 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 <typename 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 df39bbcea..9824b138c 100644
--- a/src/scheme/DiscreteFunctionUtils.cpp
+++ b/src/scheme/DiscreteFunctionUtils.cpp
@@ -17,10 +17,10 @@ getCommonMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
     std::visit(
       [&](auto&& discrete_function) {
         if (not mesh_id.has_value()) {
-          mesh_v  = std::make_shared<MeshVariant>(discrete_function.mesh());
-          mesh_id = discrete_function.mesh()->id();
+          mesh_v  = discrete_function.meshVariant();
+          mesh_id = discrete_function.meshVariant()->id();
         } else {
-          if (mesh_id != discrete_function.mesh()->id()) {
+          if (mesh_id != discrete_function.meshVariant()->id()) {
             is_same_mesh = false;
             mesh_v.reset();
           }
@@ -42,9 +42,9 @@ hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& d
     std::visit(
       [&](auto&& discrete_function) {
         if (not mesh_id.has_value()) {
-          mesh_id = discrete_function.mesh()->id();
+          mesh_id = discrete_function.meshVariant()->id();
         } else {
-          if (mesh_id != discrete_function.mesh()->id()) {
+          if (mesh_id != discrete_function.meshVariant()->id()) {
             same_mesh = false;
           }
         }
@@ -65,7 +65,7 @@ hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& d
   for (const auto& discrete_function_variant : discrete_function_variant_list) {
     std::visit(
       [&](auto&& discrete_function) {
-        if (mesh_id != discrete_function.mesh()->id()) {
+        if (mesh_id != discrete_function.meshVariant()->id()) {
           same_mesh = false;
         }
       },
@@ -75,26 +75,22 @@ hasSameMesh(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& d
   return same_mesh;
 }
 
-template <typename MeshType, typename DiscreteFunctionT>
+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 size_t function_connectivity_id = f.mesh()->shared_connectivity()->id();
+  const size_t function_connectivity_id = f.meshVariant()->shared_connectivity()->id();
 
-  if (mesh->shared_connectivity()->id() != function_connectivity_id) {
+  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");
   }
 }
 
@@ -104,15 +100,16 @@ shallowCopy(const std::shared_ptr<const MeshVariant>& mesh_v,
 {
   return std::visit(
     [&](auto&& f) {
-      const size_t mesh_id        = std::visit([](auto&& mesh) { return mesh->id(); }, mesh_v->meshPointer());
-      const size_t mesh_dimension = std::visit([](auto&& mesh) { return mesh->dimension(); }, mesh_v->meshPointer());
-      if (mesh_id == f.mesh()->id()) {
+      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");
       }
 
-      return std::visit([&](auto&& mesh) { return shallowCopy(mesh, f); }, mesh_v->meshPointer());
+      return shallowCopy(mesh_v, f);
     },
     discrete_function_variant->discreteFunction());
 }
diff --git a/src/scheme/DiscreteFunctionVariant.hpp b/src/scheme/DiscreteFunctionVariant.hpp
index af1b1cc37..901b79890 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 f4722ab42..25b460a2e 100644
--- a/src/scheme/DiscreteFunctionVectorIntegrator.cpp
+++ b/src/scheme/DiscreteFunctionVectorIntegrator.cpp
@@ -65,7 +65,7 @@ DiscreteFunctionVectorIntegrator::_integrateOnZoneList() const
       }
     });
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(p_mesh, cell_array);
+  return DiscreteFunctionP0Vector<DataType>(m_mesh, cell_array);
 }
 
 template <typename MeshType, typename DataType>
@@ -77,9 +77,10 @@ DiscreteFunctionVectorIntegrator::_integrateGlobally() const
   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 <typename MeshType, typename DataType>
@@ -131,9 +132,9 @@ DiscreteFunctionVectorIntegrator::integrate() const
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
 
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_integrate<MeshType>();
       }
     },
-    m_mesh->meshPointer());
+    m_mesh->variant());
 }
diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
index b9bd7b7d0..b30fe107b 100644
--- a/src/scheme/DiscreteFunctionVectorInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
@@ -63,7 +63,7 @@ DiscreteFunctionVectorInterpoler::_interpolateOnZoneList() const
       }
     });
 
-  return DiscreteFunctionP0Vector<Dimension, DataType>(p_mesh, cell_array);
+  return DiscreteFunctionP0Vector<DataType>(p_mesh, cell_array);
 }
 
 template <typename MeshType, typename DataType>
@@ -78,10 +78,9 @@ DiscreteFunctionVectorInterpoler::_interpolateGlobally() const
   using MeshDataType      = MeshData<Dimension>;
   MeshDataType& 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 <typename MeshType, typename DataType>
@@ -154,9 +153,9 @@ DiscreteFunctionVectorInterpoler::interpolate() const
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
 
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         return this->_interpolate<MeshType>();
       }
     },
-    m_mesh->meshPointer());
+    m_mesh->variant());
 }
diff --git a/src/scheme/FluxingAdvectionSolver.cpp b/src/scheme/FluxingAdvectionSolver.cpp
index 0ee4e25f5..e7ec1994b 100644
--- a/src/scheme/FluxingAdvectionSolver.cpp
+++ b/src/scheme/FluxingAdvectionSolver.cpp
@@ -80,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()));
   }
@@ -197,7 +197,7 @@ class FluxingAdvectionSolver
       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");
     }
 
@@ -709,18 +709,14 @@ FluxingAdvectionSolver<MeshType>::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());
@@ -736,18 +732,14 @@ FluxingAdvectionSolver<MeshType>::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());
@@ -886,5 +878,5 @@ advectByFluxing(const std::shared_ptr<const MeshVariant> new_mesh_v,
         throw NormalError("incompatible mesh types");
       }
     },
-    old_mesh_v->meshPointer(), new_mesh_v->meshPointer());
+    old_mesh_v->variant(), new_mesh_v->variant());
 }
diff --git a/src/scheme/HyperelasticSolver.cpp b/src/scheme/HyperelasticSolver.cpp
index edd4e4950..c0d011fed 100644
--- a/src/scheme/HyperelasticSolver.cpp
+++ b/src/scheme/HyperelasticSolver.cpp
@@ -22,37 +22,30 @@
 #include <variant>
 #include <vector>
 
-template <typename MeshType>
 double
-hyperelastic_dt(const DiscreteFunctionP0<MeshType::Dimension, const double>& c)
+hyperelastic_dt(const std::shared_ptr<const DiscreteFunctionVariant>& c_v)
 {
-  const MeshType& mesh = *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_v = getCommonMesh({c});
-
-  return std::visit(
-    [&](auto&& mesh) {
-      using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
-        return hyperelastic_dt<MeshType>(c->get<DiscreteFunctionP0<MeshType::Dimension, const double>>());
+        return min(local_dt);
       } else {
         throw NormalError("unexpected mesh type");
       }
     },
-    mesh_v->meshPointer());
+    c.meshVariant()->variant());
 }
 
 template <typename MeshType>
@@ -66,9 +59,9 @@ class HyperelasticSolverHandler::HyperelasticSolver final : public HyperelasticS
 
   using MeshDataType = MeshData<Dimension>;
 
-  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;
@@ -955,11 +948,11 @@ HyperelasticSolverHandler::HyperelasticSolverHandler(const std::shared_ptr<const
   std::visit(
     [&](auto&& mesh) {
       using MeshType = typename std::decay_t<decltype(mesh)>::element_type;
-      if constexpr (is_polygonal_mesh<MeshType>) {
+      if constexpr (is_polygonal_mesh_v<MeshType>) {
         m_hyperelastic_solver = std::make_unique<HyperelasticSolver<MeshType>>();
       } else {
         throw NormalError("unexpected mesh type");
       }
     },
-    i_mesh->meshPointer());
+    i_mesh->variant());
 }
diff --git a/src/utils/PugsTraits.hpp b/src/utils/PugsTraits.hpp
index 285d7978d..2e1d40957 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/test_DiscreteFunctionIntegrator.cpp b/tests/test_DiscreteFunctionIntegrator.cpp
index 7a42c1f0c..1ab77c2e3 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();
@@ -104,7 +102,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -121,7 +119,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -138,7 +136,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -155,7 +153,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -175,7 +173,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -195,7 +193,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -215,7 +213,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -235,7 +233,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -255,7 +253,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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")
@@ -275,7 +273,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
           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();
@@ -340,7 +336,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -357,7 +353,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -374,7 +370,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -391,7 +387,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -411,7 +407,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -431,7 +427,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -451,7 +447,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -471,7 +467,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -491,7 +487,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -511,7 +507,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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();
@@ -576,7 +570,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -593,7 +587,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -610,7 +604,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -627,7 +621,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -647,7 +641,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -667,7 +661,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -687,7 +681,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -707,7 +701,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -727,7 +721,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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")
@@ -747,7 +741,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
           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 2e0d3dad2..608a523b4 100644
--- a/tests/test_DiscreteFunctionIntegratorByZone.cpp
+++ b/tests/test_DiscreteFunctionIntegratorByZone.cpp
@@ -49,8 +49,6 @@ 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_v = MeshDataBaseForTests::get().unordered1DMesh();
@@ -118,7 +116,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -145,7 +143,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -172,7 +170,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -199,7 +197,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -228,7 +226,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -257,7 +255,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -286,7 +284,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -315,7 +313,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -344,7 +342,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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")
@@ -373,14 +371,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
       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_v = MeshDataBaseForTests::get().hybrid2DMesh();
@@ -448,7 +444,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -475,7 +471,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -502,7 +498,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -529,7 +525,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -558,7 +554,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -587,7 +583,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -616,7 +612,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -645,7 +641,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -674,7 +670,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -703,14 +699,12 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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_v = MeshDataBaseForTests::get().hybrid3DMesh();
@@ -778,7 +772,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -805,7 +799,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -832,7 +826,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -859,7 +853,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -888,7 +882,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -917,7 +911,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -946,7 +940,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -975,7 +969,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -1004,7 +998,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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")
@@ -1033,7 +1027,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
       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 b4c024d3f..bb27d85aa 100644
--- a/tests/test_DiscreteFunctionInterpoler.cpp
+++ b/tests/test_DiscreteFunctionInterpoler.cpp
@@ -108,7 +108,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -130,7 +130,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -152,7 +152,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -174,7 +174,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -198,7 +198,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -222,7 +222,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -246,7 +246,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -270,7 +270,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -295,7 +295,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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")
@@ -327,7 +327,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                                 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>>()));
         }
       }
     }
@@ -398,7 +398,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -420,7 +420,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -442,7 +442,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -464,7 +464,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -488,7 +488,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -512,7 +512,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -536,7 +536,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -560,7 +560,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -585,7 +585,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -618,7 +618,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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>>()));
         }
       }
     }
@@ -689,7 +689,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -711,7 +711,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -733,7 +733,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -755,7 +755,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -779,7 +779,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -803,7 +803,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -827,7 +827,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -851,7 +851,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -876,7 +876,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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")
@@ -909,7 +909,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                                 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 e62ab89fe..9a228fa7c 100644
--- a/tests/test_DiscreteFunctionInterpolerByZone.cpp
+++ b/tests/test_DiscreteFunctionInterpolerByZone.cpp
@@ -120,7 +120,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -146,7 +146,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -172,7 +172,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -198,7 +198,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -226,7 +226,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -254,7 +254,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -282,7 +282,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -310,7 +310,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -339,7 +339,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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")
@@ -375,7 +375,7 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[0]) + 3, sin(
                                             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>>()));
     }
   }
 
@@ -454,7 +454,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -480,7 +480,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -506,7 +506,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -532,7 +532,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -560,7 +560,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -588,7 +588,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -616,7 +616,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -644,7 +644,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -673,7 +673,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -710,7 +710,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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>>()));
     }
   }
 
@@ -789,7 +789,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -815,7 +815,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -841,7 +841,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -867,7 +867,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -895,7 +895,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -923,7 +923,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -951,7 +951,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -979,7 +979,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -1008,7 +1008,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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")
@@ -1045,7 +1045,7 @@ let R3x3_non_linear_3d: R^3 -> R^3x3, x -> [[2 * exp(x[0]) * sin(x[1]) + 3, sin(
                                             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 e43e1958d..5b1133b4e 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()->get<Mesh<1>>();
-          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()->get<Mesh<2>>();
-          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()->get<Mesh<3>>();
+          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,8 +189,6 @@ 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) {
@@ -193,17 +196,17 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
         {
           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()->get<Mesh<2>>();
+          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,8 +243,6 @@ 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) {
@@ -251,17 +250,17 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
         {
           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()->get<Mesh<1>>();
+          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,8 +383,6 @@ 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) {
@@ -400,7 +395,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
             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()->get<Mesh<3>>();
+          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,8 +586,7 @@ 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())
@@ -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,8 +669,6 @@ 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) {
@@ -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()->get<Mesh<2>>();
-
-          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,8 +1832,6 @@ 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) {
@@ -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,8 +2480,7 @@ 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())
@@ -2504,7 +2489,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           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,8 +2822,6 @@ 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) {
@@ -2848,7 +2831,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           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,8 +3164,6 @@ 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) {
@@ -3192,7 +3173,7 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
           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];
@@ -3628,10 +3609,11 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             auto mesh_1 = named_mesh.mesh()->get<Mesh<1>>();
 
-            std::shared_ptr mesh_2 = std::make_shared<Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
+            std::shared_ptr mesh_2 =
+              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);
@@ -3654,10 +3636,11 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             auto mesh_1 = named_mesh.mesh()->get<Mesh<2>>();
 
-            std::shared_ptr mesh_2 = std::make_shared<Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
+            std::shared_ptr mesh_2 =
+              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,10 +3663,11 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
           {
             auto mesh_1 = named_mesh.mesh()->get<Mesh<3>>();
 
-            std::shared_ptr mesh_2 = std::make_shared<Mesh<Dimension>>(mesh_1->shared_connectivity(), mesh_1->xr());
+            std::shared_ptr mesh_2 =
+              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 4521e8fa8..78a3f6d5d 100644
--- a/tests/test_DiscreteFunctionP0Vector.cpp
+++ b/tests/test_DiscreteFunctionP0Vector.cpp
@@ -35,12 +35,12 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
         SECTION(named_mesh.name())
         {
           auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          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);
@@ -83,12 +83,12 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
         SECTION(named_mesh.name())
         {
           auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          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);
@@ -131,12 +131,12 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
         SECTION(named_mesh.name())
         {
           auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          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);
@@ -195,7 +195,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           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));
@@ -215,7 +215,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
         SECTION(named_mesh.name())
         {
           auto mesh = named_mesh.mesh()->get<Mesh<Dimension>>();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          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()->get<Mesh<Dimension>>();
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          DiscreteFunctionP0Vector<double> f{named_mesh.mesh(), size};
           f.fill(3.2);
 
           REQUIRE(all_values_equal(f, 3.2));
@@ -275,7 +272,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           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));
@@ -321,7 +318,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           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));
@@ -368,7 +365,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
           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));
@@ -419,7 +416,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           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(
@@ -462,7 +459,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           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()->get<Mesh<Dimension>>();
-
-          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];
                 }
@@ -552,7 +549,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           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();
@@ -635,7 +632,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           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();
@@ -718,7 +715,7 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
           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();
@@ -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));
               }
@@ -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));
               }
@@ -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 f7adede0e..daeaf70f0 100644
--- a/tests/test_DiscreteFunctionUtils.cpp
+++ b/tests/test_DiscreteFunctionUtils.cpp
@@ -30,11 +30,11 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -47,12 +47,12 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -72,9 +72,9 @@ 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_v, 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);
 
@@ -88,9 +88,9 @@ 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_v, 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);
 
@@ -104,9 +104,9 @@ 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_v, 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);
 
@@ -120,9 +120,9 @@ 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_v, 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);
 
@@ -136,9 +136,9 @@ 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_v, 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);
 
@@ -152,9 +152,9 @@ 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_v, 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);
 
@@ -168,9 +168,9 @@ 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_v, 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);
 
@@ -183,9 +183,8 @@ 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));
+          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);
@@ -217,11 +216,11 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -234,12 +233,12 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -259,9 +258,9 @@ 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_v, 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);
 
@@ -275,9 +274,9 @@ 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_v, 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);
 
@@ -291,9 +290,9 @@ 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_v, 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);
 
@@ -307,9 +306,9 @@ 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_v, 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);
 
@@ -323,9 +322,9 @@ 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_v, 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);
 
@@ -339,9 +338,9 @@ 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_v, 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);
 
@@ -355,9 +354,9 @@ 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_v, 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);
 
@@ -370,9 +369,8 @@ 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));
+          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);
@@ -389,8 +387,6 @@ 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) {
@@ -405,11 +401,11 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -422,12 +418,12 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
         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);
@@ -447,9 +443,9 @@ 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_v, 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);
 
@@ -463,9 +459,9 @@ 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_v, 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);
 
@@ -479,9 +475,9 @@ 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_v, 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);
 
@@ -495,9 +491,9 @@ 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_v, 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);
 
@@ -511,9 +507,9 @@ 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_v, 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);
 
@@ -527,9 +523,9 @@ 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_v, 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);
 
@@ -543,9 +539,9 @@ 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_v, 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);
 
@@ -558,9 +554,8 @@ 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));
+          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);
@@ -592,7 +587,7 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
             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_v, uh), "error: cannot shallow copy when connectivity changes");
         }
@@ -601,13 +596,11 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
 
     SECTION("incompatible mesh dimension")
     {
-      constexpr size_t Dimension = 1;
-
       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_v, uh), "error: incompatible mesh dimensions");
     }
diff --git a/tests/test_DiscreteFunctionVectorIntegrator.cpp b/tests/test_DiscreteFunctionVectorIntegrator.cpp
index 58ad9cdf2..c176efb65 100644
--- a/tests/test_DiscreteFunctionVectorIntegrator.cpp
+++ b/tests/test_DiscreteFunctionVectorIntegrator.cpp
@@ -113,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());
@@ -203,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());
@@ -238,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();
@@ -293,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());
diff --git a/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp b/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
index 2522f3651..180bc3b2a 100644
--- a/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
+++ b/tests/test_DiscreteFunctionVectorIntegratorByZone.cpp
@@ -126,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>>()));
     }
 
     {
@@ -144,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>>()));
     }
 
     {
@@ -162,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>>()));
     }
 
     {
@@ -180,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());
@@ -255,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>>()));
     }
 
     {
@@ -273,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>>()));
     }
 
     {
@@ -291,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>>()));
     }
 
     {
@@ -309,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());
@@ -384,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>>()));
     }
 
     {
@@ -402,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>>()));
     }
 
     {
@@ -420,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>>()));
     }
 
     {
@@ -438,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 b86f833a2..466e88bb5 100644
--- a/tests/test_DiscreteFunctionVectorInterpoler.cpp
+++ b/tests/test_DiscreteFunctionVectorInterpoler.cpp
@@ -113,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>>()));
         }
 
         {
@@ -125,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>>()));
         }
 
         {
@@ -137,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>>()));
         }
 
         {
@@ -149,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());
@@ -218,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>>()));
         }
 
         {
@@ -230,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>>()));
         }
 
         {
@@ -242,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>>()));
         }
 
         {
@@ -254,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());
@@ -323,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>>()));
         }
 
         {
@@ -335,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>>()));
         }
 
         {
@@ -347,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>>()));
         }
 
         {
@@ -359,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());
diff --git a/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp b/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
index c17a9b397..e5e99b854 100644
--- a/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
+++ b/tests/test_DiscreteFunctionVectorInterpolerByZone.cpp
@@ -126,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>>()));
     }
 
     {
@@ -142,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>>()));
     }
 
     {
@@ -158,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>>()));
     }
 
     {
@@ -174,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());
@@ -252,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>>()));
     }
 
     {
@@ -268,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>>()));
     }
 
     {
@@ -284,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>>()));
     }
 
     {
@@ -300,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());
@@ -378,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>>()));
     }
 
     {
@@ -394,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>>()));
     }
 
     {
@@ -410,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>>()));
     }
 
     {
@@ -426,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());
diff --git a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
index 7ebb75f85..5843a8ae7 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions1D.cpp
@@ -21,22 +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()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh = std::make_shared<Mesh<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();
 
@@ -582,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 989b66d39..11582b982 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions2D.cpp
@@ -23,22 +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()->get<Mesh<Dimension>>();
 
-        std::shared_ptr other_mesh = std::make_shared<Mesh<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();
 
@@ -584,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 25afe5c0a..edca0a4eb 100644
--- a/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionMathFunctions3D.cpp
@@ -21,22 +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()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh = std::make_shared<Mesh<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();
 
@@ -582,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 e790e4608..2703b7ace 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators1D.cpp
@@ -12,22 +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()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh = std::make_shared<Mesh<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 0efe0cfbe..54263a0a0 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators2D.cpp
@@ -12,22 +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()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh = std::make_shared<Mesh<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 a024c2dde..2d2cf930d 100644
--- a/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionOperators3D.cpp
@@ -12,22 +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()->get<Mesh<Dimension>>();
 
-      std::shared_ptr other_mesh = std::make_shared<Mesh<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 464313cb5..20ae4d80f 100644
--- a/tests/test_EmbeddedDiscreteFunctionUtils.cpp
+++ b/tests/test_EmbeddedDiscreteFunctionUtils.cpp
@@ -36,21 +36,17 @@ TEST_CASE("EmbeddedDiscreteFunctionUtils", "[language]")
         {
           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)");
         }
       }
@@ -65,7 +61,7 @@ TEST_CASE("EmbeddedDiscreteFunctionUtils", "[language]")
         {
           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_ParallelChecker_write.cpp b/tests/test_ParallelChecker_write.cpp
index e34165999..8ca7ceb50 100644
--- a/tests/test_ParallelChecker_write.cpp
+++ b/tests/test_ParallelChecker_write.cpp
@@ -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;
@@ -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;
@@ -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;
@@ -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;
-- 
GitLab