diff --git a/tests/test_PolynomialReconstruction.cpp b/tests/test_PolynomialReconstruction.cpp
index 3c3d3e0dc7b647728597059553741588a0060cf3..12307226697c6a6aa0ce1a414b81e49241914b74 100644
--- a/tests/test_PolynomialReconstruction.cpp
+++ b/tests/test_PolynomialReconstruction.cpp
@@ -33,33 +33,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; };
-          auto xj     = MeshDataManager::instance().getMeshData(mesh).xj();
+          auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; };
+          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] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
 
-          DiscreteFunctionP0<double> gh{p_mesh};
+          auto reconstructions = PolynomialReconstruction{}.build(fh);
 
-          parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { gh[cell_id] = 0; });
-
-          DiscreteFunctionVariant gh_v(gh);
-          std::shared_ptr<const DiscreteFunctionVariant> p_gh_v = std::make_shared<DiscreteFunctionVariant>(fh);
-
-          std::shared_ptr<DiscreteFunctionP0<const double>> g_fh =
-            std::make_shared<DiscreteFunctionP0<const double>>(fh);
-
-          auto reconstructions = PolynomialReconstruction{}.build(g_fh, p_gh_v, gh_v, fh);
-
-          auto dpk = reconstructions[3]->get<DiscreteFunctionDPk<1, const double>>();
+          auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, 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[cell_id](xj[cell_id]) - affine(xj[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(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -68,7 +57,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1})) / 0.2;
+                (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2;
 
               max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7));
             }
@@ -78,7 +67,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^d data")
+    SECTION("R^3 data")
     {
       using R3 = TinyVector<3>;
 
@@ -88,25 +77,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R1& x) -> R3 {
+          auto R3_affine = [](const R1& x) -> R3 {
             return R3{+2.3 + 1.7 * x[0],   //
                       +1.4 - 0.6 * x[0],   //
                       -0.2 + 3.1 * x[0]};
           };
           auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
 
-          DiscreteFunctionP0<R3> fh{p_mesh};
+          DiscreteFunctionP0<R3> uh{p_mesh};
 
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(uh);
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          auto dpk_uh = reconstructions[0]->get<DiscreteFunctionDPk<1, 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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -115,7 +105,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const R3 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1}));
+                (1 / 0.2) * (dpk_uh[cell_id](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1}));
 
               max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1}));
             }
