diff --git a/tests/test_DiscreteFunctionInterpoler.cpp b/tests/test_DiscreteFunctionInterpoler.cpp
index c697af1eeaa1c4a39ae3104d226ac173caceb475..d0f9f274f3ff95580a810d62718631198dd2ad86 100644
--- a/tests/test_DiscreteFunctionInterpoler.cpp
+++ b/tests/test_DiscreteFunctionInterpoler.cpp
@@ -43,10 +43,14 @@ TEST_CASE("DiscreteFunctionInterpoler", "[scheme]")
   {
     constexpr size_t Dimension = 1;
 
-    const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-    auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-    std::string_view data = R"(
+    for (auto [section_name, mesh_1d] : mesh_list) {
+      SECTION(section_name)
+      {
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+        std::string_view data = R"(
 import math;
 let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4);
 let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
@@ -59,274 +63,276 @@ let R1x1_non_linear_1d: R^1 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[0]) + 3);
 let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
 let R3x3_non_linear_1d: R^1 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0], -4*x[0], 2*x[0]+1, 3, -6*x[0], exp(x[0]));
 )";
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-    auto ast = ASTBuilder::build(input);
+        auto ast = ASTBuilder::build(input);
 
-    ASTModulesImporter{*ast};
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-    ASTSymbolTableBuilder{*ast};
-    ASTNodeDataTypeBuilder{*ast};
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
 
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-    ASTNodeExpressionBuilder{*ast};
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
 
-    std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-    TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-    position.byte = data.size();   // ensure that variables are declared at this point
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
 
-    SECTION("B_scalar_non_linear_1d")
-    {
-      auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+        SECTION("B_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
-        });
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
 
-    SECTION("N_scalar_non_linear_1d")
-    {
-      auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+        SECTION("N_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
-        });
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
 
-    SECTION("Z_scalar_non_linear_1d")
-    {
-      auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+        SECTION("Z_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
-        });
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
 
-    SECTION("R_scalar_non_linear_1d")
-    {
-      auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+        SECTION("R_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
-        });
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
 
-    SECTION("R1_non_linear_1d")
-    {
-      using DataType = TinyVector<1>;
+        SECTION("R1_non_linear_1d")
+        {
+          using DataType = TinyVector<1>;
 
-      auto [i_symbol, found] = symbol_table->find("R1_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto [i_symbol, found] = symbol_table->find("R1_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
-        });
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
 
-    SECTION("R2_non_linear_1d")
-    {
-      using DataType = TinyVector<2>;
+        SECTION("R2_non_linear_1d")
+        {
+          using DataType = TinyVector<2>;
 
-      auto [i_symbol, found] = symbol_table->find("R2_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[0]};
-        });
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[0]};
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
 
-    SECTION("R3_non_linear_1d")
-    {
-      using DataType = TinyVector<3>;
+        SECTION("R3_non_linear_1d")
+        {
+          using DataType = TinyVector<3>;
 
-      auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
-        });
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
 
-    SECTION("R1x1_non_linear_1d")
-    {
-      using DataType = TinyMatrix<1>;
+        SECTION("R1x1_non_linear_1d")
+        {
+          using DataType = TinyMatrix<1>;
 
-      auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
-        });
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
+            });
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
-    }
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
 
-    SECTION("R2x2_non_linear_1d")
-    {
-      using DataType = TinyMatrix<2>;
+        SECTION("R2x2_non_linear_1d")
+        {
+          using DataType = TinyMatrix<2>;
 
-      auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+            });
 
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id] =
-            DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
-        });
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
 
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
 
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
-    }
+        SECTION("R3x3_non_linear_1d")
+        {
+          using DataType = TinyMatrix<3>;
 
-    SECTION("R3x3_non_linear_1d")
-    {
-      using DataType = TinyMatrix<3>;
-
-      auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_1d", position);
-      REQUIRE(found);
-      REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-      FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-      CellValue<DataType> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = DataType{2 * exp(x[0]) * std::sin(x[0]) + 3,
-                                         std::sin(x[0] - 2 * x[0]),
-                                         3,
-                                         x[0] * x[0],
-                                         -4 * x[0],
-                                         2 * x[0] + 1,
-                                         3,
-                                         -6 * x[0],
-                                         std::exp(x[0])};
-        });
-
-      DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
-                                            function_symbol_id);
-      std::shared_ptr discrete_function = interpoler.interpolate();
-
-      REQUIRE(
-        same_cell_value(cell_value, dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * exp(x[0]) * std::sin(x[0]) + 3,
+                                             std::sin(x[0] - 2 * x[0]),
+                                             3,
+                                             x[0] * x[0],
+                                             -4 * x[0],
+                                             2 * x[0] + 1,
+                                             3,
+                                             -6 * x[0],
+                                             std::exp(x[0])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+      }
     }
   }
 
@@ -334,15 +340,12 @@ let R3x3_non_linear_1d: R^1 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x
   {
     constexpr size_t Dimension = 2;
 
-    std::array mesh_list =   //
-      {std::make_pair(std::string{"cartesian grid"}, MeshDataBaseForTests::get().cartesian2DMesh()),
-       std::make_pair(std::string{"hybrid grid"}, MeshDataBaseForTests::get().hybrid2DMesh())};
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    for (auto [section_name, mesh_3d] : mesh_list) {
+    for (auto [section_name, mesh_2d] : mesh_list) {
       SECTION(section_name)
       {
-        const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-        auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
         std::string_view data = R"(
 import math;
@@ -635,9 +638,7 @@ let R3x3_non_linear_2d: R^2 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x
   {
     constexpr size_t Dimension = 3;
 
-    std::array mesh_list =   //
-      {std::make_pair(std::string{"cartesian grid"}, MeshDataBaseForTests::get().cartesian3DMesh()),
-       std::make_pair(std::string{"hybrid grid"}, MeshDataBaseForTests::get().hybrid3DMesh())};
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
     for (auto [section_name, mesh_3d] : mesh_list) {
       SECTION(section_name)
diff --git a/tests/test_DiscreteFunctionP0.cpp b/tests/test_DiscreteFunctionP0.cpp
index ce9644d8b485689f5e3790097f1e28f2c80dc764..f57093c851fc7d95f83f988092df456d4321c775 100644
--- a/tests/test_DiscreteFunctionP0.cpp
+++ b/tests/test_DiscreteFunctionP0.cpp
@@ -22,128 +22,143 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
   }
 
@@ -161,65 +176,83 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian1DMesh();
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          DiscreteFunctionP0<Dimension, 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}));
+          REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian2DMesh();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          DiscreteFunctionP0<Dimension, 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}));
+          REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian3DMesh();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      REQUIRE(all_values_equal(f, 3));
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          REQUIRE(all_values_equal(f, 3));
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+          DiscreteFunctionP0<Dimension, 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}));
+        }
+      }
     }
   }
 
@@ -237,286 +270,301 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        REQUIRE(all_values_equal(f, value));
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
+
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
       constexpr size_t Dimension = 2;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            REQUIRE(all_values_equal(g, value));
+          }
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            REQUIRE(all_values_equal(g, value));
+          }
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
       constexpr size_t Dimension = 3;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        REQUIRE(all_values_equal(f, value));
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
+
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
   }
@@ -525,75 +573,79 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
+      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        SECTION("scalar functions")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          DiscreteFunctionP0<Dimension, 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;
-            });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+          SECTION("unary minus")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, 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;
+                });
 
-          Array<double> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
-        }
+              Array<double> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->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};
+                });
 
-          Array<TinyVector<VectorDimension>> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
-        }
+              Array<TinyVector<VectorDimension>> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->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, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
 
-          Array<TinyMatrix<MatrixDimension>> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+              Array<TinyMatrix<MatrixDimension>> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
+          }
         }
       }
     }
@@ -603,2839 +655,2919 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          DiscreteFunctionP0<Dimension, 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};
-          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};
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("sum")
+          SECTION("inner operators")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
-
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, 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;
+                });
 
-          SECTION("difference")
-          {
-            Array<double> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              DiscreteFunctionP0<Dimension, 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;
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
-          }
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->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};
+                });
 
-          SECTION("product")
-          {
-            Array<double> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              parallel_for(
+                mesh->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;
+                });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
-          }
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-          SECTION("ratio")
-          {
-            Array<double> ratio_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
-          }
-        }
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->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;
-            });
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->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};
+                });
 
-          SECTION("sum")
-          {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
+              parallel_for(
+                mesh->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;
+                });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+            }
           }
 
-          SECTION("difference")
+          SECTION("external operators")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id]     = std::abs(2 * x) + 1;
+                });
+
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->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};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->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, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> X{x, 2 - x, x * x, x * 3};
+                  f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
+            }
           }
         }
+      }
+    }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
-
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
-          parallel_for(
-            mesh->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;
-            });
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("sum")
+          SECTION("inner operators")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->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;
+                });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+              DiscreteFunctionP0<Dimension, double> g{mesh};
+              parallel_for(
+                mesh->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;
+                });
 
-          SECTION("difference")
-          {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->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};
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
-          }
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              parallel_for(
+                mesh->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;
+                });
 
-          SECTION("product")
-          {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
-          }
-        }
-      }
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id]     = std::abs(2 * x) + 1;
-            });
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
 
-          const double a = 3;
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
 
-          SECTION("sum")
-          {
+            SECTION("matrix functions")
             {
-              Array<double> sum_values{mesh->numberOfCells()};
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+                mesh->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};
+                });
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+                mesh->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;
+                });
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
             }
           }
 
-          SECTION("difference")
+          SECTION("external operators")
           {
+            SECTION("scalar functions")
             {
-              Array<double> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
-                mesh->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()};
-              parallel_for(
-                mesh->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));
-            }
-          }
+                mesh->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;
+                });
 
-          SECTION("product")
-          {
-            {
-              Array<double> product_values{mesh->numberOfCells()};
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->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};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+                mesh->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};
+                });
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+                mesh->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};
+                });
 
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
             }
+          }
+        }
+      }
+    }
 
-            {
-              Array<TinyVector<3>> product_values{mesh->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; });
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
             {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  f[cell_id]     = 2 * x + y - z;
                 });
 
+              DiscreteFunctionP0<Dimension, double> g{mesh};
               parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
-
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+                mesh->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];
+                  g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y) + 2 * z) + 1;
+                });
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              const TinyMatrix<2> A{1, 2, 3, 4};
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
-                mesh->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));
-            }
+                mesh->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};
+                });
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
               parallel_for(
                 mesh->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};
+                  const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
+                  g[cell_id] = X;
                 });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
-            }
-          }
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-          SECTION("ratio")
-          {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
 
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
             }
-          }
-        }
-
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
-
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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, const TinyVector<VectorDimension>> const_f = f;
-
-          SECTION("sum")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->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));
-            }
+            SECTION("matrix functions")
             {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+              constexpr std::uint64_t MatrixDimension = 2;
 
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
-            }
-          }
-
-          SECTION("difference")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+                mesh->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};
+                });
 
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+                mesh->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;
+                });
 
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
             }
           }
 
-          SECTION("product")
+          SECTION("external operators")
           {
+            SECTION("scalar functions")
             {
-              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]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  f[cell_id]     = std::abs(2 * x + y * z) + 1;
                 });
 
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-
-            {
-              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]; });
-
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
-
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->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};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->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};
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  const TinyVector<VectorDimension> X{x + y - z, 2 - x * y};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+                mesh->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];
+                  const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * z + 3};
+                  f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->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));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  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]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->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(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  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]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  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; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
             }
           }
         }
+      }
+    }
+  }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+  SECTION("math functions")
+  {
+#define CHECK_STD_MATH_FUNCTION(data_expression, FCT)                           \
+  {                                                                             \
+    DiscreteFunctionP0 data   = data_expression;                                \
+    DiscreteFunctionP0 result = FCT(data);                                      \
+    bool is_same              = true;                                           \
+    parallel_for(data.cellValues().numberOfItems(), [&](const CellId cell_id) { \
+      if (result[cell_id] != std::FCT(data[cell_id])) {                         \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> X{x, 2 - x, x * x, x * 3};
-              f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
-            });
+#define CHECK_STD_BINARY_MATH_FUNCTION(lhs_expression, rhs_expression, FCT)    \
+  {                                                                            \
+    DiscreteFunctionP0 lhs    = lhs_expression;                                \
+    DiscreteFunctionP0 rhs    = rhs_expression;                                \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                 \
+    using namespace std;                                                       \
+    bool is_same = true;                                                       \
+    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \
+      if (result[cell_id] != FCT(lhs[cell_id], rhs[cell_id])) {                \
+        is_same = false;                                                       \
+      }                                                                        \
+    });                                                                        \
+    REQUIRE(is_same);                                                          \
+  }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(lhs, rhs_expression, FCT) \
+  {                                                                             \
+    DiscreteFunctionP0 rhs    = rhs_expression;                                 \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
+    bool is_same              = true;                                           \
+    using namespace std;                                                        \
+    parallel_for(rhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
+      if (result[cell_id] != FCT(lhs, rhs[cell_id])) {                          \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-          SECTION("sum")
-          {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(lhs_expression, rhs, FCT) \
+  {                                                                             \
+    DiscreteFunctionP0 lhs    = lhs_expression;                                 \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
+    bool is_same              = true;                                           \
+    using namespace std;                                                        \
+    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
+      if (result[cell_id] != FCT(lhs[cell_id], rhs)) {                          \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
-          }
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("difference")
-          {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
+          const double min_value = min(positive_function);
+          SECTION("min")
+          {
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
           }
 
-          SECTION("product")
+          const double max_value = max(positive_function);
+          SECTION("max")
           {
-            {
-              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]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->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(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
+          }
 
-            {
-              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]; });
+          REQUIRE(min_value < max_value);
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
 
-            {
-              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; });
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("abs")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
           }
-        }
-      }
-    }
 
-    SECTION("2D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
+          }
 
-      constexpr size_t Dimension = 2;
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
+          }
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->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> unit_function{mesh};
 
-          DiscreteFunctionP0<Dimension, double> g{mesh};
           parallel_for(
-            mesh->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;
+            mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
             });
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
-          DiscreteFunctionP0<Dimension, const double> const_g{g};
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-          SECTION("sum")
+          SECTION("asin")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("atan")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
           }
 
-          SECTION("difference")
+          SECTION("cosh")
           {
-            Array<double> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("sinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
           }
 
-          SECTION("product")
+          SECTION("tanh")
           {
-            Array<double> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
+          }
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+          SECTION("acosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
           }
 
-          SECTION("ratio")
+          SECTION("asinh")
           {
-            Array<double> ratio_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
+          }
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
+          SECTION("atanh")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
           }
-        }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->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;
-            });
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-          SECTION("sum")
+          SECTION("max(uh,0.2)")
           {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("atan2(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
           }
 
-          SECTION("difference")
+          SECTION("atan2(0.5,uh)")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("atan2(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-          parallel_for(
-            mesh->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;
-            });
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+          SECTION("pow(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
+          }
 
-          SECTION("sum")
+          SECTION("min(uh,hv)")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
           }
 
-          SECTION("difference")
+          SECTION("min(uh,0.2)")
           {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
           }
 
-          SECTION("product")
+          SECTION("min(uh,0.5)")
           {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
           }
-        }
-      }
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->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;
-            });
+          SECTION("dot(uh,hv)")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
 
-          const double a = 3;
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
+          }
 
-          SECTION("sum")
+          SECTION("dot(uh,v)")
           {
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+            DiscreteFunctionP0<Dimension, 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};
+              });
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+            const TinyVector<2> v{1, 2};
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
           }
 
-          SECTION("difference")
+          SECTION("dot(u,hv)")
           {
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->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));
-            }
+            const TinyVector<2> u{3, -2};
 
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->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));
-            }
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
+
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
           }
 
-          SECTION("product")
+          SECTION("scalar sum")
           {
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+            REQUIRE(sum(cell_value) == sum(positive_function));
+          }
 
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
-            }
+          SECTION("vector sum")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
 
-            {
-              Array<TinyVector<3>> product_values{mesh->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; });
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          SECTION("matrix sum")
+          {
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
-                });
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+          SECTION("integrate scalar")
+          {
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+            CellValue<double> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-            {
-              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; });
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("integrate vector")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
-              parallel_for(
-                mesh->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};
-                });
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
-            }
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
           }
 
-          SECTION("ratio")
+          SECTION("integrate matrix")
           {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
-            }
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
+
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
+      }
+    }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
-
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-          SECTION("sum")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-              REQUIRE(same_values(v + f, sum_values));
-              REQUIRE(same_values(v + const_f, sum_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
 
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
-            }
-          }
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
 
-          SECTION("difference")
+          const double min_value = min(positive_function);
+          SECTION("min")
           {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
-
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
-
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
           }
 
-          SECTION("product")
+          const double max_value = max(positive_function);
+          SECTION("max")
           {
-            {
-              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]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->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(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          REQUIRE(min_value < max_value);
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
 
-            {
-              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]; });
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("abs")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
+          }
 
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
-              parallel_for(
-                mesh->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};
-                });
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
+          }
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
-            }
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
           parallel_for(
-            mesh->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};
+            mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
             });
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-          SECTION("sum")
+          SECTION("asin")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
+          }
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+          SECTION("atan")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
+          }
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
+          SECTION("cosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
           }
 
-          SECTION("difference")
+          SECTION("sinh")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
+          }
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+          SECTION("tanh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
+          }
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
-            }
+          SECTION("acosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
           }
 
-          SECTION("product")
+          SECTION("asinh")
           {
-            {
-              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]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("atanh")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
+          }
 
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-            {
-              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]; });
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("max(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-            {
-              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; });
+          SECTION("atan2(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("atan2(0.5,uh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
           }
-        }
-      }
-    }
 
-    SECTION("3D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+          SECTION("atan2(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
+          }
 
-      constexpr size_t Dimension = 3;
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
+          }
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
+          }
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->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];
-              f[cell_id]     = 2 * x + y - z;
-            });
+          SECTION("pow(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
+          }
 
-          DiscreteFunctionP0<Dimension, double> g{mesh};
-          parallel_for(
-            mesh->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];
-              g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y) + 2 * z) + 1;
-            });
+          SECTION("min(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
-          DiscreteFunctionP0<Dimension, const double> const_g{g};
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
+          }
 
-          SECTION("sum")
+          SECTION("min(uh,0.2)")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
           }
 
-          SECTION("difference")
+          SECTION("min(uh,0.5)")
           {
-            Array<double> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
           }
 
-          SECTION("product")
+          SECTION("dot(uh,hv)")
           {
-            Array<double> product_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              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};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
           }
 
-          SECTION("ratio")
+          SECTION("dot(uh,v)")
           {
-            Array<double> ratio_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+              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};
+              });
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
-          }
-        }
+            const TinyVector<2> v{1, 2};
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-            });
+          SECTION("dot(u,hv)")
+          {
+            const TinyVector<2> u{3, -2};
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->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, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
+          }
 
-          SECTION("sum")
+          SECTION("scalar sum")
           {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+            REQUIRE(sum(cell_value) == sum(positive_function));
           }
 
-          SECTION("difference")
+          SECTION("vector sum")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              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};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(sum(cell_value) == sum(uh));
           }
-        }
-
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->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};
-          parallel_for(
-            mesh->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;
-            });
+          SECTION("matrix sum")
+          {
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-          SECTION("sum")
+          SECTION("integrate scalar")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<double> cell_value{mesh->connectivity()};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
           }
 
-          SECTION("difference")
+          SECTION("integrate vector")
           {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, 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};
+              });
+
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
             parallel_for(
               mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
           }
 
-          SECTION("product")
+          SECTION("integrate matrix")
           {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
+
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
             parallel_for(
               mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->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];
-              f[cell_id]     = std::abs(2 * x + y * z) + 1;
-            });
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
 
-          const double a = 3;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("sum")
-          {
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
+          const double min_value = min(positive_function);
+          SECTION("min")
+          {
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
           }
 
-          SECTION("difference")
+          const double max_value = max(positive_function);
+          SECTION("max")
           {
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->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));
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
+          }
 
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->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));
-            }
+          REQUIRE(min_value < max_value);
+
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
+
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
           }
 
