From c612d4c04e2981067bc2959568c476bd0de0bf0d Mon Sep 17 00:00:00 2001
From: Julie PATELA <julie.patela.ocre@cea.fr>
Date: Wed, 5 Aug 2020 16:52:40 +0200
Subject: [PATCH] Add interpolation of boundary nodes for Neumann BC.

---
 .../algorithms/HeatDiamondAlgorithm.cpp       | 311 ++++++++++++++----
 1 file changed, 255 insertions(+), 56 deletions(-)

diff --git a/src/language/algorithms/HeatDiamondAlgorithm.cpp b/src/language/algorithms/HeatDiamondAlgorithm.cpp
index aef3cd1ae..be6552c27 100644
--- a/src/language/algorithms/HeatDiamondAlgorithm.cpp
+++ b/src/language/algorithms/HeatDiamondAlgorithm.cpp
@@ -52,6 +52,11 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
   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<double> dirichlet_value{mesh->connectivity()};
+  dirichlet_value.fill(std::numeric_limits<double>::signaling_NaN());
+
   for (const auto& bc_descriptor : bc_descriptor_list) {
     bool is_valid_boundary_condition = true;
 
@@ -81,6 +86,14 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
             InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::node>(g_id, mesh->xr(),
                                                                                                       node_list);
 
+          for (size_t i_node = 0; i_node < node_list.size(); ++i_node) {
+            NodeId node_id = node_list[i_node];
+            if (not is_dirichlet[node_id]) {
+              is_dirichlet[node_id]    = true;
+              dirichlet_value[node_id] = value_list[i_node];
+            }
+          }
+
           boundary_condition_list.push_back(DirichletBoundaryCondition{node_list, value_list});
         }
       }
@@ -93,9 +106,30 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
       break;
     }
     case IBoundaryConditionDescriptor::Type::neumann: {
-      // const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor =
-      //   dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*bc_descriptor);
-      throw NotImplementedError("NIY");
+      const NeumannBoundaryConditionDescriptor& neumann_bc_descriptor =
+        dynamic_cast<const NeumannBoundaryConditionDescriptor&>(*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 == neumann_bc_descriptor.boundaryDescriptor()) {
+          const FunctionSymbolId g_id = neumann_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 double> value_list =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::face>(g_id,
+                                                                                                        mesh_data.xl(),
+                                                                                                        face_list);
+            boundary_condition_list.push_back(NeumannBoundaryCondition{face_list, value_list});
+          } else {
+            throw NotImplementedError("Neumann conditions are not supported in 1d");
+          }
+        }
+      }
       break;
     }
     default: {
@@ -116,8 +150,66 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
         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, 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];
+                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();
+    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;
+        }
+      }
+    }
 
-    auto dof_number = [&](CellId cell_id) { return cell_dof_number[cell_id]; };
+    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;
+      }
+    }
 
     MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
 
@@ -126,9 +218,17 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
 
     NodeValue<double> Tr(mesh->connectivity());
     const NodeValue<const TinyVector<Dimension>>& xr = mesh->xr();
+
+    const FaceValue<const TinyVector<Dimension>>& xl = mesh_data.xl();
     const CellValue<const TinyVector<Dimension>>& xj = mesh_data.xj();
     const 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};
@@ -138,27 +238,77 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
       }
       const auto& node_to_cell = node_to_cell_matrix[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++) {
+      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++) {
-          const CellId J = node_to_cell[j];
-          A(i, j)        = xj[J][i - 1];
+          A(0, j) = 1;
         }
-      }
-      Vector<double> x{node_to_cell.size()};
-      x = 0;
+        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;
 
-      LocalRectangularMatrix B = transpose(A) * A;
-      Vector y                 = transpose(A) * b;
-      PCG<false>{y, B, B, x, 10, 1e-12};
+        LocalRectangularMatrix B = transpose(A) * A;
+        Vector y                 = transpose(A) * b;
+        PCG<false>{y, B, B, x, 10, 1e-12};
 
-      Tr[i_node] = 0;
-      for (size_t j = 0; j < node_to_cell.size(); j++) {
-        Tr[i_node] += x[j] * Tj[node_to_cell[j]];
-        w_rj(i_node, j) = x[j];
+        Tr[i_node] = 0;
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          Tr[i_node] += x[j] * Tj[node_to_cell[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(); 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;
+
+        LocalRectangularMatrix B = transpose(A) * A;
+        Vector y                 = transpose(A) * b;
+        PCG<false>{y, B, B, x, 10, 1e-12};
+
+        Tr[i_node] = 0;
+        for (size_t j = 0; j < node_to_cell.size(); j++) {
+          Tr[i_node] += x[j] * Tj[node_to_cell[j]];
+          w_rj(i_node, j) = x[j];
+        }
+        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]) {
+            w_rl(i_node, i_face) = x[cpt_face++];
+          }
+        }
       }
     }
 
@@ -243,26 +393,7 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
 
         return computed_alpha_l;
       }();
-
-      const auto& primal_face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
-      const auto& face_to_cell_matrix        = mesh->connectivity().faceToCellMatrix();
-
-      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;
-          }
-        }
-      }
-
-      SparseMatrixDescriptor S{mesh->numberOfCells()};
+      SparseMatrixDescriptor S{number_of_dof};
       for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
         const auto& primal_face_to_cell = face_to_cell_matrix[face_id];
         const double beta_l             = 0.5 * alpha_l[face_id] * mes_l[face_id];
