From 03c2721760b4bea294531c229223f3dbb50119b6 Mon Sep 17 00:00:00 2001
From: labourasse <labourassee@gmail.com>
Date: Wed, 23 Dec 2020 19:43:15 +0100
Subject: [PATCH] Put the calculation of the weights out of the schemes and add
 an unsteady elasticity scheme

---
 src/language/algorithms/CMakeLists.txt        |   1 +
 src/language/algorithms/ParabolicHeat.cpp     | 148 +++-
 src/language/algorithms/ParabolicHeat.hpp     |   4 +-
 .../algorithms/UnsteadyElasticity.cpp         | 663 +++++++++++++++
 .../algorithms/UnsteadyElasticity.hpp         |  37 +
 src/language/modules/SchemeModule.cpp         |  55 +-
 src/scheme/ScalarDiamondScheme.hpp            | 106 +--
 src/scheme/VectorDiamondScheme.hpp            | 785 ++++++++++++++++++
 8 files changed, 1697 insertions(+), 102 deletions(-)
 create mode 100644 src/language/algorithms/UnsteadyElasticity.cpp
 create mode 100644 src/language/algorithms/UnsteadyElasticity.hpp
 create mode 100644 src/scheme/VectorDiamondScheme.hpp

diff --git a/src/language/algorithms/CMakeLists.txt b/src/language/algorithms/CMakeLists.txt
index c1089d014..6292a5830 100644
--- a/src/language/algorithms/CMakeLists.txt
+++ b/src/language/algorithms/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_library(PugsLanguageAlgorithms
   AcousticSolverAlgorithm.cpp
   ElasticityDiamondAlgorithm.cpp
+  UnsteadyElasticity.cpp
   Heat5PointsAlgorithm.cpp
   HeatDiamondAlgorithm.cpp
   HeatDiamondAlgorithm2.cpp
diff --git a/src/language/algorithms/ParabolicHeat.cpp b/src/language/algorithms/ParabolicHeat.cpp
index e4527a682..6ae956ec0 100644
--- a/src/language/algorithms/ParabolicHeat.cpp
+++ b/src/language/algorithms/ParabolicHeat.cpp
@@ -29,6 +29,7 @@ ParabolicHeatScheme<Dimension>::ParabolicHeatScheme(
   std::shared_ptr<const IMesh> i_mesh,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
   const FunctionSymbolId& T_id,
+  const FunctionSymbolId& T_init_id,
   const FunctionSymbolId& kappa_id,
   const FunctionSymbolId& f_id,
   const double& Tf,
@@ -237,18 +238,19 @@ ParabolicHeatScheme<Dimension>::ParabolicHeatScheme(
     CellValue<double> Tj =
       InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj());
     CellValue<double> Temperature =
-      InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_id, mesh_data.xj());
+      InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(T_init_id,
+                                                                                                mesh_data.xj());
     //{mesh->connectivity()};
     FaceValue<double> Tl =
       InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_id, mesh_data.xl());
     FaceValue<double> Temperature_face =
-      InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_id, mesh_data.xl());
-    NodeValue<double> Tr(mesh->connectivity());
+      InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(T_init_id,
+                                                                                                mesh_data.xl());
     {
       VTKWriter vtk_writer("Tanalytic_" + std::to_string(Dimension), 0.01);
 
       vtk_writer.write(mesh,
-                       {NamedItemValue{"Tr", Tr}, NamedItemValue{"temperature", Tj},
+                       {NamedItemValue{"exact_temperature", Tj}, NamedItemValue{"init_temperature", Temperature},
                         NamedItemValue{"coords", mesh->xr()}, NamedItemValue{"xj", mesh_data.xj()},
                         NamedItemValue{"cell_owner", mesh->connectivity().cellOwner()},
                         NamedItemValue{"node_owner", mesh->connectivity().nodeOwner()}},
@@ -258,6 +260,11 @@ ParabolicHeatScheme<Dimension>::ParabolicHeatScheme(
     Assert(dt > 0, "The time-step have to be positive!");
     const CellValue<const double> primal_Vj = mesh_data.Vj();
     double output_time                      = 0;
+
+    InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary);
+    iwm.compute();
+    CellValuePerNode<double> w_rj = iwm.wrj();
+    FaceValuePerNode<double> w_rl = iwm.wrl();
     do {
       {
         if (time >= output_time) {
@@ -271,7 +278,7 @@ ParabolicHeatScheme<Dimension>::ParabolicHeatScheme(
       double deltat = std::min(dt, Tf - time);
       std::cout << "Current time = " << time << " time-step = " << deltat << " final time = " << Tf << "\n";
       ScalarDiamondScheme<Dimension>(i_mesh, bc_descriptor_list, kappa_id, f_id, Temperature, Temperature_face, Tf,
-                                     deltat);
+                                     deltat, w_rj, w_rl);
       time += deltat;
     } while (time < Tf && std::abs(time - Tf) > 1e-15);
     {
@@ -447,12 +454,141 @@ class ParabolicHeatScheme<Dimension>::SymmetryBoundaryCondition
   ~SymmetryBoundaryCondition() = default;
 };
 
+template <size_t Dimension>
+class ParabolicHeatScheme<Dimension>::InterpolationWeightsManager
+{
+ private:
+  std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh;
+  FaceValue<bool> m_primal_face_is_on_boundary;
+  NodeValue<bool> m_primal_node_is_on_boundary;
+  CellValuePerNode<double> m_w_rj;
+  FaceValuePerNode<double> m_w_rl;
+
+ public:
+  InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh,
+                              FaceValue<bool> primal_face_is_on_boundary,
+                              NodeValue<bool> primal_node_is_on_boundary)
+    : m_mesh(mesh),
+      m_primal_face_is_on_boundary(primal_face_is_on_boundary),
+      m_primal_node_is_on_boundary(primal_node_is_on_boundary)
+  {}
+  ~InterpolationWeightsManager() = default;
+  CellValuePerNode<double>&
+  wrj()
+  {
+    return m_w_rj;
+  }
+  FaceValuePerNode<double>&
+  wrl()
+  {
+    return m_w_rl;
+  }
+  void
+  compute()
+  {
+    using MeshDataType      = MeshData<Dimension>;
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh);
+
+    const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr();
+
+    const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl();
+    const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj();
+    const auto& node_to_cell_matrix                  = m_mesh->connectivity().nodeToCellMatrix();
+    const auto& node_to_face_matrix                  = m_mesh->connectivity().nodeToFaceMatrix();
+    CellValuePerNode<double> w_rj{m_mesh->connectivity()};
+    FaceValuePerNode<double> w_rl{m_mesh->connectivity()};
+
+    for (size_t i = 0; i < w_rl.numberOfValues(); ++i) {
+      w_rl[i] = std::numeric_limits<double>::signaling_NaN();
+    }
+
+    for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) {
+      Vector<double> b{Dimension + 1};
+      b[0] = 1;
+      for (size_t i = 1; i < Dimension + 1; i++) {
+        b[i] = xr[i_node][i - 1];
+      }
+      const auto& node_to_cell = node_to_cell_matrix[i_node];
+
+      if (not m_primal_node_is_on_boundary[i_node]) {
+        LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size()};
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          A(0, j) = 1;
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          for (size_t j = 0; j < node_to_cell.size(); j++) {
+            const CellId J = node_to_cell[j];
+            A(i, j)        = xj[J][i - 1];
+          }
+        }
+        Vector<double> x{node_to_cell.size()};
+        x = 0;
+
+        LeastSquareSolver ls_solver;
+        ls_solver.solveLocalSystem(A, x, b);
+
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          w_rj(i_node, j) = x[j];
+        }
+      } else {
+        int nb_face_used = 0;
+        for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[i_node][i_face];
+          if (m_primal_face_is_on_boundary[face_id]) {
+            nb_face_used++;
+          }
+        }
+        LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used};
+        for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) {
+          A(0, j) = 1;
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          for (size_t j = 0; j < node_to_cell.size(); j++) {
+            const CellId J = node_to_cell[j];
+            A(i, j)        = xj[J][i - 1];
+          }
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          int cpt_face = 0;
+          for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+            FaceId face_id = node_to_face_matrix[i_node][i_face];
+            if (m_primal_face_is_on_boundary[face_id]) {
+              A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1];
+              cpt_face++;
+            }
+          }
+        }
+
+        Vector<double> x{node_to_cell.size() + nb_face_used};
+        x = 0;
+
+        LeastSquareSolver ls_solver;
+        ls_solver.solveLocalSystem(A, x, b);
+
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          w_rj(i_node, j) = x[j];
+        }
+        int cpt_face = node_to_cell.size();
+        for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[i_node][i_face];
+          if (m_primal_face_is_on_boundary[face_id]) {
+            w_rl(i_node, i_face) = x[cpt_face++];
+          }
+        }
+      }
+    }
+    m_w_rj = w_rj;
+    m_w_rl = w_rl;
+  }
+};
+
 template ParabolicHeatScheme<1>::ParabolicHeatScheme(
   std::shared_ptr<const IMesh>,
   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
   const FunctionSymbolId&,
   const FunctionSymbolId&,
   const FunctionSymbolId&,
+  const FunctionSymbolId&,
   const double&,
   const double&,
   const double&);
