diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 45db502f0eb5b3940c4082ba686fc16cf8c5cab4..edbce1e3f20264e1a5da949314b1f2a4123c133e 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/ScalarNodalScheme.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 #include <scheme/VectorDiamondScheme.hpp>
 #include <utils/Socket.hpp>
@@ -601,6 +602,24 @@ SchemeModule::SchemeModule()
         return ScalarDiamondSchemeHandler{alpha, mub_dual, mu_dual, f, bc_descriptor_list}.solution();
       }
 
+      ));
+  this->_addBuiltinFunction(
+    "nodalheat",
+    std::make_shared<BuiltinFunctionEmbedder<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::shared_ptr<const IDiscreteFunction>&,
+                               const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&)>>(
+
+      [](const std::shared_ptr<const IDiscreteFunction>& alpha,
+         const std::shared_ptr<const IDiscreteFunction>& mub_dual,
+         const std::shared_ptr<const IDiscreteFunction>& mu_dual, const std::shared_ptr<const IDiscreteFunction>& f,
+         const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
+        -> const std::shared_ptr<const IDiscreteFunction> {
+        return ScalarNodalSchemeHandler{alpha, mub_dual, mu_dual, f, bc_descriptor_list}.solution();
+      }
+
       ));
 
   this->_addBuiltinFunction("unsteadyelasticity",
diff --git a/src/scheme/ScalarNodalScheme.cpp b/src/scheme/ScalarNodalScheme.cpp
index 791e1c8ff7b94c39663bea036faff5ecc5a08b51..215cf8510ec40f3cf19268d70c561f1034fc6ac6 100644
--- a/src/scheme/ScalarNodalScheme.cpp
+++ b/src/scheme/ScalarNodalScheme.cpp
@@ -3,7 +3,6 @@
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
 
-
 class ScalarNodalSchemeHandler::IScalarNodalScheme
 {
  public:
@@ -56,6 +55,7 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
    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 FaceId>&
@@ -64,14 +64,22 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
       return m_face_list;
     }
 
+    const Array<const NodeId>&
+    nodeList() const
+    {
+      return m_node_list;
+    }
+
     const Array<const double>&
     valueList() const
     {
       return m_value_list;
     }
 
-    NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list)
-      : m_value_list{value_list}, m_face_list{face_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());
     }
@@ -118,7 +126,6 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
     ~FourierBoundaryCondition() = default;
   };
 
-
  public:
   std::shared_ptr<const IDiscreteFunction>
   getSolution() const final
@@ -127,15 +134,14 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
   }
 
   ScalarNodalScheme(const std::shared_ptr<const MeshType>& mesh,
-                      const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& alpha,
-                      const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyMatrix<Dimension>>>& cell_k_bound,
-                      const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyMatrix<Dimension>>>& cell_k,
-                      const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f,
-                      const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
+                    const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& alpha,
+                    const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyMatrix<Dimension>>>& cell_k_bound,
+                    const std::shared_ptr<const DiscreteFunctionP0<Dimension, TinyMatrix<Dimension>>>& cell_k,
+                    const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f,
+                    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
   {
-
-    using BoundaryCondition = std::variant<DirichletBoundaryCondition, FourierBoundaryCondition,
-                                           NeumannBoundaryCondition>;
+    using BoundaryCondition =
+      std::variant<DirichletBoundaryCondition, FourierBoundaryCondition, NeumannBoundaryCondition>;
 
     using BoundaryConditionList = std::vector<BoundaryCondition>;
 
@@ -179,6 +185,8 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
         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);
@@ -189,10 +197,11 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
                                                                                                       mesh_face_boundary
                                                                                                         .faceList());
 
-          boundary_condition_list.push_back(NeumannBoundaryCondition{mesh_face_boundary.faceList(), value_list});
+          boundary_condition_list.push_back(
+            NeumannBoundaryCondition{mesh_face_boundary.faceList(), mesh_node_boundary.nodeList(), value_list});
 
         } else {
-          throw NotImplementedError("Dirichlet BC in 1d");
+          throw NotImplementedError("Neumann BC in 1d");
         }
         break;
       }