-          SECTION("product")
+          SECTION("abs")
           {
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
+          }
 
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
-            }
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-            {
-              Array<TinyVector<3>> product_values{mesh->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; });
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-              parallel_for(
-                mesh->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(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
+            });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          SECTION("asin")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
+          }
 
-            {
-              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; });
+          SECTION("atan")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("cosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
+          }
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
-              parallel_for(
-                mesh->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};
-                });
+          SECTION("sinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
+          }
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+          SECTION("tanh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
+          }
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
-            }
+          SECTION("acosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
           }
 
-          SECTION("ratio")
+          SECTION("asinh")
           {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
+          }
 
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+          SECTION("atanh")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
+          }
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
-            }
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
           }
-        }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->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];
-              const TinyVector<VectorDimension> X{x + y - z, 2 - x * y};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-          SECTION("sum")
+          SECTION("max(uh,0.2)")
           {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-              REQUIRE(same_values(v + f, sum_values));
-              REQUIRE(same_values(v + const_f, sum_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+          SECTION("atan2(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
+          }
 
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
-            }
+          SECTION("atan2(0.5,uh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
           }
 
-          SECTION("difference")
+          SECTION("atan2(uh,0.2)")
           {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
+          }
 
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
+          }
 
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
-            }
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
           }
 
-          SECTION("product")
+          SECTION("pow(uh,0.2)")
           {
-            {
-              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]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("min(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
+          }
 
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
+          }
 
-            {
-              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]; });
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
+          }
 
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
-              parallel_for(
-                mesh->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};
-                });
+          SECTION("dot(uh,hv)")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          SECTION("dot(uh,v)")
+          {
+            DiscreteFunctionP0<Dimension, 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, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->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];
-              const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * z + 3};
-              f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
-            });
+            const TinyVector<2> v{1, 2};
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
+          }
 
-          SECTION("sum")
+          SECTION("dot(u,hv)")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+            const TinyVector<2> u{3, -2};
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
           }
 
-          SECTION("difference")
+          SECTION("scalar sum")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+            REQUIRE(sum(cell_value) == sum(positive_function));
+          }
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
-            }
+          SECTION("vector sum")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
+
+            REQUIRE(sum(cell_value) == sum(uh));
           }
 
-          SECTION("product")
+          SECTION("matrix sum")
           {
-            {
-              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]; });
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("integrate scalar")
+          {
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+            CellValue<double> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
+          }
 
-            {
-              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]; });
+          SECTION("integrate vector")
+          {
+            DiscreteFunctionP0<Dimension, 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};
+              });
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-            {
-              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; });
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
+          }
+
+          SECTION("integrate matrix")
+          {
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
+
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
+
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
       }
     }
   }
 
-  SECTION("math functions")
+#ifndef NDEBUG
+  SECTION("error")
   {
-#define CHECK_STD_MATH_FUNCTION(data_expression, FCT)                           \
-  {                                                                             \
-    DiscreteFunctionP0 data   = data_expression;                                \
-    DiscreteFunctionP0 result = FCT(data);                                      \
-    bool is_same              = true;                                           \
-    parallel_for(data.cellValues().numberOfItems(), [&](const CellId cell_id) { \
-      if (result[cell_id] != std::FCT(data[cell_id])) {                         \
-        is_same = false;                                                        \
-      }                                                                         \
-    });                                                                         \
-    REQUIRE(is_same);                                                           \
-  }
+    SECTION("different meshes")
+    {
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
 
-#define CHECK_STD_BINARY_MATH_FUNCTION(lhs_expression, rhs_expression, FCT)    \
-  {                                                                            \
-    DiscreteFunctionP0 lhs    = lhs_expression;                                \
-    DiscreteFunctionP0 rhs    = rhs_expression;                                \
-    DiscreteFunctionP0 result = FCT(lhs, rhs);                                 \
-    using namespace std;                                                       \
-    bool is_same = true;                                                       \
-    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \
-      if (result[cell_id] != FCT(lhs[cell_id], rhs[cell_id])) {                \
-        is_same = false;                                                       \
-      }                                                                        \
-    });                                                                        \
-    REQUIRE(is_same);                                                          \
-  }
-
-#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(lhs, rhs_expression, FCT) \
-  {                                                                             \
-    DiscreteFunctionP0 rhs    = rhs_expression;                                 \
-    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
-    bool is_same              = true;                                           \
-    using namespace std;                                                        \
-    parallel_for(rhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
-      if (result[cell_id] != FCT(lhs, rhs[cell_id])) {                          \
-        is_same = false;                                                        \
-      }                                                                         \
-    });                                                                         \
-    REQUIRE(is_same);                                                           \
-  }
-
-#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(lhs_expression, rhs, FCT) \
-  {                                                                             \
-    DiscreteFunctionP0 lhs    = lhs_expression;                                 \
-    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
-    bool is_same              = true;                                           \
-    using namespace std;                                                        \
-    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
-      if (result[cell_id] != FCT(lhs[cell_id], rhs)) {                          \
-        is_same = false;                                                        \
-      }                                                                         \
-    });                                                                         \
-    REQUIRE(is_same);                                                           \
-  }
-
-    SECTION("1D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      constexpr size_t Dimension = 1;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      DiscreteFunctionP0<Dimension, double> positive_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(),
-        PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
-
-      const double min_value = min(positive_function);
-      SECTION("min")
-      {
-        double local_min = std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_min = std::min(local_min, positive_function[cell_id]);
-        }
-        REQUIRE(min_value == parallel::allReduceMin(local_min));
-      }
-
-      const double max_value = max(positive_function);
-      SECTION("max")
-      {
-        double local_max = -std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_max = std::max(local_max, positive_function[cell_id]);
-        }
-        REQUIRE(max_value == parallel::allReduceMax(local_max));
-      }
-
-      REQUIRE(min_value < max_value);
-
-      DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
-
-      SECTION("sqrt")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
-      }
-
-      SECTION("abs")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, abs);
-      }
-
-      SECTION("cos")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cos);
-      }
-
-      SECTION("sin")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sin);
-      }
-
-      SECTION("tan")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tan);
-      }
-
-      DiscreteFunctionP0<Dimension, double> unit_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-          unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
-        });
-
-      SECTION("acos")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, acos);
-      }
-
-      SECTION("asin")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, asin);
-      }
-
-      SECTION("atan")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atan);
-      }
-
-      SECTION("cosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cosh);
-      }
-
-      SECTION("sinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sinh);
-      }
-
-      SECTION("tanh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tanh);
-      }
-
-      SECTION("acosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, acosh);
-      }
-
-      SECTION("asinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, asinh);
-      }
-
-      SECTION("atanh")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atanh);
-      }
-
-      SECTION("exp")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, exp);
-      }
-
-      SECTION("log")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, log);
-      }
-
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
-      }
-
-      SECTION("max(0.2,vh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
-      }
-
-      SECTION("max(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
-      }
-
-      SECTION("atan2(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(0.5,uh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
-      }
-
-      SECTION("pow(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
-      }
-
-      SECTION("min(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
-      }
-
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
-      }
-
-      SECTION("dot(uh,hv)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
-      }
-
-      SECTION("dot(uh,v)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const TinyVector<2> v{1, 2};
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
-      }
-
-      SECTION("dot(u,hv)")
-      {
-        const TinyVector<2> u{3, -2};
-
-        DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
-      }
-
-      SECTION("scalar sum")
-      {
-        const CellValue<const double> cell_value = positive_function.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(positive_function));
-      }
-
-      SECTION("vector sum")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-        const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("matrix sum")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
-          });
-        const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("integrate scalar")
-      {
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<double> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
-          });
-
-        REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
-      }
-
-      SECTION("integrate vector")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
-        REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
-      }
-
-      SECTION("integrate matrix")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
-        REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
-        REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
-        REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
-      }
-    }
-
-    SECTION("2D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
-      constexpr size_t Dimension = 2;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      DiscreteFunctionP0<Dimension, double> positive_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(),
-        PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
-
-      const double min_value = min(positive_function);
-      SECTION("min")
-      {
-        double local_min = std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_min = std::min(local_min, positive_function[cell_id]);
-        }
-        REQUIRE(min_value == parallel::allReduceMin(local_min));
-      }
-
-      const double max_value = max(positive_function);
-      SECTION("max")
-      {
-        double local_max = -std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_max = std::max(local_max, positive_function[cell_id]);
-        }
-        REQUIRE(max_value == parallel::allReduceMax(local_max));
-      }
-
-      REQUIRE(min_value < max_value);
-
-      DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
-
-      SECTION("sqrt")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
-      }
-
-      SECTION("abs")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, abs);
-      }
-
-      SECTION("cos")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cos);
-      }
-
-      SECTION("sin")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sin);
-      }
-
-      SECTION("tan")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tan);
-      }
-
-      DiscreteFunctionP0<Dimension, double> unit_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-          unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
-        });
-
-      SECTION("acos")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, acos);
-      }
-
-      SECTION("asin")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, asin);
-      }
-
-      SECTION("atan")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atan);
-      }
-
-      SECTION("cosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cosh);
-      }
-
-      SECTION("sinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sinh);
-      }
-
-      SECTION("tanh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tanh);
-      }
-
-      SECTION("acosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, acosh);
-      }
-
-      SECTION("asinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, asinh);
-      }
-
-      SECTION("atanh")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atanh);
-      }
-
-      SECTION("exp")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, exp);
-      }
-
-      SECTION("log")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, log);
-      }
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
-      }
-
-      SECTION("max(0.2,vh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
-      }
-
-      SECTION("max(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
-      }
-
-      SECTION("atan2(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(0.5,uh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
-      }
-
-      SECTION("pow(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
-      }
-
-      SECTION("min(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
-      }
-
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
-      }
-
-      SECTION("dot(uh,hv)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
-      }
-
-      SECTION("dot(uh,v)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const TinyVector<2> v{1, 2};
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
-      }
-
-      SECTION("dot(u,hv)")
-      {
-        const TinyVector<2> u{3, -2};
-
-        DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
-      }
-
-      SECTION("scalar sum")
-      {
-        const CellValue<const double> cell_value = positive_function.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(positive_function));
-      }
-
-      SECTION("vector sum")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-        const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("matrix sum")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
-          });
-        const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("integrate scalar")
-      {
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<double> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
-          });
-
-        REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
-      }
-
-      SECTION("integrate vector")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
-        REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
-      }
-
-      SECTION("integrate matrix")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
-        REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
-        REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
-        REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
-      }
-    }
-
-    SECTION("3D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
-      constexpr size_t Dimension = 3;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      DiscreteFunctionP0<Dimension, double> positive_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(),
-        PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
-
-      const double min_value = min(positive_function);
-      SECTION("min")
-      {
-        double local_min = std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_min = std::min(local_min, positive_function[cell_id]);
-        }
-        REQUIRE(min_value == parallel::allReduceMin(local_min));
-      }
-
-      const double max_value = max(positive_function);
-      SECTION("max")
-      {
-        double local_max = -std::numeric_limits<double>::max();
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          local_max = std::max(local_max, positive_function[cell_id]);
+        for (auto [section_name, mesh_1] : mesh_list) {
+          SECTION(section_name)
+          {
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
         }
-        REQUIRE(max_value == parallel::allReduceMax(local_max));
-      }
-
-      REQUIRE(min_value < max_value);
-
-      DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
-
-      SECTION("sqrt")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
-      }
-
-      SECTION("abs")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, abs);
-      }
-
-      SECTION("cos")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cos);
-      }
-
-      SECTION("sin")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sin);
-      }
-
-      SECTION("tan")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tan);
-      }
-
-      DiscreteFunctionP0<Dimension, double> unit_function{mesh};
-
-      parallel_for(
-        mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-          unit_function[cell_id] = (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
-        });
-
-      SECTION("acos")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, acos);
-      }
-
-      SECTION("asin")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, asin);
-      }
-
-      SECTION("atan")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atan);
-      }
-
-      SECTION("cosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, cosh);
-      }
-
-      SECTION("sinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, sinh);
-      }
-
-      SECTION("tanh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, tanh);
-      }
-
-      SECTION("acosh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, acosh);
-      }
-
-      SECTION("asinh")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, asinh);
-      }
-
-      SECTION("atanh")
-      {
-        CHECK_STD_MATH_FUNCTION(unit_function, atanh);
-      }
-
-      SECTION("exp")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, exp);
-      }
-
-      SECTION("log")
-      {
-        CHECK_STD_MATH_FUNCTION(positive_function, log);
-      }
-
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
-      }
-
-      SECTION("max(0.2,vh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
-      }
-
-      SECTION("max(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
-      }
-
-      SECTION("atan2(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(0.5,uh)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
-      }
-
-      SECTION("atan2(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
-      }
-
-      SECTION("pow(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
-      }
-
-      SECTION("pow(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
-      }
-
-      SECTION("min(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
-      }
-
-      SECTION("max(uh,hv)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.5)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
-      }
-
-      SECTION("min(uh,0.2)")
-      {
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
-      }
-
-      SECTION("dot(uh,hv)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
-      }
-
-      SECTION("dot(uh,v)")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const TinyVector<2> v{1, 2};
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
-      }
-
-      SECTION("dot(u,hv)")
-      {
-        const TinyVector<2> u{3, -2};
-
-        DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
-          });
-
-        CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
-      }
-
-      SECTION("scalar sum")
-      {
-        const CellValue<const double> cell_value = positive_function.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(positive_function));
-      }
-
-      SECTION("vector sum")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-        const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("matrix sum")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
-          });
-        const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
-
-        REQUIRE(sum(cell_value) == sum(uh));
-      }
-
-      SECTION("integrate scalar")
-      {
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<double> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
-          });
-
-        REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
-      }
-
-      SECTION("integrate vector")
-      {
-        DiscreteFunctionP0<Dimension, 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};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
-        REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
-      }
-
-      SECTION("integrate matrix")
-      {
-        DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-            const double x = xj[cell_id][0];
-            uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
-          });
-
-        const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
-
-        CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(),
-          PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
-
-        REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
-        REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
-        REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
-        REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
-      }
-    }
-  }
-
-#ifndef NDEBUG
-  SECTION("error")
-  {
-    SECTION("different meshes")
-    {
-      SECTION("1D")
-      {
-        constexpr size_t Dimension = 1;
-
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesian1DMesh();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
-
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
-
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
       }
 
       SECTION("2D")
       {
         constexpr size_t Dimension = 2;
 
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesian2DMesh();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
-
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
+        for (auto [section_name, mesh_1] : mesh_list) {
+          SECTION(section_name)
+          {
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
+        }
       }
 
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
 
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesian3DMesh();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
-
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+        std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
+        for (auto [section_name, mesh_1] : mesh_list) {
+          SECTION(section_name)
+          {
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
+        }
       }
     }
   }
diff --git a/tests/test_DiscreteFunctionP0Vector.cpp b/tests/test_DiscreteFunctionP0Vector.cpp
index 72daecc2901162b1712c41f421a49d7bbe9cee7f..217418fe5db664fdc8ae85dd8f1baa0f461ca409 100644
--- a/tests/test_DiscreteFunctionP0Vector.cpp
+++ b/tests/test_DiscreteFunctionP0Vector.cpp
@@ -27,123 +27,141 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian1DMesh();
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      REQUIRE(f.mesh().get() == mesh.get());
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0Vector<Dimension, 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());
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      h_arrays.fill(1);
+          h_arrays.fill(1);
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
 
     SECTION("2D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian2DMesh();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0Vector<Dimension, 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.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      h_arrays.fill(1);
+          h_arrays.fill(1);
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
 
     SECTION("3D")
     {
       const size_t size = 2;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian3DMesh();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0Vector<Dimension, 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.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      h_arrays.fill(1);
+          h_arrays.fill(1);
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
   }
 
@@ -166,39 +184,57 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian1DMesh();
-      constexpr size_t Dimension = 1;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(3);
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
+        }
+      }
     }
 
     SECTION("2D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian2DMesh();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(2.3);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(2.3);
 
-      REQUIRE(all_values_equal(f, 2.3));
+          REQUIRE(all_values_equal(f, 2.3));
+        }
+      }
     }
 
     SECTION("3D")
     {
       const size_t size = 2;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesian3DMesh();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(3.2);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(3.2);
 
-      REQUIRE(all_values_equal(f, 3.2));
+          REQUIRE(all_values_equal(f, 3.2));
+        }
+      }
     }
   }
 
@@ -220,623 +256,670 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-      REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(f, value));
 
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-      copy_to(g, f);
-      g.fill(zero);
+          copy_to(g, f);
+          g.fill(zero);
 
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(shallow_g, zero));
-      REQUIRE(all_values_equal(h, value));
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(shallow_g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-      copy_to(h, g);
+          copy_to(h, g);
 
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
       constexpr size_t Dimension = 2;
+      std::array mesh_list       = MeshDataBaseForTests::get().all2DMeshes();
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-      REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(f, value));
 
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-      copy_to(g, f);
-      g.fill(zero);
+          copy_to(g, f);
+          g.fill(zero);
 
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(shallow_g, zero));
-      REQUIRE(all_values_equal(h, value));
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(shallow_g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-      copy_to(h, g);
+          copy_to(h, g);
 
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
       constexpr size_t Dimension = 3;
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
-
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
-
-      REQUIRE(all_values_equal(f, value));
-
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
-
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
-
-      copy_to(g, f);
-      g.fill(zero);
-
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
-
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(h, value));
-
-      copy_to(h, g);
-
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
-    }
-  }
-
-  SECTION("unary operators")
-  {
-    SECTION("1D")
-    {
-      const size_t size    = 3;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      constexpr size_t Dimension = 1;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i;
-            }
-          });
-
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
-
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
-      }
-    }
-
-    SECTION("2D")
-    {
-      const size_t size    = 3;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
-      constexpr size_t Dimension = 2;
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i * y;
-            }
-          });
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+          REQUIRE(all_values_equal(f, value));
 
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
-      }
-    }
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-    SECTION("3D")
-    {
-      const size_t size    = 2;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+          copy_to(g, f);
+          g.fill(zero);
 
-      constexpr size_t Dimension = 3;
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->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];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i * y - z;
-            }
-          });
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
+          copy_to(h, g);
 
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
       }
     }
   }
 
-  SECTION("binary operators")
+  SECTION("unary operators")
   {
     SECTION("1D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
       constexpr size_t Dimension = 1;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id][0]  = 2 * x + 1;
-              f[cell_id][1]  = x * x - 1;
-              f[cell_id][2]  = 2 + x;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              g[cell_id][0]  = (x + 1) * (x - 2) + 1;
-              g[cell_id][1]  = 3 * (x + 2) - 1;
-              g[cell_id][2]  = (x + 3) * 5;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("unary minus")
           {
-            Table<double> sum_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
                 for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                  f[cell_id][i] = 2 * x + i;
                 }
               });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
 
-          SECTION("difference")
-          {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i;
-            }
-          });
+    SECTION("2D")
+    {
+      const size_t size = 3;
+
+      constexpr size_t Dimension = 2;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        SECTION("product")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          SECTION("scalar lhs")
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("unary minus")
           {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
+                const double y = xj[cell_id][1];
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
+                  f[cell_id][i] = 2 * x + i * y;
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-          }
-
-          SECTION("DiscreteFunctionP0 lhs")
-          {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                const double x = xj[cell_id][0];
-                a[cell_id]     = 2 * x + 1;
-              });
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
 
-            Table<double> product_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
     }
 
-    SECTION("2D")
+    SECTION("3D")
     {
-      const size_t size = 3;
-
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
+      const size_t size = 2;
 
-      constexpr size_t Dimension = 2;
+      constexpr size_t Dimension = 3;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              f[cell_id][0]  = 2 * x + 1;
-              f[cell_id][1]  = x * x - y;
-              f[cell_id][2]  = 2 + x * y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              g[cell_id][0]  = (x + 1) * (y - 2) + 1;
-              g[cell_id][1]  = 3 * (x + 2) - y;
-              g[cell_id][2]  = (x + 3) + 5 * y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("unary minus")
           {
-            Table<double> sum_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->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];
                 for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                  f[cell_id][i] = 2 * x + i * y - z;
                 }
               });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
 
