diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index ca49d0b16565877fc526dc9c5f7a8740975582d4..d1fa4bf7ce816e054d36a216cf66fc1084a4c6c7 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -42,6 +42,7 @@
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
 #include <scheme/ScalarDiamondScheme.hpp>
+#include <scheme/ScalarHybridScheme.hpp>
 #include <scheme/ScalarNodalScheme.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 #include <scheme/VectorDiamondScheme.hpp>
@@ -637,6 +638,42 @@ SchemeModule::SchemeModule()
                                   .solution();
                               }));
 
+  this->_addBuiltinFunction("hybridheat",
+                            std::make_shared<BuiltinFunctionEmbedder<std::tuple<
+                              std::shared_ptr<const IDiscreteFunction>,
+                              std::shared_ptr<const IDiscreteFunction>>(const std::shared_ptr<const IDiscreteFunction>&,
+                                                                        const std::shared_ptr<const IDiscreteFunction>&,
+                                                                        const std::shared_ptr<const IDiscreteFunction>&,
+                                                                        const std::vector<std::shared_ptr<
+                                                                          const IBoundaryConditionDescriptor>>&,
+                                                                        const std::vector<std::shared_ptr<
+                                                                          const IBoundaryConditionDescriptor>>&,
+                                                                        const std::shared_ptr<const IDiscreteFunction>&,
+                                                                        const double&, const double&, const double&)>>(
+
+                              [](const std::shared_ptr<const IDiscreteFunction>& kappa,
+                                 const std::shared_ptr<const IDiscreteFunction>& f,
+                                 const std::shared_ptr<const IDiscreteFunction>& f_prev,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_descriptor_list,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                   bc_prev_descriptor_list,
+                                 const std::shared_ptr<const IDiscreteFunction>& P, const double& dt,
+                                 const double& cn_coeff,
+                                 const double& lambda) -> const std::tuple<std::shared_ptr<const IDiscreteFunction>,
+                                                                           std::shared_ptr<const IDiscreteFunction>> {
+                                return ScalarHybridSchemeHandler{kappa,
+                                                                 f,
+                                                                 f_prev,
+                                                                 bc_descriptor_list,
+                                                                 bc_prev_descriptor_list,
+                                                                 P,
+                                                                 dt,
+                                                                 cn_coeff,
+                                                                 lambda}
+                                  .solution();
+                              }));
+
   this->_addBuiltinFunction("unsteadyelasticity",
                             std::make_shared<BuiltinFunctionEmbedder<
                               void(std::shared_ptr<const IMesh>,
diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt
index b7f170fb75eca85662f60696a7c58b5c798246dd..810c615f3e6cf39047bc868cb614c9bd6fe6e30a 100644
--- a/src/scheme/CMakeLists.txt
+++ b/src/scheme/CMakeLists.txt
@@ -10,4 +10,5 @@ add_library(
   DiscreteFunctionVectorInterpoler.cpp
   ScalarDiamondScheme.cpp
   VectorDiamondScheme.cpp
-  ScalarNodalScheme.cpp)
+  ScalarNodalScheme.cpp
+  ScalarHybridScheme.cpp)
diff --git a/src/scheme/ScalarHybridScheme.cpp b/src/scheme/ScalarHybridScheme.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10d41c948a8e3ccacb6b3acca3e28b7e61f96b78
--- /dev/null
+++ b/src/scheme/ScalarHybridScheme.cpp
@@ -0,0 +1,1525 @@
+#include <scheme/ScalarHybridScheme.hpp>
+
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <scheme/DiscreteFunctionUtils.hpp>
+
+class ScalarHybridSchemeHandler::IScalarHybridScheme
+{
+ public:
+  virtual std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> getSolution()
+    const = 0;
+
+  // virtual std::shared_ptr<const IDiscreteFunction> getSolution() const = 0;
+
+  IScalarHybridScheme()          = default;
+  virtual ~IScalarHybridScheme() = default;
+};
+
+template <size_t Dimension>
+class ScalarHybridSchemeHandler::ScalarHybridScheme : public ScalarHybridSchemeHandler::IScalarHybridScheme
+{
+ private:
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+  using MeshDataType     = MeshData<Dimension>;
+
+  std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_cell_temperature;
+  std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_node_temperature;
+  //  std::shared_ptr<const DiscreteFunctionP0<Dimension, double>> m_solution;
+
+  class DirichletBoundaryCondition
+  {
+   private:
+    const Array<const double> m_node_value_list;
+    const Array<const double> m_face_value_list;
+    const Array<const FaceId> m_face_list;
+    const Array<const NodeId> m_node_list;
+
+   public:
+    const Array<const NodeId>&
+    nodeList() const
+    {
+      return m_node_list;
+    }
+
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+    const Array<const double>&
+    nodeValueList() const
+    {
+      return m_node_value_list;
+    }
+
+    const Array<const double>&
+    faceValueList() const
+    {
+      return m_face_value_list;
+    }
+
+    DirichletBoundaryCondition(const Array<const FaceId>& face_list,
+                               const Array<const NodeId>& node_list,
+                               const Array<const double>& node_value_list,
+                               const Array<const double>& face_value_list)
+      : m_node_value_list{node_value_list},
+        m_face_value_list{face_value_list},
+        m_face_list{face_list},
+        m_node_list{node_list}
+    {
+      Assert(m_node_value_list.size() == m_node_list.size());
+      Assert(m_face_value_list.size() == m_face_list.size());
+    }
+
+    ~DirichletBoundaryCondition() = default;
+  };
+
+  class NeumannBoundaryCondition
+  {
+   private:
+    const Array<const double> m_value_list;
+    const Array<const FaceId> m_face_list;
+    const Array<const NodeId> m_node_list;
+
+   public:
+    const Array<const NodeId>&
+    nodeList() const
+    {
+      return m_node_list;
+    }
+
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+    const Array<const double>&
+    valueList() const
+    {
+      return m_value_list;
+    }
+
+    NeumannBoundaryCondition(const Array<const FaceId>& face_list,
+                             const Array<const NodeId>& node_list,
+                             const Array<const double>& value_list)
+      : m_value_list{value_list}, m_face_list{face_list}, m_node_list{node_list}
+    {
+      Assert(m_value_list.size() == m_face_list.size());
+    }
+
+    ~NeumannBoundaryCondition() = default;
+  };
+
+  class FourierBoundaryCondition
+  {
+   private:
+    const Array<const double> m_coef_list;
+    const Array<const double> m_value_list;
+    const Array<const FaceId> m_face_list;
+
+   public:
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+    const Array<const double>&
+    valueList() const
+    {
+      return m_value_list;
+    }
+
+    const Array<const double>&
+    coefList() const
+    {
+      return m_coef_list;
+    }
+
+   public:
+    FourierBoundaryCondition(const Array<const FaceId>& face_list,
+                             const Array<const double>& coef_list,
+                             const Array<const double>& value_list)
+      : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list}
+    {
+      Assert(m_coef_list.size() == m_face_list.size());
+      Assert(m_value_list.size() == m_face_list.size());
+    }
+
+    ~FourierBoundaryCondition() = default;
+  };
+
+ public:
+  std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>>
+  getSolution() const
+  {
+    return {m_cell_temperature, m_node_temperature};
+  }
+  // std::shared_ptr<const IDiscreteFunction>
+  // getSolution() const final
+  // {
+  //   return m_solution;
+  // }
+
+  ScalarHybridScheme(const std::shared_ptr<const MeshType>& mesh,
+                     const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyMatrix<Dimension>>>& cell_k,
+                     const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f,
+                     const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f_prev,
+                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_prev_descriptor_list,
+                     const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& P,
+                     const double& dt,
+                     const double& cn_coeff,
+                     const double& lambda)
+  {
+    using BoundaryCondition =
+      std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition>;
+
+    using BoundaryConditionList = std::vector<BoundaryCondition>;
+
+    BoundaryConditionList boundary_condition_list;
+
+    for (const auto& bc_descriptor : bc_descriptor_list) {
+      bool is_valid_boundary_condition = true;
+
+      switch (bc_descriptor->type()) {
+      case IBoundaryConditionDescriptor::Type::dirichlet: {
+        const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
+          dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
+        if constexpr (Dimension > 1) {
+          MeshFaceBoundary<Dimension> mesh_face_boundary =
+            getMeshFaceBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor());
+          MeshNodeBoundary<Dimension> mesh_node_boundary =
+            getMeshNodeBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor());
+
+          const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+
+          Array<const double> node_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(),
+                                                                                                      mesh_node_boundary
+                                                                                                        .nodeList());
+          Array<const double> face_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+          boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(),
+                                                                       mesh_node_boundary.nodeList(), node_value_list,
+                                                                       face_value_list});
+        } else {
+          throw NotImplementedError("Dirichlet BC in 1d");
+        }
+        break;
+      }
+      case IBoundaryConditionDescriptor::Type::fourier: {
+        throw NotImplementedError("NIY");
+        break;
+      }
+      case IBoundaryConditionDescriptor::Type::neumann: {
+        const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor =
+          dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor);
+
+        if constexpr (Dimension > 1) {
+          MeshFaceBoundary<Dimension> mesh_face_boundary =
+            getMeshFaceBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor());
+          MeshNodeBoundary<Dimension> mesh_node_boundary =
+            getMeshNodeBoundary(*mesh, neumann_bc_descriptor.boundaryDescriptor());
+
+          const FunctionSymbolId g_id = neumann_bc_descriptor.rhsSymbolId();
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+
+          Array<const double> value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+
+          boundary_condition_list.push_back(
+            NeumannBoundaryCondition{mesh_face_boundary.faceList(), mesh_node_boundary.nodeList(), value_list});
+
+        } else {
+          throw NotImplementedError("Neumann BC in 1d");
+        }
+        break;
+      }
+      default: {
+        is_valid_boundary_condition = false;
+      }
+      }
+      if (not is_valid_boundary_condition) {
+        std::ostringstream error_msg;
+        error_msg << *bc_descriptor << " is an invalid boundary condition for heat equation";
+        throw NormalError(error_msg.str());
+      }
+    }
+
+    BoundaryConditionList boundary_prev_condition_list;
+
+    for (const auto& bc_prev_descriptor : bc_prev_descriptor_list) {
+      bool is_valid_boundary_condition = true;
+
+      switch (bc_prev_descriptor->type()) {
+      case IBoundaryConditionDescriptor::Type::dirichlet: {
+        const DirichletBoundaryConditionDescriptor& dirichlet_bc_prev_descriptor =
+          dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_prev_descriptor);
+        if constexpr (Dimension > 1) {
+          MeshFaceBoundary<Dimension> mesh_face_boundary =
+            getMeshFaceBoundary(*mesh, dirichlet_bc_prev_descriptor.boundaryDescriptor());
+          MeshNodeBoundary<Dimension> mesh_node_boundary =
+            getMeshNodeBoundary(*mesh, dirichlet_bc_prev_descriptor.boundaryDescriptor());
+
+          const FunctionSymbolId g_id = dirichlet_bc_prev_descriptor.rhsSymbolId();
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+
+          Array<const double> node_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(),
+                                                                                                      mesh_node_boundary
+                                                                                                        .nodeList());
+          Array<const double> face_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+          boundary_prev_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(),
+                                                                            mesh_node_boundary.nodeList(),
+                                                                            node_value_list, face_value_list});
+
+        } else {
+          throw NotImplementedError("Dirichlet BC in 1d");
+        }
+        break;
+      }
+      case IBoundaryConditionDescriptor::Type::fourier: {
+        throw NotImplementedError("NIY");
+        break;
+      }
+      case IBoundaryConditionDescriptor::Type::neumann: {
+        const NeumannBoundaryConditionDescriptor& neumann_bc_prev_descriptor =
+          dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_prev_descriptor);
+
+        if constexpr (Dimension > 1) {
+          MeshFaceBoundary<Dimension> mesh_face_boundary =
+            getMeshFaceBoundary(*mesh, neumann_bc_prev_descriptor.boundaryDescriptor());
+          MeshNodeBoundary<Dimension> mesh_node_boundary =
+            getMeshNodeBoundary(*mesh, neumann_bc_prev_descriptor.boundaryDescriptor());
+
+          const FunctionSymbolId g_id = neumann_bc_prev_descriptor.rhsSymbolId();
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+
+          Array<const double> value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+
+          boundary_prev_condition_list.push_back(
+            NeumannBoundaryCondition{mesh_face_boundary.faceList(), mesh_node_boundary.nodeList(), value_list});
+
+        } else {
+          throw NotImplementedError("Neumann BC in 1d");
+        }
+        break;
+      }
+      default: {
+        is_valid_boundary_condition = false;
+      }
+      }
+      if (not is_valid_boundary_condition) {
+        std::ostringstream error_msg;
+        error_msg << *bc_prev_descriptor << " is an invalid boundary condition for heat equation";
+        throw NormalError(error_msg.str());
+      }
+    }
+
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+    std::shared_ptr dual_mesh = DualMeshManager::instance().getMedianDualMesh(*mesh);
+
+    std::shared_ptr mapper =
+      DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(mesh->connectivity());
+
+    const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr();
+
+    const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl();
+    const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj();
+
+    const NodeValuePerCell<const TinyVector<Dimension>>& Cjr = mesh_data.Cjr();
+    const CellValue<const TinyMatrix<Dimension>> cell_kappaj = cell_k->cellValues();
+
+    const auto is_boundary_node = mesh->connectivity().isBoundaryNode();
+    const auto is_boundary_face = mesh->connectivity().isBoundaryFace();
+
+    const auto& face_to_cell_matrix               = mesh->connectivity().faceToCellMatrix();
+    const auto& node_to_face_matrix               = mesh->connectivity().nodeToFaceMatrix();
+    const auto& face_to_node_matrix               = mesh->connectivity().faceToNodeMatrix();
+    const auto& cell_to_node_matrix               = mesh->connectivity().cellToNodeMatrix();
+    const auto& cell_to_face_matrix               = mesh->connectivity().cellToFaceMatrix();
+    const auto& node_local_numbers_in_their_cells = mesh->connectivity().nodeLocalNumbersInTheirCells();
+    const auto& cell_local_numbers_in_their_faces = mesh->connectivity().cellLocalNumbersInTheirFaces();
+    const auto& face_local_numbers_in_their_cells = mesh->connectivity().faceLocalNumbersInTheirCells();
+    const CellValue<const double> Vj              = mesh_data.Vj();
+    const auto& node_to_cell_matrix               = mesh->connectivity().nodeToCellMatrix();
+    const auto& nl                                = mesh_data.nl();
+
+    const NodeValue<const bool> node_is_neumann = [&] {
+      NodeValue<bool> compute_node_is_neumann{mesh->connectivity()};
+      compute_node_is_neumann.fill(false);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list = bc.faceList();
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id   = face_list[i_face];
+                const auto& face_nodes = face_to_node_matrix[face_id];
+                for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) {
+                  const NodeId node_id             = face_nodes[i_node];
+                  compute_node_is_neumann[node_id] = true;
+                }
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_node_is_neumann;
+    }();
+
+    const FaceValue<const bool> face_is_neumann = [&] {
+      FaceValue<bool> compute_face_is_neumann{mesh->connectivity()};
+      compute_face_is_neumann.fill(false);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list = bc.faceList();
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id             = face_list[i_face];
+                compute_face_is_neumann[face_id] = true;
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_face_is_neumann;
+    }();
+
+    const NodeValue<const bool> node_is_dirichlet = [&] {
+      NodeValue<bool> compute_node_is_dirichlet{mesh->connectivity()};
+      compute_node_is_dirichlet.fill(false);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& node_list = bc.nodeList();
+
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                const NodeId node_id = node_list[i_node];
+
+                compute_node_is_dirichlet[node_id] = true;
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_node_is_dirichlet;
+    }();
+
+    const FaceValue<const bool> face_is_dirichlet = [&] {
+      FaceValue<bool> compute_face_is_dirichlet{mesh->connectivity()};
+      compute_face_is_dirichlet.fill(false);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& face_list = bc.faceList();
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id               = face_list[i_face];
+                compute_face_is_dirichlet[face_id] = true;
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_face_is_dirichlet;
+    }();
+
+    const NodeValue<const bool> node_is_corner = [&] {
+      NodeValue<bool> compute_node_is_corner{mesh->connectivity()};
+      compute_node_is_corner.fill(false);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (is_boundary_node[node_id]) {
+            const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+            const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+            for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+              const unsigned int i_node = node_local_number_in_its_cell[i_cell];
+              const CellId cell_id      = node_to_cell[i_cell];
+              const auto& cell_nodes    = cell_to_node_matrix[cell_id];
+              NodeId i_node_prev;
+              NodeId i_node_next;
+              if (i_node == 0) {
+                i_node_prev = cell_nodes.size() - 1;
+              } else {
+                i_node_prev = i_node - 1;
+              }
+              if (i_node == cell_nodes.size() - 1) {
+                i_node_next = 0;
+              } else {
+                i_node_next = i_node + 1;
+              }
+              const NodeId prev_node_id = cell_to_node_matrix[cell_id][i_node_prev];
+              const NodeId next_node_id = cell_to_node_matrix[cell_id][i_node_next];
+              if (is_boundary_node[prev_node_id] and is_boundary_node[next_node_id]) {
+                compute_node_is_corner[node_id] = true;
+              }
+            }
+          }
+        });
+      return compute_node_is_corner;
+    }();
+
+    const NodeValue<const bool> node_is_angle = [&] {
+      NodeValue<bool> compute_node_is_angle{mesh->connectivity()};
+      compute_node_is_angle.fill(false);
+      // for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (is_boundary_node[node_id]) {
+            const auto& node_to_face = node_to_face_matrix[node_id];
+
+            TinyVector<Dimension> n1 = zero;
+            TinyVector<Dimension> n2 = zero;
+
+            for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
+              FaceId face_id = node_to_face[i_face];
+              if (is_boundary_face[face_id]) {
+                if (l2Norm(n1) == 0) {
+                  n1 = nl[face_id];
+                } else {
+                  n2 = nl[face_id];
+                }
+              }
+            }
+            if (l2Norm(n1 - n2) > (1.E-15) and l2Norm(n1 + n2) > (1.E-15) and not node_is_corner[node_id]) {
+              compute_node_is_angle[node_id] = true;
+            }
+            // std::cout << node_id << "  " << n1 << "  " << n2 << "  " << compute_node_is_angle[node_id] << "\n";
+          }
+        });
+      return compute_node_is_angle;
+    }();
+
+    const NodeValue<const TinyVector<Dimension>> exterior_normal = [&] {
+      NodeValue<TinyVector<Dimension>> compute_exterior_normal{mesh->connectivity()};
+      compute_exterior_normal.fill(zero);
+      for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+        if (is_boundary_node[node_id]) {
+          const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+          const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id      = node_to_cell[i_cell];
+            const unsigned int i_node = node_local_number_in_its_cell[i_cell];
+            compute_exterior_normal[node_id] += Cjr(cell_id, i_node);
+          }
+          const double norm_exterior_normal = l2Norm(compute_exterior_normal[node_id]);
+          compute_exterior_normal[node_id] *= 1. / norm_exterior_normal;
+          // std::cout << node_id << "  " << compute_exterior_normal[node_id] << "\n";
+        }
+      }
+      return compute_exterior_normal;
+    }();
+
+    // for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+    //  std::cout << node_id << "  " << exterior_normal[node_id] << "  " << node_is_angle[node_id] << "\n";
+    //};
+
+    const FaceValue<const double> mes_l = [&] {
+      if constexpr (Dimension == 1) {
+        FaceValue<double> compute_mes_l{mesh->connectivity()};
+        compute_mes_l.fill(1);
+        return compute_mes_l;
+      } else {
+        return mesh_data.ll();
+      }
+    }();
+
+    const NodeValue<const TinyVector<Dimension>> normal_angle_base = [&] {
+      NodeValue<TinyVector<Dimension>> compute_normal_base{mesh->connectivity()};
+      compute_normal_base.fill(zero);
+      for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+        if (node_is_angle[node_id]) {
+          const auto& node_to_face = node_to_face_matrix[node_id];
+          TinyMatrix<Dimension> A;
+          A(0, 0) = 1000;
+          A(0, 1) = 1000;
+          A(1, 0) = 1000;
+          A(1, 1) = 1000;
+          for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
+            const FaceId face_id = node_to_face[i_face];
+            if (is_boundary_face[face_id]) {
+              if (A(0, 0) == 1000) {
+                if (dot(nl[face_id], exterior_normal[node_id]) >= 0) {
+                  A(0, 0) = nl[face_id][0] * mes_l[face_id];
+                  A(1, 0) = nl[face_id][1] * mes_l[face_id];
+                } else {
+                  A(0, 0) = -nl[face_id][0] * mes_l[face_id];
+                  A(1, 0) = -nl[face_id][1] * mes_l[face_id];
+                }
+              } else {
+                if (dot(nl[face_id], exterior_normal[node_id]) >= 0) {
+                  A(0, 1) = nl[face_id][0] * mes_l[face_id];
+                  A(1, 1) = nl[face_id][1] * mes_l[face_id];
+                } else {
+                  A(0, 1) = -nl[face_id][0] * mes_l[face_id];
+                  A(1, 1) = -nl[face_id][1] * mes_l[face_id];
+                }
+              }
+            }
+          }
+          compute_normal_base[node_id] = inverse(A) * exterior_normal[node_id];
+        }
+      }
+      return compute_normal_base;
+    }();
+
+    const NodeValue<const TinyMatrix<Dimension>> node_kappar = [&] {
+      NodeValue<TinyMatrix<Dimension>> kappa{mesh->connectivity()};
+      kappa.fill(zero);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          kappa[node_id]           = zero;
+          const auto& node_to_cell = node_to_cell_matrix[node_id];
+          double weight            = 0;
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id = node_to_cell[i_cell];
+            double local_weight  = 1. / l2Norm(xr[node_id] - xj[cell_id]);
+            kappa[node_id] += local_weight * cell_kappaj[cell_id];
+            weight += local_weight;
+          }
+          kappa[node_id] = 1. / weight * kappa[node_id];
+        });
+      return kappa;
+    }();
+
+    const FaceValue<const TinyMatrix<Dimension>> face_kappal = [&] {
+      FaceValue<TinyMatrix<Dimension>> kappa{mesh->connectivity()};
+      kappa.fill(zero);
+      parallel_for(
+        mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) {
+          const auto& face_to_node = face_to_node_matrix[face_id];
+          kappa[face_id]           = 0.5 * (node_kappar[face_to_node[0]] + node_kappar[face_to_node[1]]);
+        });
+      return kappa;
+    }();
+
+    const FaceValuePerCell<const TinyVector<Dimension>> xj1_xj2 = [&] {
+      FaceValuePerCell<TinyVector<Dimension>> compute_xj1_xj2{mesh->connectivity()};
+      compute_xj1_xj2.fill(zero);
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        const auto& cell_to_face = cell_to_face_matrix[cell_id];
+
+        for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+          const FaceId face_id     = cell_to_face[i_face];
+          const auto& face_to_cell = face_to_cell_matrix[face_id];
+          TinyVector<Dimension> xj1;
+          TinyVector<Dimension> xj2;
+
+          if (not is_boundary_face[face_id]) {
+            if (face_to_cell[0] == cell_id) {
+              xj1 = xj[face_to_cell[1]];
+              xj2 = xj[cell_id];
+            } else {
+              xj1 = xj[face_to_cell[0]];
+              xj2 = xj[cell_id];
+            }
+          } else {
+            if (face_to_cell[0] == cell_id) {
+              xj1 = xl[face_id];
+              xj2 = xj[cell_id];
+            } else {
+              xj1 = xl[face_id];
+              xj2 = xj[cell_id];
+            }
+          }
+          compute_xj1_xj2(cell_id, i_face) = xj1 - xj2;
+        }
+      }
+      return compute_xj1_xj2;
+    }();
+
+    const FaceValuePerCell<const TinyVector<Dimension>> xj1_xj2_orth = [&] {
+      FaceValuePerCell<TinyVector<Dimension>> compute_xj1_xj2_orth{mesh->connectivity()};
+      compute_xj1_xj2_orth.fill(zero);
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        const auto& cell_to_face = cell_to_face_matrix[cell_id];
+
+        for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+          const FaceId face_id = cell_to_face[i_face];
+
+          compute_xj1_xj2_orth(cell_id, i_face)[0] = -xj1_xj2(cell_id, i_face)[1];
+          compute_xj1_xj2_orth(cell_id, i_face)[1] = xj1_xj2(cell_id, i_face)[0];
+        }
+      }
+      return compute_xj1_xj2_orth;
+    }();
+
+    const FaceValuePerCell<const TinyVector<Dimension>> normal_face_base = [&] {
+      FaceValuePerCell<TinyVector<Dimension>> compute_normal_base{mesh->connectivity()};
+      compute_normal_base.fill(zero);
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        const auto& cell_to_face = cell_to_face_matrix[cell_id];
+
+        for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+          const FaceId face_id = cell_to_face[i_face];
+          TinyMatrix<Dimension> A;
+          TinyVector<Dimension> face_normal;
+
+          A(0, 0) = xj1_xj2_orth(cell_id, i_face)[0];
+          A(0, 1) = xj1_xj2(cell_id, i_face)[0];
+          A(1, 0) = xj1_xj2_orth(cell_id, i_face)[1];
+          A(1, 1) = xj1_xj2(cell_id, i_face)[1];
+
+          if (dot(nl[face_id], xj1_xj2(cell_id, i_face)) > 0) {
+            face_normal = nl[face_id];
+          } else {
+            face_normal = -nl[face_id];
+          }
+
+          compute_normal_base(cell_id, i_face) = inverse(A) * (face_kappal[face_id] * face_normal);
+        }
+      }
+
+      return compute_normal_base;
+    }();
+
+    const FaceValue<const double> face_boundary_values = [&] {
+      FaceValue<double> compute_face_boundary_values{mesh->connectivity()};
+      FaceValue<double> sum_mes_l{mesh->connectivity()};
+      compute_face_boundary_values.fill(0);
+      sum_mes_l.fill(0);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& face_list       = bc.faceList();
+              const auto& face_value_list = bc.faceValueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id = face_list[i_face];
+
+                compute_face_boundary_values[face_id] = face_value_list[i_face];
+              }
+            } else if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list  = bc.faceList();
+              const auto& value_list = bc.valueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id = face_list[i_face];
+
+                compute_face_boundary_values[face_id] = value_list[i_face];
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_face_boundary_values;
+    }();
+
+    const FaceValue<const double> face_boundary_prev_values = [&] {
+      FaceValue<double> compute_face_boundary_values{mesh->connectivity()};
+      FaceValue<double> sum_mes_l{mesh->connectivity()};
+      compute_face_boundary_values.fill(0);
+      sum_mes_l.fill(0);
+      for (const auto& boundary_condition : boundary_prev_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& face_list       = bc.faceList();
+              const auto& face_value_list = bc.faceValueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id = face_list[i_face];
+
+                compute_face_boundary_values[face_id] = face_value_list[i_face];
+              }
+            } else if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list  = bc.faceList();
+              const auto& value_list = bc.valueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id = face_list[i_face];
+
+                compute_face_boundary_values[face_id] = value_list[i_face];
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_face_boundary_values;
+    }();
+
+    const NodeValue<const double> node_boundary_values = [&] {
+      NodeValue<double> compute_node_boundary_values{mesh->connectivity()};
+      NodeValue<double> sum_mes_l{mesh->connectivity()};
+      compute_node_boundary_values.fill(0);
+      sum_mes_l.fill(0);
+      for (const auto& boundary_condition : boundary_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list  = bc.faceList();
+              const auto& value_list = bc.valueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id   = face_list[i_face];
+                const auto& face_nodes = face_to_node_matrix[face_id];
+                for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) {
+                  const NodeId node_id = face_nodes[i_node];
+                  if (not node_is_dirichlet[node_id]) {
+                    if (node_is_angle[node_id]) {
+                      const auto& node_to_face = node_to_face_matrix[node_id];
+                      for (size_t i_face_node = 0; i_face_node < node_to_face.size(); ++i_face_node) {
+                        const FaceId face_node_id = node_to_face[i_face_node];
+                        if (face_id == face_node_id) {
+                          compute_node_boundary_values[node_id] +=
+                            value_list[i_face] * mes_l[face_id] * normal_angle_base[node_id][i_face_node];
+                        }
+                      }
+                    } else {
+                      compute_node_boundary_values[node_id] += value_list[i_face] * mes_l[face_id];
+                      sum_mes_l[node_id] += mes_l[face_id];
+                    }
+                  }
+                }
+              }
+
+            } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& node_list       = bc.nodeList();
+              const auto& node_value_list = bc.nodeValueList();
+
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                const NodeId node_id = node_list[i_node];
+
+                compute_node_boundary_values[node_id] = node_value_list[i_node];
+              }
+            }
+          },
+          boundary_condition);
+      }
+      for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+        if ((not node_is_dirichlet[node_id]) && (node_is_neumann[node_id]) and not node_is_angle[node_id]) {
+          compute_node_boundary_values[node_id] /= sum_mes_l[node_id];
+        }
+        // std::cout << node_id << "  " << compute_node_boundary_values[node_id] << "\n";
+      }
+      return compute_node_boundary_values;
+    }();
+
+    const NodeValue<const double> node_boundary_prev_values = [&] {
+      NodeValue<double> compute_node_boundary_values{mesh->connectivity()};
+      NodeValue<double> sum_mes_l{mesh->connectivity()};
+      compute_node_boundary_values.fill(0);
+      sum_mes_l.fill(0);
+      for (const auto& boundary_condition : boundary_prev_condition_list) {
+        std::visit(
+          [&](auto&& bc) {
+            using T = std::decay_t<decltype(bc)>;
+            if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& face_list  = bc.faceList();
+              const auto& value_list = bc.valueList();
+
+              for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
+                const FaceId face_id   = face_list[i_face];
+                const auto& face_nodes = face_to_node_matrix[face_id];
+                for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) {
+                  const NodeId node_id = face_nodes[i_node];
+                  if (not node_is_dirichlet[node_id]) {
+                    if (node_is_angle[node_id]) {
+                      const auto& node_to_face = node_to_face_matrix[node_id];
+                      for (size_t i_face_node = 0; i_face_node < node_to_face.size(); ++i_face_node) {
+                        const FaceId face_node_id = node_to_face[i_face_node];
+                        if (face_id == face_node_id) {
+                          compute_node_boundary_values[node_id] +=
+                            value_list[i_face] * mes_l[face_id] * normal_angle_base[node_id][i_face_node];
+                        }
+                      }
+                    } else {
+                      compute_node_boundary_values[node_id] += value_list[i_face] * mes_l[face_id];
+                      sum_mes_l[node_id] += mes_l[face_id];
+                    }
+                  }
+                }
+              }
+            } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& node_list       = bc.nodeList();
+              const auto& node_value_list = bc.nodeValueList();
+
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                const NodeId node_id = node_list[i_node];
+
+                compute_node_boundary_values[node_id] = node_value_list[i_node];
+              }
+            }
+          },
+          boundary_condition);
+      }
+      for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+        if ((not node_is_dirichlet[node_id]) && (node_is_neumann[node_id]) and not node_is_angle[node_id]) {
+          compute_node_boundary_values[node_id] /= sum_mes_l[node_id];
+        }
+      }
+      return compute_node_boundary_values;
+    }();
+
+    const NodeValue<const TinyMatrix<Dimension>> node_betar = [&] {
+      NodeValue<TinyMatrix<Dimension>> beta{mesh->connectivity()};
+      beta.fill(zero);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+          const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+          beta[node_id]                             = zero;
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id      = node_to_cell[i_cell];
+            const unsigned int i_node = node_local_number_in_its_cell[i_cell];
+            beta[node_id] += tensorProduct(Cjr(cell_id, i_node), xr[node_id] - xj[cell_id]);
+          }
+        });
+      return beta;
+    }();
+
+    const NodeValue<const TinyMatrix<Dimension>> kappar_invBetar = [&] {
+      NodeValue<TinyMatrix<Dimension>> kappa_invBeta{mesh->connectivity()};
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (not node_is_corner[node_id]) {
+            kappa_invBeta[node_id] = node_kappar[node_id] * inverse(node_betar[node_id]);
+          }
+        });
+      return kappa_invBeta;
+    }();
+
+    const NodeValue<const TinyMatrix<Dimension>> corner_betar = [&] {
+      NodeValue<TinyMatrix<Dimension>> beta{mesh->connectivity()};
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (node_is_corner[node_id]) {
+            const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+            const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+
+            size_t i_cell             = 0;
+            const CellId cell_id      = node_to_cell[i_cell];
+            const unsigned int i_node = node_local_number_in_its_cell[i_cell];
+
+            const auto& cell_nodes  = cell_to_node_matrix[cell_id];
+            const NodeId node_id_p1 = cell_to_node_matrix[cell_id][(i_node + 1) % cell_nodes.size()];
+            const NodeId node_id_p2 = cell_to_node_matrix[cell_id][(i_node + 2) % cell_nodes.size()];
+            const NodeId node_id_m1 = cell_to_node_matrix[cell_id][(i_node - 1) % cell_nodes.size()];
+
+            const TinyVector<Dimension> xj1 = 1. / 3. * (xr[node_id] + xr[node_id_m1] + xr[node_id_p2]);
+            const TinyVector<Dimension> xj2 = 1. / 3. * (xr[node_id] + xr[node_id_p2] + xr[node_id_p1]);
+
+            const TinyVector<Dimension> xrm1 = xr[node_id_m1];
+            const TinyVector<Dimension> xrp1 = xr[node_id_p1];
+            const TinyVector<Dimension> xrp2 = xr[node_id_p2];
+
+            TinyVector<Dimension> Cjr1;
+            TinyVector<Dimension> Cjr2;
+
+            if (Dimension == 2) {
+              Cjr1[0] = -0.5 * (xrm1[1] - xrp2[1]);
+              Cjr1[1] = 0.5 * (xrm1[0] - xrp2[0]);
+              Cjr2[0] = -0.5 * (xrp2[1] - xrp1[1]);
+              Cjr2[1] = 0.5 * (xrp2[0] - xrp1[0]);
+            } else {
+              throw NotImplementedError("The scheme is not implemented in this dimension.");
+            }
+
+            beta[node_id] = tensorProduct(Cjr1, (xr[node_id] - xj1)) + tensorProduct(Cjr2, (xr[node_id] - xj2));
+          }
+        });
+      return beta;
+    }();
+
+    const NodeValue<const TinyMatrix<Dimension>> corner_kappar_invBetar = [&] {
+      NodeValue<TinyMatrix<Dimension>> kappa_invBeta{mesh->connectivity()};
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (node_is_corner[node_id]) {
+            kappa_invBeta[node_id] = node_kappar[node_id] * inverse(corner_betar[node_id]);
+          }
+        });
+      return kappa_invBeta;
+    }();
+
+    const NodeValue<const TinyVector<Dimension>> sum_Cjr = [&] {
+      NodeValue<TinyVector<Dimension>> compute_sum_Cjr{mesh->connectivity()};
+      compute_sum_Cjr.fill(zero);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+          const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+          compute_sum_Cjr[node_id]                  = zero;
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id = node_to_cell[i_cell];
+            const size_t i_node  = node_local_number_in_its_cell[i_cell];
+            compute_sum_Cjr[node_id] += Cjr(cell_id, i_node);
+          }
+        });
+      return compute_sum_Cjr;
+    }();
+
+    const NodeValue<const TinyVector<Dimension>> v = [&] {
+      NodeValue<TinyVector<Dimension>> compute_v{mesh->connectivity()};
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (is_boundary_node[node_id]) {
+            compute_v[node_id] = 1. / l2Norm(node_kappar[node_id] * exterior_normal[node_id]) * node_kappar[node_id] *
+                                 exterior_normal[node_id];
+          }
+        });
+      return compute_v;
+    }();
+
+    const NodeValuePerCell<const double> theta = [&] {
+      NodeValuePerCell<double> compute_theta{mesh->connectivity()};
+      parallel_for(
+        mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+          const auto& cell_nodes = cell_to_node_matrix[cell_id];
+          for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) {
+            const NodeId node_id = cell_nodes[i_node];
+            if (is_boundary_node[node_id] && not node_is_corner[node_id]) {
+              compute_theta(cell_id, i_node) = dot(inverse(node_betar[node_id]) * Cjr(cell_id, i_node), v[node_id]);
+            }
+          }
+        });
+      return compute_theta;
+    }();
+
+    const NodeValue<const double> sum_theta = [&] {
+      NodeValue<double> compute_sum_theta{mesh->connectivity()};
+      compute_sum_theta.fill(0);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if ((is_boundary_node[node_id]) && (not node_is_corner[node_id])) {
+            const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+            const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+            compute_sum_theta[node_id]                = 0;
+            for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+              const CellId cell_id = node_to_cell[i_cell];
+              const size_t i_node  = node_local_number_in_its_cell[i_cell];
+              compute_sum_theta[node_id] += theta(cell_id, i_node);
+            }
+          }
+        });
+      return compute_sum_theta;
+    }();
+
+    const Array<int> non_zeros{mesh->numberOfCells()};
+    non_zeros.fill(4);   // Modif pour optimiser
+    CRSMatrixDescriptor<double> S1(mesh->numberOfCells(), mesh->numberOfCells(), non_zeros);
+    CRSMatrixDescriptor<double> S2(mesh->numberOfCells(), mesh->numberOfCells(), non_zeros);
+
+    for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+      const auto& node_to_cell = node_to_cell_matrix[node_id];
+      for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+        const CellId cell_id_j = node_to_cell[i_cell_j];
+        for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+          const CellId cell_id_k   = node_to_cell[i_cell_k];
+          S1(cell_id_j, cell_id_k) = 0;
+          S2(cell_id_j, cell_id_k) = 0;
+        }
+      }
+    }
+
+    for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+      const auto& node_to_cell                   = node_to_cell_matrix[node_id];
+      const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id);
+
+      if (not is_boundary_node[node_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = node_to_cell[i_cell_j];
+          const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+          for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+            const CellId cell_id_k = node_to_cell[i_cell_k];
+            const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+            S1(cell_id_j, cell_id_k) +=
+              (1 - lambda) * dt * (1. - cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+            S2(cell_id_j, cell_id_k) -=
+              (1 - lambda) * dt * (cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+
+            const auto& cell_to_face = cell_to_face_matrix[cell_id_j];
+
+            for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+              const FaceId face_id     = cell_to_face[i_face];
+              const auto& face_to_node = face_to_node_matrix[face_id];
+
+              if (face_to_node[0] == node_id or face_to_node[1] == node_id) {
+                S1(cell_id_j, cell_id_k) +=
+                  lambda * dt * (1. - cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(node_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+                S2(cell_id_j, cell_id_k) -=
+                  lambda * dt * (cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(node_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+              }
+            }
+          }
+        }
+      } else if ((node_is_neumann[node_id]) && (not node_is_corner[node_id]) && (not node_is_dirichlet[node_id])) {
+        const TinyMatrix<Dimension> I   = identity;
+        const TinyVector<Dimension> n   = exterior_normal[node_id];
+        const TinyMatrix<Dimension> nxn = tensorProduct(n, n);
+        const TinyMatrix<Dimension> Q   = I - nxn;
+
+        for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = node_to_cell[i_cell_j];
+          const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+          for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+            const CellId cell_id_k = node_to_cell[i_cell_k];
+            const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+            S1(cell_id_j, cell_id_k) +=
+              (1 - lambda) * dt * (1. - cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] *
+                    (Cjr(cell_id_k, i_node_k) - theta(cell_id_k, i_node_k) / sum_theta[node_id] * sum_Cjr[node_id]),
+                  Q * Cjr(cell_id_j, i_node_j));
+            S2(cell_id_j, cell_id_k) -=
+              (1 - lambda) * dt * (cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] *
+                    (Cjr(cell_id_k, i_node_k) - theta(cell_id_k, i_node_k) / sum_theta[node_id] * sum_Cjr[node_id]),
+                  Q * Cjr(cell_id_j, i_node_j));
+          }
+        }
+
+      } else if ((node_is_dirichlet[node_id]) && (not node_is_corner[node_id])) {
+        for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = node_to_cell[i_cell_j];
+          const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+          for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+            const CellId cell_id_k = node_to_cell[i_cell_k];
+            const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+            S1(cell_id_j, cell_id_k) +=
+              (1 - lambda) * dt * (1. - cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+            S2(cell_id_j, cell_id_k) -=
+              (1 - lambda) * dt * (cn_coeff / 2.) *
+              dot(kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+
+            const auto& cell_to_face = cell_to_face_matrix[cell_id_j];
+
+            for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+              const FaceId face_id     = cell_to_face[i_face];
+              const auto& face_to_node = face_to_node_matrix[face_id];
+
+              if (face_to_node[0] == node_id or face_to_node[1] == node_id) {
+                S1(cell_id_j, cell_id_k) +=
+                  lambda * dt * (1. - cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(node_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+                S2(cell_id_j, cell_id_k) -=
+                  lambda * dt * (cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(node_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+              }
+            }
+          }
+        }
+
+      } else if (node_is_dirichlet[node_id] && node_is_corner[node_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = node_to_cell[i_cell_j];
+          const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+          for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+            const CellId cell_id_k = node_to_cell[i_cell_k];
+            const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+            S1(cell_id_j, cell_id_k) +=
+              (1 - lambda) * dt * (1. - cn_coeff / 2.) *
+              dot(corner_kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+            S2(cell_id_j, cell_id_k) -=
+              (1 - lambda) * dt * (cn_coeff / 2.) *
+              dot(corner_kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k), Cjr(cell_id_j, i_node_j));
+
+            const auto& cell_to_face = cell_to_face_matrix[cell_id_j];
+
+            for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+              const FaceId face_id     = cell_to_face[i_face];
+              const auto& face_to_node = face_to_node_matrix[face_id];
+
+              if (face_to_node[0] == node_id or face_to_node[1] == node_id) {
+                S1(cell_id_j, cell_id_k) +=
+                  lambda * dt * (1. - cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(corner_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+
+                S2(cell_id_j, cell_id_k) -=
+                  lambda * dt * (cn_coeff / 2.) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  dot(inverse(corner_betar[node_id]) * Cjr(cell_id_k, i_node_k), xj1_xj2_orth(cell_id_j, i_node_j));
+              }
+            }
+          }
+        }
+      }
+    }
+
+    for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+      const auto& face_to_cell                   = face_to_cell_matrix[face_id];
+      const auto& face_local_number_in_its_cells = face_local_numbers_in_their_cells.itemArray(face_id);
+      if (not is_boundary_face[face_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < face_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = face_to_cell[i_cell_j];
+          const size_t i_face    = face_local_number_in_its_cells[i_cell_j];
+
+          for (size_t i_cell_k = 0; i_cell_k < face_to_cell.size(); ++i_cell_k) {
+            const CellId cell_id_k = face_to_cell[i_cell_k];
+
+            if (cell_id_j == cell_id_k) {
+              S1(cell_id_j, cell_id_k) -=
+                lambda * dt * (1. - cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+              S2(cell_id_j, cell_id_k) +=
+                lambda * dt * (cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+            } else {
+              S1(cell_id_j, cell_id_k) +=
+                lambda * dt * (1. - cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+              S2(cell_id_j, cell_id_k) -=
+                lambda * dt * (cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+            }
+          }
+        }
+      } else if (face_is_dirichlet[face_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < face_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = face_to_cell[i_cell_j];
+          const size_t i_face    = face_local_number_in_its_cells[i_cell_j];
+          S1(cell_id_j, cell_id_j) +=
+            lambda * dt * (1. - cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+          S2(cell_id_j, cell_id_j) -=
+            lambda * dt * (cn_coeff / 2.) * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1];
+        }
+      }
+    };
+
+    for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+      S1(cell_id, cell_id) += Vj[cell_id];
+      S2(cell_id, cell_id) += Vj[cell_id];
+    }
+
+    CellValue<const double> fj      = f->cellValues();
+    CellValue<const double> fj_prev = f_prev->cellValues();
+    CellValue<const double> Pj      = P->cellValues();
+
+    Vector<double> Ph{mesh->numberOfCells()};
+    Ph = zero;
+    for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+      Ph[cell_id] = Pj[cell_id];
+    };
+
+    std::cout << "P = " << Ph << "\n";
+
+    CRSMatrix A1{S1.getCRSMatrix()};
+    CRSMatrix A2{S2.getCRSMatrix()};
+
+    // std::cout << A1 << "\n";
+
+    Vector<double> b{mesh->numberOfCells()};
+    b = zero;
+    for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+      b[cell_id] = Vj[cell_id] * fj[cell_id];
+    };
+
+    Vector<double> b_prev{mesh->numberOfCells()};
+    b_prev = zero;
+    for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+      b_prev[cell_id] = Vj[cell_id] * fj_prev[cell_id];
+    };
+
+    for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+      const auto& node_to_cell                   = node_to_cell_matrix[node_id];
+      const auto& node_local_number_in_its_cells = node_local_numbers_in_their_cells.itemArray(node_id);
+      const TinyMatrix<Dimension> I              = identity;
+      const TinyVector<Dimension> n              = exterior_normal[node_id];
+      const TinyMatrix<Dimension> nxn            = tensorProduct(n, n);
+      const TinyMatrix<Dimension> Q              = I - nxn;
+      if ((node_is_neumann[node_id]) && (not node_is_dirichlet[node_id])) {
+        if ((is_boundary_node[node_id]) and (not node_is_corner[node_id])) {
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id = node_to_cell[i_cell];
+            const size_t i_node  = node_local_number_in_its_cells[i_cell];
+
+            b[cell_id] += (1 - lambda) * node_boundary_values[node_id] *
+                          (1. / (sum_theta[node_id] * l2Norm(node_kappar[node_id] * n)) *
+                             dot(kappar_invBetar[node_id] * sum_Cjr[node_id], Q * Cjr(cell_id, i_node)) +
+                           dot(Cjr(cell_id, i_node), n));
+            b_prev[cell_id] += (1 - lambda) * node_boundary_prev_values[node_id] *
+                               (1. / (sum_theta[node_id] * l2Norm(node_kappar[node_id] * n)) *
+                                  dot(kappar_invBetar[node_id] * sum_Cjr[node_id], Q * Cjr(cell_id, i_node)) +
+                                dot(Cjr(cell_id, i_node), n));
+          }
+        } else if (node_is_corner[node_id]) {
+          const auto& node_to_face = node_to_face_matrix[node_id];
+          const CellId cell_id     = node_to_cell[0];
+          double sum_mes_l         = 0;
+          for (size_t i_face = 0; i_face < node_to_face.size(); ++i_face) {
+            FaceId face_id = node_to_face[i_face];
+            sum_mes_l += mes_l[face_id];
+          }
+          b[cell_id] += (1 - lambda) * 0.5 * node_boundary_values[node_id] * sum_mes_l;
+          b_prev[cell_id] += (1 - lambda) * 0.5 * node_boundary_prev_values[node_id] * sum_mes_l;
+        }
+
+      } else if (node_is_dirichlet[node_id]) {
+        if (not node_is_corner[node_id]) {
+          for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+            const CellId cell_id_j = node_to_cell[i_cell_j];
+            const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+            for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+              const CellId cell_id_k = node_to_cell[i_cell_k];
+              const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+              b[cell_id_j] +=
+                (1 - lambda) * dot(node_boundary_values[node_id] * kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k),
+                                   Cjr(cell_id_j, i_node_j));
+              b_prev[cell_id_j] += (1 - lambda) * dot(node_boundary_prev_values[node_id] * kappar_invBetar[node_id] *
+                                                        Cjr(cell_id_k, i_node_k),
+                                                      Cjr(cell_id_j, i_node_j));
+            }
+            const auto& cell_to_face = cell_to_face_matrix[cell_id_j];
+
+            for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+              const FaceId face_id     = cell_to_face[i_face];
+              const auto& face_to_node = face_to_node_matrix[face_id];
+
+              if (face_to_node[0] == node_id or face_to_node[1] == node_id) {
+                b[cell_id_j] += (1 - lambda) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                                face_boundary_values[face_id] *
+                                dot(inverse(node_betar[node_id]) * sum_Cjr[node_id], xj1_xj2_orth(cell_id_j, i_node_j));
+                b_prev[cell_id_j] +=
+                  (1 - lambda) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  face_boundary_prev_values[face_id] *
+                  dot(inverse(node_betar[node_id]) * sum_Cjr[node_id], xj1_xj2_orth(cell_id_j, i_node_j));
+              }
+            }
+          }
+
+        } else if (node_is_corner[node_id]) {
+          for (size_t i_cell_j = 0; i_cell_j < node_to_cell.size(); ++i_cell_j) {
+            const CellId cell_id_j = node_to_cell[i_cell_j];
+            const size_t i_node_j  = node_local_number_in_its_cells[i_cell_j];
+
+            for (size_t i_cell_k = 0; i_cell_k < node_to_cell.size(); ++i_cell_k) {
+              const CellId cell_id_k = node_to_cell[i_cell_k];
+              const size_t i_node_k  = node_local_number_in_its_cells[i_cell_k];
+
+              b[cell_id_j] += (1 - lambda) * dot(node_boundary_values[node_id] * corner_kappar_invBetar[node_id] *
+                                                   Cjr(cell_id_k, i_node_k),
+                                                 Cjr(cell_id_j, i_node_j));
+              b_prev[cell_id_j] += (1 - lambda) * dot(node_boundary_prev_values[node_id] *
+                                                        corner_kappar_invBetar[node_id] * Cjr(cell_id_k, i_node_k),
+                                                      Cjr(cell_id_j, i_node_j));
+            }
+            const auto& cell_to_face = cell_to_face_matrix[cell_id_j];
+
+            for (size_t i_face = 0; i_face < cell_to_face.size(); ++i_face) {
+              const FaceId face_id     = cell_to_face[i_face];
+              const auto& face_to_node = face_to_node_matrix[face_id];
+
+              if (face_to_node[0] == node_id or face_to_node[1] == node_id) {
+                b[cell_id_j] +=
+                  (1 - lambda) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  face_boundary_values[face_id] *
+                  dot(inverse(corner_betar[node_id]) * sum_Cjr[node_id], xj1_xj2_orth(cell_id_j, i_node_j));
+                b_prev[cell_id_j] +=
+                  (1 - lambda) * 0.5 * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[0] *
+                  face_boundary_prev_values[face_id] *
+                  dot(inverse(corner_betar[node_id]) * sum_Cjr[node_id], xj1_xj2_orth(cell_id_j, i_node_j));
+              }
+            }
+          }
+        }
+      }
+    };
+
+    for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+      const auto& face_to_cell                   = face_to_cell_matrix[face_id];
+      const auto& face_local_number_in_its_cells = face_local_numbers_in_their_cells.itemArray(face_id);
+      if (face_is_dirichlet[face_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < face_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = face_to_cell[i_cell_j];
+          const size_t i_face    = face_local_number_in_its_cells[i_cell_j];
+
+          b[cell_id_j] +=
+            lambda * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1] * face_boundary_values[face_id];
+          b_prev[cell_id_j] +=
+            lambda * mes_l[face_id] * normal_face_base(cell_id_j, i_face)[1] * face_boundary_prev_values[face_id];
+        }
+      } else if (face_is_neumann[face_id]) {
+        for (size_t i_cell_j = 0; i_cell_j < face_to_cell.size(); ++i_cell_j) {
+          const CellId cell_id_j = face_to_cell[i_cell_j];
+
+          b[cell_id_j] += lambda * mes_l[face_id] * face_boundary_values[face_id];
+          b_prev[cell_id_j] += lambda * mes_l[face_id] * face_boundary_prev_values[face_id];
+        }
+      }
+    }
+
+    Vector<double> T{mesh->numberOfCells()};
+    T = zero;
+
+    // Array<double> Ph2  = Ph;
+    Vector<double> A2P = A2 * Ph;
+
+    Vector<double> B{mesh->numberOfCells()};
+    parallel_for(
+      mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+        B[cell_id] = dt * ((1. - cn_coeff / 2.) * b[cell_id] + cn_coeff / 2. * b_prev[cell_id]) + A2P[cell_id];
+      });
+
+    std::cout << "B = " << B << "\n"
+              << "A2P = " << A2P << "\n";
+
+    NodeValue<TinyVector<Dimension>> ur{mesh->connectivity()};
+    ur.fill(zero);
+    for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
+      const auto& node_to_cell                  = node_to_cell_matrix[node_id];
+      const auto& node_local_number_in_its_cell = node_local_numbers_in_their_cells.itemArray(node_id);
+      if (not is_boundary_node[node_id]) {
+        for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+          const CellId cell_id = node_to_cell[i_cell];
+          const size_t i_node  = node_local_number_in_its_cell[i_cell];
+          ur[node_id] += Pj[cell_id] * Cjr(cell_id, i_node);
+        }
+        ur[node_id] = -inverse(node_betar[node_id]) * ur[node_id];
+        // std::cout << node_id << "; ur = " << ur[node_id] << "\n";
+      } else if (is_boundary_node[node_id] and node_is_dirichlet[node_id]) {
+        for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+          const CellId cell_id = node_to_cell[i_cell];
+          const size_t i_node  = node_local_number_in_its_cell[i_cell];
+          ur[node_id] += (node_boundary_values[node_id] - Pj[cell_id]) * Cjr(cell_id, i_node);
+        }
+        if (not node_is_corner[node_id]) {
+          ur[node_id] = inverse(node_betar[node_id]) * ur[node_id];
+        } else {
+          ur[node_id] = inverse(corner_betar[node_id]) * ur[node_id];
+        }
+        // std::cout << "bord : " << node_id << "; ur = " << ur[node_id] << "\n";
+      }
+    }
+
+    LinearSolver solver;
+    solver.solveLocalSystem(A1, T, B);
+
+    const NodeValue<const double> primal_node_temperature = [&] {
+      NodeValue<double> compute_primal_node_temperature{mesh->connectivity()};
+      compute_primal_node_temperature.fill(0);
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          // for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); node_id++) {
+          const auto& node_to_cell = node_to_cell_matrix[node_id];
+          double sum_Vj            = 0;
+          for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+            const CellId cell_id = node_to_cell[i_cell];
+            compute_primal_node_temperature[node_id] += Vj[cell_id] * T[cell_id];
+            sum_Vj += Vj[cell_id];
+          }
+          compute_primal_node_temperature[node_id] /= sum_Vj;
+        });
+      return compute_primal_node_temperature;
+    }();
+
+    const CellValue<const double> dual_node_temperature = [=] {
+      CellValue<double> my_dual_temperature{dual_mesh->connectivity()};
+      mapper->toDualCell(primal_node_temperature, my_dual_temperature);
+      return my_dual_temperature;
+    }();
+
+    // m_solution     = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+    m_cell_temperature = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+    m_node_temperature = std::make_shared<DiscreteFunctionP0<Dimension, double>>(dual_mesh);
+    // auto& solution = *m_solution;
+    auto& cell_temperature = *m_cell_temperature;
+    auto& node_temperature = *m_node_temperature;
+    parallel_for(
+      mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { cell_temperature[cell_id] = T[cell_id]; });
+    parallel_for(
+      dual_mesh->numberOfCells(),
+      PUGS_LAMBDA(CellId cell_id) { node_temperature[cell_id] = dual_node_temperature[cell_id]; });
+  }
+};
+
+// std::shared_ptr<const IDiscreteFunction>
+// ScalarHybridSchemeHandler::solution() const
+// {
+//   return m_scheme->getSolution();
+// }
+std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>>
+ScalarHybridSchemeHandler::solution() const
+{
+  return m_scheme->getSolution();
+}
+
+ScalarHybridSchemeHandler::ScalarHybridSchemeHandler(
+  const std::shared_ptr<const IDiscreteFunction>& cell_k,
+  const std::shared_ptr<const IDiscreteFunction>& f,
+  const std::shared_ptr<const IDiscreteFunction>& f_prev,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_prev_descriptor_list,
+  const std::shared_ptr<const IDiscreteFunction>& P,
+  const double& dt,
+  const double& cn_coeff,
+  const double& lambda)
+{
+  const std::shared_ptr i_mesh = getCommonMesh({cell_k, f});
+  if (not i_mesh) {
+    throw NormalError("primal discrete functions are not defined on the same mesh");
+  }
+
+  checkDiscretizationType({cell_k, f}, DiscreteFunctionType::P0);
+
+  switch (i_mesh->dimension()) {
+  case 1: {
+    throw NormalError("The scheme is not implemented in dimension 1");
+    break;
+  }
+  case 2: {
+    using MeshType                   = Mesh<Connectivity<2>>;
+    using DiscreteTensorFunctionType = DiscreteFunctionP0<2, TinyMatrix<2>>;
+    using DiscreteScalarFunctionType = DiscreteFunctionP0<2, double>;
+
+    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+
+    m_scheme =
+      std::make_unique<ScalarHybridScheme<2>>(mesh, std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
+                                              std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
+                                              std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f_prev),
+                                              bc_descriptor_list, bc_prev_descriptor_list,
+                                              std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(P), dt,
+                                              cn_coeff, lambda);
+    break;
+  }
+  case 3: {
+    throw NormalError("The scheme is not implemented in dimension 3");
+    break;
+  }
+  default: {
+    throw UnexpectedError("invalid mesh dimension");
+  }
+  }
+}
+
+ScalarHybridSchemeHandler::~ScalarHybridSchemeHandler() = default;
diff --git a/src/scheme/ScalarHybridScheme.hpp b/src/scheme/ScalarHybridScheme.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b110887d7bcce04d4456f8185bb6201ed983a8ae
--- /dev/null
+++ b/src/scheme/ScalarHybridScheme.hpp
@@ -0,0 +1,59 @@
+#ifndef SCALAR_HYBRID_SCHEME_HPP
+#define SCALAR_HYBRID_SCHEME_HPP
+
+#include <algebra/CRSMatrix.hpp>
+#include <algebra/CRSMatrixDescriptor.hpp>
+#include <algebra/LeastSquareSolver.hpp>
+#include <algebra/LinearSolver.hpp>
+#include <algebra/TinyVector.hpp>
+#include <algebra/Vector.hpp>
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshFaceBoundary.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
+#include <mesh/SubItemValuePerItem.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/FourierBoundaryConditionDescriptor.hpp>
+#include <scheme/IDiscreteFunction.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+
+class ScalarHybridSchemeHandler
+{
+ private:
+  class IScalarHybridScheme;
+
+  template <size_t Dimension>
+  class ScalarHybridScheme;
+
+  template <size_t Dimension>
+  class InterpolationWeightsManager;
+
+ public:
+  std::unique_ptr<IScalarHybridScheme> m_scheme;
+
+  // std::shared_ptr<const IDiscreteFunction> solution() const;
+  std::tuple<std::shared_ptr<const IDiscreteFunction>, std::shared_ptr<const IDiscreteFunction>> solution() const;
+
+  ScalarHybridSchemeHandler(
+    const std::shared_ptr<const IDiscreteFunction>& cell_k,
+    const std::shared_ptr<const IDiscreteFunction>& f,
+    const std::shared_ptr<const IDiscreteFunction>& f_prec,
+    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_prev_descriptor_list,
+    const std::shared_ptr<const IDiscreteFunction>& P,
+    const double& dt,
+    const double& cn_coeff,
+    const double& lambda);
+
+  ~ScalarHybridSchemeHandler();
+};
+
+#endif   // SCALAR_NODAL_SCHEME_HPP
diff --git a/src/scheme/ScalarNodalScheme.cpp b/src/scheme/ScalarNodalScheme.cpp
index c7fe9279c6fa0237f07cf803ae78dae518141595..0897f6d8323c2cccd368f5cedb23fcdf33b3c7a3 100644
--- a/src/scheme/ScalarNodalScheme.cpp
+++ b/src/scheme/ScalarNodalScheme.cpp
@@ -30,7 +30,8 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
   class DirichletBoundaryCondition
   {
    private:
-    const Array<const double> m_value_list;
+    const Array<const double> m_node_value_list;
+    const Array<const double> m_face_value_list;
     const Array<const FaceId> m_face_list;
     const Array<const NodeId> m_node_list;
 
@@ -48,17 +49,28 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
     }
 
     const Array<const double>&