@@ -207,268 +216,377 @@ class ScalarNodalSchemeHandler::ScalarNodalScheme : public ScalarNodalSchemeHand
       }
     }
 
-    if constexpr (Dimension > 1) {
-      const CellValue<const size_t> cell_dof_number = [&] {
-        CellValue<size_t> compute_cell_dof_number{mesh->connectivity()};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { compute_cell_dof_number[cell_id] = cell_id; });
-        return compute_cell_dof_number;
-      }();
-      size_t number_of_dof = mesh->numberOfCells();
-
-      const FaceValue<const size_t> face_dof_number = [&] {
-        FaceValue<size_t> compute_face_dof_number{mesh->connectivity()};
-        compute_face_dof_number.fill(std::numeric_limits<size_t>::max());
-        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>) or
-                            (std::is_same_v<T, FourierBoundaryCondition>) or
-                            (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];
-                  if (compute_face_dof_number[face_id] != std::numeric_limits<size_t>::max()) {
-                    std::ostringstream os;
-                    os << "The face " << face_id << " is used at least twice for boundary conditions";
-                    throw NormalError(os.str());
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+    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 auto is_boundary_node = mesh->connectivity().isBoundaryNode();
+
+    // 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& node_local_numbers_in_their_cells = mesh->connectivity().nodeLocalNumbersInTheirCells();
+    const CellValue<const double> Vj              = mesh_data.Vj();
+    const auto& node_to_cell_matrix               = mesh->connectivity().nodeToCellMatrix();
+
+    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 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& 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_dirichlet[node_id] = true;
+                }
+              }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_node_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];
+
+            for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+              const CellId cell_id      = node_to_cell[i_cell];
+              const auto& cell_nodes    = cell_to_node_matrix[cell_id];
+              const NodeId prev_node_id = cell_to_node_matrix[cell_id][(node_id - 1) % cell_nodes.size()];
+              const NodeId next_node_id = cell_to_node_matrix[cell_id][(node_id + 1) % cell_nodes.size()];
+
+              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 TinyVector<Dimension>> exterior_normal = [&] {
+      NodeValue<TinyVector<Dimension>> compute_exterior_normal;
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          compute_exterior_normal[node_id]          = zero;
+          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_loc = node_local_number_in_its_cell[cell_id];
+            compute_exterior_normal[node_id] += Cjr(cell_id, i_node_loc);
+          }
+          compute_exterior_normal[node_id] =
+            1. / l2Norm(compute_exterior_normal[node_id]) * compute_exterior_normal[node_id];
+        });
+      return compute_exterior_normal;
+    }();
+
+    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 double> node_boundary_values = [&] {
+      NodeValue<double> compute_node_boundary_values;
+      compute_node_boundary_values.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 (node_is_dirichlet[node_id] == false) {
+                    compute_node_boundary_values[node_id] += 0.5 * value_list[i_face] * mes_l[face_id];
+                  }
+                }
+              }
+            } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              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 (node_is_neumann[node_id] == false) {
+                    compute_node_boundary_values[node_id] += 0.5 * value_list[i_face] * mes_l[face_id];
                   } else {
-                    compute_face_dof_number[face_id] = number_of_dof++;
+                    compute_node_boundary_values[node_id] = value_list[i_face] * mes_l[face_id];
                   }
                 }
               }
-            },
-            boundary_condition);
-        }
+            }
+          },
+          boundary_condition);
+      }
+      return compute_node_boundary_values;
+    }();
 
-        return compute_face_dof_number;
+    {
+      CellValue<const TinyMatrix<Dimension>> cell_kappaj  = cell_k->cellValues();
+      CellValue<const TinyMatrix<Dimension>> cell_kappajb = cell_k_bound->cellValues();
+
+      const NodeValue<const TinyMatrix<Dimension>> node_kappar = [&] {
+        NodeValue<TinyMatrix<Dimension>> kappa;
+        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 j = 0; j < node_to_cell.size(); ++j) {
+              const CellId J      = node_to_cell[j];
+              double local_weight = 1. / l2Norm(xr[node_id] - xj[J]);
+              kappa[node_id] += local_weight * cell_kappaj[J];
+              weight += local_weight;
+            }
+            kappa[node_id] = 1. / weight * kappa[node_id];
+          });
+        return kappa;
       }();
 