-          SECTION("difference")
-          {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
+    }
+  }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i * y;
-            }
-          });
+  SECTION("binary operators")
+  {
+    SECTION("1D")
+    {
+      const size_t size = 3;
+
+      constexpr size_t Dimension = 1;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        SECTION("product")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          SECTION("scalar lhs")
-          {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
-                }
-              });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id][0]  = 2 * x + 1;
+                  f[cell_id][1]  = x * x - 1;
+                  f[cell_id][2]  = 2 + x;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  g[cell_id][0]  = (x + 1) * (x - 2) + 1;
+                  g[cell_id][1]  = 3 * (x + 2) - 1;
+                  g[cell_id][2]  = (x + 3) * 5;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("DiscreteFunctionP0 lhs")
+          SECTION("external operators")
           {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
-                const double y = xj[cell_id][1];
-                a[cell_id]     = 2 * x + 1 - y;
-              });
-
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i;
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    const double x = xj[cell_id][0];
+                    a[cell_id]     = 2 * x + 1;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                REQUIRE(same_values(const_a * f, product_values));
+                REQUIRE(same_values(const_a * const_f, product_values));
+              }
+            }
           }
         }
       }
     }
 
-    SECTION("3D")
+    SECTION("2D")
     {
-      const size_t size = 2;
-
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+      const size_t size = 3;
 
-      constexpr size_t Dimension = 3;
+      constexpr size_t Dimension = 2;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->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];
-              f[cell_id][0]  = 2 * x * z + 1;
-              f[cell_id][1]  = x * z - y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->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];
-              g[cell_id][0]  = (x + 1) * (y - 2) + 1 - z;
-              g[cell_id][1]  = 3 * (x + 2) - y * z;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
-          {
-            Table<double> sum_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
-                }
-              });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  f[cell_id][0]  = 2 * x + 1;
+                  f[cell_id][1]  = x * x - y;
+                  f[cell_id][2]  = 2 + x * y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  g[cell_id][0]  = (x + 1) * (y - 2) + 1;
+                  g[cell_id][1]  = 3 * (x + 2) - y;
+                  g[cell_id][2]  = (x + 3) + 5 * y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("difference")
+          SECTION("external operators")
           {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
+                const double y = xj[cell_id][1];
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i * y;
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    const double x = xj[cell_id][0];
+                    const double y = xj[cell_id][1];
+                    a[cell_id]     = 2 * x + 1 - y;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                REQUIRE(same_values(const_a * f, product_values));
+                REQUIRE(same_values(const_a * const_f, product_values));
+              }
+            }
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->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];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i * y + z;
-            }
-          });
+    SECTION("3D")
+    {
+      const size_t size = 2;
+
+      constexpr size_t Dimension = 3;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        SECTION("product")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          SECTION("scalar lhs")
-          {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
-                }
-              });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->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];
+                  f[cell_id][0]  = 2 * x * z + 1;
+                  f[cell_id][1]  = x * z - y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->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];
+                  g[cell_id][0]  = (x + 1) * (y - 2) + 1 - z;
+                  g[cell_id][1]  = 3 * (x + 2) - y * z;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("DiscreteFunctionP0 lhs")
+          SECTION("external operators")
           {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->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];
-                a[cell_id]     = 2 * x + 1 - y * z;
-              });
-
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i * y + z;
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->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];
+                    a[cell_id]     = 2 * x + 1 - y * z;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, 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 511714ed72e367dad950959953199c0f3cba05e3..88ddd4025980c0edafe28966b78a8612e8360a80 100644
--- a/tests/test_DiscreteFunctionUtils.cpp
+++ b/tests/test_DiscreteFunctionUtils.cpp
@@ -16,144 +16,150 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
   {
     constexpr size_t Dimension = 1;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-    std::shared_ptr mesh_copy =
-      std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-    SECTION("common mesh")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
 
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
 
-      REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-      REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
-    }
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-    SECTION("check discretization type")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
-
-      std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-      std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-
-      REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
-      REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
-    }
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
 
-    SECTION("scalar function shallow copy")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
 
-      REQUIRE(uh == vh);
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
 
-    SECTION("R^1 function shallow copy")
-    {
-      using DataType     = TinyVector<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2 function shallow copy")
-    {
-      using DataType     = TinyVector<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3 function shallow copy")
-    {
-      using DataType     = TinyVector<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^1x1 function shallow copy")
-    {
-      using DataType     = TinyMatrix<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2x2 function shallow copy")
-    {
-      using DataType     = TinyMatrix<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3x3 function shallow copy")
-    {
-      using DataType     = TinyMatrix<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-      REQUIRE(uh == vh);
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          REQUIRE(uh == vh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
     }
   }
 
@@ -161,144 +167,150 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
   {
     constexpr size_t Dimension = 2;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-    std::shared_ptr mesh_copy =
-      std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    SECTION("common mesh")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
 
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
 
-      REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-      REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
-    }
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-    SECTION("check discretization type")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
-
-      std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-      std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-
-      REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
-      REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
-    }
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
 
-    SECTION("scalar function shallow copy")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
 
-      REQUIRE(uh == vh);
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
 
-    SECTION("R^1 function shallow copy")
-    {
-      using DataType     = TinyVector<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2 function shallow copy")
-    {
-      using DataType     = TinyVector<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3 function shallow copy")
-    {
-      using DataType     = TinyVector<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^1x1 function shallow copy")
-    {
-      using DataType     = TinyMatrix<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2x2 function shallow copy")
-    {
-      using DataType     = TinyMatrix<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3x3 function shallow copy")
-    {
-      using DataType     = TinyMatrix<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
     }
   }
 
@@ -306,144 +318,150 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
   {
     constexpr size_t Dimension = 3;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-    std::shared_ptr mesh_copy =
-      std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    SECTION("common mesh")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
 
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
 
-      REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
-      REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
-    }
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-    SECTION("check discretization type")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-
-      std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
-
-      std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-      std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
-
-      REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
-      REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
-      REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
-      REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
-    }
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
 
-    SECTION("scalar function shallow copy")
-    {
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
 
-      REQUIRE(uh == vh);
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
 
-    SECTION("R^1 function shallow copy")
-    {
-      using DataType     = TinyVector<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2 function shallow copy")
-    {
-      using DataType     = TinyVector<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3 function shallow copy")
-    {
-      using DataType     = TinyVector<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^1x1 function shallow copy")
-    {
-      using DataType     = TinyMatrix<1>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^2x2 function shallow copy")
-    {
-      using DataType     = TinyMatrix<2>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
 
-      REQUIRE(uh == vh);
+          REQUIRE(uh == vh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
-    }
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-    SECTION("R^3x3 function shallow copy")
-    {
-      using DataType     = TinyMatrix<3>;
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
-      std::shared_ptr vh = shallowCopy(mesh, uh);
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
 
-      REQUIRE(uh == vh);
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
 
-      std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
 
-      REQUIRE(uh != wh);
-      REQUIRE(&(uh->cellValues()[CellId{0}]) ==
-              &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
     }
   }
 
@@ -453,13 +471,19 @@ TEST_CASE("DiscreteFunctionUtils", "[scheme]")
     {
       constexpr size_t Dimension = 1;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-      std::shared_ptr other_mesh =
-        CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
+        {
+          std::shared_ptr other_mesh =
+            CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh();
 
-      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
 
-      REQUIRE_THROWS_WITH(shallowCopy(other_mesh, uh), "error: cannot shallow copy when connectivity changes");
+          REQUIRE_THROWS_WITH(shallowCopy(other_mesh, uh), "error: cannot shallow copy when connectivity changes");
+        }
+      }
     }
 
     SECTION("incompatible mesh dimension")
diff --git a/tests/test_DiscreteFunctionVectorInterpoler.cpp b/tests/test_DiscreteFunctionVectorInterpoler.cpp
index 95d5ce5d8401e2c9b5c04002aac17129aa5668e9..c0f28f11409dcf89bd3c8bffbfcff8e8be7fb0a8 100644
--- a/tests/test_DiscreteFunctionVectorInterpoler.cpp
+++ b/tests/test_DiscreteFunctionVectorInterpoler.cpp
@@ -53,296 +53,330 @@ TEST_CASE("DiscreteFunctionVectorInterpoler", "[scheme]")
   {
     constexpr size_t Dimension = 1;
 
-    const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-    auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-    std::string_view data = R"(
+    for (auto [section_name, mesh_1d] : mesh_list) {
+      SECTION(section_name)
+      {
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+        std::string_view data = R"(
 import math;
 let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4);
 let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
 let Z_scalar_non_linear_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1);
 let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
 )";
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-    auto ast = ASTBuilder::build(input);
-
-    ASTModulesImporter{*ast};
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-    ASTSymbolTableBuilder{*ast};
-    ASTNodeDataTypeBuilder{*ast};
-
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-    ASTNodeExpressionBuilder{*ast};
-
-    TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-    position.byte = data.size();   // ensure that variables are declared at this point
-
-    std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-    std::vector<FunctionSymbolId> function_id_list;
-    register_function(position, symbol_table, "B_scalar_non_linear_1d", function_id_list);
-    register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
-    register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
-    register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
-
-    DiscreteFunctionVectorInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
-                                                function_id_list);
-    std::shared_ptr discrete_function = interpoler.interpolate();
-
-    size_t i = 0;
-
-    {
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_1d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
     }
-
-    REQUIRE(i == function_id_list.size());
   }
 
   SECTION("2D")
   {
     constexpr size_t Dimension = 2;
 
-    const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-    auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
-    std::string_view data = R"(
+        std::string_view data = R"(
 import math;
 let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0]) + 3 > 4);
 let N_scalar_non_linear_2d: R^2 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
 let Z_scalar_non_linear_2d: R^2 -> Z, x -> floor(exp(2 * x[1]) - 1);
 let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
 )";
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-    auto ast = ASTBuilder::build(input);
-
-    ASTModulesImporter{*ast};
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-    ASTSymbolTableBuilder{*ast};
-    ASTNodeDataTypeBuilder{*ast};
-
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-    ASTNodeExpressionBuilder{*ast};
-
-    TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-    position.byte = data.size();   // ensure that variables are declared at this point
-
-    std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-    std::vector<FunctionSymbolId> function_id_list;
-    register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
-    register_function(position, symbol_table, "N_scalar_non_linear_2d", function_id_list);
-    register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
-    register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
-
-    DiscreteFunctionVectorInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
-                                                function_id_list);
-    std::shared_ptr discrete_function = interpoler.interpolate();
-
-    size_t i = 0;
-
-    {
-      CellValue<double> cell_value{mesh_2d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_2d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_2d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - 1);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_2d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3;
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - 1);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
     }
-
-    REQUIRE(i == function_id_list.size());
   }
 
   SECTION("3D")
   {
     constexpr size_t Dimension = 3;
 
-    const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-    auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    std::string_view data = R"(
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+        std::string_view data = R"(
 import math;
 let B_scalar_non_linear_3d: R^3 -> B, x -> (exp(2 * x[0] + x[2]) + 3 > 4);
 let N_scalar_non_linear_3d: R^3 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
 let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]);
 let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
 )";
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-    auto ast = ASTBuilder::build(input);
-
-    ASTModulesImporter{*ast};
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-    ASTSymbolTableBuilder{*ast};
-    ASTNodeDataTypeBuilder{*ast};
-
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-    ASTNodeExpressionBuilder{*ast};
-
-    TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-    position.byte = data.size();   // ensure that variables are declared at this point
-
-    std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-    std::vector<FunctionSymbolId> function_id_list;
-    register_function(position, symbol_table, "B_scalar_non_linear_3d", function_id_list);
-    register_function(position, symbol_table, "N_scalar_non_linear_3d", function_id_list);
-    register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
-    register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
-
-    DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
-                                                function_id_list);
-    std::shared_ptr discrete_function = interpoler.interpolate();
-
-    size_t i = 0;
-
-    {
-      CellValue<double> cell_value{mesh_3d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::exp(2 * x[0] + x[2]) + 3 > 4;
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_3d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_3d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - x[2]);
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
-    }
-
-    {
-      CellValue<double> cell_value{mesh_3d->connectivity()};
-      parallel_for(
-        cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3 * x[2];
-        });
-
-      REQUIRE(same_cell_value(cell_value, i++,
-                              dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0] + x[2]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - x[2]);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3 * x[2];
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
     }
-
-    REQUIRE(i == function_id_list.size());
   }
 
   SECTION("errors")
   {
-    const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-    auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    std::string_view data = R"(
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+        std::string_view data = R"(
 import math;
 let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0] + x[1]) + 3 > 4);
 let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
@@ -350,65 +384,67 @@ let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]);
 let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
 let R2_scalar_non_linear_3d: R^3 -> R^2, x -> (2 * exp(x[0] + x[1]) + 3 * x[2], x[0] - x[1]);
 )";
-    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-    auto ast = ASTBuilder::build(input);
+        auto ast = ASTBuilder::build(input);
 
-    ASTModulesImporter{*ast};
-    ASTNodeTypeCleaner<language::import_instruction>{*ast};
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-    ASTSymbolTableBuilder{*ast};
-    ASTNodeDataTypeBuilder{*ast};
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
 
-    ASTNodeTypeCleaner<language::var_declaration>{*ast};
-    ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-    ASTNodeExpressionBuilder{*ast};
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
 
-    TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-    position.byte = data.size();   // ensure that variables are declared at this point
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
 
-    std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-    SECTION("invalid function type")
-    {
-      std::vector<FunctionSymbolId> function_id_list;
-      register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
-      register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
-      register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
-      register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+        SECTION("invalid function type")
+        {
+          std::vector<FunctionSymbolId> function_id_list;
+          register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
+          register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
+          register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
 
-      DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
-                                                  function_id_list);
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                      function_id_list);
 
-      const std::string error_msg = R"(error: invalid function type
+          const std::string error_msg = R"(error: invalid function type
 note: expecting R^3 -> R
 note: provided function B_scalar_non_linear_2d: R^2 -> B)";
 
-      REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
-    }
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
 
-    SECTION("invalid value type")
-    {
-      std::vector<FunctionSymbolId> function_id_list;
-      register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
-      register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
-      register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
+        SECTION("invalid value type")
+        {
+          std::vector<FunctionSymbolId> function_id_list;
+          register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
 
-      DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
-                                                  function_id_list);
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                      function_id_list);
 
-      const std::string error_msg = R"(error: vector functions require scalar value type.
+          const std::string error_msg = R"(error: vector functions require scalar value type.
 Invalid interpolation value type: R^2)";
 
-      REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
-    }
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
 
-    SECTION("invalid discrete function type")
-    {
-      const std::string error_msg = "error: invalid discrete function type for vector interpolation";
+        SECTION("invalid discrete function type")
+        {
+          const std::string error_msg = "error: invalid discrete function type for vector interpolation";
 
-      DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
-      REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+          DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
+      }
     }
   }
 }
diff --git a/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp
index f835d1597389b456f90f3e236eec5c25f78e0475..76dae30b70ce901a94b158df6a04437b1ca1f92b 100644
--- a/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp
+++ b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp
@@ -109,494 +109,504 @@ TEST_CASE("EmbeddedIDiscreteFunctionMathFunctions", "[scheme]")
 
     using Rd = TinyVector<Dimension>;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