@@ -125,9 +115,66 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^mn data")
+    SECTION("R^3x3 data")
+    {
+      using R3x3 = TinyMatrix<3, 3>;
+
+      for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
+        SECTION(named_mesh.name())
+        {
+          auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
+          auto& mesh  = *p_mesh;
+
+          auto R3x3_affine = [](const R1& x) -> R3x3 {
+            return R3x3{
+              +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0],   //
+              +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0],
+              -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0],
+            };
+          };
+          auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
+
+          DiscreteFunctionP0<R3x3> Ah{p_mesh};
+
+          parallel_for(
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(Ah);
+
+          auto dpk_Ah = reconstructions[0]->get<DiscreteFunctionDPk<1, const R3x3>>();
+
+          {
+            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]) - R3x3_affine(xj[cell_id])));
+            }
+            REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
+          }
+
+          {
+            double max_slope_error = 0;
+            for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+              const R3x3 reconstructed_slope =
+                (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1}));
+
+              R3x3 slops = R3x3{+1.7, +2.1, -0.6,   //
+                                -2.3, +3.1, -3.6,   //
+                                +3.1, +2.9, +2.3};
+
+              max_slope_error = std::max(max_slope_error,   //
+                                         frobeniusNorm(reconstructed_slope - slops));
+            }
+            REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-13));
+          }
+        }
+      }
+    }
+
+    SECTION("list of various types")
     {
-      using R33 = TinyMatrix<3, 3>;
+      using R3x3 = TinyMatrix<3>;
+      using R3   = TinyVector<3>;
 
       for (auto named_mesh : MeshDataBaseForTests::get().all1DMeshes()) {
         SECTION(named_mesh.name())
@@ -135,27 +182,88 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<1>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R1& x) -> R33 {
-            return R33{
+          auto R_affine = [](const R1& x) { return 2.3 + 1.7 * x[0]; };
+
+          auto R3_affine = [](const R1& x) -> R3 {
+            return R3{+2.3 + 1.7 * x[0],   //
+                      +1.4 - 0.6 * x[0],   //
+                      -0.2 + 3.1 * x[0]};
+          };
+
+          auto R3x3_affine = [](const R1& x) -> R3x3 {
+            return R3x3{
               +2.3 + 1.7 * x[0], -1.7 + 2.1 * x[0], +1.4 - 0.6 * x[0],   //
               +2.4 - 2.3 * x[0], -0.2 + 3.1 * x[0], -3.2 - 3.6 * x[0],
               -4.1 + 3.1 * x[0], +0.8 + 2.9 * x[0], -1.6 + 2.3 * x[0],
             };
           };
+
           auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
 
-          DiscreteFunctionP0<R33> fh{p_mesh};
+          DiscreteFunctionP0<double> fh{p_mesh};
+          parallel_for(
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
 
+          DiscreteFunctionP0<R3> uh{p_mesh};
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
+
+          DiscreteFunctionP0<R3x3> Ah{p_mesh};
+          parallel_for(
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R3x3_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(std::make_shared<DiscreteFunctionVariant>(fh), uh,
+                                                                  std::make_shared<DiscreteFunctionP0<R3x3>>(Ah));
+
+          auto dpk_fh = reconstructions[0]->get<DiscreteFunctionDPk<1, 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(max_mean_error == Catch::Approx(0).margin(1E-14));
+          }
+
+          {
+            double max_slope_error = 0;
+            for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
+              const double reconstructed_slope =
+                (dpk_fh[cell_id](R1{0.1} + xj[cell_id]) - dpk_fh[cell_id](xj[cell_id] - R1{0.1})) / 0.2;
+
+              max_slope_error = std::max(max_slope_error, std::abs(reconstructed_slope - 1.7));
+            }
+            REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14));
+          }
+
+          auto dpk_uh = reconstructions[1]->get<DiscreteFunctionDPk<1, const R3>>();
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          {
+            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(max_mean_error == Catch::Approx(0).margin(1E-14));
+          }
+
+          {
+            double max_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](R1{0.1} + xj[cell_id]) - dpk_uh[cell_id](xj[cell_id] - R1{0.1}));
+
+              max_slope_error = std::max(max_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1}));
+            }
+            REQUIRE(max_slope_error == Catch::Approx(0).margin(1E-14));
+          }
+
+          auto dpk_Ah = reconstructions[2]->get<DiscreteFunctionDPk<1, const R3x3>>();
 
           {
             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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error =
+                std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R3x3_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
           }
@@ -163,12 +271,12 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R33 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R1{0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R1{0.1}));
+              const R3x3 reconstructed_slope =
+                (1 / 0.2) * (dpk_Ah[cell_id](R1{0.1} + xj[cell_id]) - dpk_Ah[cell_id](xj[cell_id] - R1{0.1}));
 
-              R33 slops = R33{+1.7, +2.1, -0.6,   //
-                              -2.3, +3.1, -3.6,   //
-                              +3.1, +2.9, +2.3};
+              R3x3 slops = R3x3{+1.7, +2.1, -0.6,   //
+                                -2.3, +3.1, -3.6,   //
+                                +3.1, +2.9, +2.3};
 
               max_slope_error = std::max(max_slope_error,   //
                                          frobeniusNorm(reconstructed_slope - slops));
@@ -192,21 +300,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R2& x) { return 2.3 + 1.7 * x[0] - 1.3 * x[1]; };
-          auto xj     = MeshDataManager::instance().getMeshData(mesh).xj();
+          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] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          auto reconstructions = PolynomialReconstruction{}.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[cell_id](xj[cell_id]) - affine(xj[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(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -215,7 +324,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_x_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0})) / 0.2;
+                (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));
             }