@@ -463,6 +599,7 @@ template ParabolicHeatScheme<2>::ParabolicHeatScheme(
   const FunctionSymbolId&,
   const FunctionSymbolId&,
   const FunctionSymbolId&,
+  const FunctionSymbolId&,
   const double&,
   const double&,
   const double&);
@@ -473,6 +610,7 @@ template ParabolicHeatScheme<3>::ParabolicHeatScheme(
   const FunctionSymbolId&,
   const FunctionSymbolId&,
   const FunctionSymbolId&,
+  const FunctionSymbolId&,
   const double&,
   const double&,
   const double&);
diff --git a/src/language/algorithms/ParabolicHeat.hpp b/src/language/algorithms/ParabolicHeat.hpp
index b5a2db881..1f8b46e88 100644
--- a/src/language/algorithms/ParabolicHeat.hpp
+++ b/src/language/algorithms/ParabolicHeat.hpp
@@ -17,16 +17,18 @@ class ParabolicHeatScheme
   class FourierBoundaryCondition;
   class NeumannBoundaryCondition;
   class SymmetryBoundaryCondition;
+  class InterpolationWeightsManager;
 
  public:
   ParabolicHeatScheme(std::shared_ptr<const IMesh> i_mesh,
                       const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
                       const FunctionSymbolId& T_id,
+                      const FunctionSymbolId& T_init_id,
                       const FunctionSymbolId& kappa_id,
                       const FunctionSymbolId& f_id,
                       const double& final_time,
                       const double& dt,
-                      const double&);
+                      const double& output_frequency);
 };
 
 #endif   // HEAT_DIAMOND_ALGORITHM_HPP