-      const FaceValue<const bool> face_is_neumann = [&] {
-        FaceValue<bool> face_is_neumann{mesh->connectivity()};
-        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>) or
-                            (std::is_same_v<T, FourierBoundaryCondition>)) {
-                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];
-                  face_is_neumann[face_id] = true;
-                }
-              }
-            },
-            boundary_condition);
-        }
+      const NodeValue<const TinyMatrix<Dimension>> node_kapparb = [&] {
+        NodeValue<TinyMatrix<Dimension>> kappa;
+        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 j = 0; j < node_to_cell.size(); ++j) {
+              const CellId J      = node_to_cell[j];
+              double local_weight = 1. / l2Norm(xr[node_id] - xj[J]);
+              kappa[node_id] += local_weight * cell_kappajb[J];
+              weight += local_weight;
+            }
+            kappa[node_id] = 1. / weight * kappa[node_id];
+          });
+        return kappa;
+      }();
 
-        return face_is_neumann;
+      const NodeValue<const TinyMatrix<Dimension>> node_betar = [&] {
+        NodeValue<TinyMatrix<Dimension>> beta;
+        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 j = 0; j < node_to_cell.size(); ++j) {
+              const CellId J       = node_to_cell[j];
+              const unsigned int R = node_local_number_in_its_cell[j];
+              beta[node_id] += tensorProduct(Cjr(J, R), xr[node_id] - xj[J]);
+            }
+          });
+        return beta;
       }();
 
+      const NodeValue<const TinyMatrix<Dimension>> kappar_invBetar = [&] {
+        NodeValue<TinyMatrix<Dimension>> kappa_invBeta;
+        parallel_for(
+          mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+            kappa_invBeta[node_id] = node_kappar[node_id] * inverse(node_betar[node_id]);
+          });
+        return kappa_invBeta;
+      }();
 
-      MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+      const Array<int> non_zeros{mesh->numberOfCells()};
+      non_zeros.fill(Dimension);   // Modif pour optimiser
+      CRSMatrixDescriptor<double> S(mesh->numberOfCells(), mesh->numberOfCells(), non_zeros);
 
-      const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl();
-      const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj();
-      const NodeValue<const TinyVector<Dimension>>& xr = mesh_data.xr();
+      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_to_face_matrix = mesh->connectivity().nodeToFaceMatrix();
-      const auto& cell_to_node_matrix = mesh.connectivity().cellToNodeMatrix();
-      const auto& node_local_numbers_in_their_cells = mesh.connectivity().nodeLocalNumbersInTheirCells();
+        if (not is_boundary_node[node_id]) {
+          for (size_t cell_id_j = 0; cell_id_j < node_to_cell.size(); ++cell_id_j) {
+            const CellId J                          = node_to_cell[cell_id_j];
+            const auto& node_local_number_in_j_cell = node_local_numbers_in_their_cells.itemArray(J);
+            const size_t node_id_j                  = node_local_number_in_j_cell[node_id];
 
-      const NodeValuePerCell<const TinyVector<Dimension>>& Cjr = mesh_data.Cjr();
+            for (size_t cell_id_k = 0; cell_id_k < node_to_cell.size(); ++cell_id_k) {
+              const CellId K                          = node_to_cell[cell_id_k];
+              const auto& node_local_number_in_k_cell = node_local_numbers_in_their_cells.itemArray(K);
+              const size_t node_id_k                  = node_local_number_in_k_cell[node_id];
 
-      {
+              S(J, K) += dot(kappar_invBetar[node_id] * Cjr(K, node_id_k), Cjr(J, node_id_j));
+            }
+          }
+        } else if (not node_is_corner[node_id]) {
+        }
+      }
 
+      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& node_list = bc.nodeList();
 
