diff --git a/tests/test_EdgeIntegrator.cpp b/tests/test_EdgeIntegrator.cpp
index f8ea09cee6bdeef7352281f207e2dfcc3b66e408..5dfccbc6c7427a2bd27634c49957209c61c6f3ce 100644
--- a/tests/test_EdgeIntegrator.cpp
+++ b/tests/test_EdgeIntegrator.cpp
@@ -1,6 +1,7 @@
 #include <catch2/catch_approx.hpp>
 #include <catch2/catch_test_macros.hpp>
 
+#include <algebra/TinyMatrix.hpp>
 #include <analysis/GaussLegendreQuadratureDescriptor.hpp>
 #include <analysis/GaussLobattoQuadratureDescriptor.hpp>
 #include <analysis/GaussQuadratureDescriptor.hpp>
@@ -15,242 +16,745 @@
 
 TEST_CASE("EdgeIntegrator", "[scheme]")
 {
-  SECTION("3D")
+  SECTION("scalar")
   {
-    using R3 = TinyVector<3>;
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const double> int_f_per_edge = [=] {
+          Array<double> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              double integral     = 0;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
 
-    auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+              int_f[edge_id] = integral;
+            });
 
-    auto f = [](const R3& X) -> double {
-      const double x = X[0];
-      const double y = X[1];
-      const double z = X[2];
-      return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
-    };
+          return int_f;
+        }();
 
-    std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
-    mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
-    mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
-
-    for (auto mesh_info : mesh_list) {
-      auto mesh_name = mesh_info.first;
-      auto mesh      = mesh_info.second;
-
-      Array<const double> int_f_per_edge = [=] {
-        Array<double> int_f(mesh->numberOfEdges());
-        auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
-
-        parallel_for(
-          mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
-            auto edge_node_list = edge_to_node_matrix[edge_id];
-            auto xr             = mesh->xr();
-            double integral     = 0;
-
-            LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
-            auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
-            for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
-              const auto& xi = qf.point(i);
-              integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
-            }
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<double> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
 
-            int_f[edge_id] = integral;
-          });
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
-        return int_f;
-      }();
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
 
-      SECTION(mesh_name)
-      {
-        SECTION("direct formula")
-        {
-          SECTION("all edges")
-          {
-            SECTION("EdgeValue")
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
             {
-              EdgeValue<double> values(mesh->connectivity());
-              EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
-                                          values);
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<double> values =
+                  EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
+          }
 
-            SECTION("Array")
+          SECTION("tensorial formula")
+          {
+            SECTION("all edges")
             {
-              Array<double> values(mesh->numberOfEdges());
+              SECTION("EdgeValue")
+              {
+                EdgeValue<double> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
 
-              EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
 
-            SECTION("SmallArray")
+            SECTION("edge list")
             {
-              SmallArray<double> values(mesh->numberOfEdges());
-              EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<double> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<double> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
           }
+        }
+      }
+    }
+  }
+
+  SECTION("vector")
+  {
+    using R2 = TinyVector<2>;
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x + 3 * y - 1};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2> int_f_per_edge = [=] {
+          Array<R2> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              R2 integral         = zero;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
+
+              int_f[edge_id] = integral;
+            });
 
-          SECTION("edge list")
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
           {
-            SECTION("Array")
+            SECTION("all edges")
             {
-              Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
 
+              SECTION("SmallArray")
               {
-                size_t k = 0;
-                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
-                  edge_list[k] = edge_id;
+                SmallArray<R2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
                 }
 
-                REQUIRE(k == edge_list.size());
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
+            }
 
-              Array<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
 
-              double error = 0;
-              for (size_t i = 0; i < edge_list.size(); ++i) {
-                error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
+          }
 
-            SECTION("SmallArray")
+          SECTION("tensorial formula")
+          {
+            SECTION("all edges")
             {
-              SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
               {
-                size_t k = 0;
-                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
-                  edge_list[k] = edge_id;
+                Array<R2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
                 }
 
-                REQUIRE(k == edge_list.size());
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              SmallArray<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
-              double error = 0;
-              for (size_t i = 0; i < edge_list.size(); ++i) {
-                error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2> values = EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
           }
         }
+      }
+    }
+  }
 
-        SECTION("tensorial formula")
+  SECTION("matrix")
+  {
+    using R2x2  = TinyMatrix<2>;
+    auto l2Norm = [](const R2x2& A) -> double {
+      return std::sqrt(A(0, 0) * A(0, 0) + A(1, 0) * A(1, 0) + A(0, 1) * A(0, 1) + A(1, 1) * A(1, 1));
+    };
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x + 3 * y - 1, 2 * x * x + 3 * x * y - z * z,
+                    3 - 2 * x + 3 * y};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2x2> int_f_per_edge = [=] {
+          Array<R2x2> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              R2x2 integral       = zero;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
+
+              int_f[edge_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
         {
-          SECTION("all edges")
+          SECTION("direct formula")
           {
-            SECTION("EdgeValue")
+            SECTION("all edges")
             {
-              EdgeValue<double> values(mesh->connectivity());
-              EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
-                                          *mesh, values);
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2x2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
-            }
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfEdges());
 
-            SECTION("Array")
-            {
-              Array<double> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
 
-              EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
 
-            SECTION("SmallArray")
+            SECTION("edge list")
             {
-              SmallArray<double> values(mesh->numberOfEdges());
-              EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
 
-              double error = 0;
-              for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
-                error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2x2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2x2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
           }
 
-          SECTION("edge list")
+          SECTION("tensorial formula")
           {
-            SECTION("Array")
+            SECTION("all edges")
             {
-              Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
-
+              SECTION("EdgeValue")
               {
-                size_t k = 0;
-                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
-                  edge_list[k] = edge_id;
+                EdgeValue<R2x2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
                 }
 
-                REQUIRE(k == edge_list.size());
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              Array<double> values =
-                EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
 
-              double error = 0;
-              for (size_t i = 0; i < edge_list.size(); ++i) {
-                error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
 
-            SECTION("SmallArray")
+            SECTION("edge list")
             {
-              SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
-
+              SECTION("Array")
               {
-                size_t k = 0;
-                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
-                  edge_list[k] = edge_id;
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
                 }
 
-                REQUIRE(k == edge_list.size());
-              }
+                Array<R2x2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
 
-              SmallArray<double> values =
-                EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
 
-              double error = 0;
-              for (size_t i = 0; i < edge_list.size(); ++i) {
-                error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
               }
 
-              REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2x2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
             }
           }
         }