diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 0f3dadaadcb2bb955ab11b59d25dd00f77f0e71f..212afc5523875f4b25c8af5d03155e136629d299 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -682,6 +682,18 @@ SchemeModule::SchemeModule()
 
                               ));
 
+#warning REMOVE AFTER TESTS FINISHED
+  this->_addBuiltinFunction("test_reconstruction",
+                            std::function(
+
+                              [](const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>&
+                                   discrete_function_variant_list,
+                                 uint64_t degree) -> void {
+                                test_reconstruction(discrete_function_variant_list, degree);
+                              }
+
+                              ));
+
   MathFunctionRegisterForVh{*this};
 }
 
diff --git a/src/scheme/PolynomialReconstruction.cpp b/src/scheme/PolynomialReconstruction.cpp
index c20fbc897c060fa182eb30683b109160938d68c6..0d0c829c7ce0d7f91c635dd59ae7636717ab7e2b 100644
--- a/src/scheme/PolynomialReconstruction.cpp
+++ b/src/scheme/PolynomialReconstruction.cpp
@@ -270,17 +270,15 @@ _computeEjkMean(const QuadratureFormula<3>& quadrature,
 }
 
 template <typename NodeListT, size_t Dimension>
-void
-_computeEjkMean(const TinyVector<Dimension>& Xj,
-                const NodeValue<const TinyVector<Dimension>>& xr,
-                const NodeListT& node_list,
-                const CellType& cell_type,
-                const double Vi,
-                const size_t degree,
-                const size_t basis_dimension,
-                SmallArray<double>& inv_Vj_wq_detJ_ek,
-                SmallArray<double>& mean_of_ejk)
-{}
+void _computeEjkMean(const TinyVector<Dimension>& Xj,
+                     const NodeValue<const TinyVector<Dimension>>& xr,
+                     const NodeListT& node_list,
+                     const CellType& cell_type,
+                     const double Vi,
+                     const size_t degree,
+                     const size_t basis_dimension,
+                     SmallArray<double>& inv_Vj_wq_detJ_ek,
+                     SmallArray<double>& mean_of_ejk);
 
 template <typename NodeListT>
 void
@@ -306,6 +304,86 @@ _computeEjkMean(const TinyVector<1>& Xj,
   }
 }
 
