diff --git a/tests/test_PolynomialReconstruction_degree_1.cpp b/tests/test_PolynomialReconstruction_degree_1.cpp
index 7c4f07266514eaf8b277af5f26751c2ec17bc8ae..6c333913e85fa811abe34af119176d3d7209f156 100644
--- a/tests/test_PolynomialReconstruction_degree_1.cpp
+++ b/tests/test_PolynomialReconstruction_degree_1.cpp
@@ -1196,24 +1196,24 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]")
       }
     }
 
-    std::vector<PolynomialReconstructionDescriptor> descriptor_list =
-      {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1,
-                                          std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
-                                            std::make_shared<NamedBoundaryDescriptor>("XMIN")}},
-       PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1,
-                                          std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
-                                            std::make_shared<NamedBoundaryDescriptor>("XMIN")}},
-       PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1,
-                                          std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
-                                            std::make_shared<NamedBoundaryDescriptor>("XMIN")}}};
-
-    for (auto descriptor : descriptor_list) {
-      SECTION(name(descriptor.integrationMethodType()))
-      {
-        SECTION("1D")
+    SECTION("1D")
+    {
+      std::vector<PolynomialReconstructionDescriptor> descriptor_list =
+        {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1,
+                                            std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
+                                              std::make_shared<NamedBoundaryDescriptor>("XMIN")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1,
+                                            std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
+                                              std::make_shared<NamedBoundaryDescriptor>("XMIN")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1,
+                                            std::vector<std::shared_ptr<const IBoundaryDescriptor>>{
+                                              std::make_shared<NamedBoundaryDescriptor>("XMIN")}}};
+
+      using R1 = TinyVector<1>;
+
+      for (auto descriptor : descriptor_list) {
+        SECTION(name(descriptor.integrationMethodType()))
         {
-          using R1 = TinyVector<1>;
-
           SECTION("R^1 data")
           {
             auto p_mesh = MeshDataBaseForTests::get().unordered1DMesh()->get<Mesh<1>>();
@@ -1323,588 +1323,381 @@ TEST_CASE("PolynomialReconstruction_degree_1", "[scheme]")
             }
           }
         }
+      }
+    }
 