-    std::shared_ptr other_mesh =
-      std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-    CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-    CellValue<double> values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> positive_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> bounded_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    std::shared_ptr p_u            = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
-    std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
-    std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
-    std::shared_ptr p_bounded_u  = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
-
-    std::shared_ptr p_R1_u = [=] {
-      CellValue<TinyVector<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R1_v = [=] {
-      CellValue<TinyVector<1>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R1_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-    constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1] + x[2]};
-      }
-    };
-
-    std::shared_ptr p_R2_u = [=] {
-      CellValue<TinyVector<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2_v = [=] {
-      CellValue<TinyVector<2>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R2_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-    constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1], x[0] + x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1], x[2]};
-      }
-    };
-
-    std::shared_ptr p_R3_u = [=] {
-      CellValue<TinyVector<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3_v = [=] {
-      CellValue<TinyVector<3>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R3_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-    std::shared_ptr p_R1x1_u = [=] {
-      CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2x2_u = [=] {
-      CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                         2 * x[1], -x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3x3_u = [=] {
-      CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                         2 * x[1],        -x[0],           x[0] - x[1],   //
-                         3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_Vector3_u = [=] {
-      CellArray<double> uj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj_vector[cell_id][0] = 2 * x[0] + 1;
-          uj_vector[cell_id][1] = 1 - x[1] * x[2];
-          uj_vector[cell_id][2] = x[0] + x[2];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-    }();
-
-    std::shared_ptr p_Vector3_v = [=] {
-      CellArray<double> vj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj_vector[cell_id][0] = x[0] * x[1] + 1;
-          vj_vector[cell_id][1] = 2 * x[1];
-          vj_vector[cell_id][2] = x[2] * x[0];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-    }();
-
-    std::shared_ptr p_Vector2_w = [=] {
-      CellArray<double> wj_vector{mesh->connectivity(), 2};
-      parallel_for(
-        wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          wj_vector[cell_id][0] = x[0] + x[1] * 2;
-          wj_vector[cell_id][1] = x[0] * x[1];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-    }();
-
-    SECTION("sqrt Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
-      REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-    SECTION("abs Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
-      REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                             2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                             2 * x[1],        -x[0],           x[0] - x[1],   //
+                             3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
-      REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
-      REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
-      REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
-      REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
-      REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
-      REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
-      REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
-      REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
-      REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
-      REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
-      REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
-      REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("exp Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
-      REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("log Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
-      REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-      REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-    }
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
 
-    SECTION("atan2 R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
-      REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("min Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
-      REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("min R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
-      REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh -> R")
-    {
-      REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
-      REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
-      REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("max Vh -> R")
-    {
-      REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("max R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
-      REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
-      REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
-      REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("pow R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
-      REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("dot Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-      {
-        auto p_UV = dot(p_Vector3_u, p_Vector3_v);
-        REQUIRE(p_UV.use_count() == 1);
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
 
-        auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
-        auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
 
-        bool is_same = true;
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          if (UV[cell_id] != direct_UV[cell_id]) {
-            is_same = false;
-            break;
-          }
-        }
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
 
-        REQUIRE(is_same);
-      }
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
 
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
-    }
+            REQUIRE(is_same);
+          }
 
-    SECTION("dot Vh*Rd -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
-      REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
-                          "error: incompatible operand types Vh(P0:R^2) and R^3");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
-    }
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
 
-    SECTION("dot Rd*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
-      REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
-                          "error: incompatible operand types R^3 and Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
-    }
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
 
-    SECTION("sum_of_R* Vh -> R*")
-    {
-      REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
-
-      REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
-    }
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
 
-    SECTION("integral_of_R* Vh -> R*")
-    {
-      auto integrate_locally = [&](const auto& cell_values) {
-        const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
-        using DataType = decltype(double{} * cell_values[CellId{0}]);
-        CellValue<DataType> local_integral{mesh->connectivity()};
-        parallel_for(
-          local_integral.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
-        return local_integral;
-      };
-
-      REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
-
-      REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
     }
   }
 
@@ -606,494 +616,504 @@ TEST_CASE("EmbeddedIDiscreteFunctionMathFunctions", "[scheme]")
 
     using Rd = TinyVector<Dimension>;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
-    std::shared_ptr other_mesh =
-      std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-    CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-    CellValue<double> values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> positive_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> bounded_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    std::shared_ptr p_u            = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
-    std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
-    std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
-    std::shared_ptr p_bounded_u  = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
-
-    std::shared_ptr p_R1_u = [=] {
-      CellValue<TinyVector<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R1_v = [=] {
-      CellValue<TinyVector<1>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R1_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-    constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1] + x[2]};
-      }
-    };
-
-    std::shared_ptr p_R2_u = [=] {
-      CellValue<TinyVector<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2_v = [=] {
-      CellValue<TinyVector<2>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R2_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-    constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1], x[0] + x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1], x[2]};
-      }
-    };
-
-    std::shared_ptr p_R3_u = [=] {
-      CellValue<TinyVector<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3_v = [=] {
-      CellValue<TinyVector<3>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R3_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-    std::shared_ptr p_R1x1_u = [=] {
-      CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2x2_u = [=] {
-      CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                         2 * x[1], -x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3x3_u = [=] {
-      CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                         2 * x[1],        -x[0],           x[0] - x[1],   //
-                         3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_Vector3_u = [=] {
-      CellArray<double> uj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj_vector[cell_id][0] = 2 * x[0] + 1;
-          uj_vector[cell_id][1] = 1 - x[1] * x[2];
-          uj_vector[cell_id][2] = x[0] + x[2];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-    }();
-
-    std::shared_ptr p_Vector3_v = [=] {
-      CellArray<double> vj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj_vector[cell_id][0] = x[0] * x[1] + 1;
-          vj_vector[cell_id][1] = 2 * x[1];
-          vj_vector[cell_id][2] = x[2] * x[0];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-    }();
-
-    std::shared_ptr p_Vector2_w = [=] {
-      CellArray<double> wj_vector{mesh->connectivity(), 2};
-      parallel_for(
-        wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          wj_vector[cell_id][0] = x[0] + x[1] * 2;
-          wj_vector[cell_id][1] = x[0] * x[1];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-    }();
-
-    SECTION("sqrt Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
-      REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    SECTION("abs Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
-      REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                             2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                             2 * x[1],        -x[0],           x[0] - x[1],   //
+                             3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
-      REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
-      REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
-      REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
-      REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
-      REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
-      REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
-      REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
-      REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
-      REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
-      REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
-      REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
-      REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("exp Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
-      REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("log Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
-      REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-      REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-    }
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
 
-    SECTION("atan2 R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
-      REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("min Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
-      REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("min R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
-      REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh -> R")
-    {
-      REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
-      REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
-      REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("max Vh -> R")
-    {
-      REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("max R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
-      REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
-      REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
-      REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("pow R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
-      REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("dot Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-      {
-        auto p_UV = dot(p_Vector3_u, p_Vector3_v);
-        REQUIRE(p_UV.use_count() == 1);
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
 
-        auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
-        auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
 
-        bool is_same = true;
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          if (UV[cell_id] != direct_UV[cell_id]) {
-            is_same = false;
-            break;
-          }
-        }
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
 
-        REQUIRE(is_same);
-      }
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
 
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
-    }
+            REQUIRE(is_same);
+          }
 
-    SECTION("dot Vh*Rd -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
-      REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
-                          "error: incompatible operand types Vh(P0:R^2) and R^3");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
-    }
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
 
-    SECTION("dot Rd*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
-      REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
-                          "error: incompatible operand types R^3 and Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
-    }
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
 
-    SECTION("sum_of_R* Vh -> R*")
-    {
-      REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
-
-      REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
-    }
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
+
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
 
-    SECTION("integral_of_R* Vh -> R*")
-    {
-      auto integrate_locally = [&](const auto& cell_values) {
-        const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
-        using DataType = decltype(double{} * cell_values[CellId{0}]);
-        CellValue<DataType> local_integral{mesh->connectivity()};
-        parallel_for(
-          local_integral.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
-        return local_integral;
-      };
-
-      REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
-
-      REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
     }
   }
 
@@ -1103,494 +1123,504 @@ TEST_CASE("EmbeddedIDiscreteFunctionMathFunctions", "[scheme]")
 
     using Rd = TinyVector<Dimension>;
 
-    std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
-    std::shared_ptr other_mesh =
-      std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-    CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-    CellValue<double> values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> positive_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    CellValue<double> bounded_values = [=] {
-      CellValue<double> build_values{mesh->connectivity()};
-      parallel_for(
-        build_values.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
-      return build_values;
-    }();
-
-    std::shared_ptr p_u            = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
-    std::shared_ptr p_other_mesh_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
-    std::shared_ptr p_positive_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
-    std::shared_ptr p_bounded_u  = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
-
-    std::shared_ptr p_R1_u = [=] {
-      CellValue<TinyVector<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R1_v = [=] {
-      CellValue<TinyVector<1>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(),
-        PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R1_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-    constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1] + x[2]};
-      }
-    };
-
-    std::shared_ptr p_R2_u = [=] {
-      CellValue<TinyVector<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2_v = [=] {
-      CellValue<TinyVector<2>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R2_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-    constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-      if constexpr (Dimension == 1) {
-        return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-      } else if constexpr (Dimension == 2) {
-        return {x[0], x[1], x[0] + x[1]};
-      } else if constexpr (Dimension == 3) {
-        return {x[0], x[1], x[2]};
-      }
-    };
-
-    std::shared_ptr p_R3_u = [=] {
-      CellValue<TinyVector<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3_v = [=] {
-      CellValue<TinyVector<3>> vj{mesh->connectivity()};
-      parallel_for(
-        vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-    }();
-
-    std::shared_ptr p_other_mesh_R3_u =
-      std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-    std::shared_ptr p_R1x1_u = [=] {
-      CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R2x2_u = [=] {
-      CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<2> x = to_2d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                         2 * x[1], -x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_R3x3_u = [=] {
-      CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-      parallel_for(
-        uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-
-          uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                         2 * x[1],        -x[0],           x[0] - x[1],   //
-                         3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-        });
-
-      return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-    }();
-
-    std::shared_ptr p_Vector3_u = [=] {
-      CellArray<double> uj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          uj_vector[cell_id][0] = 2 * x[0] + 1;
-          uj_vector[cell_id][1] = 1 - x[1] * x[2];
-          uj_vector[cell_id][2] = x[0] + x[2];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-    }();
-
-    std::shared_ptr p_Vector3_v = [=] {
-      CellArray<double> vj_vector{mesh->connectivity(), 3};
-      parallel_for(
-        vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          vj_vector[cell_id][0] = x[0] * x[1] + 1;
-          vj_vector[cell_id][1] = 2 * x[1];
-          vj_vector[cell_id][2] = x[2] * x[0];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-    }();
-
-    std::shared_ptr p_Vector2_w = [=] {
-      CellArray<double> wj_vector{mesh->connectivity(), 2};
-      parallel_for(
-        wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<3> x = to_3d(xj[cell_id]);
-          wj_vector[cell_id][0] = x[0] + x[1] * 2;
-          wj_vector[cell_id][1] = x[0] * x[1];
-        });
-
-      return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-    }();
-
-    SECTION("sqrt Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
-      REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    SECTION("abs Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
-      REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+    for (auto [section_name, mesh] : mesh_list) {
+      SECTION(section_name)
+      {
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return {x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return {x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                             2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                             2 * x[1],        -x[0],           x[0] - x[1],   //
+                             3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
-      REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
-      REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
-      REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asin Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
-      REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acos Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
-      REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
-      REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("sinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
-      REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("cosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
-      REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("tanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
-      REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("asinh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
-      REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("acosh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
-      REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atanh Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
-      REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("exp Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
-      REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("log Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
-      REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-      REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-    }
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("atan2 Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
-      REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
 
-    SECTION("atan2 R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
-      REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
-      REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("min Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
-      REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("min R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
-      REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("min Vh -> R")
-    {
-      REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
-      REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("max Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
-      REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("max Vh -> R")
-    {
-      REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
-      REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
-                          "error: invalid operand type Vh(P0:R^1)");
-    }
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("max R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
-      REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
-      REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
-    }
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-    SECTION("pow Vh*R -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
-      REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
-    }
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
 
-    SECTION("pow R*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
-      REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
-    }
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
 
-    SECTION("dot Vh*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
-      CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
 
-      {
-        auto p_UV = dot(p_Vector3_u, p_Vector3_v);
-        REQUIRE(p_UV.use_count() == 1);
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
 
-        auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
-        auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
 
-        bool is_same = true;
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          if (UV[cell_id] != direct_UV[cell_id]) {
-            is_same = false;
-            break;
-          }
-        }
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
 
-        REQUIRE(is_same);
-      }
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
 
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
-      REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
-    }
+            REQUIRE(is_same);
+          }
 
-    SECTION("dot Vh*Rd -> Vh")
-    {
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
-      CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
-      REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})), "error: incompatible operand types Vh(P0:R^1) and R^2");
-      REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
-                          "error: incompatible operand types Vh(P0:R^2) and R^3");
-      REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
-    }
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
 
-    SECTION("dot Rd*Vh -> Vh")
-    {
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
-      CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
-      REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u), "error: incompatible operand types R^2 and Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
-                          "error: incompatible operand types R^3 and Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
-    }
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
 
-    SECTION("sum_of_R* Vh -> R*")
-    {
-      REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
-      REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
-      REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
-
-      REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
-    }
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
 
-    SECTION("integral_of_R* Vh -> R*")
-    {
-      auto integrate_locally = [&](const auto& cell_values) {
-        const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
-        using DataType = decltype(double{} * cell_values[CellId{0}]);
-        CellValue<DataType> local_integral{mesh->connectivity()};
-        parallel_for(
-          local_integral.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
-        return local_integral;
-      };
-
-      REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
-      REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
-      REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
-
-      REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
-      REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
     }
   }
 }
diff --git a/tests/test_EmbeddedIDiscreteFunctionOperators.cpp b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp
index 0b0db97f811afb1682679240d7d2240986f2d4d1..a20047a27a420027aff9a39fa24c3189e1a72b0b 100644
--- a/tests/test_EmbeddedIDiscreteFunctionOperators.cpp
+++ b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp
@@ -195,668 +195,691 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      CellValue<double> v_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
-      std::shared_ptr p_other_mesh_R_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
-      std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
-
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1_v = [=] {
-        CellValue<TinyVector<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2_v = [=] {
-        CellValue<TinyVector<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3_v = [=] {
-        CellValue<TinyVector<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1x1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
-
-      std::shared_ptr p_R1x1_v = [=] {
-        CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2x2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
-
-      std::shared_ptr p_R2x2_v = [=] {
-        CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
-                           2 * x[1] + x[0], x[1] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3x3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
-
-      std::shared_ptr p_R3x3_v = [=] {
-        CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
-                           2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
-                           2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      std::shared_ptr p_other_mesh_Vector3_u =
-        std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
-
-      std::shared_ptr p_Vector3_v = [=] {
-        CellArray<double> vj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj_vector[cell_id][0] = x[0] * x[1] + 1;
-            vj_vector[cell_id][1] = 2 * x[1];
-            vj_vector[cell_id][2] = x[2] * x[0];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-      }();
-
-      std::shared_ptr p_Vector2_w = [=] {
-        CellArray<double> wj_vector{mesh->connectivity(), 2};
-        parallel_for(
-          wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            wj_vector[cell_id][0] = x[0] + x[1] * 2;
-            wj_vector[cell_id][1] = x[0] * x[1];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-      }();
-
-      SECTION("sum")
-      {
-        SECTION("Vh + Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh + X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
-
-        SECTION("X + Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  +, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
-
-      SECTION("difference")
-      {
-        SECTION("Vh - Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh - X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        SECTION("X - Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  -, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
-
-      SECTION("product")
-      {
-        SECTION("Vh * Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
+                               2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                               2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                               2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
+          SECTION("difference")
           {
-            std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-            REQUIRE(p_fuv.use_count() > 0);
-            REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-            const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
-            auto lhs_values = p_R_u->cellValues();
-            auto rhs_arrays = p_Vector3_v->cellArrays();
-            bool is_same    = true;
-            for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
-              for (size_t i = 0; i < fuv.size(); ++i) {
-                if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
-                  is_same = false;
-                  break;
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
                 }
-              }
-            }
 
-            REQUIRE(is_same);
-          }
+                REQUIRE(is_same);
+              }
 
-          REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
-
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
-
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("Vh * X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3");
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^2) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3x3");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
-          REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
-        }
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
 
-        SECTION("X * Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3_u);
 
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3x3_u);
 
-          CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
-        }
-      }
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
 
-      SECTION("ratio")
-      {
-        SECTION("Vh / Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
 
-          REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
 
-          REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("X / Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
         }
       }
     }
@@ -867,668 +890,691 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      CellValue<double> v_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
-      std::shared_ptr p_other_mesh_R_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
-      std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
-
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1_v = [=] {
-        CellValue<TinyVector<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2_v = [=] {
-        CellValue<TinyVector<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3_v = [=] {
-        CellValue<TinyVector<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1x1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
-
-      std::shared_ptr p_R1x1_v = [=] {
-        CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2x2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
-
-      std::shared_ptr p_R2x2_v = [=] {
-        CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
-                           2 * x[1] + x[0], x[1] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3x3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
-
-      std::shared_ptr p_R3x3_v = [=] {
-        CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
-                           2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
-                           2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      std::shared_ptr p_other_mesh_Vector3_u =
-        std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
-
-      std::shared_ptr p_Vector3_v = [=] {
-        CellArray<double> vj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj_vector[cell_id][0] = x[0] * x[1] + 1;
-            vj_vector[cell_id][1] = 2 * x[1];
-            vj_vector[cell_id][2] = x[2] * x[0];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-      }();
-
-      std::shared_ptr p_Vector2_w = [=] {
-        CellArray<double> wj_vector{mesh->connectivity(), 2};
-        parallel_for(
-          wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            wj_vector[cell_id][0] = x[0] + x[1] * 2;
-            wj_vector[cell_id][1] = x[0] * x[1];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-      }();
-
-      SECTION("sum")
-      {
-        SECTION("Vh + Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh + X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
-
-        SECTION("X + Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  +, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
-
-      SECTION("difference")
-      {
-        SECTION("Vh - Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh - X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
-
-        SECTION("X - Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  -, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      SECTION("product")
-      {
-        SECTION("Vh * Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
+                               2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                               2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                               2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
+          SECTION("difference")
           {
-            std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-            REQUIRE(p_fuv.use_count() > 0);
-            REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-            const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
-            auto lhs_values = p_R_u->cellValues();
-            auto rhs_arrays = p_Vector3_v->cellArrays();
-            bool is_same    = true;
-            for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
-              for (size_t i = 0; i < fuv.size(); ++i) {
-                if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
-                  is_same = false;
-                  break;
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
                 }
-              }
-            }
 
-            REQUIRE(is_same);
-          }
+                REQUIRE(is_same);
+              }
 
-          REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
-
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
-
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("Vh * X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3");
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^2) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3x3");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
-          REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
-        }
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
 
-        SECTION("X * Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3_u);
 
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3x3_u);
 
-          CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
-        }
-      }
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
 
-      SECTION("ratio")
-      {
-        SECTION("Vh / Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
 
-          REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
 
-          REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("X / Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
         }
       }
     }
@@ -1539,668 +1585,691 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
-
-      std::shared_ptr other_mesh =
-        std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
-
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      CellValue<double> v_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
-      std::shared_ptr p_other_mesh_R_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
-      std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
-
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1_v = [=] {
-        CellValue<TinyVector<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
-
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2_v = [=] {
-        CellValue<TinyVector<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3_v = [=] {
-        CellValue<TinyVector<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R1x1_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
-
-      std::shared_ptr p_R1x1_v = [=] {
-        CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R2x2_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
-
-      std::shared_ptr p_R2x2_v = [=] {
-        CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
-                           2 * x[1] + x[0], x[1] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_other_mesh_R3x3_u =
-        std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
-
-      std::shared_ptr p_R3x3_v = [=] {
-        CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
-        parallel_for(
-          vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
-                           2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
-                           2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      std::shared_ptr p_other_mesh_Vector3_u =
-        std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
-
-      std::shared_ptr p_Vector3_v = [=] {
-        CellArray<double> vj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            vj_vector[cell_id][0] = x[0] * x[1] + 1;
-            vj_vector[cell_id][1] = 2 * x[1];
-            vj_vector[cell_id][2] = x[2] * x[0];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
-      }();
-
-      std::shared_ptr p_Vector2_w = [=] {
-        CellArray<double> wj_vector{mesh->connectivity(), 2};
-        parallel_for(
-          wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            wj_vector[cell_id][0] = x[0] + x[1] * 2;
-            wj_vector[cell_id][1] = x[0] * x[1];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
-      }();
-
-      SECTION("sum")
-      {
-        SECTION("Vh + Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh + X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        SECTION("X + Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  +, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
-
-      SECTION("difference")
-      {
-        SECTION("Vh - Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
-
-          CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
-
-          REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
-
-          REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
-
-        SECTION("Vh - X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}), "error: incompatible operand types Vh(P0:R) and R^3");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^1");
-          REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^2");
-        }
-
-        SECTION("X - Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}),
-                                  -, p_R3x3_u);
-
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u, "error: incompatible operand types R^3 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
-                              "error: incompatible operand types R^1 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
-                              "error: incompatible operand types R^2 and Vh(P0Vector:R)");
-        }
-      }
-
-      SECTION("product")
-      {
-        SECTION("Vh * Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
-
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = {x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = {0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = {x[0] + 0.3, 1 - x[1] - x[0],   //
+                               2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = {0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                               2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                               2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-          CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
-          CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
+          SECTION("difference")
           {
-            std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
 
-            REQUIRE(p_fuv.use_count() > 0);
-            REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
 
-            const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
 
-            auto lhs_values = p_R_u->cellValues();
-            auto rhs_arrays = p_Vector3_v->cellArrays();
-            bool is_same    = true;
-            for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
-              for (size_t i = 0; i < fuv.size(); ++i) {
-                if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
-                  is_same = false;
-                  break;
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
                 }
-              }
-            }
 
-            REQUIRE(is_same);
-          }
+                REQUIRE(is_same);
+              }
 
-          REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
-
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
-                              "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
-
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
-          REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("Vh * X -> Vh")
-        {
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
-          CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
-
-          CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
-          CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
-                                  (TinyMatrix<3>{3.2, 7.1, 5.2,   //
-                                                 4.7, 2.3, 7.1,   //
-                                                 9.7, 3.2, 6.8}));
-
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R^2) and R^2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3");
-          REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R^1) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^2) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^3) and R^3x3");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
-          REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
-                              "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
-          REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
-
-          REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
-                              "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
-          REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
-        }
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
 
-        SECTION("X * Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
-
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
-
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3_u);
 
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
-          CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
                                                                      4.7, 2.3, 7.1,   //
                                                                      9.7, 3.2, 6.8}),
                                                       *, p_R3x3_u);
 
-          CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
-          CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u, "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
-          REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
-                              "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
-
-          REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
-                              "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
-          REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
-                              "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
-        }
-      }
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
 
-      SECTION("ratio")
-      {
-        SECTION("Vh / Vh -> Vh")
-        {
-          CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
 
-          REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
-          REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
-          REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
 
-          REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
-        }
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
 
-        SECTION("X / Vh -> Vh")
-        {
-          CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
-          CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
         }
       }
     }
@@ -2214,133 +2283,138 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
-
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      SECTION("unary minus")
-      {
-        SECTION("- Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
 
-          CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
         }
       }
     }
@@ -2351,133 +2425,138 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian2DMesh();
-
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
-
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      SECTION("unary minus")
-      {
-        SECTION("- Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
 
-          CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
         }
       }
     }
@@ -2488,133 +2567,138 @@ TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
 
       using Rd = TinyVector<Dimension>;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesian3DMesh();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      CellValue<double> u_R_values = [=] {
-        CellValue<double> build_values{mesh->connectivity()};
-        parallel_for(
-          build_values.numberOfItems(),
-          PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
-        return build_values;
-      }();
-
-      std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
-
-      std::shared_ptr p_R1_u = [=] {
-        CellValue<TinyVector<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
-      }();
-
-      constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1] + x[2]};
-        }
-      };
-
-      std::shared_ptr p_R2_u = [=] {
-        CellValue<TinyVector<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
-      }();
-
-      constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
-        if constexpr (Dimension == 1) {
-          return {x[0], 1 + x[0] * x[0], 2 - x[0]};
-        } else if constexpr (Dimension == 2) {
-          return {x[0], x[1], x[0] + x[1]};
-        } else if constexpr (Dimension == 3) {
-          return {x[0], x[1], x[2]};
-        }
-      };
-
-      std::shared_ptr p_R3_u = [=] {
-        CellValue<TinyVector<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R1x1_u = [=] {
-        CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R2x2_u = [=] {
-        CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<2> x = to_2d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
-                           2 * x[1], -x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_R3x3_u = [=] {
-        CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
-        parallel_for(
-          uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-
-            uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
-                           2 * x[1],        -x[0],           x[0] - x[1],   //
-                           3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
-          });
-
-        return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
-      }();
-
-      std::shared_ptr p_Vector3_u = [=] {
-        CellArray<double> uj_vector{mesh->connectivity(), 3};
-        parallel_for(
-          uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<3> x = to_3d(xj[cell_id]);
-            uj_vector[cell_id][0] = 2 * x[0] + 1;
-            uj_vector[cell_id][1] = 1 - x[1] * x[2];
-            uj_vector[cell_id][2] = x[0] + x[2];
-          });
-
-        return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
-      }();
-
-      SECTION("unary minus")
-      {
-        SECTION("- Vh -> Vh")
+      for (auto [section_name, mesh] : mesh_list) {
+        SECTION(section_name)
         {
-          CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return {x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return {x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return {x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = {2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = {2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1, 1 - x[1],   //
+                               2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = {2 * x[0] + 1,    1 - x[1],        3,             //
+                               2 * x[1],        -x[0],           x[0] - x[1],   //
+                               3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
 
-          CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
-          CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
 
-          CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
         }
       }
     }
diff --git a/tests/test_EmbeddedIDiscreteFunctionUtils.cpp b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp
index b0227a356f067992a6451266e92833222c50a8fb..8a64813728fcc205ea09d26af27eb6bca976e9d5 100644
--- a/tests/test_EmbeddedIDiscreteFunctionUtils.cpp
+++ b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp
@@ -29,28 +29,42 @@ TEST_CASE("EmbeddedIDiscreteFunctionUtils", "[language]")
 
     SECTION("discrete P0 function")
     {
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, double>{mesh_1d}) == "Vh(P0:R)");
-
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1>{mesh_1d}) == "Vh(P0:R^1)");
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2>{mesh_1d}) == "Vh(P0:R^2)");
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3>{mesh_1d}) == "Vh(P0:R^3)");
-
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1x1>{mesh_1d}) ==
-              "Vh(P0:R^1x1)");
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2x2>{mesh_1d}) ==
-              "Vh(P0:R^2x2)");
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3x3>{mesh_1d}) ==
-              "Vh(P0:R^3x3)");
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, double>{mesh_1d}) ==
+                  "Vh(P0:R)");
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1>{mesh_1d}) ==
+                  "Vh(P0:R^1)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2>{mesh_1d}) ==
+                  "Vh(P0:R^2)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3>{mesh_1d}) ==
+                  "Vh(P0:R^3)");
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1x1>{mesh_1d}) ==
+                  "Vh(P0:R^1x1)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2x2>{mesh_1d}) ==
+                  "Vh(P0:R^2x2)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3x3>{mesh_1d}) ==
+                  "Vh(P0:R^3x3)");
+        }
+      }
     }
 
     SECTION("discrete P0Vector function")
     {
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<1, double>{mesh_1d, 2}) ==
-              "Vh(P0Vector:R)");
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<1, double>{mesh_1d, 2}) ==
+                  "Vh(P0Vector:R)");
+        }
+      }
     }
   }
 
@@ -58,59 +72,77 @@ TEST_CASE("EmbeddedIDiscreteFunctionUtils", "[language]")
   {
     SECTION("from shared_ptr")
     {
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      REQUIRE(
-        EmbeddedIDiscreteFunctionUtils::isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d),
-                                                             std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d)));
-
-      REQUIRE(not EmbeddedIDiscreteFunctionUtils::
-                isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d),
-                                     std::make_shared<DiscreteFunctionP0Vector<1, double>>(mesh_1d, 1)));
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(
+                                                                         mesh_1d),
+                                                                       std::make_shared<DiscreteFunctionP0<1, double>>(
+                                                                         mesh_1d)));
+
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::
+                    isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d),
+                                         std::make_shared<DiscreteFunctionP0Vector<1, double>>(mesh_1d, 1)));
+        }
+      }
     }
 
     SECTION("from value")
     {
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, double>{mesh_1d}));
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, double>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R1>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R1>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R2>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R2>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R3>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R3>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1x1>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R1x1>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1x1>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R1x1>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2x2>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2x2>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R2x2>{mesh_1d}));
 
-      REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
-                                                                   DiscreteFunctionP0<1, R3x3>{mesh_1d}));
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R3x3>{mesh_1d}));
 