@@ -226,7 +335,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_y_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.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})) / 0.2;
 
               max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
             }
@@ -236,7 +345,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^d data")
+    SECTION("R^3 data")
     {
       using R3 = TinyVector<3>;
 
@@ -246,25 +355,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R2& x) -> R3 {
+          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> fh{p_mesh};
+          DiscreteFunctionP0<R3> uh{p_mesh};
 
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(uh);
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -273,7 +383,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             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[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0}));
+                (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}));
             }
@@ -284,7 +394,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             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[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.1}));
+                (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}));
             }
@@ -294,9 +404,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^mn data")
+    SECTION("R^2x2 data")
     {
-      using R22 = TinyMatrix<2, 2>;
+      using R2x2 = TinyMatrix<2, 2>;
 
       for (auto named_mesh : MeshDataBaseForTests::get().all2DMeshes()) {
         SECTION(named_mesh.name())
@@ -304,24 +414,26 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<2>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R2& x) -> R22 {
-            return R22{+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 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<R22> fh{p_mesh};
+          DiscreteFunctionP0<R2x2> Ah{p_mesh};
 
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(Ah);
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error =
+                std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
           }
@@ -329,11 +441,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_x_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R22 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R2{0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0.1, 0}));
+              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 - R22{+1.7, +2.1,   //
-                                                                                                      -0.6, -2.3}));
+              max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, +2.1,   //
+                                                                                                       -0.6, -2.3}));
             }
             REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12));
           }
@@ -341,11 +453,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_y_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R22 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R2{0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R2{0, 0.1}));
+              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 - R22{+1.2, -2.2,   //
-                                                                                                      -2.1, +1.3}));
+              max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2,   //
+                                                                                                       -2.1, +1.3}));
             }
             REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12));
           }
@@ -366,21 +478,22 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
           auto& mesh  = *p_mesh;
 
-          auto 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();
+          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] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = R_affine(xj[cell_id]); });
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          auto reconstructions = PolynomialReconstruction{}.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[cell_id](xj[cell_id]) - affine(xj[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(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -389,7 +502,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_x_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0})) / 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})) / 0.2;
 
               max_x_slope_error = std::max(max_x_slope_error, std::abs(reconstructed_slope - 1.7));
             }
@@ -400,7 +513,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_y_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0})) / 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})) / 0.2;
 
               max_y_slope_error = std::max(max_y_slope_error, std::abs(reconstructed_slope - (-1.3)));
             }
@@ -411,7 +524,7 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
             double max_z_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
               const double reconstructed_slope =
-                (dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.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})) / 0.2;
 
               max_z_slope_error = std::max(max_z_slope_error, std::abs(reconstructed_slope - 2.1));
             }
@@ -421,36 +534,34 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^d data")
+    SECTION("R^3 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 affine = [](const R3& x) -> R4 {
-            return R4{+2.3 + 1.7 * x[0] - 2.2 * x[1] + 1.8 * x[2],   //
+          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],   //
-                      -1.2 - 1.5 * x[0] - 0.1 * x[1] - 1.6 * x[2]};
+                      -0.2 + 3.1 * x[0] - 1.1 * x[1] + 1.9 * x[2]};
           };
           auto xj = MeshDataManager::instance().getMeshData(mesh).xj();
 
-          DiscreteFunctionP0<R4> fh{p_mesh};
+          DiscreteFunctionP0<R3> uh{p_mesh};
 
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { uh[cell_id] = R3_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(uh);
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error = std::max(max_mean_error, l2Norm(dpk_uh[cell_id](xj[cell_id]) - R3_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-14));
           }
@@ -458,10 +569,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_x_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R4 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
+              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 - R4{1.7, -0.6, 3.1, -1.5}));
+              max_x_slope_error = std::max(max_x_slope_error, l2Norm(reconstructed_slope - R3{1.7, -0.6, 3.1}));
             }
             REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-13));
           }