+template <typename ConformTransformationT>
+void
+_computeEjkBoundaryMean(const QuadratureFormula<1>& quadrature,
+                        const ConformTransformationT& T,
+                        const TinyVector<2>& Xj,
+                        const double Vi,
+                        const size_t degree,
+                        const size_t dimension,
+                        SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek,
+                        SmallArray<double>& mean_of_ejk)
+{
+  using Rd = TinyVector<2>;
+
+  const double velocity_perp_e1 = T.velocity()[1];
+
+  for (size_t i_q = 0; i_q < quadrature.numberOfPoints(); ++i_q) {
+    const double wq          = quadrature.weight(i_q);
+    const TinyVector<1> xi_q = quadrature.point(i_q);
+
+    const Rd X_Xj = T(xi_q) - Xj;
+
+    const double x_xj = X_Xj[0];
+    const double y_yj = X_Xj[1];
+
+    {
+      size_t k                                 = 0;
+      inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = x_xj * wq * velocity_perp_e1 / Vi;
+      for (; k <= degree; ++k) {
+        inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k] =
+          x_xj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k - 1] * ((1. * k) / (k + 1));
+      }
+
+      for (size_t i_y = 1; i_y <= degree; ++i_y) {
+        const size_t begin_i_y_1 = ((i_y - 1) * (2 * degree - i_y + 4)) / 2;
+        for (size_t l = 0; l <= degree - i_y; ++l) {
+          inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k++] = y_yj * inv_Vj_alpha_p_1_wq_X_prime_orth_ek[begin_i_y_1 + l];
+        }
+      }
+    }
+
+    for (size_t k = 1; k < dimension; ++k) {
+      mean_of_ejk[k - 1] += inv_Vj_alpha_p_1_wq_X_prime_orth_ek[k];
+    }
+  }
+}
+
+void
+_computeEjkMeanByBoundary(const TinyVector<2>& Xj,
+                          const CellId& cell_id,
+                          const NodeValue<const TinyVector<2>>& xr,
+                          const auto& cell_to_face_matrix,
+                          const auto& face_to_node_matrix,
+                          const auto& cell_face_is_reversed,
+                          const CellValue<const double>& Vi,
+                          const size_t degree,
+                          const size_t basis_dimension,
+                          SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek,
+                          SmallArray<double>& mean_of_ejk)
+{
+  const auto& quadrature = QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(degree + 1));
+
+  mean_of_ejk.fill(0);
+  auto cell_face_list = cell_to_face_matrix[cell_id];
+  for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) {
+    bool is_reversed = cell_face_is_reversed[cell_id][i_face];
+
+    const FaceId face_id = cell_face_list[i_face];
+    auto face_node_list  = face_to_node_matrix[face_id];
+    if (is_reversed) {
+      const LineTransformation<2> T{xr[face_node_list[1]], xr[face_node_list[0]]};
+      _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension,
+                              inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk);
+    } else {
+      const LineTransformation<2> T{xr[face_node_list[0]], xr[face_node_list[1]]};
+      _computeEjkBoundaryMean(quadrature, T, Xj, Vi[cell_id], degree, basis_dimension,
+                              inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_of_ejk);
+    }
+  }
+}
+
 template <typename NodeListT>
 void
 _computeEjkMean(const TinyVector<2>& Xj,
@@ -481,6 +559,7 @@ PolynomialReconstruction::_build(
   auto cell_is_owned       = mesh.connectivity().cellIsOwned();
   auto cell_type           = mesh.connectivity().cellType();
   auto cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix();
+  auto cell_to_face_matrix = mesh.connectivity().cellToFaceMatrix();
 
   const size_t max_stencil_size = [&]() {
     size_t max_size = 0;
@@ -509,6 +588,9 @@ PolynomialReconstruction::_build(
   SmallArray<SmallArray<double>> mean_j_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency());
   SmallArray<SmallArray<double>> mean_i_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency());
 
+  SmallArray<SmallArray<double>> inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool(Kokkos::DefaultExecutionSpace::concurrency());
+  SmallArray<SmallArray<double>> mean_l_of_ejk_pool(Kokkos::DefaultExecutionSpace::concurrency());
+
   for (size_t i = 0; i < A_pool.size(); ++i) {
     A_pool[i] = SmallMatrix<double>(max_stencil_size, basis_dimension - 1);
     B_pool[i] = SmallMatrix<double>(max_stencil_size, number_of_columns);
@@ -518,15 +600,17 @@ PolynomialReconstruction::_build(
     inv_Vj_wq_detJ_ek_pool[i] = SmallArray<double>(basis_dimension);
     mean_j_of_ejk_pool[i]     = SmallArray<double>(basis_dimension - 1);
     mean_i_of_ejk_pool[i]     = SmallArray<double>(basis_dimension - 1);
+
+    inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[i] = SmallArray<double>(basis_dimension);
   }
 
 #warning remove after rework
-  const bool FORCE_INTEGRATE = []() {
+  [[maybe_unused]] const int INTEGRATE = []() {
     auto value = std::getenv("INTEGRATE");
     if (value == nullptr) {
-      return false;
+      return 0;
     } else {
-      return true;
+      return std::atoi(value);
     }
   }();
 
@@ -596,7 +680,7 @@ PolynomialReconstruction::_build(
 
         ShrinkMatrixView A(A_pool[t], stencil_cell_list.size());
 
-        if ((m_descriptor.degree() == 1) and (not FORCE_INTEGRATE)) {
+        if ((m_descriptor.degree() == 1) and (INTEGRATE == 0)) {
           const Rd& Xj = xj[cell_j_id];
           for (size_t i = 0; i < stencil_cell_list.size(); ++i) {
             const CellId cell_i_id = stencil_cell_list[i];
@@ -605,26 +689,59 @@ PolynomialReconstruction::_build(
               A(i, l) = Xi_Xj[l];
             }
           }
-        } else {
-          SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t];
-          SmallArray<double>& mean_j_of_ejk     = mean_j_of_ejk_pool[t];
-          SmallArray<double>& mean_i_of_ejk     = mean_i_of_ejk_pool[t];
 
-          const Rd& Xj = xj[cell_j_id];
+        } else if ((INTEGRATE == 2) or (INTEGRATE == 1)) {
+          if ((INTEGRATE == 2) and (MeshType::Dimension == 2)) {
+            if constexpr (MeshType::Dimension == 2) {
+              SmallArray<double>& inv_Vj_alpha_p_1_wq_X_prime_orth_ek = inv_Vj_alpha_p_1_wq_X_prime_orth_ek_pool[t];
+              SmallArray<double>& mean_j_of_ejk                       = mean_j_of_ejk_pool[t];
+              SmallArray<double>& mean_i_of_ejk                       = mean_i_of_ejk_pool[t];
 
-          _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id],
-                          m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk);
+              auto face_to_node_matrix   = p_mesh->connectivity().faceToNodeMatrix();
+              auto cell_face_is_reversed = p_mesh->connectivity().cellFaceIsReversed();
+              const Rd& Xj               = xj[cell_j_id];
 
-          for (size_t i = 0; i < stencil_cell_list.size(); ++i) {
-            const CellId cell_i_id = stencil_cell_list[i];
+              _computeEjkMeanByBoundary(Xj, cell_j_id, xr, cell_to_face_matrix, face_to_node_matrix,
+                                        cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension,
+                                        inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_j_of_ejk);
 
-            _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id],
-                            m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk);
+              for (size_t i = 0; i < stencil_cell_list.size(); ++i) {
+                const CellId cell_i_id = stencil_cell_list[i];
 
-            for (size_t l = 0; l < basis_dimension - 1; ++l) {
-              A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l];
+                _computeEjkMeanByBoundary(Xj, cell_i_id, xr, cell_to_face_matrix, face_to_node_matrix,
+                                          cell_face_is_reversed, Vj, m_descriptor.degree(), basis_dimension,
+                                          inv_Vj_alpha_p_1_wq_X_prime_orth_ek, mean_i_of_ejk);
+
+                for (size_t l = 0; l < basis_dimension - 1; ++l) {
+                  A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l];
+                }
+              }
+            } else {
+              throw UnexpectedError("invalid mesh dimension");
+            }
+          } else {
+            SmallArray<double>& inv_Vj_wq_detJ_ek = inv_Vj_wq_detJ_ek_pool[t];
+            SmallArray<double>& mean_j_of_ejk     = mean_j_of_ejk_pool[t];
+            SmallArray<double>& mean_i_of_ejk     = mean_i_of_ejk_pool[t];
+
+            const Rd& Xj = xj[cell_j_id];
+
+            _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_j_id], cell_type[cell_j_id], Vj[cell_j_id],
+                            m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_j_of_ejk);
+
+            for (size_t i = 0; i < stencil_cell_list.size(); ++i) {
+              const CellId cell_i_id = stencil_cell_list[i];
+
+              _computeEjkMean(Xj, xr, cell_to_node_matrix[cell_i_id], cell_type[cell_i_id], Vj[cell_i_id],
+                              m_descriptor.degree(), basis_dimension, inv_Vj_wq_detJ_ek, mean_i_of_ejk);
+
+              for (size_t l = 0; l < basis_dimension - 1; ++l) {
+                A(i, l) = mean_i_of_ejk[l] - mean_j_of_ejk[l];
+              }
             }
           }