-      REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
-                                                                       DiscreteFunctionP0<1, R1>{mesh_1d}));
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R1>{mesh_1d}));
 
-      REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
-                                                                       DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R2x2>{mesh_1d}));
 
-      REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
-                                                                       DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+        }
+      }
     }
 
     SECTION("invalid data type")
     {
-      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-
-      REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, int64_t>{mesh_1d},
-                                                                               DiscreteFunctionP0<1, int64_t>{mesh_1d}),
-                          "unexpected error: invalid data type Vh(P0:Z)");
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1,
+                                                                                                      int64_t>{mesh_1d},
+                                                                                   DiscreteFunctionP0<1, int64_t>{
+                                                                                     mesh_1d}),
+                              "unexpected error: invalid data type Vh(P0:Z)");
+        }
+      }
     }
   }
 
diff --git a/tests/test_InterpolateItemArray.cpp b/tests/test_InterpolateItemArray.cpp
index 90047e886f72dfe27b56fce2320e772a43f18adf..df01d8130ccfc52d1b69c67722bc5c5c06a3be00 100644
--- a/tests/test_InterpolateItemArray.cpp
+++ b/tests/test_InterpolateItemArray.cpp
@@ -46,195 +46,213 @@ TEST_CASE("InterpolateItemArray", "[language]")
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      std::string_view data = R"(
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_1d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 2;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) + 3;
-        });
+          CellArray<double> cell_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 2;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("2D")
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_2d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) + 3;
-        });
+          CellArray<double> cell_array{mesh_2d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("3D")
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_3d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-        });
+          CellArray<double> cell_array{mesh_3d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
   }
 
@@ -255,213 +273,231 @@ let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      Array<const CellId> cell_id_list = [&] {
-        Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
-        for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
-          cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
-        }
-        return cell_ids;
-      }();
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          Array<const CellId> cell_id_list = [&] {
+            Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
+            for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
+              cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
+            }
+            return cell_ids;
+          }();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
   import math;
   let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
   let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
   )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 2;
-          cell_array[i][1]               = 2 * exp(x[0]) + 3;
-        });
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 2;
+              cell_array[i][1]               = 2 * exp(x[0]) + 3;
+            });
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("2D")
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
-      std::string_view data = R"(
+          Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2;
-          cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) + 3;
-        });
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2;
+              cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) + 3;
+            });
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("3D")
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
-      std::string_view data = R"(
+          Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-        });
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+            });
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
   }
 }
diff --git a/tests/test_InterpolateItemValue.cpp b/tests/test_InterpolateItemValue.cpp
index 0cb4621874bc837b4d397920a048b6b7da2de083..202eee282d2a65d3f2d8eb0f1a771f14115ed94b 100644
--- a/tests/test_InterpolateItemValue.cpp
+++ b/tests/test_InterpolateItemValue.cpp
@@ -43,10 +43,14 @@ TEST_CASE("InterpolateItemValue", "[language]")
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      std::string_view data = R"(
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
@@ -55,149 +59,152 @@ let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
 let R2x2_affine_1d: R^1 -> R^2x2, x -> (2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2);
 let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          auto ast = ASTBuilder::build(input);
 
-      SECTION("scalar_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-        CellValue<double> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 2;
-          });
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-        CellValue<double> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) + 3;
-          });
-
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("scalar_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R3_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<double> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 2;
+              });
 
-        CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
-          });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("scalar_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R3_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<double> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) + 3;
+              });
 
-        CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
-          });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("R3_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R2x2_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
+              });
 
-        CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
-          });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("R3_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R2x2_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
+              });
 
-        CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
-          });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          SECTION("R2x2_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
+              });
+
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] =
+                  TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              });
+
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -205,10 +212,14 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
@@ -217,143 +228,146 @@ let R3_non_linear_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3)
 let R2x2_affine_2d: R^2 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2);
 let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) + 3;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) + 3;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] =
+                  TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -361,10 +375,14 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      std::string_view data = R"(
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
@@ -373,145 +391,147 @@ let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[
 let R2x2_affine_3d: R^3 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2);
 let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] =
-              TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]),
-                                                3, x[0] * x[1] * x[2]};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2],
+                                                    2 * x[0] + x[1] + x[2], 2};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]),
+                                                    sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
   }
@@ -532,18 +552,22 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      Array<const CellId> cell_id_list = [&] {
-        Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
-        for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
-          cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
-        }
-        return cell_ids;
-      }();
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
-      std::string_view data = R"(
+          Array<const CellId> cell_id_list = [&] {
+            Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
+            for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
+              cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
+            }
+            return cell_ids;
+          }();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
@@ -552,149 +576,155 @@ let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
 let R2x2_affine_1d: R^1 -> R^2x2, x -> (2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2);
 let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          auto ast = ASTBuilder::build(input);
 
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 2;
-          });
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) + 3;
-          });
-
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
-          });
-
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
-          });
-
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
-          });
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+          SECTION("scalar_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      SECTION("R2x2_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 2;
+              });
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
 
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
-          });
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) + 3;
+              });
+
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
+              });
+
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
+              });
+
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
+              });
+
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              });
+
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -702,15 +732,19 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+          Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
@@ -719,144 +753,150 @@ let R3_non_linear_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3)
 let R2x2_affine_2d: R^2 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2);
 let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2;
-          });
-
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) + 3;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2;
+              });
+
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) + 3;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -864,15 +904,19 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesian3DMesh();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
-      std::string_view data = R"(
+          Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
@@ -881,145 +925,151 @@ let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[
 let R2x2_affine_3d: R^3 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2);
 let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] =
-              TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
-                                          x[0] * x[1] * x[2]};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2],
+                                              2 * x[0] + x[1] + x[2], 2};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                                              x[0] * x[1] * x[2]};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_cell_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
   }
diff --git a/tests/test_ItemArray.cpp b/tests/test_ItemArray.cpp
index 2cb868465d8b7144a9b325a364afd43fbde2d52f..c8a4bd8555f3b75e4b3179474142208507159b80 100644
--- a/tests/test_ItemArray.cpp
+++ b/tests/test_ItemArray.cpp
@@ -28,118 +28,142 @@ TEST_CASE("ItemArray", "[mesh]")
 
   SECTION("1D")
   {
-    const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-    const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
-
-    REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 3};
-    EdgeArray<int> edge_value{connectivity, 3};
-    FaceArray<int> face_value{connectivity, 3};
-    CellArray<int> cell_value{connectivity, 3};
-
-    REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
-
-    REQUIRE(node_value.sizeOfArrays() == 3);
-    REQUIRE(edge_value.sizeOfArrays() == 3);
-    REQUIRE(face_value.sizeOfArrays() == 3);
-    REQUIRE(cell_value.sizeOfArrays() == 3);
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto [section_name, mesh_1d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
+
+        REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 3};
+        EdgeArray<int> edge_value{connectivity, 3};
+        FaceArray<int> face_value{connectivity, 3};
+        CellArray<int> cell_value{connectivity, 3};
+
+        REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+
+        REQUIRE(node_value.sizeOfArrays() == 3);
+        REQUIRE(edge_value.sizeOfArrays() == 3);
+        REQUIRE(face_value.sizeOfArrays() == 3);
+        REQUIRE(cell_value.sizeOfArrays() == 3);
+      }
+    }
   }
 
   SECTION("2D")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 2});
-
-    REQUIRE(NodeArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 2}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 2};
-    EdgeArray<int> edge_value{connectivity, 2};
-    FaceArray<int> face_value{connectivity, 2};
-    CellArray<int> cell_value{connectivity, 2};
-
-    REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
-
-    REQUIRE(node_value.sizeOfArrays() == 2);
-    REQUIRE(edge_value.sizeOfArrays() == 2);
-    REQUIRE(face_value.sizeOfArrays() == 2);
-    REQUIRE(cell_value.sizeOfArrays() == 2);
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 2});
+
+        REQUIRE(NodeArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 2}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 2};
+        EdgeArray<int> edge_value{connectivity, 2};
+        FaceArray<int> face_value{connectivity, 2};
+        CellArray<int> cell_value{connectivity, 2};
+
+        REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+
+        REQUIRE(node_value.sizeOfArrays() == 2);
+        REQUIRE(edge_value.sizeOfArrays() == 2);
+        REQUIRE(face_value.sizeOfArrays() == 2);
+        REQUIRE(cell_value.sizeOfArrays() == 2);
+      }
+    }
   }
 
   SECTION("3D")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
-
-    REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 3};
-    EdgeArray<int> edge_value{connectivity, 3};
-    FaceArray<int> face_value{connectivity, 3};
-    CellArray<int> cell_value{connectivity, 3};
-
-    REQUIRE(node_value.sizeOfArrays() == 3);
-    REQUIRE(edge_value.sizeOfArrays() == 3);
-    REQUIRE(face_value.sizeOfArrays() == 3);
-    REQUIRE(cell_value.sizeOfArrays() == 3);
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
+
+        REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 3};
+        EdgeArray<int> edge_value{connectivity, 3};
+        FaceArray<int> face_value{connectivity, 3};
+        CellArray<int> cell_value{connectivity, 3};
+
+        REQUIRE(node_value.sizeOfArrays() == 3);
+        REQUIRE(edge_value.sizeOfArrays() == 3);
+        REQUIRE(face_value.sizeOfArrays() == 3);
+        REQUIRE(cell_value.sizeOfArrays() == 3);
+      }
+    }
   }
 
   SECTION("set values from array")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    CellArray<size_t> cell_array{connectivity, 3};
-
-    Table<size_t> table{cell_array.numberOfItems(), cell_array.sizeOfArrays()};
-    {
-      size_t k = 0;
-      for (size_t i = 0; i < table.numberOfRows(); ++i) {
-        for (size_t j = 0; j < table.numberOfColumns(); ++j) {
-          table(i, j) = k++;
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        CellArray<size_t> cell_array{connectivity, 3};
+
+        Table<size_t> table{cell_array.numberOfItems(), cell_array.sizeOfArrays()};
+        {
+          size_t k = 0;
+          for (size_t i = 0; i < table.numberOfRows(); ++i) {
+            for (size_t j = 0; j < table.numberOfColumns(); ++j) {
+              table(i, j) = k++;
+            }
+          }
         }
+        cell_array = table;
+
+        auto is_same = [](const CellArray<size_t>& cell_array, const Table<size_t>& table) {
+          bool is_same = true;
+          for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) {
+            Array sub_array = cell_array[cell_id];
+            for (size_t i = 0; i < sub_array.size(); ++i) {
+              is_same &= (sub_array[i] == table(cell_id, i));
+            }
+          }
+          return is_same;
+        };
+
+        REQUIRE(is_same(cell_array, table));
       }
     }
-    cell_array = table;
-
-    auto is_same = [](const CellArray<size_t>& cell_array, const Table<size_t>& table) {
-      bool is_same = true;
-      for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) {
-        Array sub_array = cell_array[cell_id];
-        for (size_t i = 0; i < sub_array.size(); ++i) {
-          is_same &= (sub_array[i] == table(cell_id, i));
-        }
-      }
-      return is_same;
-    };
-
-    REQUIRE(is_same(cell_array, table));
   }
 
   SECTION("copy")
@@ -155,43 +179,55 @@ TEST_CASE("ItemArray", "[mesh]")
       return is_same;
     };
 
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    CellArray<int> cell_array{connectivity, 4};
-    cell_array.fill(parallel::rank());
+        CellArray<int> cell_array{connectivity, 4};
+        cell_array.fill(parallel::rank());
 
-    CellArray<const int> cell_array_const_view{cell_array};
-    REQUIRE(cell_array.numberOfItems() == cell_array_const_view.numberOfItems());
-    REQUIRE(cell_array.sizeOfArrays() == cell_array_const_view.sizeOfArrays());
-    REQUIRE(is_same(cell_array_const_view, static_cast<std::int64_t>(parallel::rank())));
+        CellArray<const int> cell_array_const_view{cell_array};
+        REQUIRE(cell_array.numberOfItems() == cell_array_const_view.numberOfItems());
+        REQUIRE(cell_array.sizeOfArrays() == cell_array_const_view.sizeOfArrays());
+        REQUIRE(is_same(cell_array_const_view, static_cast<std::int64_t>(parallel::rank())));
 
-    CellArray<const int> const_cell_array;
-    const_cell_array = copy(cell_array);
+        CellArray<const int> const_cell_array;
+        const_cell_array = copy(cell_array);
 
-    CellArray<int> duplicated_cell_array{connectivity, cell_array.sizeOfArrays()};
-    copy_to(const_cell_array, duplicated_cell_array);
+        CellArray<int> duplicated_cell_array{connectivity, cell_array.sizeOfArrays()};
+        copy_to(const_cell_array, duplicated_cell_array);
 
-    cell_array.fill(0);
+        cell_array.fill(0);
 
-    REQUIRE(is_same(cell_array, 0));
-    REQUIRE(is_same(cell_array_const_view, 0));
-    REQUIRE(is_same(const_cell_array, static_cast<std::int64_t>(parallel::rank())));
-    REQUIRE(is_same(duplicated_cell_array, static_cast<std::int64_t>(parallel::rank())));
+        REQUIRE(is_same(cell_array, 0));
+        REQUIRE(is_same(cell_array_const_view, 0));
+        REQUIRE(is_same(const_cell_array, static_cast<std::int64_t>(parallel::rank())));
+        REQUIRE(is_same(duplicated_cell_array, static_cast<std::int64_t>(parallel::rank())));
+      }
+    }
   }
 
   SECTION("WeakItemArray")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    WeakFaceArray<int> weak_face_array{connectivity, 5};
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    weak_face_array.fill(parallel::rank());
+        WeakFaceArray<int> weak_face_array{connectivity, 5};
 
-    FaceArray<const int> face_array{weak_face_array};
+        weak_face_array.fill(parallel::rank());
 
-    REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+        FaceArray<const int> face_array{weak_face_array};
+
+        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+      }
+    }
   }
 
 #ifndef NDEBUG