-        CellValue<const TinyMatrix<Dimension>> cell_kappaj  = cell_k->cellValues();
-        CellValue<const TinyMatrix<Dimension>> cell_kappajb = cell_k_bound->cellValues();
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                const NodeId node_id     = node_list[i_node];
+                const auto& node_to_cell = node_to_cell_matrix[node_id];
 
-        const CellValue<const double> Vj = mesh_data.Vj();
-        const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix();
+                for (size_t cell_id_j = 0; cell_id_j < node_to_cell.size(); ++cell_id_j) {
+                  const CellId J                          = node_to_cell[cell_id_j];
+                  const auto& node_local_number_in_j_cell = node_local_numbers_in_their_cells.itemArray(J);
+                  const size_t node_id_j                  = node_local_number_in_j_cell[node_id];
 
-        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();
-          }
-        }();
+                  for (size_t cell_id_k = 0; cell_id_k < node_to_cell.size(); ++cell_id_k) {
+                    const CellId K                          = node_to_cell[cell_id_k];
+                    const auto& node_local_number_in_k_cell = node_local_numbers_in_their_cells.itemArray(K);
+                    const size_t node_id_k                  = node_local_number_in_k_cell[node_id];
 
-        const NodeValue<const TinyMatrix<Dimension>> node_kappar = [&] {
-          NodeValue<const TinyMatrix<Dimension>> kappa;
-          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 j = 0; j < node_to_cell.size(); ++j) {
-                  const CellId J       = node_to_cell[j];
-                  double local_weight = 1 / l2Norm(xr[node_id]-xj[J]);
-                  kappa[node_id] += cell_kappaj[J] * local_weight;
-                  weight += local_weight;
+                    const TinyMatrix<Dimension> I   = identity;
+                    const TinyVector<Dimension> n   = exterior_normal[node_id];
+                    const TinyMatrix<Dimension> nxn = tensorProduct(n, n);
+                    const TinyMatrix<Dimension> P   = I - nxn;
+
+                    S(J, K) += dot(kappar_invBetar[node_id] * Cjr(K, node_id_k), P * Cjr(J, node_id_j));
+                  }
                 }