diff --git a/src/language/algorithms/UnsteadyElasticity.cpp b/src/language/algorithms/UnsteadyElasticity.cpp
new file mode 100644
index 000000000..d95134359
--- /dev/null
+++ b/src/language/algorithms/UnsteadyElasticity.cpp
@@ -0,0 +1,663 @@
+#include <language/algorithms/UnsteadyElasticity.hpp>
+
+#include <algebra/CRSMatrix.hpp>
+#include <algebra/LeastSquareSolver.hpp>
+#include <algebra/LinearSolver.hpp>
+#include <algebra/LocalRectangularMatrix.hpp>
+#include <algebra/SparseMatrixDescriptor.hpp>
+#include <algebra/TinyVector.hpp>
+#include <algebra/Vector.hpp>
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <output/VTKWriter.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <scheme/VectorDiamondScheme.hpp>
+
+template <size_t Dimension>
+UnsteadyElasticity<Dimension>::UnsteadyElasticity(
+  std::shared_ptr<const IMesh> i_mesh,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  const FunctionSymbolId& lambda_id,
+  const FunctionSymbolId& mu_id,
+  const FunctionSymbolId& f_id,
+  const FunctionSymbolId& U_id,
+  const FunctionSymbolId& U_init_id,
+  const double& Tf,
+  const double& dt,
+  const double& output_frequency)
+{
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+  using MeshDataType     = MeshData<Dimension>;
+
+  constexpr ItemType FaceType = [] {
+    if constexpr (Dimension > 1) {
+      return ItemType::face;
+    } else {
+      return ItemType::node;
+    }
+  }();
+
+  using BoundaryCondition =
+    std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>;
+
+  using BoundaryConditionList = std::vector<BoundaryCondition>;
+
+  BoundaryConditionList boundary_condition_list;
+
+  std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n';
+  std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+
+  NodeValue<bool> is_dirichlet{mesh->connectivity()};
+  is_dirichlet.fill(false);
+  NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()};
+  {
+    TinyVector<Dimension> nan_tiny_vector;
+    for (size_t i = 0; i < Dimension; ++i) {
+      nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN();
+    }
+    dirichlet_value.fill(nan_tiny_vector);
+  }
+
+  for (const auto& bc_descriptor : bc_descriptor_list) {
+    bool is_valid_boundary_condition = true;
+
+    switch (bc_descriptor->type()) {
+    case IBoundaryConditionDescriptor::Type::symmetry: {
+      const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor =
+        dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+      for (size_t i_ref_face_list = 0; i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>();
+           ++i_ref_face_list) {
+        const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+        const RefId& ref          = ref_face_list.refId();
+        if (ref == sym_bc_descriptor.boundaryDescriptor()) {
+          if constexpr (Dimension > 1) {
+            Array<const FaceId> face_list = ref_face_list.list();
+            boundary_condition_list.push_back(SymmetryBoundaryCondition{face_list});
+          } else {
+            throw NotImplementedError("Symmetry conditions are not supported in 1d");
+          }
+        }
+      }
+      break;
+    }
+    case IBoundaryConditionDescriptor::Type::dirichlet: {
+      const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
+        dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
+      if (dirichlet_bc_descriptor.name() == "dirichlet") {
+        for (size_t i_ref_face_list = 0;
+             i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+          const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+          const RefId& ref          = ref_face_list.refId();
+          if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) {
+            const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+
+            if constexpr (Dimension > 1) {
+              MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+              Array<const FaceId> face_list                 = ref_face_list.list();
+              Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>(
+                TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), face_list);
+              boundary_condition_list.push_back(DirichletBoundaryCondition{face_list, value_list});
+            } else {
+              throw NotImplementedError("Neumann conditions are not supported in 1d");
+            }
+          }
+        }
+      } else if (dirichlet_bc_descriptor.name() == "normal_strain") {
+        for (size_t i_ref_face_list = 0;
+             i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+          const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+          const RefId& ref          = ref_face_list.refId();
+          if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) {
+            const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+
+            if constexpr (Dimension > 1) {
+              MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+              Array<const FaceId> face_list                 = ref_face_list.list();
+              Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>(
+                TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), face_list);
+              boundary_condition_list.push_back(NormalStrainBoundaryCondition{face_list, value_list});
+            } else {
+              throw NotImplementedError("Neumann conditions are not supported in 1d");
+            }
+          }
+        }
+      } else {
+        is_valid_boundary_condition = false;
+      }
+      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 elasticity equation";
+      throw NormalError(error_msg.str());
+    }
+  }
+
+  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, NormalStrainBoundaryCondition>) or
+                          (std::is_same_v<T, SymmetryBoundaryCondition>) 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());
+                } else {
+                  compute_face_dof_number[face_id] = number_of_dof++;
+                }
+              }
+            }
+          },
+          boundary_condition);
+      }
+
+      return compute_face_dof_number;
+    }();
+
+    const auto& primal_face_to_node_matrix             = mesh->connectivity().faceToNodeMatrix();
+    const auto& face_to_cell_matrix                    = mesh->connectivity().faceToCellMatrix();
+    const FaceValue<const bool> primal_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, NormalStrainBoundaryCondition>)) {
+              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);
+      }
+
+      return face_is_neumann;
+    }();
+
+    FaceValue<bool> primal_face_is_symmetry = [&] {
+      FaceValue<bool> face_is_symmetry{mesh->connectivity()};
+      face_is_symmetry.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, SymmetryBoundaryCondition>)) {
+              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_symmetry[face_id] = true;
+              }
+            }
+          },
+          boundary_condition);
+      }
+
+      return face_is_symmetry;
+    }();
+
+    NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity());
+    if (parallel::size() > 1) {
+      throw NotImplementedError("Calculation of node_is_on_boundary is incorrect");
+    }
+
+    primal_node_is_on_boundary.fill(false);
+    for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+      if (face_to_cell_matrix[face_id].size() == 1) {
+        for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) {
+          NodeId node_id                      = primal_face_to_node_matrix[face_id][i_node];
+          primal_node_is_on_boundary[node_id] = true;
+        }
+      }
+    }
+
+    FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity());
+    if (parallel::size() > 1) {
+      throw NotImplementedError("Calculation of face_is_on_boundary is incorrect");
+    }
+
+    primal_face_is_on_boundary.fill(false);
+    for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+      if (face_to_cell_matrix[face_id].size() == 1) {
+        primal_face_is_on_boundary[face_id] = true;
+      }
+    }
+
+    FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity());
+    if (parallel::size() > 1) {
+      throw NotImplementedError("Calculation of face_is_neumann is incorrect");
+    }
+
+    primal_face_is_dirichlet.fill(false);
+    for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+      primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] && (!primal_face_is_neumann[face_id]) &&
+                                           (!primal_face_is_symmetry[face_id]));
+    }
+    MeshDataType& mesh_data         = MeshDataManager::instance().getMeshData(*mesh);
+    const auto& node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix();
+    const auto& node_to_face_matrix = mesh->connectivity().nodeToFaceMatrix();
+
+    {
+      CellValue<TinyVector<Dimension>> velocity = InterpolateItemValue<TinyVector<Dimension>(
+        TinyVector<Dimension>)>::template interpolate<ItemType::cell>(U_id, mesh_data.xj());
+      CellValue<TinyVector<Dimension>> Uj       = InterpolateItemValue<TinyVector<Dimension>(
+        TinyVector<Dimension>)>::template interpolate<ItemType::cell>(U_init_id, mesh_data.xj());
+
+      CellValue<TinyVector<Dimension>> fj            = InterpolateItemValue<TinyVector<Dimension>(
+        TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj());
+      FaceValue<TinyVector<Dimension>> velocity_face = InterpolateItemValue<TinyVector<Dimension>(
+        TinyVector<Dimension>)>::template interpolate<ItemType::face>(U_id, mesh_data.xl());
+      FaceValue<TinyVector<Dimension>> Ul            = InterpolateItemValue<TinyVector<Dimension>(
+        TinyVector<Dimension>)>::template interpolate<ItemType::face>(U_init_id, mesh_data.xl());
+
+      {
+        VTKWriter vtk_writer("Uanalytic_" + std::to_string(Dimension), 0.01);
+
+        vtk_writer.write(mesh,
+                         {NamedItemValue{"exact_velocity", velocity}, NamedItemValue{"init_velocity", Uj},
+                          NamedItemValue{"coords", mesh->xr()}, NamedItemValue{"xj", mesh_data.xj()},
+                          NamedItemValue{"cell_owner", mesh->connectivity().cellOwner()},
+                          NamedItemValue{"node_owner", mesh->connectivity().nodeOwner()}},
+                         0, true);   // forces last output
+      }
+      double time = 0;
+      Assert(dt > 0, "The time-step have to be positive!");
+      const CellValue<const double> primal_Vj = mesh_data.Vj();
+      double output_time                      = 0;
+      InterpolationWeightsManager iwm(mesh, primal_face_is_on_boundary, primal_node_is_on_boundary,
+                                      primal_face_is_symmetry);
+      iwm.compute();
+      CellValuePerNode<double> w_rj = iwm.wrj();
+      FaceValuePerNode<double> w_rl = iwm.wrl();
+      do {
+        {
+          if (time >= output_time) {
+            VTKWriter vtk_writer("Velocity_" + std::to_string(Dimension) + "_time_" + std::to_string(time), 0.01);
+
+            vtk_writer.write(mesh, {NamedItemValue{"U", Uj}, NamedItemValue{"Vj", primal_Vj}}, 0,
+                             true);   // forces last output
+            output_time += output_frequency;
+          }
+        }
+        double deltat = std::min(dt, Tf - time);
+        std::cout << "Current time = " << time << " time-step = " << deltat << " final time = " << Tf << "\n";
+        VectorDiamondScheme<Dimension>(i_mesh, bc_descriptor_list, lambda_id, mu_id, f_id, Uj, Ul, Tf, deltat, w_rj,
+                                       w_rl);
+        time += deltat;
+      } while (time < Tf && std::abs(time - Tf) > 1e-15);
+
+      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();
+        }
+      }();
+
+      Vector<double> Uexacte{mesh->numberOfCells() * Dimension};
+      for (CellId j = 0; j < mesh->numberOfCells(); ++j) {
+        for (size_t l = 0; l < Dimension; ++l) {
+          Uexacte[(cell_dof_number[j] * Dimension) + l] = velocity[j][l];
+        }
+      }
+
+      Vector<double> error{mesh->numberOfCells() * Dimension};
+      for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+        for (size_t i = 0; i < Dimension; ++i) {
+          error[(cell_id * Dimension) + i] = (Uj[cell_id][i] - velocity[cell_id][i]) * sqrt(primal_Vj[cell_id]);
+        }
+      }
+      Vector<double> error_face{mesh->numberOfFaces() * Dimension};
+      parallel_for(
+        mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) {
+          if (primal_face_is_on_boundary[face_id]) {
+            for (size_t i = 0; i < Dimension; ++i) {
+              error_face[face_id * Dimension + i] = (Ul[face_id][i] - velocity_face[face_id][i]) * sqrt(mes_l[face_id]);
+            }
+          } else {
+            error_face[face_id] = 0;
+          }
+        });
+
+      std::cout << "||Error||_2 (cell)= " << std::sqrt((error, error)) << "\n";
+      std::cout << "||Error||_2 (face)= " << std::sqrt((error_face, error_face)) << "\n";
+      std::cout << "||Error||_2 (total)= " << std::sqrt((error, error)) + std::sqrt((error_face, error_face)) << "\n";
+
+      VTKWriter vtk_writer("Speed_" + std::to_string(Dimension), 0.01);
+
+      NodeValue<TinyVector<3>> ur3d{mesh->connectivity()};
+      ur3d.fill(zero);
+
+      parallel_for(
+        mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          TinyVector<Dimension> x = zero;
+          const auto node_cells   = node_to_cell_matrix[node_id];
+          for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) {
+            CellId cell_id = node_cells[i_cell];
+            x += w_rj(node_id, i_cell) * Uj[cell_id];
+          }
+          const auto node_faces = node_to_face_matrix[node_id];
+          for (size_t i_face = 0; i_face < node_faces.size(); ++i_face) {
+            FaceId face_id = node_faces[i_face];
+            if (primal_face_is_on_boundary[face_id]) {
+              x += w_rl(node_id, i_face) * Ul[face_id];
+            }
+          }
+          for (size_t i = 0; i < Dimension; ++i) {
+            ur3d[node_id][i] = x[i];
+          }
+        });
+
+      vtk_writer.write(mesh,
+                       {NamedItemValue{"U", Uj}, NamedItemValue{"Ur3d", ur3d}, NamedItemValue{"Uexacte", velocity}}, 0,
+                       true);   // forces last output
+    }
+  } else {
+    throw NotImplementedError("not done in 1d");
+  }
+}
+
+template <size_t Dimension>
+class UnsteadyElasticity<Dimension>::DirichletBoundaryCondition
+{
+ private:
+  const Array<const TinyVector<Dimension>> m_value_list;
+  const Array<const FaceId> m_face_list;
+
+ public:
+  const Array<const FaceId>&
+  faceList() const
+  {
+    return m_face_list;
+  }
+
+  const Array<const TinyVector<Dimension>>&
+  valueList() const
+  {
+    return m_value_list;
+  }
+
+  DirichletBoundaryCondition(const Array<const FaceId>& face_list, const Array<const TinyVector<Dimension>>& value_list)
+    : m_value_list{value_list}, m_face_list{face_list}
+  {
+    Assert(m_value_list.size() == m_face_list.size());
+  }
+
+  ~DirichletBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class UnsteadyElasticity<Dimension>::NormalStrainBoundaryCondition
+{
+ private:
+  const Array<const TinyVector<Dimension>> m_value_list;
+  const Array<const FaceId> m_face_list;
+
+ public:
+  const Array<const FaceId>&
+  faceList() const
+  {
+    return m_face_list;
+  }
+
+  const Array<const TinyVector<Dimension>>&
+  valueList() const
+  {
+    return m_value_list;
+  }
+
+  NormalStrainBoundaryCondition(const Array<const FaceId>& face_list,
+                                const Array<const TinyVector<Dimension>>& value_list)
+    : m_value_list{value_list}, m_face_list{face_list}
+  {
+    Assert(m_value_list.size() == m_face_list.size());
+  }
+
+  ~NormalStrainBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class UnsteadyElasticity<Dimension>::SymmetryBoundaryCondition
+{
+ private:
+  const Array<const TinyVector<Dimension>> m_value_list;
+  const Array<const FaceId> m_face_list;
+
+ public:
+  const Array<const FaceId>&
+  faceList() const
+  {
+    return m_face_list;
+  }
+
+ public:
+  SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {}
+
+  ~SymmetryBoundaryCondition() = default;
+};
+
+template <size_t Dimension>
+class UnsteadyElasticity<Dimension>::InterpolationWeightsManager
+{
+ private:
+  std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh;
+  FaceValue<bool> m_primal_face_is_on_boundary;
+  NodeValue<bool> m_primal_node_is_on_boundary;
+  FaceValue<bool> m_primal_face_is_symmetry;
+  CellValuePerNode<double> m_w_rj;
+  FaceValuePerNode<double> m_w_rl;
+
+ public:
+  InterpolationWeightsManager(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh,
+                              FaceValue<bool> primal_face_is_on_boundary,
+                              NodeValue<bool> primal_node_is_on_boundary,
+                              FaceValue<bool> primal_face_is_symmetry)
+    : m_mesh(mesh),
+      m_primal_face_is_on_boundary(primal_face_is_on_boundary),
+      m_primal_node_is_on_boundary(primal_node_is_on_boundary),
+      m_primal_face_is_symmetry(primal_face_is_symmetry)
+  {}
+  ~InterpolationWeightsManager() = default;
+  CellValuePerNode<double>&
+  wrj()
+  {
+    return m_w_rj;
+  }
+  FaceValuePerNode<double>&
+  wrl()
+  {
+    return m_w_rl;
+  }
+  void
+  compute()
+  {
+    using MeshDataType      = MeshData<Dimension>;
+    MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*m_mesh);
+
+    const NodeValue<const TinyVector<Dimension>>& xr = m_mesh->xr();
+
+    const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl();
+    const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj();
+    const auto& node_to_cell_matrix                  = m_mesh->connectivity().nodeToCellMatrix();
+    const auto& node_to_face_matrix                  = m_mesh->connectivity().nodeToFaceMatrix();
+    const auto& face_to_cell_matrix                  = m_mesh->connectivity().faceToCellMatrix();
+
+    CellValuePerNode<double> w_rj{m_mesh->connectivity()};
+    FaceValuePerNode<double> w_rl{m_mesh->connectivity()};
+
+    const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr();
+    auto project_to_face = [&](const TinyVector<Dimension>& x, const FaceId face_id) -> const TinyVector<Dimension> {
+      TinyVector<Dimension> proj;
+      const TinyVector<Dimension> nil = primal_nlr(face_id, 0);
+      proj                            = x - ((x - xl[face_id]), nil) * nil;
+      return proj;
+    };
+
+    for (size_t i = 0; i < w_rl.numberOfValues(); ++i) {
+      w_rl[i] = std::numeric_limits<double>::signaling_NaN();
+    }
+
+    for (NodeId i_node = 0; i_node < m_mesh->numberOfNodes(); ++i_node) {
+      Vector<double> b{Dimension + 1};
+      b[0] = 1;
+      for (size_t i = 1; i < Dimension + 1; i++) {
+        b[i] = xr[i_node][i - 1];
+      }
+      const auto& node_to_cell = node_to_cell_matrix[i_node];
+
+      if (not m_primal_node_is_on_boundary[i_node]) {
+        LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size()};
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          A(0, j) = 1;
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          for (size_t j = 0; j < node_to_cell.size(); j++) {
+            const CellId J = node_to_cell[j];
+            A(i, j)        = xj[J][i - 1];
+          }
+        }
+
+        Vector<double> x{node_to_cell.size()};
+        x = 0;
+
+        LeastSquareSolver ls_solver;
+        ls_solver.solveLocalSystem(A, x, b);
+
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          w_rj(i_node, j) = x[j];
+        }
+      } else {
+        int nb_face_used = 0;
+        for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[i_node][i_face];
+          if (m_primal_face_is_on_boundary[face_id]) {
+            nb_face_used++;
+          }
+        }
+        LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used};
+        for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) {
+          A(0, j) = 1;
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          for (size_t j = 0; j < node_to_cell.size(); j++) {
+            const CellId J = node_to_cell[j];
+            A(i, j)        = xj[J][i - 1];
+          }
+        }
+        for (size_t i = 1; i < Dimension + 1; i++) {
+          int cpt_face = 0;
+          for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+            FaceId face_id = node_to_face_matrix[i_node][i_face];
+            if (m_primal_face_is_on_boundary[face_id]) {
+              if (m_primal_face_is_symmetry[face_id]) {
+                for (size_t j = 0; j < face_to_cell_matrix[face_id].size(); ++j) {
+                  const CellId cell_id                 = face_to_cell_matrix[face_id][j];
+                  TinyVector<Dimension> xproj          = project_to_face(xj[cell_id], face_id);
+                  A(i, node_to_cell.size() + cpt_face) = xproj[i - 1];
+                }
+              } else {
+                A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1];
+              }
+              cpt_face++;
+            }
+          }
+        }
+
+        Vector<double> x{node_to_cell.size() + nb_face_used};
+        x = 0;
+
+        LeastSquareSolver ls_solver;
+        ls_solver.solveLocalSystem(A, x, b);
+
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          w_rj(i_node, j) = x[j];
+        }
+        int cpt_face = node_to_cell.size();
+        for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[i_node][i_face];
+          if (m_primal_face_is_on_boundary[face_id]) {
+            w_rl(i_node, i_face) = x[cpt_face++];
+          }
+        }
+      }
+    }
+    m_w_rj = w_rj;
+    m_w_rl = w_rl;
+  }
+};
+
+template UnsteadyElasticity<1>::UnsteadyElasticity(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const double&,
+  const double&,
+  const double&);
+
+template UnsteadyElasticity<2>::UnsteadyElasticity(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const double&,
+  const double&,
+  const double&);
+
+template UnsteadyElasticity<3>::UnsteadyElasticity(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const double&,
+  const double&,
+  const double&);
diff --git a/src/language/algorithms/UnsteadyElasticity.hpp b/src/language/algorithms/UnsteadyElasticity.hpp
new file mode 100644
index 000000000..adbb1ad95
--- /dev/null
+++ b/src/language/algorithms/UnsteadyElasticity.hpp
@@ -0,0 +1,37 @@
+#ifndef UNSTEADY_ELASTICITY_HPP
+#define UNSTEADY_ELASTICITY_HPP
+
+#include <algebra/TinyVector.hpp>
+#include <language/utils/FunctionSymbolId.hpp>
+#include <memory>
+#include <mesh/IMesh.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <variant>
+#include <vector>
+
+template <size_t Dimension>
+class UnsteadyElasticity
+{
+ private:
+  class DirichletBoundaryCondition;
+  class NormalStrainBoundaryCondition;
+  class SymmetryBoundaryCondition;
+  class InterpolationWeightsManager;
+
+ public:
+  UnsteadyElasticity(std::shared_ptr<const IMesh> i_mesh,
+                     const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+                     const FunctionSymbolId& lambda_id,
+                     const FunctionSymbolId& mu_id,
+                     const FunctionSymbolId& f_id,
+                     const FunctionSymbolId& U_id,
+                     const FunctionSymbolId& U_init_id,
+                     const double& Tf,
+                     const double& dt,
+                     const double& output_frequency);
+};
+
+#endif   // ELASTICITY_DIAMOND_ALGORITHM2_HPP
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index e32f667e7..714d9ae1e 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -6,6 +6,7 @@
 #include <language/algorithms/HeatDiamondAlgorithm.hpp>
 #include <language/algorithms/HeatDiamondAlgorithm2.hpp>
 #include <language/algorithms/ParabolicHeat.hpp>