@@ -214,35 +250,47 @@ TEST_CASE("ItemArray", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      CellArray<int> cell_array{connectivity, 1};
-      CellId invalid_cell_id = connectivity.numberOfCells();
-      REQUIRE_THROWS_AS(cell_array[invalid_cell_id], AssertError);
+          CellArray<int> cell_array{connectivity, 1};
+          CellId invalid_cell_id = connectivity.numberOfCells();
+          REQUIRE_THROWS_AS(cell_array[invalid_cell_id], AssertError);
 
-      FaceArray<int> face_array{connectivity, 2};
-      FaceId invalid_face_id = connectivity.numberOfFaces();
-      REQUIRE_THROWS_AS(face_array[invalid_face_id], AssertError);
+          FaceArray<int> face_array{connectivity, 2};
+          FaceId invalid_face_id = connectivity.numberOfFaces();
+          REQUIRE_THROWS_AS(face_array[invalid_face_id], AssertError);
 
-      EdgeArray<int> edge_array{connectivity, 1};
-      EdgeId invalid_edge_id = connectivity.numberOfEdges();
-      REQUIRE_THROWS_AS(edge_array[invalid_edge_id], AssertError);
+          EdgeArray<int> edge_array{connectivity, 1};
+          EdgeId invalid_edge_id = connectivity.numberOfEdges();
+          REQUIRE_THROWS_AS(edge_array[invalid_edge_id], AssertError);
 
-      NodeArray<int> node_array{connectivity, 0};
-      NodeId invalid_node_id = connectivity.numberOfNodes();
-      REQUIRE_THROWS_AS(node_array[invalid_node_id], AssertError);
+          NodeArray<int> node_array{connectivity, 0};
+          NodeId invalid_node_id = connectivity.numberOfNodes();
+          REQUIRE_THROWS_AS(node_array[invalid_node_id], AssertError);
+        }
+      }
     }
 
     SECTION("set values from invalid array size")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellArray<size_t> cell_array{connectivity, 2};
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      Table<size_t> values{3, connectivity.numberOfCells() + 3};
-      REQUIRE_THROWS_AS(cell_array = values, AssertError);
+          CellArray<size_t> cell_array{connectivity, 2};
+
+          Table<size_t> values{3, connectivity.numberOfCells() + 3};
+          REQUIRE_THROWS_AS(cell_array = values, AssertError);
+        }
+      }
     }
   }
 #endif   // NDEBUG
diff --git a/tests/test_ItemArrayUtils.cpp b/tests/test_ItemArrayUtils.cpp
index c746a22f5937a1d6180466c3b2333536002d29e5..204986ce6f3ad9e88565559a8558e9dbf42faeb2 100644
--- a/tests/test_ItemArrayUtils.cpp
+++ b/tests/test_ItemArrayUtils.cpp
@@ -19,750 +19,773 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        NodeArray<const size_t> node_array{weak_node_array};
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+          SECTION("node")
+          {
+            WeakNodeArray<size_t> weak_node_array{connectivity, 4};
+            auto node_number = connectivity.nodeNumber();
 
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
+            for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+              Array array         = weak_node_array[i_node];
+              const size_t number = node_number[i_node];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_node_array);
-
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            NodeArray<const size_t> node_array{weak_node_array};
+
+            REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto node_owner    = connectivity.nodeOwner();
+              auto node_is_owned = connectivity.nodeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                if (node_is_owned[i_node]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_node_array);
 
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
-        auto edge_number = connectivity.edgeNumber();
+            {   // after synchronization
+              auto node_owner = connectivity.nodeOwner();
 
-        for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        EdgeArray<const size_t> edge_array{weak_edge_array};
-
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
-
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+              bool is_synchronized = true;
+              for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
               }
-            }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
-
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
-
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
-        auto face_number = connectivity.faceNumber();
-
-        for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        FaceArray<const size_t> face_array{weak_face_array};
-
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
-
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
+          SECTION("edge")
+          {
+            WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
+            auto edge_number = connectivity.edgeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
+            for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+              Array array         = weak_edge_array[i_edge];
+              const size_t number = edge_number[i_edge];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_face_array);
 
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+            EdgeArray<const size_t> edge_array{weak_edge_array};
+
+            REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto edge_owner    = connectivity.edgeOwner();
+              auto edge_is_owned = connectivity.edgeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                if (edge_is_owned[i_edge]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
-        auto cell_number = connectivity.cellNumber();
 
-        for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        CellArray<const size_t> cell_array{weak_cell_array};
-
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+            synchronize(weak_edge_array);
 
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
+            {   // after synchronization
+              auto edge_owner = connectivity.edgeOwner();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+              bool is_synchronized = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
               }
-            }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_cell_array);
-
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-    }
-
-    SECTION("2D")
-    {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 3};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        NodeArray<const size_t> node_array{weak_node_array};
+          SECTION("face")
+          {
+            WeakFaceArray<size_t> weak_face_array{connectivity, 4};
+            auto face_number = connectivity.faceNumber();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
-
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
+            for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+              Array array         = weak_face_array[i_face];
+              const size_t number = face_number[i_face];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_node_array);
 
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            FaceArray<const size_t> face_array{weak_face_array};
+
+            REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto face_owner    = connectivity.faceOwner();
+              auto face_is_owned = connectivity.faceIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                if (face_is_owned[i_face]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 3};
-        auto edge_number = connectivity.edgeNumber();
-
-        for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        EdgeArray<const size_t> edge_array{weak_edge_array};
 
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+            synchronize(weak_face_array);
 
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
+            {   // after synchronization
+              auto face_owner = connectivity.faceOwner();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+              bool is_synchronized = true;
+              for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
               }
-            }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
-
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
 
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 3};
-        auto face_number = connectivity.faceNumber();
-
-        for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        FaceArray<const size_t> face_array{weak_face_array};
+          SECTION("cell")
+          {
+            WeakCellArray<size_t> weak_cell_array{connectivity, 4};
+            auto cell_number = connectivity.cellNumber();
 
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
-
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
+            for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+              Array array         = weak_cell_array[i_cell];
+              const size_t number = cell_number[i_cell];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+            }
+
+            CellArray<const size_t> cell_array{weak_cell_array};
+
+            REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto cell_owner    = connectivity.cellOwner();
+              auto cell_is_owned = connectivity.cellIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                if (cell_is_owned[i_cell]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
               }
+
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
+            synchronize(weak_cell_array);
 
-        synchronize(weak_face_array);
+            {   // after synchronization
+              auto cell_owner = connectivity.cellOwner();
 
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+              bool is_synchronized = true;
+              for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
+    }
 
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 3};
-        auto cell_number = connectivity.cellNumber();
-
-        for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        CellArray<const size_t> cell_array{weak_cell_array};
-
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
-
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+    SECTION("2D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          for (auto [section_name, mesh_2d] : mesh_list) {
+            SECTION(section_name)
+            {
+              const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+              SECTION("node")
+              {
+                WeakNodeArray<size_t> weak_node_array{connectivity, 3};
+                auto node_number = connectivity.nodeNumber();
+
+                for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+                  Array array         = weak_node_array[i_node];
+                  const size_t number = node_number[i_node];
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    array[i] = number + (parallel::rank() + 1) * i;
+                  }
+                }
+
+                NodeArray<const size_t> node_array{weak_node_array};
+
+                REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+                {   // before synchronization
+                  auto node_owner    = connectivity.nodeOwner();
+                  auto node_is_owned = connectivity.nodeIsOwned();
+
+                  bool is_synchronized = (parallel::size() > 1);
+                  bool is_valid        = true;
+                  for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+                    Array array         = node_array[i_node];
+                    const size_t number = node_number[i_node];
+                    const size_t owner  = node_owner[i_node];
+                    if (node_is_owned[i_node]) {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_valid &= (array[i] == number + (owner + 1) * i);
+                      }
+                    } else {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_synchronized &= (array[i] == number + (owner + 1) * i);
+                      }
+                    }
+                  }
+
+                  REQUIRE(is_valid);
+                  REQUIRE(not is_synchronized);
+                }
+
+                synchronize(weak_node_array);
+
+                {   // after synchronization
+                  auto node_owner = connectivity.nodeOwner();
+
+                  bool is_synchronized = true;
+                  for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+                    Array array         = node_array[i_node];
+                    const size_t number = node_number[i_node];
+                    const size_t owner  = node_owner[i_node];
+                    for (size_t i = 0; i < array.size(); ++i) {
+                      is_synchronized &= (array[i] == number + (owner + 1) * i);
+                    }
+                  }
+
+                  REQUIRE(is_synchronized);
+                }
               }
-            }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
 
-        synchronize(weak_cell_array);
+              SECTION("edge")
+              {
+                WeakEdgeArray<size_t> weak_edge_array{connectivity, 3};
+                auto edge_number = connectivity.edgeNumber();
+
+                for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+                  Array array         = weak_edge_array[i_edge];
+                  const size_t number = edge_number[i_edge];
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    array[i] = number + (parallel::rank() + 1) * i;
+                  }
+                }
+
+                EdgeArray<const size_t> edge_array{weak_edge_array};
+
+                REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+                {   // before synchronization
+                  auto edge_owner    = connectivity.edgeOwner();
+                  auto edge_is_owned = connectivity.edgeIsOwned();
+
+                  bool is_synchronized = (parallel::size() > 1);
+                  bool is_valid        = true;
+                  for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+                    Array array         = edge_array[i_edge];
+                    const size_t number = edge_number[i_edge];
+                    const size_t owner  = edge_owner[i_edge];
+                    if (edge_is_owned[i_edge]) {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_valid &= (array[i] == number + (owner + 1) * i);
+                      }
+                    } else {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_synchronized &= (array[i] == number + (owner + 1) * i);
+                      }
+                    }
+                  }
+
+                  REQUIRE(is_valid);
+                  REQUIRE(not is_synchronized);
+                }
+
+                synchronize(weak_edge_array);
+
+                {   // after synchronization
+                  auto edge_owner = connectivity.edgeOwner();
+
+                  bool is_synchronized = true;
+                  for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+                    Array array         = edge_array[i_edge];
+                    const size_t number = edge_number[i_edge];
+                    const size_t owner  = edge_owner[i_edge];
+                    for (size_t i = 0; i < array.size(); ++i) {
+                      is_synchronized &= (array[i] == number + (owner + 1) * i);
+                    }
+                  }
+
+                  REQUIRE(is_synchronized);
+                }
+              }
 
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
+              SECTION("face")
+              {
+                WeakFaceArray<size_t> weak_face_array{connectivity, 3};
+                auto face_number = connectivity.faceNumber();
+
+                for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+                  Array array         = weak_face_array[i_face];
+                  const size_t number = face_number[i_face];
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    array[i] = number + (parallel::rank() + 1) * i;
+                  }
+                }
+
+                FaceArray<const size_t> face_array{weak_face_array};
+
+                REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+                {   // before synchronization
+                  auto face_owner    = connectivity.faceOwner();
+                  auto face_is_owned = connectivity.faceIsOwned();
+
+                  bool is_synchronized = (parallel::size() > 1);
+                  bool is_valid        = true;
+                  for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+                    Array array         = face_array[i_face];
+                    const size_t number = face_number[i_face];
+                    const size_t owner  = face_owner[i_face];
+                    if (face_is_owned[i_face]) {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_valid &= (array[i] == number + (owner + 1) * i);
+                      }
+                    } else {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_synchronized &= (array[i] == number + (owner + 1) * i);
+                      }
+                    }
+                  }
+
+                  REQUIRE(is_valid);
+                  REQUIRE(not is_synchronized);
+                }
+
+                synchronize(weak_face_array);
+
+                {   // after synchronization
+                  auto face_owner = connectivity.faceOwner();
+
+                  bool is_synchronized = true;
+                  for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+                    Array array         = face_array[i_face];
+                    const size_t number = face_number[i_face];
+                    const size_t owner  = face_owner[i_face];
+                    for (size_t i = 0; i < array.size(); ++i) {
+                      is_synchronized &= (array[i] == number + (owner + 1) * i);
+                    }
+                  }
+
+                  REQUIRE(is_synchronized);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              SECTION("cell")
+              {
+                WeakCellArray<size_t> weak_cell_array{connectivity, 3};
+                auto cell_number = connectivity.cellNumber();
+
+                for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+                  Array array         = weak_cell_array[i_cell];
+                  const size_t number = cell_number[i_cell];
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    array[i] = number + (parallel::rank() + 1) * i;
+                  }
+                }
+
+                CellArray<const size_t> cell_array{weak_cell_array};
+
+                REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+                {   // before synchronization
+                  auto cell_owner    = connectivity.cellOwner();
+                  auto cell_is_owned = connectivity.cellIsOwned();
+
+                  bool is_synchronized = (parallel::size() > 1);
+                  bool is_valid        = true;
+                  for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+                    Array array         = cell_array[i_cell];
+                    const size_t number = cell_number[i_cell];
+                    const size_t owner  = cell_owner[i_cell];
+                    if (cell_is_owned[i_cell]) {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_valid &= (array[i] == number + (owner + 1) * i);
+                      }
+                    } else {
+                      for (size_t i = 0; i < array.size(); ++i) {
+                        is_synchronized &= (array[i] == number + (owner + 1) * i);
+                      }
+                    }
+                  }
+
+                  REQUIRE(is_valid);
+                  REQUIRE(not is_synchronized);
+                }
+
+                synchronize(weak_cell_array);
+
+                {   // after synchronization
+                  auto cell_owner = connectivity.cellOwner();
+
+                  bool is_synchronized = true;
+                  for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+                    Array array         = cell_array[i_cell];
+                    const size_t number = cell_number[i_cell];
+                    const size_t owner  = cell_owner[i_cell];
+                    for (size_t i = 0; i < array.size(); ++i) {
+                      is_synchronized &= (array[i] == number + (owner + 1) * i);
+                    }
+                  }
+
+                  REQUIRE(is_synchronized);
+                }
+              }
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        NodeArray<const size_t> node_array{weak_node_array};
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+          SECTION("node")
+          {
+            WeakNodeArray<size_t> weak_node_array{connectivity, 4};
+            auto node_number = connectivity.nodeNumber();
 
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
-
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+              Array array         = weak_node_array[i_node];
+              const size_t number = node_number[i_node];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
 
-        synchronize(weak_node_array);
-
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            NodeArray<const size_t> node_array{weak_node_array};
+
+            REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto node_owner    = connectivity.nodeOwner();
+              auto node_is_owned = connectivity.nodeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                if (node_is_owned[i_node]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
-        auto edge_number = connectivity.edgeNumber();
+            synchronize(weak_node_array);
 
-        for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto node_owner = connectivity.nodeOwner();
 
-        EdgeArray<const size_t> edge_array{weak_edge_array};
+              bool is_synchronized = true;
+              for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
+          SECTION("edge")
+          {
+            WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
+            auto edge_number = connectivity.edgeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+              Array array         = weak_edge_array[i_edge];
+              const size_t number = edge_number[i_edge];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
 
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
+            EdgeArray<const size_t> edge_array{weak_edge_array};
+
+            REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto edge_owner    = connectivity.edgeOwner();
+              auto edge_is_owned = connectivity.edgeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                if (edge_is_owned[i_edge]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
 
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
-        auto face_number = connectivity.faceNumber();
+            synchronize(weak_edge_array);
 
-        for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto edge_owner = connectivity.edgeOwner();
 
-        FaceArray<const size_t> face_array{weak_face_array};
+              bool is_synchronized = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
+          SECTION("face")
+          {
+            WeakFaceArray<size_t> weak_face_array{connectivity, 4};
+            auto face_number = connectivity.faceNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
+            for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+              Array array         = weak_face_array[i_face];
+              const size_t number = face_number[i_face];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_face_array);
 
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+            FaceArray<const size_t> face_array{weak_face_array};
+
+            REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto face_owner    = connectivity.faceOwner();
+              auto face_is_owned = connectivity.faceIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                if (face_is_owned[i_face]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
 
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
-        auto cell_number = connectivity.cellNumber();
+            synchronize(weak_face_array);
 
-        for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto face_owner = connectivity.faceOwner();
 
-        CellArray<const size_t> cell_array{weak_cell_array};
+              bool is_synchronized = true;
+              for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
+          SECTION("cell")
+          {
+            WeakCellArray<size_t> weak_cell_array{connectivity, 4};
+            auto cell_number = connectivity.cellNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
+            for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+              Array array         = weak_cell_array[i_cell];
+              const size_t number = cell_number[i_cell];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+            }
+
+            CellArray<const size_t> cell_array{weak_cell_array};
+
+            REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto cell_owner    = connectivity.cellOwner();
+              auto cell_is_owned = connectivity.cellIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                if (cell_is_owned[i_cell]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
               }
+
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
+            synchronize(weak_cell_array);
 
-        synchronize(weak_cell_array);
+            {   // after synchronization
+              auto cell_owner = connectivity.cellOwner();
 
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
+              bool is_synchronized = true;
+              for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
     }
diff --git a/tests/test_ItemValue.cpp b/tests/test_ItemValue.cpp
index cf0fe9f62eaa7f9816eab66e1859721ccc5d76be..a51f65f0b9c70af2b8d73554da8e52f8d2405926 100644
--- a/tests/test_ItemValue.cpp
+++ b/tests/test_ItemValue.cpp
@@ -26,119 +26,155 @@ TEST_CASE("ItemValue", "[mesh]")
 
   SECTION("1D")
   {
-    const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-    const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
-
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
-
-    NodeValue<int> node_value{connectivity};
-    EdgeValue<int> edge_value{connectivity};
-    FaceValue<int> face_value{connectivity};
-    CellValue<int> cell_value{connectivity};
-
-    REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto [section_name, mesh_1d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
+
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
+
+        NodeValue<int> node_value{connectivity};
+        EdgeValue<int> edge_value{connectivity};
+        FaceValue<int> face_value{connectivity};
+        CellValue<int> cell_value{connectivity};
+
+        REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+      }
+    }
   }
 
   SECTION("2D")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
 
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
 
-    EdgeValue<int> edge_value{connectivity};
-    FaceValue<int> face_value{connectivity};
+        EdgeValue<int> edge_value{connectivity};
+        FaceValue<int> face_value{connectivity};
 
-    REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+        REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+      }
+    }
   }
 
   SECTION("3D")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
-
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
+
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
+      }
+    }
   }
 
   SECTION("set values from array")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    CellValue<size_t> cell_value{connectivity};
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    Array<size_t> values{cell_value.numberOfItems()};
-    for (size_t i = 0; i < values.size(); ++i) {
-      values[i] = i;
-    }
+        CellValue<size_t> cell_value{connectivity};
 
-    cell_value = values;
+        Array<size_t> values{cell_value.numberOfItems()};
+        for (size_t i = 0; i < values.size(); ++i) {
+          values[i] = i;
+        }
 
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(cell_value[i_cell] == i_cell);
+        cell_value = values;
+
+        for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+          REQUIRE(cell_value[i_cell] == i_cell);
+        }
+      }
     }
   }
 
   SECTION("copy")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    CellValue<int> cell_value{connectivity};
-    cell_value.fill(parallel::rank());
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    CellValue<const int> const_cell_value;
-    const_cell_value = copy(cell_value);
+        CellValue<int> cell_value{connectivity};
+        cell_value.fill(parallel::rank());
 
-    cell_value.fill(0);
+        CellValue<const int> const_cell_value;
+        const_cell_value = copy(cell_value);
 
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(cell_value[i_cell] == 0);
-    }
+        cell_value.fill(0);
 
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(const_cell_value[i_cell] == static_cast<std::int64_t>(parallel::rank()));
+        for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+          REQUIRE(cell_value[i_cell] == 0);
+        }
+
+        for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+          REQUIRE(const_cell_value[i_cell] == static_cast<std::int64_t>(parallel::rank()));
+        }
+      }
     }
   }
 
   SECTION("WeakItemValue")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    WeakFaceValue<int> weak_face_value{connectivity};
+        WeakFaceValue<int> weak_face_value{connectivity};
 
-    weak_face_value.fill(parallel::rank());
+        weak_face_value.fill(parallel::rank());
 
-    FaceValue<const int> face_value{weak_face_value};
+        FaceValue<const int> face_value{weak_face_value};
 
-    REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+        REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+      }
+    }
   }
 
 #ifndef NDEBUG