-        SECTION("2D")
+    SECTION("2D")
+    {
+      std::vector<PolynomialReconstructionDescriptor> descriptor_list =
+        {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX")}}};
+
+      using R2 = TinyVector<2>;
+
+      for (auto descriptor : descriptor_list) {
+        SECTION(name(descriptor.integrationMethodType()))
         {
-#warning not done
-          using R2 = TinyVector<2>;
+          SECTION("R^2 data")
+          {
+            auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
+            auto& mesh  = *p_mesh;
+
+            auto R2_affine = [](const R2& x) -> R2 { return R2{2.3 * (x[0] - 2), -1.3 * (x[1] - 1)}; };
+            auto xj        = MeshDataManager::instance().getMeshData(mesh).xj();
+
+            DiscreteFunctionP0<R2> fh{p_mesh};
+
+            parallel_for(
+              mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R2_affine(xj[cell_id]); });
+
+            auto reconstructions = PolynomialReconstruction{descriptor}.build(fh);
 
-          // SECTION("R data")
-          // {
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R_affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; };
-          //       auto xj       = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<double> fh{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(fh);
-
-          //       auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const double>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const double reconstructed_slope =
-          //             (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2;
-
-          //           max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const double reconstructed_slope =
-          //             (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1})) / 0.2;
-
-          //           max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("R^3 data")
-          // {
-          //   using R3 = TinyVector<3>;
-
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R3_affine = [](const R2& x) -> R3 {
-          //         return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1],   //
-          //                   +1.4 - 0.6 * x[0] + 1.3 * x[1],   //
-          //                   -0.2 + 3.1 * x[0] - 1.1 * x[1]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<R3> uh{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(uh);
-
-          //       auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R3>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0.1, 0} + xj[cell_id]) -
-          //                                                       dpk_uh[cell_id](xj[cell_id] - R2{0.1, 0}));
-
-          //           max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7,
-          //           -0.6, 3.1}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R2{0, 0.1} + xj[cell_id]) -
-          //                                                       dpk_uh[cell_id](xj[cell_id] - R2{0, 0.1}));
-
-          //           max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3,
-          //           -1.1}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("R^2x2 data")
-          // {
-          //   using R2x2 = TinyMatrix<2, 2>;
-
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R2x2_affine = [](const R2& x) -> R2x2 {
-          //         return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1],   //
-          //                     +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<R2x2> Ah{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]);
-          //         });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah);
-
-          //       auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2x2>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) -
-          //             R2x2_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0.1, 0} + xj[cell_id]) -
-          //                                                         dpk_Ah[cell_id](xj[cell_id] - R2{0.1, 0}));
-
-          //           max_x_slope_error =
-          //             std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1,   //
-          //                                                                                  -0.6, -2.3}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R2{0, 0.1} + xj[cell_id]) -
-          //                                                         dpk_Ah[cell_id](xj[cell_id] - R2{0, 0.1}));
-
-          //           max_y_slope_error =
-          //             std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2,   //
-          //                                                                                  -2.1, +1.3}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("vector data")
-          // {
-          //   using R4 = TinyVector<4>;
-
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto vector_affine = [](const R2& x) -> R4 {
-          //         return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1], -1.7 + 2.1 * x[0] - 2.2 * x[1],   //
-          //                   +1.4 - 0.6 * x[0] - 2.1 * x[1], +2.4 - 2.3 * x[0] + 1.3 * x[1]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0Vector<double> Vh{p_mesh, 4};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-          //           auto vector = vector_affine(xj[cell_id]);
-          //           for (size_t i = 0; i < vector.dimension(); ++i) {
-          //             Vh[cell_id][i] = vector[i];
-          //           }
-          //         });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh);
-
-          //       auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const double>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           auto vector = vector_affine(xj[cell_id]);
-          //           for (size_t i = 0; i < vector.dimension(); ++i) {
-          //             max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) -
-          //             vector[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         const R4 slope{+1.7, +2.1, -0.6, -2.3};
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           for (size_t i = 0; i < slope.dimension(); ++i) {
-          //             const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0.1, 0} + xj[cell_id]) -
-          //                                                             dpk_Vh(cell_id, i)(xj[cell_id] - R2{0.1, 0}));
-
-          //             max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         const R4 slope{+1.2, -2.2, -2.1, +1.3};
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           for (size_t i = 0; i < slope.dimension(); ++i) {
-          //             const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R2{0, 0.1} + xj[cell_id]) -
-          //                                                             dpk_Vh(cell_id, i)(xj[cell_id] - R2{0, 0.1}));
-
-          //             max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-          //     }
-          //   }
-          // }
+            auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<2, const R2>>();
+
+            {
+              double max_mean_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                max_mean_error =
+                  std::max(max_mean_error, l2Norm(dpk_fh[cell_id](xj[cell_id]) - R2_affine(xj[cell_id])));
+              }
+              REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
+            }
+
+            {
+              double max_x_slope_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R2 reconstructed_slope =
+                  1. / 0.2 * (dpk_fh[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0.1, 0}));
+
+                max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R2{2.3, 0}));
+              }
+              REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+
+            {
+              double max_y_slope_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R2 reconstructed_slope =
+                  1 / 0.2 * (dpk_fh[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R2{0, 0.1}));
+
+                max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R2{0, -1.3})));
+              }
+              REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12));
+            }
+          }
+
+          SECTION("vector of R2")
+          {
+            using R4 = TinyVector<4>;
+
+            auto p_mesh = MeshDataBaseForTests::get().hybrid2DMesh()->get<Mesh<2>>();
+            auto& mesh  = *p_mesh;
+
+            auto vector_affine = [](const R2& x) -> R4 {
+              return R4{+1.7 * (x[0] - 2),   //
+                        -0.6 * (x[1] - 1),   //
+                        -2.3 * (x[0] - 2),   //
+                        +1.1 * (x[1] - 1)};
+            };
+            auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
+
+            DiscreteFunctionP0Vector<R2> Vh{p_mesh, 2};
+
+            parallel_for(
+              mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                auto vector       = vector_affine(xj[cell_id]);
+                Vh[cell_id][0][0] = vector[0];
+                Vh[cell_id][0][1] = vector[1];
+                Vh[cell_id][1][0] = vector[2];
+                Vh[cell_id][1][1] = vector[3];
+              });
+
+            auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh);
+
+            auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<2, const R2>>();
+
+            {
+              double max_mean_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                auto vector = vector_affine(xj[cell_id]);
+
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[2]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[3]));
+              }
+              REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
+            }
+
+            {
+              double max_x_slope_error = 0;
+              const R4 slope{+1.7, 0, -2.3, 0};
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0.1, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0.1, 0}));
+
+                const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0.1, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0.1, 0}));
+
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[2]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[3]));
+              }
+              REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+
+            {
+              double max_y_slope_error = 0;
+              const R4 slope{0, -0.6, 0, 1.1};
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R2 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R2{0, 0.1} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 0)(xj[cell_id] - R2{0, 0.1}));
+
+                const R2 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R2{0, 0.1} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 1)(xj[cell_id] - R2{0, 0.1}));
+
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[2]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[3]));
+              }
+              REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+          }
         }