@@ -469,10 +580,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_y_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R4 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
+              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 - R4{-2.2, 1.3, -1.1, -0.1}));
+              max_y_slope_error = std::max(max_y_slope_error, l2Norm(reconstructed_slope - R3{-2.2, 1.3, -1.1}));
             }
             REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-13));
           }
@@ -480,10 +591,10 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_z_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R4 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
+              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 - R4{1.8, -3.7, 1.9, -1.6}));
+              max_z_slope_error = std::max(max_z_slope_error, l2Norm(reconstructed_slope - R3{1.8, -3.7, 1.9}));
             }
             REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-13));
           }
@@ -491,9 +602,9 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
       }
     }
 
-    SECTION("R^mn data")
+    SECTION("R^2x2 data")
     {
-      using R22 = TinyMatrix<2, 2>;
+      using R2x2 = TinyMatrix<2, 2>;
 
       for (auto named_mesh : MeshDataBaseForTests::get().all3DMeshes()) {
         SECTION(named_mesh.name())
@@ -501,25 +612,27 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           auto p_mesh = named_mesh.mesh()->get<Mesh<3>>();
           auto& mesh  = *p_mesh;
 
-          auto affine = [](const R3& x) -> R22 {
-            return R22{+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 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<R22> fh{p_mesh};
+          DiscreteFunctionP0<R2x2> Ah{p_mesh};
 
           parallel_for(
-            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { fh[cell_id] = affine(xj[cell_id]); });
+            mesh.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { Ah[cell_id] = R2x2_affine(xj[cell_id]); });
+
+          auto reconstructions = PolynomialReconstruction{}.build(Ah);
 
-          PolynomialReconstruction reconstruction;
-          auto dpk = reconstruction.build(mesh, fh);
+          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[cell_id](xj[cell_id]) - affine(xj[cell_id])));
+              max_mean_error =
+                std::max(max_mean_error, frobeniusNorm(dpk_Ah[cell_id](xj[cell_id]) - R2x2_affine(xj[cell_id])));
             }
             REQUIRE(max_mean_error == Catch::Approx(0).margin(1E-13));
           }
@@ -527,11 +640,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_x_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R22 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0.1, 0, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0.1, 0, 0}));
+              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 - R22{+1.7, 2.1,   //
-                                                                                                      -2.3, +3.1}));
+              max_x_slope_error = std::max(max_x_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.7, 2.1,   //
+                                                                                                       -2.3, +3.1}));
             }
             REQUIRE(max_x_slope_error == Catch::Approx(0).margin(1E-12));
           }
@@ -539,11 +652,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_y_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R22 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0, 0.1, 0} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0.1, 0}));
+              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 - R22{+1.2, -2.2,   //
-                                                                                                      1.3, +0.8}));
+              max_y_slope_error = std::max(max_y_slope_error, frobeniusNorm(reconstructed_slope - R2x2{+1.2, -2.2,   //
+                                                                                                       1.3, +0.8}));
             }
             REQUIRE(max_y_slope_error == Catch::Approx(0).margin(1E-12));
           }
@@ -551,11 +664,11 @@ TEST_CASE("PolynomialReconstruction", "[scheme]")
           {
             double max_z_slope_error = 0;
             for (CellId cell_id = 0; cell_id < mesh.numberOfCells(); ++cell_id) {
-              const R22 reconstructed_slope =
-                (1 / 0.2) * (dpk[cell_id](R3{0, 0, 0.1} + xj[cell_id]) - dpk[cell_id](xj[cell_id] - R3{0, 0, 0.1}));
+              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 - R22{-1.3, -2.4,   //
-                                                                                                      +1.4, -1.8}));
+              max_z_slope_error = std::max(max_z_slope_error, frobeniusNorm(reconstructed_slope - R2x2{-1.3, -2.4,   //
+                                                                                                       +1.4, -1.8}));
             }
             REQUIRE(max_z_slope_error == Catch::Approx(0).margin(1E-12));
           }