-                kappa[node_id] /= weight;
-            }
-          );
-          return kappa;
-        }();
-
-        const NodeValue<const TinyMatrix<Dimension>> node_kapparb = [&] {
-          NodeValue<const TinyMatrix<Dimension>> kappa;
-          parallel_for(
-              mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
-                kappa[node_id] = zero;
+              }
+            } else if constexpr (std::is_same_v<T, DirichletBoundaryCondition>) {
+              const auto& node_list = bc.faceList();
+
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                const NodeId node_id     = node_list[i_node];
                 const auto& node_to_cell = node_to_cell_matrix[node_id];
-                double weight = 0;
-                for (size_t j = 0; j < node_to_cell.size(); ++j) {
-                  const CellId J       = node_to_cell[j];
-                  double local_weight = 1 / l2Norm(xr[node_id]-xj[J]);
-                  kappa[node_id] += cell_kappajb[J] * local_weight;
-                  weight += local_weight;
-                }
-                kappa[node_id] /= weight;
-            });
-          return kappa;
-        }();
-
-        const NodeValue<const TinyMatrix<Dimension>> node_betar = [&] {
-          NodeValue<const TinyMatrix<Dimension>> beta;
-          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 j = 0; j < node_to_cell.size(); ++j) {
-                  const CellId J = node_to_cell[j];
-                  const unsigned int R = node_local_number_in_its_cell[j];
-                  beta[node_id] += tensorProduct(Cjr(J,R),xr[node_id]-xj[J]);
-                }
-            });
-            return beta;
-        }();
-
-
-        FaceValue<const double> alpha_l = [&] {
-          FaceValue<double> alpha_j{mesh->connectivity()};
-
-          parallel_for(
-            mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) {
-              alpha_j[face_id] = 1;
-            });
-
-          return alpha_j;
-        }();
-
-        FaceValue<const double> alphab_l = [&] {
-          FaceValue<double> alpha_lb{mesh->connectivity()};
-
-          parallel_for(
-            mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) {
-              alpha_lb[face_id] = 1; //Refaire
-            });
-
-          return alpha_lb;
-        }();
-
-
-        const Array<int> non_zeros{number_of_dof};
-        non_zeros.fill(Dimension); //Modif pour optimiser
-        CRSMatrixDescriptor<double> S(number_of_dof, number_of_dof, non_zeros);
-
-        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
-          const auto& face_to_cell = face_to_cell_matrix[face_id];
-          const double beta_l             = l2Norm(dualClj[face_id]) * alpha_l[face_id] * mes_l[face_id];
-          const double betab_l            = l2Norm(dualClj[face_id]) * alphab_l[face_id] * mes_l[face_id];
-          for (size_t i_cell = 0; i_cell < face_to_cell.size(); ++i_cell) {
-            const CellId cell_id1 = face_to_cell[i_cell];
-            for (size_t j_cell = 0; j_cell < face_to_cell.size(); ++j_cell) {
-              const CellId cell_id2 = face_to_cell[j_cell];
-              if (i_cell == j_cell) {
-                S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += beta_l;
-                if (face_is_neumann[face_id]) {
-                  S(face_dof_number[face_id], cell_dof_number[cell_id2]) -= betab_l;
+
+                for (size_t cell_id_j = 0; cell_id_j < node_to_cell.size(); ++cell_id_j) {
+                  const CellId J                          = node_to_cell[cell_id_j];
+                  const auto& node_local_number_in_j_cell = node_local_numbers_in_their_cells.itemArray(J);
+                  const size_t node_id_j                  = node_local_number_in_j_cell[node_id];
+
+                  for (size_t cell_id_k = 0; cell_id_k < node_to_cell.size(); ++cell_id_k) {
+                    const CellId K                          = node_to_cell[cell_id_k];
+                    const auto& node_local_number_in_k_cell = node_local_numbers_in_their_cells.itemArray(K);
+                    const size_t node_id_k                  = node_local_number_in_k_cell[node_id];
+
+                    S(J, K) += dot(kappar_invBetar[node_id] * Cjr(K, node_id_k), Cjr(J, node_id_j));
+                  }
                 }
-              } else {
-                S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l;
               }
             }
-          }
-        }
+          },
+          boundary_condition);
+      }
 
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          const size_t j = cell_dof_number[cell_id];
-          S(j, j) += (*alpha)[cell_id] * Vj[cell_id];
-        }
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        S(cell_id, cell_id) += Vj[cell_id];
+      }
 
+      CellValue<const double> fj = f->cellValues();
 
-        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
-          if (face_is_dirichlet[face_id]) {
-            S(face_dof_number[face_id], face_dof_number[face_id]) += 1;
-          }
-        }
-
-        CellValue<const double> fj = f->cellValues();
+      CRSMatrix A{S.getCRSMatrix()};
+      Vector<double> b{mesh->numberOfCells()};
+      b = zero;
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        b[cell_id] = fj[cell_id] * Vj[cell_id];
+      }
 
-        CRSMatrix A{S.getCRSMatrix()};
-        Vector<double> b{number_of_dof};
-        b = zero;
-        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-          b[cell_dof_number[cell_id]] = fj[cell_id] * Vj[cell_id];
-        }
-        // Dirichlet on b^L_D
-        {
-          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& 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];
-                    b[face_dof_number[face_id]] += value_list[i_face];
-                  }
-                }
-              },
-              boundary_condition);
-          }
-        }
-        // EL b^L
-        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>) or
-                            (std::is_same_v<T, FourierBoundaryCondition>)) {
-                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) {
-                  FaceId face_id = face_list[i_face];
-                  b[face_dof_number[face_id]] += mes_l[face_id] * value_list[i_face];
+      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>) {   // To do
+              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];
+                b[face_id] += value_list[i_face];
+              }
+            } else if constexpr (std::is_same_v<T, NeumannBoundaryCondition>) {
+              const auto& node_list  = bc.faceList();
+              const auto& value_list = bc.valueList();
+
+              for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+                NodeId node_id                = node_list[i_node];
+                const auto& node_to_cell      = node_to_cell_matrix[node_id];
+                const TinyVector<Dimension> n = exterior_normal[node_id];
+
+                for (size_t i_cell = 0; i_cell < node_to_cell.size(); ++i_cell) {
+                  CellId J                              = node_to_cell[i_cell];
+                  const auto& node_local_number_in_cell = node_local_numbers_in_their_cells.itemArray(J);
+                  const size_t node_id_j                = node_local_number_in_cell[node_id];
+                  b[J] -= dot(Cjr(J, node_id_j), n) * value_list[i_node];
                 }
               }