+      }
+    }
 
-        SECTION("3D")
+    SECTION("3D")
+    {
+      std::vector<PolynomialReconstructionDescriptor> descriptor_list =
+        {PolynomialReconstructionDescriptor{IntegrationMethodType::cell_center, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "ZMAX")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::element, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "ZMAX")}},
+         PolynomialReconstructionDescriptor{IntegrationMethodType::boundary, 1,
+                                            std::vector<std::shared_ptr<
+                                              const IBoundaryDescriptor>>{std::
+                                                                            make_shared<NamedBoundaryDescriptor>(
+                                                                              "XMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "YMAX"),
+                                                                          std::make_shared<NamedBoundaryDescriptor>(
+                                                                            "ZMAX")}}};
+
+      using R3 = TinyVector<3>;
+
+      for (auto descriptor : descriptor_list) {
+        SECTION(name(descriptor.integrationMethodType()))
         {
-          using R3 = TinyVector<3>;
-#warning not done
-
-          // SECTION("R data")
-          // {
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R_affine = [](const R3& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1] + 2.1 * x[2]; };
-          //       auto xj       = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<double> fh{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(fh);
-
-          //       auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const double>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, std::abs(dpk_fh[cell_id](xj[cell_id]) - R_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const double reconstructed_slope =
-          //             (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0})) /
-          //             0.2;
-
-          //           max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const double reconstructed_slope =
-          //             (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0})) /
-          //             0.2;
-
-          //           max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_z_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const double reconstructed_slope =
-          //             (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1})) /
-          //             0.2;
-
-          //           max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1));
-          //         }//         REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("R^3 data")
-          // {
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R3_affine = [](const R3& x) -> R3 {
-          //         return R3{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2],   //
-          //                   +1.4 - 0.6 * x[0] + 1.3 * x[1] - 3.7 * x[2],   //
-          //                   -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<R3> uh{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
-
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(uh);
-
-          //       auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) -
-          //                                                       dpk_uh[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
-
-          //           max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7,
-          //           -0.6, 3.1}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) -
-          //                                                       dpk_uh[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
-
-          //           max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3,
-          //           -1.1}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-
-          //       {
-          //         double max_z_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R3 reconstructed_slope = (1 / 0.2) * (dpk_uh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) -
-          //                                                       dpk_uh[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
-
-          //           max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8,
-          //           -3.7, 1.9}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("R^2x2 data")
-          // {
-          //   using R2x2 = TinyMatrix<2, 2>;
-
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto R2x2_affine = [](const R3& x) -> R2x2 {
-          //         return R2x2{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 *
-          //         x[2],
-          //                     //
-          //                     +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 *
-          //                     x[2]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0<R2x2> Ah{p_mesh};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]);
-          //         });
-
-          //       descriptor.setRowWeighting(false);
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(Ah);
-
-          //       auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<3, const R2x2>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           max_mean_error =
-          //             std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) -
-          //             R2x2_affine(xj[cell_id])));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0.1, 0, 0} + xj[cell_id]) -
-          //                                                         dpk_Ah[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
-
-          //           max_x_slope_error =
-          //             std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1,   //
-          //                                                                                  -2.3, +3.1}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0.1, 0} + xj[cell_id]) -
-          //                                                         dpk_Ah[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
-
-          //           max_y_slope_error =
-          //             std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2,   //
-          //                                                                                  1.3, +0.8}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-
-          //       {
-          //         double max_z_slope_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           const R2x2 reconstructed_slope = (1 / 0.2) * (dpk_Ah[cell_id](R3{0, 0, 0.1} + xj[cell_id]) -
-          //                                                         dpk_Ah[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
-
-          //           max_z_slope_error =
-          //             std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4,   //
-          //                                                                                  +1.4, -1.8}));
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-          //     }
-          //   }
-          // }
-
-          // SECTION("vector data")
-          // {
-          //   using R4 = TinyVector<4>;
-
-          //   for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
-          //     SECTION(named_mesh.name())
-          //     {
-          //       auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
-          //       auto& mesh  = *p_mesh;
-
-          //       auto vector_affine = [](const R3& x) -> R4 {
-          //         return R4{+2.3 + 1.7 * x[0] + 1.2 * x[1] - 1.3 * x[2], -1.7 + 2.1 * x[0] - 2.2 * x[1] - 2.4 * x[2],
-          //                   //
-          //                   +2.4 - 2.3 * x[0] + 1.3 * x[1] + 1.4 * x[2], -0.2 + 3.1 * x[0] + 0.8 * x[1] - 1.8 *
-          //                   x[2]};
-          //       };
-          //       auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
-
-          //       DiscreteFunctionP0Vector<double> Vh{p_mesh, 4};
-
-          //       parallel_for(
-          //         mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
-          //           auto vector = vector_affine(xj[cell_id]);
-          //           for (size_t i = 0; i < vector.dimension(); ++i) {
-          //             Vh[cell_id][i] = vector[i];
-          //           }
-          //         });
-
-          //       descriptor.setPreconditioning(false);
-          //       auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh);
-
-          //       auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const double>>();
-
-          //       {
-          //         double max_mean_error = 0;
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           auto vector = vector_affine(xj[cell_id]);
-          //           for (size_t i = 0; i < vector.dimension(); ++i) {
-          //             max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, i)(xj[cell_id]) -
-          //             vector[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
-          //       }
-
-          //       {
-          //         double max_x_slope_error = 0;
-          //         const R4 slope{+1.7, 2.1, -2.3, +3.1};
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           for (size_t i = 0; i < slope.dimension(); ++i) {
-          //             const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0.1, 0, 0} + xj[cell_id])
-          //             -
-          //                                                             dpk_Vh(cell_id, i)(xj[cell_id] - R3{0.1, 0,
-          //                                                             0}));
-
-          //             max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - slope[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-
-          //       {
-          //         double max_y_slope_error = 0;
-          //         const R4 slope{+1.2, -2.2, 1.3, +0.8};
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           for (size_t i = 0; i < slope.dimension(); ++i) {
-          //             const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0.1, 0} + xj[cell_id])
-          //             -
-          //                                                             dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0.1,
-          //                                                             0}));
-
-          //             max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - slope[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12));
-          //       }
-
-          //       {
-          //         double max_z_slope_error = 0;
-          //         const R4 slope{-1.3, -2.4, +1.4, -1.8};
-          //         for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-          //           for (size_t i = 0; i < slope.dimension(); ++i) {
-          //             const double reconstructed_slope = (1 / 0.2) * (dpk_Vh(cell_id, i)(R3{0, 0, 0.1} + xj[cell_id])
-          //             -
-          //                                                             dpk_Vh(cell_id, i)(xj[cell_id] - R3{0, 0,
-          //                                                             0.1}));
-
-          //             max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - slope[i]));
-          //           }
-          //         }
-          //         REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13));
-          //       }
-          //     }
-          //   }
-          // }
+          SECTION("R^3 data")
+          {
+            auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
+            auto& mesh  = *p_mesh;
+
+            auto R3_affine = [](const R3& x) -> R3 {
+              return R3{2.3 * (x[0] - 2), -1.3 * (x[1] - 1), 1.4 * (x[2] - 1)};
+            };
+            auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
+
+            DiscreteFunctionP0<R3> fh{p_mesh};
+
+            parallel_for(
+              mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R3_affine(xj[cell_id]); });
+
+            auto reconstructions = PolynomialReconstruction{descriptor}.build(fh);
+
+            auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<3, const R3>>();
+
+            {
+              double max_mean_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                max_mean_error =
+                  std::max(max_mean_error, l2Norm(dpk_fh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
+              }
+              REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
+            }
+
+            {
+              double max_x_slope_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope =
+                  1. / 0.2 *
+                  (dpk_fh[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
+
+                max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{2.3, 0, 0}));
+              }
+              REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+
+            {
+              double max_y_slope_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope =
+                  1 / 0.2 *
+                  (dpk_fh[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
+
+                max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - (R3{0, -1.3, 0})));
+              }
+              REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-12));
+            }
+
+            {
+              double max_z_slope_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope =
+                  1 / 0.2 *
+                  (dpk_fh[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
+
+                max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - (R3{0, 0, 1.4})));
+              }
+              REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-12));
+            }
+          }
+
+          SECTION("vector of R2")
+          {
+            using R6 = TinyVector<6>;
+
+            auto p_mesh = MeshDataBaseForTests::get().hybrid3DMesh()->get<Mesh<3>>();
+            auto& mesh  = *p_mesh;
+
+            auto vector_affine = [](const R3& x) -> R6 {
+              return R6{+1.7 * (x[0] - 2),   //
+                        -0.6 * (x[1] - 1),   //
+                        +1.2 * (x[2] - 1),   //
+                        -2.3 * (x[0] - 2),   //
+                        +1.1 * (x[1] - 1),   //
+                        -0.3 * (x[2] - 1)};
+            };
+            auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
+
+            DiscreteFunctionP0Vector<R3> Vh{p_mesh, 2};
+
+            parallel_for(
+              mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                auto vector       = vector_affine(xj[cell_id]);
+                Vh[cell_id][0][0] = vector[0];
+                Vh[cell_id][0][1] = vector[1];
+                Vh[cell_id][0][2] = vector[2];
+                Vh[cell_id][1][0] = vector[3];
+                Vh[cell_id][1][1] = vector[4];
+                Vh[cell_id][1][2] = vector[5];
+              });
+
+            auto reconstructions = PolynomialReconstruction{descriptor}.build(Vh);
+
+            auto dpk_Vh = reconstructions[0]->get<DiscreteFunctionDPkVector<3, const R3>>();
+
+            {
+              double max_mean_error = 0;
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                auto vector = vector_affine(xj[cell_id]);
+
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[0] - vector[0]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[1] - vector[1]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 0)(xj[cell_id])[2] - vector[2]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[0] - vector[3]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[1] - vector[4]));
+                max_mean_error = std::max(max_mean_error, std::abs(dpk_Vh(cell_id, 1)(xj[cell_id])[2] - vector[5]));
+              }
+              REQUIRE(parallel::allReduceMax(max_mean_error) == Catch::Approx(0).margin(1E-14));
+            }
+
+            {
+              double max_x_slope_error = 0;
+              const R6 slope{+1.7, 0, 0, -2.3, 0, 0};
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0.1, 0, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0.1, 0, 0}));
+
+                const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0.1, 0, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0.1, 0, 0}));
+
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[0] - slope[0]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[1] - slope[1]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope0[2] - slope[2]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[0] - slope[3]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[1] - slope[4]));
+                max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope1[2] - slope[5]));
+              }
+              REQUIRE(parallel::allReduceMax(max_x_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+
+            {
+              double max_y_slope_error = 0;
+              const R6 slope{0, -0.6, 0, 0, 1.1, 0};
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0.1, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0.1, 0}));
+
+                const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0.1, 0} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0.1, 0}));
+
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[0] - slope[0]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[1] - slope[1]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope0[2] - slope[2]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[0] - slope[3]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[1] - slope[4]));
+                max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope1[2] - slope[5]));
+              }
+              REQUIRE(parallel::allReduceMax(max_y_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+
+            {
+              double max_z_slope_error = 0;
+              const R6 slope{0, 0, 1.2, 0, 0, -0.3};
+              for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+                const R3 reconstructed_slope0 = (1 / 0.2) * (dpk_Vh(cell_id, 0)(R3{0, 0, 0.1} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 0)(xj[cell_id] - R3{0, 0, 0.1}));
+
+                const R3 reconstructed_slope1 = (1 / 0.2) * (dpk_Vh(cell_id, 1)(R3{0, 0, 0.1} + xj[cell_id]) -
+                                                             dpk_Vh(cell_id, 1)(xj[cell_id] - R3{0, 0, 0.1}));
+
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[0] - slope[0]));
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[1] - slope[1]));
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope0[2] - slope[2]));
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[0] - slope[3]));
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[1] - slope[4]));
+                max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope1[2] - slope[5]));
+              }
+              REQUIRE(parallel::allReduceMax(max_z_slope_error) == Catch::Approx(0).margin(1E-13));
+            }
+          }
         }
       }
     }