@@ -161,48 +197,72 @@ TEST_CASE("ItemValue", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      CellValue<int> cell_value{connectivity};
-      CellId invalid_cell_id = connectivity.numberOfCells();
-      REQUIRE_THROWS_AS(cell_value[invalid_cell_id], AssertError);
-
-      FaceValue<int> face_value{connectivity};
-      FaceId invalid_face_id = connectivity.numberOfFaces();
-      REQUIRE_THROWS_AS(face_value[invalid_face_id], AssertError);
-
-      EdgeValue<int> edge_value{connectivity};
-      EdgeId invalid_edge_id = connectivity.numberOfEdges();
-      REQUIRE_THROWS_AS(edge_value[invalid_edge_id], AssertError);
-
-      NodeValue<int> node_value{connectivity};
-      NodeId invalid_node_id = connectivity.numberOfNodes();
-      REQUIRE_THROWS_AS(node_value[invalid_node_id], AssertError);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          CellValue<int> cell_value{connectivity};
+          CellId invalid_cell_id = connectivity.numberOfCells();
+          REQUIRE_THROWS_AS(cell_value[invalid_cell_id], AssertError);
+
+          FaceValue<int> face_value{connectivity};
+          FaceId invalid_face_id = connectivity.numberOfFaces();
+          REQUIRE_THROWS_AS(face_value[invalid_face_id], AssertError);
+
+          EdgeValue<int> edge_value{connectivity};
+          EdgeId invalid_edge_id = connectivity.numberOfEdges();
+          REQUIRE_THROWS_AS(edge_value[invalid_edge_id], AssertError);
+
+          NodeValue<int> node_value{connectivity};
+          NodeId invalid_node_id = connectivity.numberOfNodes();
+          REQUIRE_THROWS_AS(node_value[invalid_node_id], AssertError);
+        }
+      }
     }
 
     SECTION("set values from invalid array size")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      CellValue<size_t> cell_value{connectivity};
+          CellValue<size_t> cell_value{connectivity};
 
-      Array<size_t> values{3 + cell_value.numberOfItems()};
-      REQUIRE_THROWS_AS(cell_value = values, AssertError);
+          Array<size_t> values{3 + cell_value.numberOfItems()};
+          REQUIRE_THROWS_AS(cell_value = values, AssertError);
+        }
+      }
     }
 
     SECTION("invalid copy_to")
     {
-      const Mesh<Connectivity<2>>& mesh_2d   = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity_2d = mesh_2d.connectivity();
-
-      const Mesh<Connectivity<3>>& mesh_3d   = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity_3d = mesh_3d.connectivity();
-
-      CellValue<int> cell_2d_value{connectivity_2d};
-      CellValue<int> cell_3d_value{connectivity_3d};
-      REQUIRE_THROWS_AS(copy_to(cell_2d_value, cell_3d_value), AssertError);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<2>& connectivity_2d = mesh_2d->connectivity();
+
+          std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+          for (auto [section_name, mesh_3d] : mesh_list) {
+            SECTION(section_name)
+            {
+              const Connectivity<3>& connectivity_3d = mesh_3d->connectivity();
+
+              CellValue<int> cell_2d_value{connectivity_2d};
+              CellValue<int> cell_3d_value{connectivity_3d};
+              REQUIRE_THROWS_AS(copy_to(cell_2d_value, cell_3d_value), AssertError);
+            }
+          }
+        }
+      }
     }
   }
 #endif   // NDEBUG
diff --git a/tests/test_ItemValueUtils.cpp b/tests/test_ItemValueUtils.cpp
index 5c02d764da7798d6a74092ef12889797fa881e63..b818aee299c51269d6b5710c884bfc6d202f135e 100644
--- a/tests/test_ItemValueUtils.cpp
+++ b/tests/test_ItemValueUtils.cpp
@@ -17,38 +17,44 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 {
   SECTION("Synchronize")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    WeakFaceValue<int> weak_face_value{connectivity};
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    weak_face_value.fill(parallel::rank());
+        WeakFaceValue<int> weak_face_value{connectivity};
 
-    FaceValue<const int> face_value{weak_face_value};
+        weak_face_value.fill(parallel::rank());
 
-    REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+        FaceValue<const int> face_value{weak_face_value};
 
-    {   // before synchronization
-      auto face_owner    = connectivity.faceOwner();
-      auto face_is_owned = connectivity.faceIsOwned();
+        REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
 
-      for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-        if (face_is_owned[i_face]) {
-          REQUIRE(face_owner[i_face] == face_value[i_face]);
-        } else {
-          REQUIRE(face_owner[i_face] != face_value[i_face]);
+        {   // before synchronization
+          auto face_owner    = connectivity.faceOwner();
+          auto face_is_owned = connectivity.faceIsOwned();
+
+          for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+            if (face_is_owned[i_face]) {
+              REQUIRE(face_owner[i_face] == face_value[i_face]);
+            } else {
+              REQUIRE(face_owner[i_face] != face_value[i_face]);
+            }
+          }
         }
-      }
-    }
 
-    synchronize(weak_face_value);
+        synchronize(weak_face_value);
 
-    {   // after synchronization
-      auto face_owner    = connectivity.faceOwner();
-      auto face_is_owned = connectivity.faceIsOwned();
+        {   // after synchronization
+          auto face_owner    = connectivity.faceOwner();
+          auto face_is_owned = connectivity.faceIsOwned();
 
-      for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-        REQUIRE(face_owner[i_face] == face_value[i_face]);
+          for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+            REQUIRE(face_owner[i_face] == face_value[i_face]);
+          }
+        }
       }
     }
   }
@@ -57,59 +63,77 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_1d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
 
-      REQUIRE(min(cell_value) == 10);
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_2d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
 
-      REQUIRE(min(cell_value) == 10);
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
+
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_3d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
 
-      REQUIRE(min(cell_value) == 10);
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
   }
 
@@ -117,59 +141,77 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_1d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
 
-      REQUIRE(max(cell_value) == parallel::size());
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
+
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_2d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
 
-      REQUIRE(max(cell_value) == parallel::size());
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_3d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
 
-      REQUIRE(max(cell_value) == parallel::size());
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
+
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
   }
 
@@ -177,65 +219,83 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(5);
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(5);
 
-      const size_t global_number_of_cells = [&] {
-        size_t number_of_cells = 0;
-        for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) {
-          number_of_cells += cell_is_owned[cell_id];
-        }
-        return parallel::allReduceSum(number_of_cells);
-      }();
+          auto cell_is_owned = connectivity.cellIsOwned();
+
+          const size_t global_number_of_cells = [&] {
+            size_t number_of_cells = 0;
+            for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) {
+              number_of_cells += cell_is_owned[cell_id];
+            }
+            return parallel::allReduceSum(number_of_cells);
+          }();
 
-      REQUIRE(sum(cell_value) == 5 * global_number_of_cells);
+          REQUIRE(sum(cell_value) == 5 * global_number_of_cells);
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      FaceValue<size_t> face_value{connectivity};
-      face_value.fill(2);
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      auto face_is_owned = connectivity.faceIsOwned();
+          FaceValue<size_t> face_value{connectivity};
+          face_value.fill(2);
 
-      const size_t global_number_of_faces = [&] {
-        size_t number_of_faces = 0;
-        for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) {
-          number_of_faces += face_is_owned[face_id];
-        }
-        return parallel::allReduceSum(number_of_faces);
-      }();
+          auto face_is_owned = connectivity.faceIsOwned();
 
-      REQUIRE(sum(face_value) == 2 * global_number_of_faces);
+          const size_t global_number_of_faces = [&] {
+            size_t number_of_faces = 0;
+            for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) {
+              number_of_faces += face_is_owned[face_id];
+            }
+            return parallel::allReduceSum(number_of_faces);
+          }();
+
+          REQUIRE(sum(face_value) == 2 * global_number_of_faces);
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      NodeValue<size_t> node_value{connectivity};
-      node_value.fill(3);
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      auto node_is_owned = connectivity.nodeIsOwned();
+          NodeValue<size_t> node_value{connectivity};
+          node_value.fill(3);
 
-      const size_t global_number_of_nodes = [&] {
-        size_t number_of_nodes = 0;
-        for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) {
-          number_of_nodes += node_is_owned[node_id];
-        }
-        return parallel::allReduceSum(number_of_nodes);
-      }();
+          auto node_is_owned = connectivity.nodeIsOwned();
+
+          const size_t global_number_of_nodes = [&] {
+            size_t number_of_nodes = 0;
+            for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) {
+              number_of_nodes += node_is_owned[node_id];
+            }
+            return parallel::allReduceSum(number_of_nodes);
+          }();
 
-      REQUIRE(sum(node_value) == 3 * global_number_of_nodes);
+          REQUIRE(sum(node_value) == 3 * global_number_of_nodes);
+        }
+      }
     }
   }
 }
diff --git a/tests/test_SubItemArrayPerItem.cpp b/tests/test_SubItemArrayPerItem.cpp
index 3caa8a5b957fd9f4a37f565d9826bee71b0d52a3..01b1b721913d57f80d51efeab2b5f74f62f13c38 100644
--- a/tests/test_SubItemArrayPerItem.cpp
+++ b/tests/test_SubItemArrayPerItem.cpp
@@ -64,457 +64,477 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
 
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id)) and
-              (node_array_per_cell.itemTable(cell_id).numberOfRows() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        const NodeArrayPerCell<const int> const_node_array_per_cell = node_array_per_cell;
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (const_node_array_per_cell.itemTable(cell_id).numberOfRows() ==
-                           node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &=
+                  (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id)) and
+                  (node_array_per_cell.itemTable(cell_id).numberOfRows() ==
+                   node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+            const NodeArrayPerCell<const int> const_node_array_per_cell = node_array_per_cell;
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (const_node_array_per_cell.itemTable(cell_id).numberOfRows() ==
+                               node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 2};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 2);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 2};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 2);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 2};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 2);
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 2};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per node")
-      {
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 4};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 4);
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+          SECTION("per node")
+          {
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 4};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 5};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 5);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 3);
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
-        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
-        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 5};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 5);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 5};
-        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
-        REQUIRE(node_array_per_edge.sizeOfArrays() == 5);
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 3);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+            NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+            REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
+            REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per node")
-      {
-        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 4};
-        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
-        REQUIRE(edge_array_per_node.sizeOfArrays() == 4);
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+            NodeArrayPerEdge<int> node_array_per_edge{connectivity, 5};
+            REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
+            REQUIRE(node_array_per_edge.sizeOfArrays() == 5);
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerNode<int> face_array_per_node{connectivity, 3};
-        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
-        REQUIRE(face_array_per_node.sizeOfArrays() == 3);
 
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per node")
+          {
+            EdgeArrayPerNode<int> edge_array_per_node{connectivity, 4};
+            REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
+            REQUIRE(edge_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 2};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 2);
+            FaceArrayPerNode<int> face_array_per_node{connectivity, 3};
+            REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
+            REQUIRE(face_array_per_node.sizeOfArrays() == 3);
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 2};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 2);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
+
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 5};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 5);
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3};
-        REQUIRE(edge_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(edge_array_per_face.numberOfArrays() == number_of_arrays(edge_array_per_face));
-        REQUIRE(edge_array_per_face.sizeOfArrays() == 3);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_edge_matrix[face_id].size() == edge_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
-        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
-        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 5};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 5);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        FaceArrayPerEdge<int> face_array_per_edge{connectivity, 5};
-        REQUIRE(face_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(face_array_per_edge.numberOfArrays() == number_of_arrays(face_array_per_edge));
-        REQUIRE(face_array_per_edge.sizeOfArrays() == 5);
+            EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3};
+            REQUIRE(edge_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(edge_array_per_face.numberOfArrays() == number_of_arrays(edge_array_per_face));
+            REQUIRE(edge_array_per_face.sizeOfArrays() == 3);
+
+            auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_edge_matrix[face_id].size() == edge_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_face_matrix[edge_id].size() == face_array_per_edge.numberOfSubArrays(edge_id));
+            NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+            REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
+            REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
-        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
-        REQUIRE(node_array_per_edge.sizeOfArrays() == 3);
-
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-      SECTION("per node")
-      {
-        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3};
-        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
-        REQUIRE(edge_array_per_node.sizeOfArrays() == 3);
+            FaceArrayPerEdge<int> face_array_per_edge{connectivity, 5};
+            REQUIRE(face_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(face_array_per_edge.numberOfArrays() == number_of_arrays(face_array_per_edge));
+            REQUIRE(face_array_per_edge.sizeOfArrays() == 5);
+
+            auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_face_matrix[edge_id].size() == face_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+            NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+            REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
+            REQUIRE(node_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerNode<int> face_array_per_node{connectivity, 4};
-        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
-        REQUIRE(face_array_per_node.sizeOfArrays() == 4);
 
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per node")
+          {
+            EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3};
+            REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
+            REQUIRE(edge_array_per_node.sizeOfArrays() == 3);
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 5};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 5);
+            FaceArrayPerNode<int> face_array_per_node{connectivity, 4};
+            REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
+            REQUIRE(face_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 5};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 5);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
@@ -524,325 +544,355 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      EdgeArrayPerCell<size_t> edge_arrays_per_cell{connectivity, 3};
-      {
-        size_t array = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
-            for (size_t i = 0; i < edge_arrays_per_cell(cell_id, i_edge).size(); ++i) {
-              edge_arrays_per_cell(cell_id, i_edge)[i] = array++;
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          EdgeArrayPerCell<size_t> edge_arrays_per_cell{connectivity, 3};
+          {
+            size_t array = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+                for (size_t i = 0; i < edge_arrays_per_cell(cell_id, i_edge).size(); ++i) {
+                  edge_arrays_per_cell(cell_id, i_edge)[i] = array++;
+                }
+              }
             }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
-            is_same &= (edge_arrays_per_cell[i][j] == k);
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
+                is_same &= (edge_arrays_per_cell[i][j] == k);
+              }
+            }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
 
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
-            edge_arrays_per_cell[i][j] = k * k + 1;
-          }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
-            for (size_t l = 0; l < edge_arrays_per_cell(cell_id, i_edge).size(); ++l, ++i) {
-              is_same &= (edge_arrays_per_cell(cell_id, i_edge)[l] == i * i + 1);
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
+                edge_arrays_per_cell[i][j] = k * k + 1;
+              }
             }
           }
-        }
-        REQUIRE(is_same);
-      }
-
-      const EdgeArrayPerCell<const size_t> const_edge_arrays_per_cell = edge_arrays_per_cell;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          const auto& cell_table = const_edge_arrays_per_cell.itemTable(cell_id);
-          for (size_t i_edge = 0; i_edge < cell_table.numberOfRows(); ++i_edge) {
-            const auto& array = cell_table[i_edge];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == i * i + 1);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+                for (size_t l = 0; l < edge_arrays_per_cell(cell_id, i_edge).size(); ++l, ++i) {
+                  is_same &= (edge_arrays_per_cell(cell_id, i_edge)[l] == i * i + 1);
+                }
+              }
+            }
+            REQUIRE(is_same);
+          }
+
+          const EdgeArrayPerCell<const size_t> const_edge_arrays_per_cell = edge_arrays_per_cell;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              const auto& cell_table = const_edge_arrays_per_cell.itemTable(cell_id);
+              for (size_t i_edge = 0; i_edge < cell_table.numberOfRows(); ++i_edge) {
+                const auto& array = cell_table[i_edge];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == i * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellArrayPerFace<size_t> cell_arrays_per_face{connectivity, 3};
-      {
-        size_t array = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
-            for (size_t i = 0; i < cell_arrays_per_face(face_id, i_cell).size(); ++i) {
-              cell_arrays_per_face(face_id, i_cell)[i] = array++;
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          CellArrayPerFace<size_t> cell_arrays_per_face{connectivity, 3};
+          {
+            size_t array = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+                for (size_t i = 0; i < cell_arrays_per_face(face_id, i_cell).size(); ++i) {
+                  cell_arrays_per_face(face_id, i_cell)[i] = array++;
+                }
+              }
             }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
-            is_same &= (cell_arrays_per_face[i][j] == k);
-          }
-        }
-        REQUIRE(is_same);
-      }
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
-            cell_arrays_per_face[i][j] = 3 * k + 1;
-          }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
-            for (size_t l = 0; l < cell_arrays_per_face(face_id, i_cell).size(); ++l, ++i) {
-              is_same &= (cell_arrays_per_face(face_id, i_cell)[l] == 3 * i + 1);
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
+                is_same &= (cell_arrays_per_face[i][j] == k);
+              }
+            }
+            REQUIRE(is_same);
+          }
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
+                cell_arrays_per_face[i][j] = 3 * k + 1;
+              }
             }
           }
-        }
-        REQUIRE(is_same);
-      }
-
-      const CellArrayPerFace<const size_t> const_cell_arrays_per_face = cell_arrays_per_face;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          const auto& face_table = const_cell_arrays_per_face.itemTable(face_id);
-          for (size_t i_cell = 0; i_cell < face_table.numberOfRows(); ++i_cell) {
-            const auto& array = face_table[i_cell];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == 3 * i + 1);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+                for (size_t l = 0; l < cell_arrays_per_face(face_id, i_cell).size(); ++l, ++i) {
+                  is_same &= (cell_arrays_per_face(face_id, i_cell)[l] == 3 * i + 1);
+                }
+              }
+            }
+            REQUIRE(is_same);
+          }
+
+          const CellArrayPerFace<const size_t> const_cell_arrays_per_face = cell_arrays_per_face;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              const auto& face_table = const_cell_arrays_per_face.itemTable(face_id);
+              for (size_t i_cell = 0; i_cell < face_table.numberOfRows(); ++i_cell) {
+                const auto& array = face_table[i_cell];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == 3 * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      FaceArrayPerNode<size_t> face_arrays_per_node{connectivity, 3};
-      {
-        size_t array = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
-            for (size_t i = 0; i < face_arrays_per_node(node_id, i_face).size(); ++i)
-              face_arrays_per_node(node_id, i_face)[i] = array++;
-          }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
-            is_same &= (face_arrays_per_node[i][j] == k);
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          FaceArrayPerNode<size_t> face_arrays_per_node{connectivity, 3};
+          {
+            size_t array = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+                for (size_t i = 0; i < face_arrays_per_node(node_id, i_face).size(); ++i)
+                  face_arrays_per_node(node_id, i_face)[i] = array++;
+              }
+            }
           }
-          REQUIRE(is_same);
-        }
-      }
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
-            face_arrays_per_node[i][j] = 3 + k * k;
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
+                is_same &= (face_arrays_per_node[i][j] == k);
+              }
+              REQUIRE(is_same);
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
-            for (size_t l = 0; l < face_arrays_per_node(node_id, i_face).size(); ++l, ++i) {
-              is_same &= (face_arrays_per_node(node_id, i_face)[l] == 3 + i * i);
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
+                face_arrays_per_node[i][j] = 3 + k * k;
+              }
             }
           }
-        }
-        REQUIRE(is_same);
-      }
-
-      const FaceArrayPerNode<const size_t> const_face_arrays_per_node = face_arrays_per_node;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          const auto& node_table = const_face_arrays_per_node.itemTable(node_id);
-          for (size_t i_face = 0; i_face < node_table.numberOfRows(); ++i_face) {
-            const auto& array = node_table[i_face];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == 3 + i * i);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+                for (size_t l = 0; l < face_arrays_per_node(node_id, i_face).size(); ++l, ++i) {
+                  is_same &= (face_arrays_per_node(node_id, i_face)[l] == 3 + i * i);
+                }
+              }
             }
+            REQUIRE(is_same);
+          }
+
+          const FaceArrayPerNode<const size_t> const_face_arrays_per_node = face_arrays_per_node;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              const auto& node_table = const_face_arrays_per_node.itemTable(node_id);
+              for (size_t i_face = 0; i_face < node_table.numberOfRows(); ++i_face) {
+                const auto& array = node_table[i_face];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == 3 + i * i);
+                }
+              }
+            }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
   }
 
   SECTION("copy")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    SECTION("classic")
-    {
-      NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
       {
-        size_t k = 0;
-        for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
-            node_array_per_cell[i][j] = k;
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        SECTION("classic")
+        {
+          NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
           }
-        }
-      }
-      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(is_same);
           }
-        }
 