+#include <language/algorithms/UnsteadyElasticity.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/TypeDescriptor.hpp>
 #include <mesh/Mesh.hpp>
@@ -267,27 +268,27 @@ SchemeModule::SchemeModule()
                               void(std::shared_ptr<const IMesh>,
                                    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
                                    const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&,
-                                   const double&, const double&, const double&)>>(
+                                   const FunctionSymbolId&, const double&, const double&, const double&)>>(
 
                               [](std::shared_ptr<const IMesh> p_mesh,
                                  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
                                    bc_descriptor_list,
-                                 const FunctionSymbolId& T_id, const FunctionSymbolId& kappa_id,
-                                 const FunctionSymbolId& f_id, const double& final_time, const double& dt,
-                                 const double& output_frequency) -> void {
+                                 const FunctionSymbolId& T_id, const FunctionSymbolId& T_init_id,
+                                 const FunctionSymbolId& kappa_id, const FunctionSymbolId& f_id,
+                                 const double& final_time, const double& dt, const double& output_frequency) -> void {
                                 switch (p_mesh->dimension()) {
                                 case 1: {
-                                  ParabolicHeatScheme<1>{p_mesh, bc_descriptor_list, T_id, kappa_id,
+                                  ParabolicHeatScheme<1>{p_mesh, bc_descriptor_list, T_id, T_init_id,       kappa_id,
                                                          f_id,   final_time,         dt,   output_frequency};
                                   break;
                                 }
                                 case 2: {
-                                  ParabolicHeatScheme<2>{p_mesh, bc_descriptor_list, T_id, kappa_id,
+                                  ParabolicHeatScheme<2>{p_mesh, bc_descriptor_list, T_id, T_init_id,       kappa_id,
                                                          f_id,   final_time,         dt,   output_frequency};
                                   break;
                                 }
                                 case 3: {
-                                  ParabolicHeatScheme<3>{p_mesh, bc_descriptor_list, T_id, kappa_id,
+                                  ParabolicHeatScheme<3>{p_mesh, bc_descriptor_list, T_id, T_init_id,       kappa_id,
                                                          f_id,   final_time,         dt,   output_frequency};
                                   break;
                                 }
@@ -299,6 +300,46 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this
+    ->_addBuiltinFunction("unsteadyelasticity",
+                          std::make_shared<BuiltinFunctionEmbedder<
+                            void(std::shared_ptr<const IMesh>,
+                                 const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+                                 const FunctionSymbolId&, const FunctionSymbolId&, const FunctionSymbolId&,
+                                 const FunctionSymbolId&, const FunctionSymbolId&, const double&, const double&,
+                                 const double&)>>(
+
+                            [](std::shared_ptr<const IMesh> p_mesh,
+                               const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&
+                                 bc_descriptor_list,
+                               const FunctionSymbolId& lambda_id, const FunctionSymbolId& mu_id,
+                               const FunctionSymbolId& f_id, const FunctionSymbolId& U_id,
+                               const FunctionSymbolId& U_init_id, const double& final_time, const double& dt,
+                               const double& output_frequency) -> void {
+                              switch (p_mesh->dimension()) {
+                              case 1: {
+                                UnsteadyElasticity<1>{p_mesh, bc_descriptor_list, lambda_id,  mu_id, f_id,
+                                                      U_id,   U_init_id,          final_time, dt,    output_frequency};
+                                break;
+                              }
+                              case 2: {
+                                UnsteadyElasticity<2>{p_mesh, bc_descriptor_list, lambda_id,  mu_id, f_id,
+                                                      U_id,   U_init_id,          final_time, dt,    output_frequency};
+                                break;
+                              }
+                              case 3: {
+                                UnsteadyElasticity<3>{p_mesh, bc_descriptor_list, lambda_id,  mu_id, f_id,
+                                                      U_id,   U_init_id,          final_time, dt,    output_frequency};
+                                break;
+                              }
+                              default: {
+                                throw UnexpectedError("invalid mesh dimension");
+                              }
+                              }
+                            }
+
+                            ));
+
   this->_addBuiltinFunction("heat2",
                             std::make_shared<BuiltinFunctionEmbedder<
                               void(std::shared_ptr<const IMesh>,
diff --git a/src/scheme/ScalarDiamondScheme.hpp b/src/scheme/ScalarDiamondScheme.hpp
index 7259c8442..70c375b3d 100644
--- a/src/scheme/ScalarDiamondScheme.hpp
+++ b/src/scheme/ScalarDiamondScheme.hpp
@@ -1,3 +1,6 @@
+#ifndef SCALAR_DIAMOND_SCHEME_HPP
+#define SCALAR_DIAMOND_SCHEME_HPP
+
 #include <algebra/CRSMatrix.hpp>
 #include <algebra/LeastSquareSolver.hpp>
 #include <algebra/LinearSolver.hpp>
@@ -146,7 +149,9 @@ class ScalarDiamondScheme
                       CellValue<double>& Temperature,
                       FaceValue<double>& Temperature_face,
                       const double& Tf,
-                      const double& dt)
+                      const double& dt,
+                      const CellValuePerNode<double>& w_rj,
+                      const FaceValuePerNode<double>& w_rl)
   {
     using ConnectivityType = Connectivity<Dimension>;
     using MeshType         = Mesh<ConnectivityType>;
@@ -343,94 +348,9 @@ class ScalarDiamondScheme
 
       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 auto& node_to_cell_matrix                  = mesh->connectivity().nodeToCellMatrix();
       const auto& node_to_face_matrix                  = mesh->connectivity().nodeToFaceMatrix();
-      CellValuePerNode<double> w_rj{mesh->connectivity()};
-      FaceValuePerNode<double> w_rl{mesh->connectivity()};
-
-      for (size_t i = 0; i < w_rl.numberOfValues(); ++i) {
-        w_rl[i] = std::numeric_limits<double>::signaling_NaN();
-      }
-
-      for (NodeId i_node = 0; i_node < mesh->numberOfNodes(); ++i_node) {
-        Vector<double> b{Dimension + 1};
-        b[0] = 1;
-        for (size_t i = 1; i < Dimension + 1; i++) {
-          b[i] = xr[i_node][i - 1];
-        }
-        const auto& node_to_cell = node_to_cell_matrix[i_node];
-
-        if (not primal_node_is_on_boundary[i_node]) {
-          LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size()};
-          for (size_t j = 0; j < node_to_cell.size(); j++) {
-            A(0, j) = 1;
-          }
-          for (size_t i = 1; i < Dimension + 1; i++) {
-            for (size_t j = 0; j < node_to_cell.size(); j++) {
-              const CellId J = node_to_cell[j];
-              A(i, j)        = xj[J][i - 1];
-            }
-          }
-          Vector<double> x{node_to_cell.size()};
-          x = 0;
-
-          LeastSquareSolver ls_solver;
-          ls_solver.solveLocalSystem(A, x, b);
-
-          for (size_t j = 0; j < node_to_cell.size(); j++) {
-            w_rj(i_node, j) = x[j];
-          }
-        } else {
-          int nb_face_used = 0;
-          for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
-            FaceId face_id = node_to_face_matrix[i_node][i_face];
-            if (primal_face_is_on_boundary[face_id]) {
-              nb_face_used++;
-            }
-          }
-          LocalRectangularMatrix<double> A{Dimension + 1, node_to_cell.size() + nb_face_used};
-          for (size_t j = 0; j < node_to_cell.size() + nb_face_used; j++) {
-            A(0, j) = 1;
-          }
-          for (size_t i = 1; i < Dimension + 1; i++) {
-            for (size_t j = 0; j < node_to_cell.size(); j++) {
-              const CellId J = node_to_cell[j];
-              A(i, j)        = xj[J][i - 1];
-            }
-          }
-          for (size_t i = 1; i < Dimension + 1; i++) {
-            int cpt_face = 0;
-            for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
-              FaceId face_id = node_to_face_matrix[i_node][i_face];
-              if (primal_face_is_on_boundary[face_id]) {
-                A(i, node_to_cell.size() + cpt_face) = xl[face_id][i - 1];
-                cpt_face++;
-              }
-            }
-          }
-
-          Vector<double> x{node_to_cell.size() + nb_face_used};
-          x = 0;
-
-          LeastSquareSolver ls_solver;
-          ls_solver.solveLocalSystem(A, x, b);
-
-          for (size_t j = 0; j < node_to_cell.size(); j++) {
-            w_rj(i_node, j) = x[j];
-          }
-          int cpt_face = node_to_cell.size();
-          for (size_t i_face = 0; i_face < node_to_face_matrix[i_node].size(); ++i_face) {
-            FaceId face_id = node_to_face_matrix[i_node][i_face];
-            if (primal_face_is_on_boundary[face_id]) {
-              w_rl(i_node, i_face) = x[cpt_face++];
-            }
-          }
-        }
-      }
 
       {
         std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
@@ -722,7 +642,9 @@ template ScalarDiamondScheme<1>::ScalarDiamondScheme(
   CellValue<double>&,
   FaceValue<double>&,
   const double&,
-  const double&);
+  const double&,
+  const CellValuePerNode<double>& w_rj,
+  const FaceValuePerNode<double>& w_rl);
 
 template ScalarDiamondScheme<2>::ScalarDiamondScheme(
   std::shared_ptr<const IMesh>,
@@ -732,7 +654,9 @@ template ScalarDiamondScheme<2>::ScalarDiamondScheme(
   CellValue<double>&,
   FaceValue<double>&,
   const double&,
-  const double&);
+  const double&,
+  const CellValuePerNode<double>& w_rj,
+  const FaceValuePerNode<double>& w_rl);
 
 template ScalarDiamondScheme<3>::ScalarDiamondScheme(
   std::shared_ptr<const IMesh>,
@@ -742,4 +666,8 @@ template ScalarDiamondScheme<3>::ScalarDiamondScheme(
   CellValue<double>&,
   FaceValue<double>&,
   const double&,
-  const double&);
+  const double&,
+  const CellValuePerNode<double>& w_rj,
+  const FaceValuePerNode<double>& w_rl);
+
+#endif   // SCALAR_DIAMOND_SCHEME_HPP
diff --git a/src/scheme/VectorDiamondScheme.hpp b/src/scheme/VectorDiamondScheme.hpp
new file mode 100644
index 000000000..bb67cd15d
--- /dev/null
+++ b/src/scheme/VectorDiamondScheme.hpp
@@ -0,0 +1,785 @@
+#ifndef VECTOR_DIAMOND_SCHEME_HPP
+#define VECTOR_DIAMOND_SCHEME_HPP
+#include <algebra/CRSMatrix.hpp>
+#include <algebra/LeastSquareSolver.hpp>
+#include <algebra/LinearSolver.hpp>
+#include <algebra/LocalRectangularMatrix.hpp>
+#include <algebra/SparseMatrixDescriptor.hpp>
+#include <algebra/TinyVector.hpp>
+#include <algebra/Vector.hpp>
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <output/VTKWriter.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+
+template <size_t Dimension>
+class VectorDiamondScheme
+{
+ private:
+  class DirichletBoundaryCondition
+  {
+   private:
+    const Array<const TinyVector<Dimension>> m_value_list;
+    const Array<const FaceId> m_face_list;
+
+   public:
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+    const Array<const TinyVector<Dimension>>&
+    valueList() const
+    {
+      return m_value_list;
+    }
+
+    DirichletBoundaryCondition(const Array<const FaceId>& face_list,
+                               const Array<const TinyVector<Dimension>>& value_list)
+      : m_value_list{value_list}, m_face_list{face_list}
+    {
+      Assert(m_value_list.size() == m_face_list.size());
+    }
+
+    ~DirichletBoundaryCondition() = default;
+  };
+
+  class NormalStrainBoundaryCondition
+  {
+   private:
+    const Array<const TinyVector<Dimension>> m_value_list;
+    const Array<const FaceId> m_face_list;
+
+   public:
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+    const Array<const TinyVector<Dimension>>&
+    valueList() const
+    {
+      return m_value_list;
+    }
+
+    NormalStrainBoundaryCondition(const Array<const FaceId>& face_list,
+                                  const Array<const TinyVector<Dimension>>& value_list)
+      : m_value_list{value_list}, m_face_list{face_list}
+    {
+      Assert(m_value_list.size() == m_face_list.size());
+    }
+
+    ~NormalStrainBoundaryCondition() = default;
+  };
+
+  class SymmetryBoundaryCondition
+  {
+   private:
+    const Array<const TinyVector<Dimension>> m_value_list;
+    const Array<const FaceId> m_face_list;
+
+   public:
+    const Array<const FaceId>&
+    faceList() const
+    {
+      return m_face_list;
+    }
+
+   public:
+    SymmetryBoundaryCondition(const Array<const FaceId>& face_list) : m_face_list{face_list} {}
+
+    ~SymmetryBoundaryCondition() = default;
+  };
+
+ public:
+  VectorDiamondScheme(std::shared_ptr<const IMesh> i_mesh,
+                      const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+                      const FunctionSymbolId& lambda_id,
+                      const FunctionSymbolId& mu_id,
+                      const FunctionSymbolId& f_id,
+                      CellValue<TinyVector<Dimension>>& Uj,
+                      FaceValue<TinyVector<Dimension>>& Ul,
+                      const double& Tf,
+                      const double& dt,
+                      CellValuePerNode<double>& w_rj,
+                      FaceValuePerNode<double>& w_rl)
+  {
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+    using MeshDataType     = MeshData<Dimension>;
+
+    constexpr ItemType FaceType = [] {
+      if constexpr (Dimension > 1) {
+        return ItemType::face;
+      } else {
+        return ItemType::node;
+      }
+    }();
+
+    using BoundaryCondition =
+      std::variant<DirichletBoundaryCondition, NormalStrainBoundaryCondition, SymmetryBoundaryCondition>;
+
+    using BoundaryConditionList = std::vector<BoundaryCondition>;
+
+    BoundaryConditionList boundary_condition_list;
+
+    std::cout << "number of bc descr = " << bc_descriptor_list.size() << '\n';
+    std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+
+    NodeValue<bool> is_dirichlet{mesh->connectivity()};
+    is_dirichlet.fill(false);
+    NodeValue<TinyVector<Dimension>> dirichlet_value{mesh->connectivity()};
+    {
+      TinyVector<Dimension> nan_tiny_vector;
+      for (size_t i = 0; i < Dimension; ++i) {
+        nan_tiny_vector[i] = std::numeric_limits<double>::signaling_NaN();
+      }
+      dirichlet_value.fill(nan_tiny_vector);
+    }
+
+    for (const auto& bc_descriptor : bc_descriptor_list) {
+      bool is_valid_boundary_condition = true;
+
+      switch (bc_descriptor->type()) {
+      case IBoundaryConditionDescriptor::Type::symmetry: {
+        const SymmetryBoundaryConditionDescriptor& sym_bc_descriptor =
+          dynamic_cast<const SymmetryBoundaryConditionDescriptor&>(*bc_descriptor);
+        for (size_t i_ref_face_list = 0;
+             i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+          const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+          const RefId& ref          = ref_face_list.refId();
+          if (ref == sym_bc_descriptor.boundaryDescriptor()) {
+            if constexpr (Dimension > 1) {
+              Array<const FaceId> face_list = ref_face_list.list();
+              boundary_condition_list.push_back(SymmetryBoundaryCondition{face_list});
+            } else {
+              throw NotImplementedError("Symmetry conditions are not supported in 1d");
+            }
+          }
+        }
+        break;
+      }
+      case IBoundaryConditionDescriptor::Type::dirichlet: {
+        const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
+          dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
+        if (dirichlet_bc_descriptor.name() == "dirichlet") {
+          for (size_t i_ref_face_list = 0;
+               i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+            const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+            const RefId& ref          = ref_face_list.refId();
+            if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) {
+              const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+
+              if constexpr (Dimension > 1) {
+                MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+                Array<const FaceId> face_list                 = ref_face_list.list();
+                Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>(
+                  TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), face_list);
+                boundary_condition_list.push_back(DirichletBoundaryCondition{face_list, value_list});
+              } else {
+                throw NotImplementedError("Neumann conditions are not supported in 1d");
+              }
+            }
+          }
+        } else if (dirichlet_bc_descriptor.name() == "normal_strain") {
+          for (size_t i_ref_face_list = 0;
+               i_ref_face_list < mesh->connectivity().template numberOfRefItemList<FaceType>(); ++i_ref_face_list) {
+            const auto& ref_face_list = mesh->connectivity().template refItemList<FaceType>(i_ref_face_list);
+            const RefId& ref          = ref_face_list.refId();
+            if (ref == dirichlet_bc_descriptor.boundaryDescriptor()) {
+              const FunctionSymbolId g_id = dirichlet_bc_descriptor.rhsSymbolId();
+
+              if constexpr (Dimension > 1) {
+                MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+                Array<const FaceId> face_list                 = ref_face_list.list();
+                Array<const TinyVector<Dimension>> value_list = InterpolateItemValue<TinyVector<Dimension>(
+                  TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id, mesh_data.xl(), face_list);
+                boundary_condition_list.push_back(NormalStrainBoundaryCondition{face_list, value_list});
+              } else {
+                throw NotImplementedError("Neumann conditions are not supported in 1d");
+              }
+            }
+          }
+        } else {
+          is_valid_boundary_condition = false;
+        }
+        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 elasticity equation";
+        throw NormalError(error_msg.str());
+      }
+    }
+
+    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, NormalStrainBoundaryCondition>) or
+                            (std::is_same_v<T, SymmetryBoundaryCondition>) 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());
+                  } else {
+                    compute_face_dof_number[face_id] = number_of_dof++;
+                  }
+                }
+              }
+            },
+            boundary_condition);
+        }
+
+        return compute_face_dof_number;
+      }();
+
+      const auto& primal_face_to_node_matrix             = mesh->connectivity().faceToNodeMatrix();
+      const auto& face_to_cell_matrix                    = mesh->connectivity().faceToCellMatrix();
+      const FaceValue<const bool> primal_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, NormalStrainBoundaryCondition>)) {
+                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);
+        }
+
+        return face_is_neumann;
+      }();
+
+      const FaceValue<const bool> primal_face_is_symmetry = [&] {
+        FaceValue<bool> face_is_symmetry{mesh->connectivity()};
+        face_is_symmetry.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, SymmetryBoundaryCondition>)) {
+                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_symmetry[face_id] = true;
+                }
+              }
+            },
+            boundary_condition);
+        }
+
+        return face_is_symmetry;
+      }();
+
+      NodeValue<bool> primal_node_is_on_boundary(mesh->connectivity());
+      if (parallel::size() > 1) {
+        throw NotImplementedError("Calculation of node_is_on_boundary is incorrect");
+      }
+
+      primal_node_is_on_boundary.fill(false);
+      for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+        if (face_to_cell_matrix[face_id].size() == 1) {
+          for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) {
+            NodeId node_id                      = primal_face_to_node_matrix[face_id][i_node];
+            primal_node_is_on_boundary[node_id] = true;
+          }
+        }
+      }
+
+      FaceValue<bool> primal_face_is_on_boundary(mesh->connectivity());
+      if (parallel::size() > 1) {
+        throw NotImplementedError("Calculation of face_is_on_boundary is incorrect");
+      }
+
+      primal_face_is_on_boundary.fill(false);
+      for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+        if (face_to_cell_matrix[face_id].size() == 1) {
+          primal_face_is_on_boundary[face_id] = true;
+        }
+      }
+
+      FaceValue<bool> primal_face_is_dirichlet(mesh->connectivity());
+      if (parallel::size() > 1) {
+        throw NotImplementedError("Calculation of face_is_neumann is incorrect");
+      }
+
+      primal_face_is_dirichlet.fill(false);
+      for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+        primal_face_is_dirichlet[face_id] = (primal_face_is_on_boundary[face_id] &&
+                                             (!primal_face_is_neumann[face_id]) && (!primal_face_is_symmetry[face_id]));
+      }
+      MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
+
+      const FaceValue<const TinyVector<Dimension>>& xl               = mesh_data.xl();
+      const CellValue<const TinyVector<Dimension>>& xj               = mesh_data.xj();
+      const auto& node_to_cell_matrix                                = mesh->connectivity().nodeToCellMatrix();
+      const auto& node_to_face_matrix                                = mesh->connectivity().nodeToFaceMatrix();
+      const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr();
+
+      {
+        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+
+        MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
+
+        std::shared_ptr mapper =
+          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
+            mesh->connectivity());
+
+        CellValue<double> dual_muj =
+          InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(mu_id,
+                                                                                                    diamond_mesh_data
+                                                                                                      .xj());
+
+        CellValue<double> dual_lambdaj =
+          InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(lambda_id,
+                                                                                                    diamond_mesh_data
+                                                                                                      .xj());
+
+        CellValue<TinyVector<Dimension>> fj = InterpolateItemValue<TinyVector<Dimension>(
+          TinyVector<Dimension>)>::template interpolate<ItemType::cell>(f_id, mesh_data.xj());
+
+        const CellValue<const double> dual_Vj = diamond_mesh_data.Vj();
+
+        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 CellValue<const double> dual_mes_l_j = [=] {
+          CellValue<double> compute_mes_j{diamond_mesh->connectivity()};
+          mapper->toDualCell(mes_l, compute_mes_j);
+
+          return compute_mes_j;
+        }();
+
+        const CellValue<const double> primal_Vj   = mesh_data.Vj();
+        FaceValue<const CellId> face_dual_cell_id = [=]() {
+          FaceValue<CellId> computed_face_dual_cell_id{mesh->connectivity()};
+          CellValue<CellId> dual_cell_id{diamond_mesh->connectivity()};
+          parallel_for(
+            diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { dual_cell_id[cell_id] = cell_id; });
+
+          mapper->fromDualCell(dual_cell_id, computed_face_dual_cell_id);
+
+          return computed_face_dual_cell_id;
+        }();
+
+        NodeValue<const NodeId> dual_node_primal_node_id = [=]() {
+          CellValue<NodeId> cell_ignored_id{mesh->connectivity()};
+          cell_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()});
+
+          NodeValue<NodeId> node_primal_id{mesh->connectivity()};
+
+          parallel_for(
+            mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { node_primal_id[node_id] = node_id; });
+
+          NodeValue<NodeId> computed_dual_node_primal_node_id{diamond_mesh->connectivity()};
+
+          mapper->toDualNode(node_primal_id, cell_ignored_id, computed_dual_node_primal_node_id);
+
+          return computed_dual_node_primal_node_id;
+        }();
+
+        CellValue<NodeId> primal_cell_dual_node_id = [=]() {
+          CellValue<NodeId> cell_id{mesh->connectivity()};
+          NodeValue<NodeId> node_ignored_id{mesh->connectivity()};
+          node_ignored_id.fill(NodeId{std::numeric_limits<unsigned int>::max()});
+
+          NodeValue<NodeId> dual_node_id{diamond_mesh->connectivity()};
+
+          parallel_for(
+            diamond_mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { dual_node_id[node_id] = node_id; });
+
+          CellValue<NodeId> computed_primal_cell_dual_node_id{mesh->connectivity()};
+
+          mapper->fromDualNode(dual_node_id, node_ignored_id, cell_id);
+
+          return cell_id;
+        }();
+        const auto& dual_Cjr                     = diamond_mesh_data.Cjr();
+        FaceValue<TinyVector<Dimension>> dualClj = [&] {
+          FaceValue<TinyVector<Dimension>> computedClj{mesh->connectivity()};
+          const auto& dual_node_to_cell_matrix = diamond_mesh->connectivity().nodeToCellMatrix();
+          const auto& dual_cell_to_node_matrix = diamond_mesh->connectivity().cellToNodeMatrix();
+          parallel_for(
+            mesh->numberOfFaces(), PUGS_LAMBDA(FaceId face_id) {
+              const auto& primal_face_to_cell = face_to_cell_matrix[face_id];
+              for (size_t i = 0; i < primal_face_to_cell.size(); i++) {
+                CellId cell_id            = primal_face_to_cell[i];
+                const NodeId dual_node_id = primal_cell_dual_node_id[cell_id];
+                for (size_t i_dual_cell = 0; i_dual_cell < dual_node_to_cell_matrix[dual_node_id].size();
+                     i_dual_cell++) {
+                  const CellId dual_cell_id = dual_node_to_cell_matrix[dual_node_id][i_dual_cell];
+                  if (face_dual_cell_id[face_id] == dual_cell_id) {
+                    for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size();
+                         i_dual_node++) {
+                      const NodeId final_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node];
+                      if (final_dual_node_id == dual_node_id) {
+                        computedClj[face_id] = dual_Cjr(dual_cell_id, i_dual_node);
+                      }
+                    }
+                  }
+                }
+              }
+            });
+          return computedClj;
+        }();
+
+        FaceValue<TinyVector<Dimension>> nlj = [&] {
+          FaceValue<TinyVector<Dimension>> computedNlj{mesh->connectivity()};
+          parallel_for(
+            mesh->numberOfFaces(),
+            PUGS_LAMBDA(FaceId face_id) { computedNlj[face_id] = 1. / l2Norm(dualClj[face_id]) * dualClj[face_id]; });
+          return computedNlj;
+        }();
+
+        FaceValue<const double> alpha_lambda_l = [&] {
+          CellValue<double> alpha_j{diamond_mesh->connectivity()};
+
+          parallel_for(
+            diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) {
+              alpha_j[diamond_cell_id] = dual_lambdaj[diamond_cell_id] / dual_Vj[diamond_cell_id];
+            });
+
+          FaceValue<double> computed_alpha_l{mesh->connectivity()};
+          mapper->fromDualCell(alpha_j, computed_alpha_l);
+          return computed_alpha_l;
+        }();
+
+        FaceValue<const double> alpha_mu_l = [&] {
+          CellValue<double> alpha_j{diamond_mesh->connectivity()};
+
+          parallel_for(
+            diamond_mesh->numberOfCells(), PUGS_LAMBDA(CellId diamond_cell_id) {
+              alpha_j[diamond_cell_id] = dual_muj[diamond_cell_id] / dual_Vj[diamond_cell_id];
+            });
+
+          FaceValue<double> computed_alpha_l{mesh->connectivity()};
+          mapper->fromDualCell(alpha_j, computed_alpha_l);
+          return computed_alpha_l;
+        }();
+
+        double unsteady    = (Tf == 0) ? 0 : 1;
+        double time_factor = (unsteady == 0) ? 1 : dt;
+
+        TinyMatrix<Dimension, double> eye = identity;
+
+        SparseMatrixDescriptor S{number_of_dof * Dimension};
+        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+          const double beta_mu_l          = l2Norm(dualClj[face_id]) * alpha_mu_l[face_id] * mes_l[face_id];
+          const double beta_lambda_l      = l2Norm(dualClj[face_id]) * alpha_lambda_l[face_id] * mes_l[face_id];
+          const auto& primal_face_to_cell = face_to_cell_matrix[face_id];
+          for (size_t i_cell = 0; i_cell < primal_face_to_cell.size(); ++i_cell) {
+            const CellId i_id                      = primal_face_to_cell[i_cell];
+            const bool is_face_reversed_for_cell_i = ((dualClj[face_id], xl[face_id] - xj[i_id]) < 0);
+
+            const TinyVector<Dimension> nil = [&] {
+              if (is_face_reversed_for_cell_i) {
+                return -nlj[face_id];
+              } else {
+                return nlj[face_id];
+              }
+            }();
+            for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) {
+              const CellId j_id = primal_face_to_cell[j_cell];
+              TinyMatrix<Dimension, double> M =
+                beta_mu_l * eye + beta_mu_l * tensorProduct(nil, nil) + beta_lambda_l * tensorProduct(nil, nil);
+              TinyMatrix<Dimension, double> N = tensorProduct(nil, nil);
+
+              if (i_cell == j_cell) {
+                for (size_t i = 0; i < Dimension; ++i) {
+                  for (size_t j = 0; j < Dimension; ++j) {
+                    S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) +=
+                      (time_factor * M(i, j) + unsteady * primal_Vj[i_id]);
+                    if (primal_face_is_neumann[face_id]) {
+                      S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) -= M(i, j);
+                    }
+                    if (primal_face_is_symmetry[face_id]) {
+                      S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) +=
+                        -((i == j) ? 1 : 0) + N(i, j);
+                      S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + j) +=
+                        (i == j) ? 1 : 0;
+                    }
+                  }
+                }
+              } else {
+                for (size_t i = 0; i < Dimension; ++i) {
+                  for (size_t j = 0; j < Dimension; ++j) {
+                    S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -=
+                      time_factor * M(i, j);
+                  }
+                }
+              }
+            }
+          }
+        }
+
+        const auto& dual_cell_to_node_matrix   = diamond_mesh->connectivity().cellToNodeMatrix();
+        const auto& primal_node_to_cell_matrix = mesh->connectivity().nodeToCellMatrix();
+        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+          const double alpha_mu_face_id     = mes_l[face_id] * alpha_mu_l[face_id];
+          const double alpha_lambda_face_id = mes_l[face_id] * alpha_lambda_l[face_id];
+
+          for (size_t i_face_cell = 0; i_face_cell < face_to_cell_matrix[face_id].size(); ++i_face_cell) {
+            CellId i_id                            = face_to_cell_matrix[face_id][i_face_cell];
+            const bool is_face_reversed_for_cell_i = ((dualClj[face_id], xl[face_id] - xj[i_id]) < 0);
+
+            for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) {
+              NodeId node_id = primal_face_to_node_matrix[face_id][i_node];
+
+              const TinyVector<Dimension> nil = [&] {
+                if (is_face_reversed_for_cell_i) {
+                  return -nlj[face_id];
+                } else {
+                  return nlj[face_id];
+                }
+              }();
+
+              CellId dual_cell_id = face_dual_cell_id[face_id];
+
+              for (size_t i_dual_node = 0; i_dual_node < dual_cell_to_node_matrix[dual_cell_id].size(); ++i_dual_node) {
+                const NodeId dual_node_id = dual_cell_to_node_matrix[dual_cell_id][i_dual_node];
+                if (dual_node_primal_node_id[dual_node_id] == node_id) {
+                  const TinyVector<Dimension> Clr = dual_Cjr(dual_cell_id, i_dual_node);
+
+                  TinyMatrix<Dimension, double> M = alpha_mu_face_id * (Clr, nil) * eye +
+                                                    alpha_mu_face_id * tensorProduct(Clr, nil) +
+                                                    alpha_lambda_face_id * tensorProduct(nil, Clr);
+
+                  for (size_t j_cell = 0; j_cell < primal_node_to_cell_matrix[node_id].size(); ++j_cell) {
+                    CellId j_id = primal_node_to_cell_matrix[node_id][j_cell];
+                    for (size_t i = 0; i < Dimension; ++i) {
+                      for (size_t j = 0; j < Dimension; ++j) {
+                        S((cell_dof_number[i_id] * Dimension) + i, (cell_dof_number[j_id] * Dimension) + j) -=
+                          time_factor * w_rj(node_id, j_cell) * M(i, j);
+                        if (primal_face_is_neumann[face_id]) {
+                          S(face_dof_number[face_id] * Dimension + i, cell_dof_number[j_id] * Dimension + j) +=
+                            w_rj(node_id, j_cell) * M(i, j);
+                        }
+                      }
+                    }
+                  }
+                  if (primal_node_is_on_boundary[node_id]) {
+                    for (size_t l_face = 0; l_face < node_to_face_matrix[node_id].size(); ++l_face) {
+                      FaceId l_id = node_to_face_matrix[node_id][l_face];
+                      if (primal_face_is_on_boundary[l_id]) {
+                        for (size_t i = 0; i < Dimension; ++i) {
+                          for (size_t j = 0; j < Dimension; ++j) {
+                            S(cell_dof_number[i_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) -=
+                              time_factor * w_rl(node_id, l_face) * M(i, j);
+                          }
+                        }
+                        if (primal_face_is_neumann[face_id]) {
+                          for (size_t i = 0; i < Dimension; ++i) {
+                            for (size_t j = 0; j < Dimension; ++j) {
+                              S(face_dof_number[face_id] * Dimension + i, face_dof_number[l_id] * Dimension + j) +=
+                                w_rl(node_id, l_face) * M(i, j);
+                            }
+                          }
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+              //            }
+            }
+          }
+        }
+        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+          if (primal_face_is_dirichlet[face_id]) {
+            for (size_t i = 0; i < Dimension; ++i) {
+              S(face_dof_number[face_id] * Dimension + i, face_dof_number[face_id] * Dimension + i) += 1;
+            }
+          }
+        }
+
+        Vector<double> b{number_of_dof * Dimension};
+        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+          for (size_t i = 0; i < Dimension; ++i) {
+            b[(cell_dof_number[cell_id] * Dimension) + i] =
+              primal_Vj[cell_id] * (time_factor * fj[cell_id][i] + unsteady * Uj[cell_id][i]);
+          }
+        }
+
+        // Dirichlet
+        NodeValue<bool> node_tag{mesh->connectivity()};
+        node_tag.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();
+                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];
+
+                  for (size_t i = 0; i < Dimension; ++i) {
+                    b[(face_dof_number[face_id] * Dimension) + i] += value_list[i_face][i];
+                  }
+                }
+              }
+            },
+            boundary_condition);
+        }
+
+        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, NormalStrainBoundaryCondition>)) {
+                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];
+                  for (size_t i = 0; i < Dimension; ++i) {
+                    b[face_dof_number[face_id] * Dimension + i] += mes_l[face_id] * value_list[i_face][i];   // sign
+                  }
+                }
+              }
+            },
+            boundary_condition);
+        }
+
+        CRSMatrix A{S};
+        Vector<double> U{number_of_dof * Dimension};
+        U        = 1;
+        Vector r = A * U - b;
+        std::cout << "initial (real) residu = " << std::sqrt((r, r)) << '\n';
+
+        LinearSolver solver;
+        solver.solveLocalSystem(A, U, b);
+
+        r = A * U - b;
+
+        std::cout << "final (real) residu = " << std::sqrt((r, r)) << '\n';
+
+        for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+          for (size_t i = 0; i < Dimension; ++i) {
+            Uj[cell_id][i] = U[(cell_dof_number[cell_id] * Dimension) + i];
+          }
+        }
+        for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+          for (size_t i = 0; i < Dimension; ++i) {
+            if (primal_face_is_on_boundary[face_id]) {
+              Ul[face_id][i] = U[(face_dof_number[face_id] * Dimension) + i];
+            }
+          }
+        }
+        NodeValue<TinyVector<3>> ur3d{mesh->connectivity()};
+        ur3d.fill(zero);
+
+        parallel_for(
+          mesh->numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+            TinyVector<Dimension> x = zero;
+            const auto node_cells   = node_to_cell_matrix[node_id];
+            for (size_t i_cell = 0; i_cell < node_cells.size(); ++i_cell) {
+              CellId cell_id = node_cells[i_cell];
+              x += w_rj(node_id, i_cell) * Uj[cell_id];
+            }
+            const auto node_faces = node_to_face_matrix[node_id];
+            for (size_t i_face = 0; i_face < node_faces.size(); ++i_face) {
+              FaceId face_id = node_faces[i_face];
+              if (primal_face_is_on_boundary[face_id]) {
+                x += w_rl(node_id, i_face) * Ul[face_id];
+              }
+            }
+            for (size_t i = 0; i < Dimension; ++i) {
+              ur3d[node_id][i] = x[i];
+            }
+          });
+      }
+    } else {
+      throw NotImplementedError("not done in 1d");
+    }
+  }
+};
+template VectorDiamondScheme<1>::VectorDiamondScheme(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  CellValue<TinyVector<1>>&,
+  FaceValue<TinyVector<1>>&,
+  const double&,
+  const double&,
+  CellValuePerNode<double>&,
+  FaceValuePerNode<double>&);
+
+template VectorDiamondScheme<2>::VectorDiamondScheme(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  CellValue<TinyVector<2>>&,
+  FaceValue<TinyVector<2>>&,
+  const double&,
+  const double&,
+  CellValuePerNode<double>&,
+  FaceValuePerNode<double>&);
+
+template VectorDiamondScheme<3>::VectorDiamondScheme(
+  std::shared_ptr<const IMesh>,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  const FunctionSymbolId&,
+  CellValue<TinyVector<3>>&,
+  FaceValue<TinyVector<3>>&,
+  const double&,
+  const double&,
+  CellValuePerNode<double>&,
+  FaceValuePerNode<double>&);
+
+#endif   // VECTOR_DIAMOND_SCHEME_HPP
-- 
GitLab