-            },
-            boundary_condition);
-        }
+            }
+          },
+          boundary_condition);
+      }
 
-        Vector<double> T{number_of_dof};
-        T = zero;
+      Vector<double> T{mesh->numberOfCells()};
+      T = zero;
 
-        LinearSolver solver;
-        solver.solveLocalSystem(A, T, b);
+      LinearSolver solver;
+      solver.solveLocalSystem(A, T, b);
 
-        m_solution     = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
-        auto& solution = *m_solution;
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { solution[cell_id] = T[cell_dof_number[cell_id]]; });
-      }
+      m_solution     = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+      auto& solution = *m_solution;
+      parallel_for(
+        mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { solution[cell_id] = T[cell_id]; });
     }
   }
 };
@@ -481,7 +599,7 @@ ScalarNodalSchemeHandler::solution() const
 
 ScalarNodalSchemeHandler::ScalarNodalSchemeHandler(
   const std::shared_ptr<const IDiscreteFunction>& alpha,
-  const std::shared_ptr<const IDiscreteFunction>& k_bound,
+  const std::shared_ptr<const IDiscreteFunction>& cell_k_bound,
   const std::shared_ptr<const IDiscreteFunction>& cell_k,
   const std::shared_ptr<const IDiscreteFunction>& f,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
@@ -490,30 +608,12 @@ ScalarNodalSchemeHandler::ScalarNodalSchemeHandler(
   if (not i_mesh) {
     throw NormalError("primal discrete functions are not defined on the same mesh");
   }
-  const std::shared_ptr i_dual_mesh = getCommonMesh({cell_k_bound, cell_k});
-  if (not i_dual_mesh) {
-    throw NormalError("dual discrete functions are not defined on the same mesh");
-  }
+
   checkDiscretizationType({alpha, cell_k_bound, cell_k, f}, DiscreteFunctionType::P0);
 
   switch (i_mesh->dimension()) {
   case 1: {
-    using MeshType                   = Mesh<Connectivity<1>>;
-    using DiscreteTensorFunctionType = DiscreteFunctionP0<1, TinyMatrix<1>>;
-    using DiscreteScalarFunctionType = DiscreteFunctionP0<1, double>;
-
-    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-
-    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) {
-      throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh");
-    }
-
-    m_scheme =
-      std::make_unique<ScalarNodalScheme<1>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k_bound),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
-                                               std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
-                                               bc_descriptor_list);
+    throw NormalError("The scheme is not implemented in dimension 1");
     break;
   }
   case 2: {
@@ -523,16 +623,12 @@ ScalarNodalSchemeHandler::ScalarNodalSchemeHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) {
-      throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh");
-    }
-
     m_scheme =
       std::make_unique<ScalarNodalScheme<2>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k_bound),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
-                                               std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
-                                               bc_descriptor_list);
+                                             std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k_bound),
+                                             std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
+                                             std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
+                                             bc_descriptor_list);
     break;
   }
   case 3: {
@@ -542,16 +638,12 @@ ScalarNodalSchemeHandler::ScalarNodalSchemeHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != i_dual_mesh) {
-      throw NormalError("dual variables are is not defined on the diamond dual of the primal mesh");
-    }
-
     m_scheme =
       std::make_unique<ScalarNodalScheme<3>>(mesh, std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(alpha),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k_bound),
-                                               std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
-                                               std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
-                                               bc_descriptor_list);
+                                             std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k_bound),
+                                             std::dynamic_pointer_cast<const DiscreteTensorFunctionType>(cell_k),
+                                             std::dynamic_pointer_cast<const DiscreteScalarFunctionType>(f),
+                                             bc_descriptor_list);
     break;
   }
   default: {