@@ -272,9 +403,9 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
           for (size_t j_cell = 0; j_cell < primal_face_to_cell.size(); ++j_cell) {
             const CellId cell_id2 = primal_face_to_cell[j_cell];
             if (i_cell == j_cell) {
-              S(dof_number(cell_id1), dof_number(cell_id2)) += beta_l;
+              S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) += beta_l;
             } else {
-              S(dof_number(cell_id1), dof_number(cell_id2)) -= beta_l;
+              S(cell_dof_number[cell_id1], cell_dof_number[cell_id2]) -= beta_l;
             }
           }
         }
@@ -328,7 +459,7 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
             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];
 
-              if (not primal_node_is_on_boundary[node_id]) {
+              if (not is_dirichlet[node_id]) {
                 const TinyVector<Dimension> nil = [&] {
                   if (is_face_reversed_for_cell_i) {
                     return -primal_nlr(face_id, i_node);
@@ -349,7 +480,7 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
 
                     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];
-                      S(dof_number(i_id), dof_number(j_id)) -= w_rj(node_id, j_cell) * a_ir;
+                      S(cell_dof_number[i_id], cell_dof_number[j_id]) -= w_rj(node_id, j_cell) * a_ir;
                     }
                   }
                 }
@@ -365,11 +496,11 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
       const CellValue<const double> primal_Vj = mesh_data.Vj();
 
       CRSMatrix A{S};
-      Vector<double> b{mesh->numberOfCells()};
+      Vector<double> b{number_of_dof};
 
       const auto& primal_cell_to_face_matrix = mesh->connectivity().cellToFaceMatrix();
       for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
-        b[dof_number(cell_id)] = fj[cell_id] * primal_Vj[cell_id];
+        b[cell_dof_number[cell_id]] = fj[cell_id] * primal_Vj[cell_id];
       }
 
       {   // Dirichlet
@@ -426,7 +557,7 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
                               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);
-                                b[dof_number(cell_id)] += alpha_l[face_id] * value_list[i_node] * (nil, Clr);
+                                b[cell_dof_number[cell_id]] += alpha_l[face_id] * value_list[i_node] * (nil, Clr);
                               }
                             }
                           }
@@ -439,10 +570,9 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
             },
             boundary_condition);
         }
-        // std::exit(0);
       }
 
-      Vector<double> T{mesh->numberOfCells()};
+      Vector<double> T{number_of_dof};
       T = 0;
 
       BiCGStab{b, A, T, 1000, 1e-15};
@@ -450,7 +580,7 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
       CellValue<double> Temperature{mesh->connectivity()};
 
       parallel_for(
-        mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[dof_number(cell_id)]; });
+        mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { Temperature[cell_id] = T[cell_dof_number[cell_id]]; });
 
       Vector<double> error{mesh->numberOfCells()};
       parallel_for(
@@ -497,29 +627,98 @@ class HeatDiamondScheme<Dimension>::DirichletBoundaryCondition
 
   DirichletBoundaryCondition(const Array<const NodeId>& node_list, const Array<const double>& value_list)
     : m_value_list{value_list}, m_node_list{node_list}
-  {}
+  {
+    Assert(m_value_list.size() == m_node_list.size());
+  }
 
   ~DirichletBoundaryCondition() = default;
 };
 
 template <size_t Dimension>
-class HeatDiamondScheme<Dimension>::FourierBoundaryCondition
+class HeatDiamondScheme<Dimension>::NeumannBoundaryCondition
 {
+ private:
+  const Array<const double> m_value_list;
+  const Array<const FaceId> m_face_list;
+
  public:
-  ~FourierBoundaryCondition() = default;
+  const Array<const FaceId>&
+  faceList() const
+  {
+    return m_face_list;
+  }
+
+  const Array<const double>&
+  valueList() const
+  {
+    return m_value_list;
+  }
+
+  NeumannBoundaryCondition(const Array<const FaceId>& face_list, const Array<const double>& value_list)
+    : m_value_list{value_list}, m_face_list{face_list}
+  {
+    Assert(m_value_list.size() == m_face_list.size());
+  }
+
+  ~NeumannBoundaryCondition() = default;
 };
 
 template <size_t Dimension>
-class HeatDiamondScheme<Dimension>::NeumannBoundaryCondition
+class HeatDiamondScheme<Dimension>::FourierBoundaryCondition
 {
+ private:
+  const Array<const double> m_coef_list;
+  const Array<const double> m_value_list;
+  const Array<const FaceId> m_face_list;
+
  public:
-  ~NeumannBoundaryCondition() = default;
+  const Array<const FaceId>&
+  faceList() const
+  {
+    return m_face_list;
+  }
+
+  const Array<const double>&
+  valueList() const
+  {
+    return m_value_list;
+  }
+
+  const Array<const double>&
+  coefList() const
+  {
+    return m_coef_list;
+  }
+
+ public:
+  FourierBoundaryCondition(const Array<const FaceId>& face_list,
+                           const Array<const double>& coef_list,
+                           const Array<const double>& value_list)
+    : m_coef_list{coef_list}, m_value_list{value_list}, m_face_list{face_list}
+  {
+    Assert(m_coef_list.size() == m_face_list.size());
+    Assert(m_value_list.size() == m_face_list.size());
+  }
+
+  ~FourierBoundaryCondition() = default;
 };
 
 template <size_t Dimension>
 class HeatDiamondScheme<Dimension>::SymmetryBoundaryCondition
 {
+ private:
+  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;
 };
 
-- 
GitLab