+        } else {
+          throw UnexpectedError("invalid integration strategy");
         }
 
         if (m_descriptor.rowWeighting()) {
@@ -679,12 +796,6 @@ PolynomialReconstruction::_build(
           Givens::solveCollectionInPlace(A, X, B);
         }
 
-        // Results are now written from the {ejk} basis to the {ek} basis
-        if (m_descriptor.degree() > 1) {
-          throw NotImplementedError(
-            "write the result from the {ejk} basis to the {ek} basis to get correct polynomial values");
-        }
-
         column_begin = 0;
         for (size_t i_dpk_variant = 0; i_dpk_variant < mutable_discrete_function_dpk_variant_list.size();
              ++i_dpk_variant) {
diff --git a/src/scheme/test_reconstruction.cpp b/src/scheme/test_reconstruction.cpp
index 3d255a5474bf9319a9079340fc7c20ab7ae7f834..4ba54bda2dc6a586e14e16fb01afee0417bb2476 100644
--- a/src/scheme/test_reconstruction.cpp
+++ b/src/scheme/test_reconstruction.cpp
@@ -4,42 +4,17 @@
 #include <utils/Timer.hpp>
 
 void
-test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list)
+test_reconstruction(const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list,
+                    uint64_t degree)
 {
-  PolynomialReconstructionDescriptor descriptor{1};
-  // {
-  //   std::cout << "** variable list one by one (50 times)\n";
-  //   Timer t;
-  //   for (auto discrete_function_v : discrete_function_variant_list) {
-  //     std::visit(
-  //       [&](auto&& discrete_function) {
-  //         auto mesh_v = discrete_function.meshVariant();
-  //         std::visit(
-  //           [&](auto&& p_mesh) {
-  //             using DiscreteFunctionT = std::decay_t<decltype(discrete_function)>;
-  //             PolynomialReconstruction reconstruction{descriptor};
-  //             if constexpr (is_discrete_function_P0_v<DiscreteFunctionT>) {
-  //               for (size_t i = 0; i < 50; ++i) {
-  //                 auto res = reconstruction.build(*p_mesh, discrete_function);
-  //               }
-  //             } else if constexpr (is_discrete_function_P0_vector_v<DiscreteFunctionT>) {
-  //               std::cout << "Omitting discrete function p0 vector!\n";
-  //             }
-  //           },
-  //           mesh_v->variant());
-  //       },
-  //       discrete_function_v->discreteFunction());
-  //   }
-  //   t.pause();
-  //   std::cout << "t = " << t << '\n';
-  // }
-
+  PolynomialReconstructionDescriptor descriptor{degree};
   {
-    std::cout << "** variable list at once (50 times)\n";
+    const size_t nb_loops = 1;
+    std::cout << "** variable list at once (" << nb_loops << " times)\n";
 
     PolynomialReconstruction reconstruction{descriptor};
     Timer t;
-    for (size_t i = 0; i < 50; ++i) {
+    for (size_t i = 0; i < nb_loops; ++i) {
       auto res = reconstruction.build(discrete_function_variant_list);
     }
     t.pause();
diff --git a/src/scheme/test_reconstruction.hpp b/src/scheme/test_reconstruction.hpp
index 6620acd44ca5e199ec7f4dcedde3a2d9afadda12..d2fc5bd031989eecdf4d837f566c137b3f874888 100644
--- a/src/scheme/test_reconstruction.hpp
+++ b/src/scheme/test_reconstruction.hpp
@@ -8,6 +8,7 @@
 #include <vector>
 
 void test_reconstruction(
-  const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list);
+  const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& discrete_function_variant_list,
+  uint64_t degree = 1);
 
 #endif   // TEST_RECONSTRUCTION_HPP