-    valueList() const
+    nodeValueList() const
     {
-      return m_value_list;
+      return m_node_value_list;
+    }
+
+    const Array<const double>&
+    faceValueList() const
+    {
+      return m_face_value_list;
     }
 
     DirichletBoundaryCondition(const Array<const FaceId>& face_list,
                                const Array<const NodeId>& node_list,
-                               const Array<const double>& value_list)
-      : m_value_list{value_list}, m_face_list{face_list}, m_node_list{node_list}
+                               const Array<const double>& node_value_list,
+                               const Array<const double>& face_value_list)
+      : m_node_value_list{node_value_list},
+        m_face_value_list{face_value_list},
+        m_face_list{face_list},
+        m_node_list{node_list}
     {
-      Assert(m_value_list.size() == m_node_list.size());
+      Assert(m_node_value_list.size() == m_node_list.size());
+      Assert(m_face_value_list.size() == m_face_list.size());
     }
 
     ~DirichletBoundaryCondition() = default;
@@ -183,15 +195,20 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
             getMeshNodeBoundary(*mesh, dirichlet_bc_descriptor.boundaryDescriptor());
 
           const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
-          // MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
 
-          Array<const double> value_list =
+          Array<const double> node_value_list =
             InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(),
                                                                                                       mesh_node_boundary
                                                                                                         .nodeList());