-        REQUIRE(is_same);
-      }
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              auto cell_table = node_array_per_cell.itemTable(cell_id);
+              for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+                auto node_array = cell_table[i_node];
+                for (size_t i = 0; i < node_array.size(); ++i) {
+                  node_array[i] = cell_id + i_node + i;
+                }
+              }
+            }
+          }
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          auto cell_table = node_array_per_cell.itemTable(cell_id);
-          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
-            auto node_array = cell_table[i_node];
-            for (size_t i = 0; i < node_array.size(); ++i) {
-              node_array[i] = cell_id + i_node + i;
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
             }
+
+            REQUIRE(not is_same);
           }
         }
-      }
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+        SECTION("from weak")
+        {
+          WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
           }
-        }
 
-        REQUIRE(not is_same);
-      }
-    }
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
 
-    SECTION("from weak")
-    {
-      WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
-            node_array_per_cell[i][j] = k;
+            REQUIRE(is_same);
           }
-        }
-      }
 
-      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              auto cell_table = node_array_per_cell.itemTable(cell_id);
+              for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+                auto node_array = cell_table[i_node];
+                for (size_t i = 0; i < node_array.size(); ++i) {
+                  node_array[i] = cell_id + i_node + i;
+                }
+              }
+            }
           }
-        }
-
-        REQUIRE(is_same);
-      }
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          auto cell_table = node_array_per_cell.itemTable(cell_id);
-          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
-            auto node_array = cell_table[i_node];
-            for (size_t i = 0; i < node_array.size(); ++i) {
-              node_array[i] = cell_id + i_node + i;
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
             }
-          }
-        }
-      }
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+            REQUIRE(not is_same);
           }
         }
-
-        REQUIRE(not is_same);
       }
     }
   }
 
   SECTION("WeakSubItemArrayPerItem")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    WeakFaceArrayPerCell<int> weak_face_array_per_cell{connectivity, 3};
-    {
-      size_t k = 0;
-      for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
-        for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j, ++k) {
-          weak_face_array_per_cell[i][j] = k;
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
+      {
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        WeakFaceArrayPerCell<int> weak_face_array_per_cell{connectivity, 3};
+        {
+          size_t k = 0;
+          for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
+            for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j, ++k) {
+              weak_face_array_per_cell[i][j] = k;
+            }
+          }
         }
-      }
-    }
 
-    FaceArrayPerCell<const int> face_array_per_cell{weak_face_array_per_cell};
+        FaceArrayPerCell<const int> face_array_per_cell{weak_face_array_per_cell};
 
-    REQUIRE(face_array_per_cell.connectivity_ptr() == weak_face_array_per_cell.connectivity_ptr());
-    REQUIRE(face_array_per_cell.sizeOfArrays() == weak_face_array_per_cell.sizeOfArrays());
+        REQUIRE(face_array_per_cell.connectivity_ptr() == weak_face_array_per_cell.connectivity_ptr());
+        REQUIRE(face_array_per_cell.sizeOfArrays() == weak_face_array_per_cell.sizeOfArrays());
 
-    bool is_same = true;
-    for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
-      for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j) {
-        is_same &= (face_array_per_cell[i][j] == weak_face_array_per_cell[i][j]);
+        bool is_same = true;
+        for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
+          for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j) {
+            is_same &= (face_array_per_cell[i][j] == weak_face_array_per_cell[i][j]);
+          }
+        }
+        REQUIRE(is_same);
       }
     }
-    REQUIRE(is_same);
   }
 
 #ifndef NDEBUG
@@ -889,71 +939,81 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
-      {
-        FaceId invalid_face_id = connectivity.numberOfFaces();
-        REQUIRE_THROWS_AS(cell_array_per_face(invalid_face_id, 0), AssertError);
-      }
-      if (connectivity.numberOfFaces() > 0) {
-        FaceId face_id         = 0;
-        const auto& face_table = cell_array_per_face.itemTable(face_id);
-        REQUIRE_THROWS_AS(cell_array_per_face(face_id, face_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(face_table[face_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[face_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()] = 2,
-                          AssertError);
-      }
-
-      FaceArrayPerNode<int> face_array_per_node{connectivity, 5};
-      {
-        NodeId invalid_node_id = connectivity.numberOfNodes();
-        REQUIRE_THROWS_AS(face_array_per_node(invalid_node_id, 0), AssertError);
-      }
-      if (connectivity.numberOfNodes() > 0) {
-        NodeId node_id         = 0;
-        const auto& node_table = face_array_per_node.itemTable(node_id);
-        REQUIRE_THROWS_AS(face_array_per_node(node_id, node_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(node_table[node_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[node_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()] = 2,
-                          AssertError);
-      }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3};
-      {
-        CellId invalid_cell_id = connectivity.numberOfCells();
-        REQUIRE_THROWS_AS(edge_array_per_cell(invalid_cell_id, 0), AssertError);
-      }
-      if (connectivity.numberOfCells() > 0) {
-        CellId cell_id         = 0;
-        const auto& cell_table = edge_array_per_cell.itemTable(cell_id);
-        REQUIRE_THROWS_AS(edge_array_per_cell(cell_id, cell_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(cell_table[cell_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[cell_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()] == 2,
-                          AssertError);
-      }
-
-      NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
-      {
-        EdgeId invalid_edge_id = connectivity.numberOfEdges();
-        REQUIRE_THROWS_AS(node_array_per_edge(invalid_edge_id, 0), AssertError);
-      }
-      if (connectivity.numberOfEdges() > 0) {
-        EdgeId edge_id         = 0;
-        const auto& edge_table = node_array_per_edge.itemTable(edge_id);
-        REQUIRE_THROWS_AS(node_array_per_edge(edge_id, edge_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(edge_table[edge_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[edge_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()] = 2,
-                          AssertError);
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+          {
+            FaceId invalid_face_id = connectivity.numberOfFaces();
+            REQUIRE_THROWS_AS(cell_array_per_face(invalid_face_id, 0), AssertError);
+          }
+          if (connectivity.numberOfFaces() > 0) {
+            FaceId face_id         = 0;
+            const auto& face_table = cell_array_per_face.itemTable(face_id);
+            REQUIRE_THROWS_AS(cell_array_per_face(face_id, face_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(face_table[face_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[face_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()] = 2,
+                              AssertError);
+          }
+
+          FaceArrayPerNode<int> face_array_per_node{connectivity, 5};
+          {
+            NodeId invalid_node_id = connectivity.numberOfNodes();
+            REQUIRE_THROWS_AS(face_array_per_node(invalid_node_id, 0), AssertError);
+          }
+          if (connectivity.numberOfNodes() > 0) {
+            NodeId node_id         = 0;
+            const auto& node_table = face_array_per_node.itemTable(node_id);
+            REQUIRE_THROWS_AS(face_array_per_node(node_id, node_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(node_table[node_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[node_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()] = 2,
+                              AssertError);
+          }
+
+          EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3};
+          {
+            CellId invalid_cell_id = connectivity.numberOfCells();
+            REQUIRE_THROWS_AS(edge_array_per_cell(invalid_cell_id, 0), AssertError);
+          }
+          if (connectivity.numberOfCells() > 0) {
+            CellId cell_id         = 0;
+            const auto& cell_table = edge_array_per_cell.itemTable(cell_id);
+            REQUIRE_THROWS_AS(edge_array_per_cell(cell_id, cell_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(cell_table[cell_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[cell_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()] == 2,
+                              AssertError);
+          }
+
+          NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+          {
+            EdgeId invalid_edge_id = connectivity.numberOfEdges();
+            REQUIRE_THROWS_AS(node_array_per_edge(invalid_edge_id, 0), AssertError);
+          }
+          if (connectivity.numberOfEdges() > 0) {
+            EdgeId edge_id         = 0;
+            const auto& edge_table = node_array_per_edge.itemTable(edge_id);
+            REQUIRE_THROWS_AS(node_array_per_edge(edge_id, edge_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(edge_table[edge_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[edge_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()] = 2,
+                              AssertError);
+          }
+        }
       }
     }
   }
diff --git a/tests/test_SubItemValuePerItem.cpp b/tests/test_SubItemValuePerItem.cpp
index 7521eb7c1c9e4c5cf6596cd0d3f4ea5abc4f071f..eeae28a821338687d0923669dc48817dc8e73060 100644
--- a/tests/test_SubItemValuePerItem.cpp
+++ b/tests/test_SubItemValuePerItem.cpp
@@ -64,660 +64,708 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
 
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and
-              (node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &=
+                  (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and
+                  (node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (const_node_value_per_cell.itemValues(cell_id).size() ==
+                               node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (const_node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
-          }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per edge")
-      {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+    SECTION("2D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerFace<int> node_value_per_face{connectivity};
+            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerEdge<int> node_value_per_edge{connectivity};
+            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            EdgeValuePerNode<int> edge_value_per_node{connectivity};
+            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerNode<int> face_value_per_node{connectivity};
+            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per node")
-      {
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+    SECTION("3D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerFace<int> edge_value_per_face{connectivity};
+            REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));
+
+            auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerFace<int> node_value_per_face{connectivity};
+            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerEdge<int> face_value_per_edge{connectivity};
+            REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));
+
+            auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerEdge<int> node_value_per_edge{connectivity};
+            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            EdgeValuePerNode<int> edge_value_per_node{connectivity};
+            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerNode<int> face_value_per_node{connectivity};
+            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
+  }
 
-    SECTION("2D")
+  SECTION("array view")
+  {
+    SECTION("1D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto [section_name, mesh_1d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+          EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
+                edge_values_per_cell(cell_id, i_edge) = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+              is_same &= (edge_values_per_cell[i] == i);
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
 
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+          for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+            edge_values_per_cell[i] = i * i + 1;
           }
-          REQUIRE(is_correct);
-        }
-
-        NodeValuePerFace<int> node_value_per_face{connectivity};
-        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
+                is_same &= (edge_values_per_cell(cell_id, i_edge) == i * i + 1);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per edge")
-      {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
-
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        NodeValuePerEdge<int> node_value_per_edge{connectivity};
-        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
-
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per node")
-      {
-        EdgeValuePerNode<int> edge_value_per_node{connectivity};
-        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
-
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerNode<int> face_value_per_node{connectivity};
-        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
-
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+    SECTION("2D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+      for (auto [section_name, mesh_2d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          CellValuePerFace<size_t> cell_values_per_face{connectivity};
+          {
+            size_t value = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
+                cell_values_per_face(face_id, i_cell) = value++;
+              }
+            }
+          }
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+              is_same &= (cell_values_per_face[i] == i);
+            }
+            REQUIRE(is_same);
+          }
+          for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+            cell_values_per_face[i] = 3 * i + 1;
+          }
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
+                is_same &= (cell_values_per_face(face_id, i_cell) == 3 * i + 1);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+          FaceValuePerNode<size_t> face_values_per_node{connectivity};
+          {
+            size_t value = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
+                face_values_per_node.itemValues(node_id)[i_face] = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+              is_same &= (face_values_per_node[i] == i);
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
 
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+          for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+            face_values_per_node[i] = 3 + i * i;
           }
-          REQUIRE(is_correct);
-        }
-
-        EdgeValuePerFace<int> edge_value_per_face{connectivity};
-        REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));
-
-        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        NodeValuePerFace<int> node_value_per_face{connectivity};
-        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
+                is_same &= (face_values_per_node.itemValues(node_id)[i_face] == 3 + i * i);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
+    }
+  }
 
-      SECTION("per edge")
+  SECTION("copy")
+  {
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto [section_name, mesh_3d] : mesh_list) {
+      SECTION(section_name)
       {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+        SECTION("classic")
         {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerEdge<int> face_value_per_edge{connectivity};
-        REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));
+          NodeValuePerCell<size_t> node_value_per_cell{connectivity};
 
-        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        NodeValuePerEdge<int> node_value_per_edge{connectivity};
-        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+          NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
 
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-      SECTION("per node")
-      {
-        EdgeValuePerNode<int> edge_value_per_node{connectivity};
-        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
-
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
 
-        FaceValuePerNode<int> face_value_per_node{connectivity};
-        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
-
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+            REQUIRE(not is_same);
           }
-          REQUIRE(is_correct);
         }
-      }
-    }
-  }
 
-  SECTION("array view")
-  {
-    SECTION("1D")
-    {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesian1DMesh();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+        SECTION("from weak")
+        {
+          WeakNodeValuePerCell<size_t> node_value_per_cell{connectivity};
 
-      EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
-      {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
-            edge_values_per_cell(cell_id, i_edge) = value++;
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = value++;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
-          is_same &= (edge_values_per_cell[i] == i);
-        }
-        REQUIRE(is_same);
-      }
 
-      for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
-        edge_values_per_cell[i] = i * i + 1;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
-            is_same &= (edge_values_per_cell(cell_id, i_edge) == i * i + 1);
-          }
-        }
-        REQUIRE(is_same);
-      }
-    }
+          NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
 
-    SECTION("2D")
-    {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-      CellValuePerFace<size_t> cell_values_per_face{connectivity};
-      {
-        size_t value = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
-            cell_values_per_face(face_id, i_cell) = value++;
+            REQUIRE(is_same);
           }
-        }
-      }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
-          is_same &= (cell_values_per_face[i] == i);
-        }
-        REQUIRE(is_same);
-      }
-      for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
-        cell_values_per_face[i] = 3 * i + 1;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
-            is_same &= (cell_values_per_face(face_id, i_cell) == 3 * i + 1);
-          }
-        }
-        REQUIRE(is_same);
-      }
-    }
-
-    SECTION("3D")
-    {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
 
-      FaceValuePerNode<size_t> face_values_per_node{connectivity};
-      {
-        size_t value = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
-            face_values_per_node.itemValues(node_id)[i_face] = value++;
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
-          is_same &= (face_values_per_node[i] == i);
-        }
-        REQUIRE(is_same);
-      }
 
-      for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
-        face_values_per_node[i] = 3 + i * i;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
-            is_same &= (face_values_per_node.itemValues(node_id)[i_face] == 3 + i * i);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
+
+            REQUIRE(not is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
   }
 
-  SECTION("copy")
+  SECTION("WeakSubItemValuePerItem")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    SECTION("classic")
-    {
-      NodeValuePerCell<size_t> node_value_per_cell{connectivity};
-
-      {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = value++;
-          }
-        }
-      }
-
-      NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
+    for (auto [section_name, mesh_2d] : mesh_list) {
+      SECTION(section_name)
       {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
-        }
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-        REQUIRE(is_same);
-      }
+        WeakFaceValuePerCell<int> weak_face_value_per_cell{connectivity};
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
-          }
+        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
+          weak_face_value_per_cell[i] = i;
         }
-      }
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
-        }
+        FaceValuePerCell<const int> face_value_per_cell{weak_face_value_per_cell};
 
-        REQUIRE(not is_same);
-      }
-    }
-
-    SECTION("from weak")
-    {
-      WeakNodeValuePerCell<size_t> node_value_per_cell{connectivity};
+        REQUIRE(face_value_per_cell.connectivity_ptr() == weak_face_value_per_cell.connectivity_ptr());
 
-      {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = value++;
-          }
-        }
-      }
-
-      NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
-
-      {
         bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
+          is_same &= (face_value_per_cell[i] == weak_face_value_per_cell[i]);
         }
-
         REQUIRE(is_same);
       }
-
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
-          }
-        }
-      }
-
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
-        }
-
-        REQUIRE(not is_same);
-      }
     }
   }
 
-  SECTION("WeakSubItemValuePerItem")
-  {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesian2DMesh();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-    WeakFaceValuePerCell<int> weak_face_value_per_cell{connectivity};
-
-    for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
-      weak_face_value_per_cell[i] = i;
-    }
-
-    FaceValuePerCell<const int> face_value_per_cell{weak_face_value_per_cell};
-
-    REQUIRE(face_value_per_cell.connectivity_ptr() == weak_face_value_per_cell.connectivity_ptr());
-
-    bool is_same = true;
-    for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
-      is_same &= (face_value_per_cell[i] == weak_face_value_per_cell[i]);
-    }
-    REQUIRE(is_same);
-  }
-
 #ifndef NDEBUG
   SECTION("error")
   {
@@ -758,59 +806,65 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesian3DMesh();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValuePerFace<int> cell_value_per_face{connectivity};
-      {
-        FaceId invalid_face_id = connectivity.numberOfFaces();
-        REQUIRE_THROWS_AS(cell_value_per_face(invalid_face_id, 0), AssertError);
-      }
-      if (connectivity.numberOfFaces() > 0) {
-        FaceId face_id          = 0;
-        const auto& cell_values = cell_value_per_face.itemValues(face_id);
-        REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError);
-        REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError);
-        REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError);
-      }
+      for (auto [section_name, mesh_3d] : mesh_list) {
+        SECTION(section_name)
+        {
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      FaceValuePerNode<int> face_value_per_node{connectivity};
-      {
-        NodeId invalid_node_id = connectivity.numberOfNodes();
-        REQUIRE_THROWS_AS(face_value_per_node(invalid_node_id, 0), AssertError);
-      }
-      if (connectivity.numberOfNodes() > 0) {
-        NodeId node_id          = 0;
-        const auto& face_values = face_value_per_node.itemValues(node_id);
-        REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError);
-        REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError);
-        REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError);
-      }
+          CellValuePerFace<int> cell_value_per_face{connectivity};
+          {
+            FaceId invalid_face_id = connectivity.numberOfFaces();
+            REQUIRE_THROWS_AS(cell_value_per_face(invalid_face_id, 0), AssertError);
+          }
+          if (connectivity.numberOfFaces() > 0) {
+            FaceId face_id          = 0;
+            const auto& cell_values = cell_value_per_face.itemValues(face_id);
+            REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError);
+            REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError);
+            REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError);
+          }
 
-      EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-      {
-        CellId invalid_cell_id = connectivity.numberOfCells();
-        REQUIRE_THROWS_AS(edge_value_per_cell(invalid_cell_id, 0), AssertError);
-      }
-      if (connectivity.numberOfCells() > 0) {
-        CellId cell_id          = 0;
-        const auto& edge_values = edge_value_per_cell.itemValues(cell_id);
-        REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError);
-        REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError);
-        REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError);
-      }
+          FaceValuePerNode<int> face_value_per_node{connectivity};
+          {
+            NodeId invalid_node_id = connectivity.numberOfNodes();
+            REQUIRE_THROWS_AS(face_value_per_node(invalid_node_id, 0), AssertError);
+          }
+          if (connectivity.numberOfNodes() > 0) {
+            NodeId node_id          = 0;
+            const auto& face_values = face_value_per_node.itemValues(node_id);
+            REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError);
+            REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError);
+            REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError);
+          }
 
-      NodeValuePerEdge<int> node_value_per_edge{connectivity};
-      {
-        EdgeId invalid_edge_id = connectivity.numberOfEdges();
-        REQUIRE_THROWS_AS(node_value_per_edge(invalid_edge_id, 0), AssertError);
-      }
-      if (connectivity.numberOfEdges() > 0) {
-        EdgeId edge_id          = 0;
-        const auto& node_values = node_value_per_edge.itemValues(edge_id);
-        REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError);
-        REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError);
-        REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError);
+          EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+          {
+            CellId invalid_cell_id = connectivity.numberOfCells();
+            REQUIRE_THROWS_AS(edge_value_per_cell(invalid_cell_id, 0), AssertError);
+          }
+          if (connectivity.numberOfCells() > 0) {
+            CellId cell_id          = 0;
+            const auto& edge_values = edge_value_per_cell.itemValues(cell_id);
+            REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError);
+            REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError);
+            REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError);
+          }
+
+          NodeValuePerEdge<int> node_value_per_edge{connectivity};
+          {
+            EdgeId invalid_edge_id = connectivity.numberOfEdges();
+            REQUIRE_THROWS_AS(node_value_per_edge(invalid_edge_id, 0), AssertError);
+          }
+          if (connectivity.numberOfEdges() > 0) {
+            EdgeId edge_id          = 0;
+            const auto& node_values = node_value_per_edge.itemValues(edge_id);
+            REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError);
+            REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError);
+            REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError);
+          }
+        }
       }
     }
   }