-          boundary_condition_list.push_back(
-            DirichletBoundaryCondition{mesh_face_boundary.faceList(), mesh_node_boundary.nodeList(), value_list});
-
+          Array<const double> face_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+          boundary_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(),
+                                                                       mesh_node_boundary.nodeList(), node_value_list,
+                                                                       face_value_list});
         } else {
           throw NotImplementedError("Dirichlet BC in 1d");
         }
@@ -255,14 +272,20 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
             getMeshNodeBoundary(*mesh, dirichlet_bc_prev_descriptor.boundaryDescriptor());
 
           const FunctionSymbolId g_id = dirichlet_bc_prev_descriptor.rhsSymbolId();
-          //          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
+          MeshDataType& mesh_data     = MeshDataManager::instance().getMeshData(*mesh);
 
-          Array<const double> value_list =
+          Array<const double> node_value_list =
             InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(),
                                                                                                       mesh_node_boundary
                                                                                                         .nodeList());
-          boundary_prev_condition_list.push_back(
-            DirichletBoundaryCondition{mesh_face_boundary.faceList(), mesh_node_boundary.nodeList(), value_list});
+          Array<const double> face_value_list =
+            InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                      mesh_data.xl(),
+                                                                                                      mesh_face_boundary
+                                                                                                        .faceList());
+          boundary_prev_condition_list.push_back(DirichletBoundaryCondition{mesh_face_boundary.faceList(),
+                                                                            mesh_node_boundary.nodeList(),
+                                                                            node_value_list, face_value_list});
 
         } else {
           throw NotImplementedError("Dirichlet BC in 1d");
@@ -559,13 +582,13 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
               }
 
             } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
-              const auto& node_list  = bc.nodeList();
-              const auto& value_list = bc.valueList();
+              const auto& node_list       = bc.nodeList();
+              const auto& node_value_list = bc.nodeValueList();
 
               for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
                 const NodeId node_id = node_list[i_node];
 
-                compute_node_boundary_values[node_id] = value_list[i_node];
+                compute_node_boundary_values[node_id] = node_value_list[i_node];
               }
             }
           },
@@ -596,30 +619,40 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
               for (size_t i_face = 0; i_face < face_list.size(); ++i_face) {
                 const FaceId face_id   = face_list[i_face];
                 const auto& face_nodes = face_to_node_matrix[face_id];
-
                 for (size_t i_node = 0; i_node < face_nodes.size(); ++i_node) {
                   const NodeId node_id = face_nodes[i_node];
                   if (not node_is_dirichlet[node_id]) {
-                    compute_node_boundary_values[node_id] += value_list[i_face] * mes_l[face_id];
-                    sum_mes_l[node_id] += mes_l[face_id];
+                    if (node_is_angle[node_id]) {
+                      const auto& node_to_face = node_to_face_matrix[node_id];
+                      for (size_t i_face_node = 0; i_face_node < node_to_face.size(); ++i_face_node) {
+                        const FaceId face_node_id = node_to_face[i_face_node];
+                        if (face_id == face_node_id) {
+                          compute_node_boundary_values[node_id] +=
+                            value_list[i_face] * mes_l[face_id] * normal_base[node_id][i_face_node];
+                        }
+                      }
+                    } else {
+                      compute_node_boundary_values[node_id] += value_list[i_face] * mes_l[face_id];
+                      sum_mes_l[node_id] += mes_l[face_id];
+                    }
                   }
                 }
               }
             } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
-              const auto& node_list  = bc.nodeList();
-              const auto& value_list = bc.valueList();
+              const auto& node_list       = bc.nodeList();
+              const auto& node_value_list = bc.nodeValueList();
 
               for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
                 const NodeId node_id = node_list[i_node];
 
-                compute_node_boundary_values[node_id] = value_list[i_node];
+                compute_node_boundary_values[node_id] = node_value_list[i_node];
               }
             }
           },
           boundary_condition);
       }
       for (NodeId node_id = 0; node_id < mesh->numberOfNodes(); ++node_id) {
-        if ((not node_is_dirichlet[node_id]) && (node_is_neumann[node_id])) {
+        if ((not node_is_dirichlet[node_id]) && (node_is_neumann[node_id]) and not node_is_angle[node_id]) {
           compute_node_boundary_values[node_id] /= sum_mes_l[node_id];
         }
       }
@@ -792,6 +825,8 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
         return compute_sum_theta;
       }();
 
+      //      const double lambda = 0;
+
       const Array<int> non_zeros{mesh->numberOfCells()};
       non_zeros.fill(4);   // Modif pour optimiser
       CRSMatrixDescriptor<double> S1(mesh->numberOfCells(), mesh->numberOfCells(), non_zeros);