From 45736307c568e1c94ab2990e1b9407178628bae0 Mon Sep 17 00:00:00 2001
From: labourasse <labourassee@gmail.com>
Date: Thu, 1 Jun 2023 12:34:10 +0200
Subject: [PATCH] Add contraction operator to TinyMatrix and an implicit mesh
 smoother

---
 src/mesh/ImplicitMeshSmoother.cpp | 463 ++++++++++++++++++++++++++++++
 src/mesh/ImplicitMeshSmoother.hpp |  65 +++++
 2 files changed, 528 insertions(+)
 create mode 100644 src/mesh/ImplicitMeshSmoother.cpp
 create mode 100644 src/mesh/ImplicitMeshSmoother.hpp

diff --git a/src/mesh/ImplicitMeshSmoother.cpp b/src/mesh/ImplicitMeshSmoother.cpp
new file mode 100644
index 000000000..9a13e8098
--- /dev/null
+++ b/src/mesh/ImplicitMeshSmoother.cpp
@@ -0,0 +1,463 @@
+#include <mesh/ImplicitMeshSmoother.hpp>
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshCellZone.hpp>
+#include <mesh/MeshFlatNodeBoundary.hpp>
+#include <mesh/MeshLineNodeBoundary.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <scheme/AxisBoundaryConditionDescriptor.hpp>
+#include <scheme/DiscreteFunctionUtils.hpp>
+#include <scheme/DiscreteFunctionVariant.hpp>
+#include <scheme/FixedBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <utils/RandomEngine.hpp>
+
+#include <variant>
+
+template <size_t Dimension>
+class ImplicitMeshSmootherHandler::ImplicitMeshSmoother
+{
+ private:
+  using Rd               = TinyVector<Dimension>;
+  using Rdxd             = TinyMatrix<Dimension>;
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  const MeshType& m_given_mesh;
+
+  //  class AxisBoundaryCondition;
+  class FixedBoundaryCondition;
+  //  class SymmetryBoundaryCondition;
+
+  // using BoundaryCondition = std::variant<AxisBoundaryCondition, FixedBoundaryCondition, SymmetryBoundaryCondition>;
+  using BoundaryCondition = std::variant<FixedBoundaryCondition>;
+
+  using BoundaryConditionList = std::vector<BoundaryCondition>;
+  BoundaryConditionList m_boundary_condition_list;
+
+  BoundaryConditionList
+  _getBCList(const MeshType& mesh,
+             const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
+  {
+    BoundaryConditionList bc_list;
+
+    for (const auto& bc_descriptor : bc_descriptor_list) {
+      switch (bc_descriptor->type()) {
+      // case IBoundaryConditionDescriptor::Type::axis: {
+      //   if constexpr (Dimension == 1) {
+      //     bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh,
+      //     bc_descriptor->boundaryDescriptor())});
+      //   } else {
+      //     bc_list.emplace_back(
+      //       AxisBoundaryCondition{getMeshLineNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
+      //   }
+      //   break;
+      // }
+      // case IBoundaryConditionDescriptor::Type::symmetry: {
+      //   bc_list.emplace_back(
+      //     SymmetryBoundaryCondition{getMeshFlatNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
+      //   break;
+      // }
+      case IBoundaryConditionDescriptor::Type::fixed: {
+        bc_list.emplace_back(FixedBoundaryCondition{getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())});
+        break;
+      }
+      default: {
+        std::ostringstream error_msg;
+        error_msg << *bc_descriptor << " is an invalid boundary condition for mesh smoother";
+        throw NormalError(error_msg.str());
+      }
+      }
+    }
+
+    return bc_list;
+  }
+
+  void
+  _browseBC(NodeValue<bool>& is_fixed) const
+  {
+    for (auto&& boundary_condition : m_boundary_condition_list) {
+      std::visit(
+        [&](auto&& bc) {
+          using BCType = std::decay_t<decltype(bc)>;
+          // if constexpr (std::is_same_v<BCType, SymmetryBoundaryCondition>) {
+          //   const Rd& n = bc.outgoingNormal();
+
+          //   const Rdxd I   = identity;
+          //   const Rdxd nxn = tensorProduct(n, n);
+          //   const Rdxd P   = I - nxn;
+
+          //   const Array<const NodeId>& node_list = bc.nodeList();
+          //   parallel_for(
+          //     node_list.size(), PUGS_LAMBDA(const size_t i_node) {
+          //       const NodeId node_id = node_list[i_node];
+
+          //       shift[node_id] = P * shift[node_id];
+          //     });
+
+          // } else if constexpr (std::is_same_v<BCType, AxisBoundaryCondition>) {
+          //   if constexpr (Dimension > 1) {
+          //     const Rd& t = bc.direction();
+
+          //     const Rdxd txt = tensorProduct(t, t);
+
+          //     const Array<const NodeId>& node_list = bc.nodeList();
+          //     parallel_for(
+          //       node_list.size(), PUGS_LAMBDA(const size_t i_node) {
+          //         const NodeId node_id = node_list[i_node];
+
+          //         shift[node_id] = txt * shift[node_id];
+          //       });
+          //   } else {
+          //     throw UnexpectedError("AxisBoundaryCondition make no sense in dimension 1");
+          //   }
+
+          // } else
+          if constexpr (std::is_same_v<BCType, FixedBoundaryCondition>) {
+            const Array<const NodeId>& node_list = bc.nodeList();
+            parallel_for(
+              node_list.size(), PUGS_LAMBDA(const size_t i_node) {
+                const NodeId node_id = node_list[i_node];
+                //                shift[node_id]       = zero;
+                is_fixed[node_id] = true;
+              });
+
+          } else {
+            throw UnexpectedError("invalid boundary condition type");
+          }
+        },
+        boundary_condition);
+    }
+  }
+
+  void
+  _computeMatrixSize(const NodeValue<bool>& is_fixed,
+                     NodeValue<int>& id_fixed,
+                     NodeValue<int>& id_free,
+                     size_t& nb_free,
+                     size_t& nb_fixed) const
+  {
+    nb_free  = 0;
+    nb_fixed = 0;
+    for (NodeId n_id = 0; n_id < m_given_mesh.numberOfNodes(); n_id++) {
+      if (is_fixed[n_id]) {
+        id_fixed[n_id] = nb_fixed;
+        nb_fixed += 1;
+      } else {
+        id_free[n_id] = nb_free;
+        nb_free += 1;
+      }
+    }
+  }
+  void
+  _findLocalIds(const NodeValue<bool>& is_fixed,
+                Array<NodeId>& gid_node_free,
+                Array<NodeId>& gid_node_fixed,
+                Array<int>& non_zeros_free,
+                Array<int>& non_zeros_fixed) const
+  {
+    const auto& node_to_face_matrix = m_given_mesh.connectivity().nodeToFaceMatrix();
+    const auto& face_to_node_matrix = m_given_mesh.connectivity().faceToNodeMatrix();
+
+    int local_free_id  = 0;
+    int local_fixed_id = 0;
+    non_zeros_free.fill(1);
+    non_zeros_fixed.fill(0);
+    for (NodeId n_id = 0; n_id < m_given_mesh.numberOfNodes(); n_id++) {
+      std::cout << " n_id " << n_id << "\n";
+      if (is_fixed[n_id]) {
+        gid_node_fixed[local_fixed_id] = n_id;
+        for (size_t i_face = 0; i_face < node_to_face_matrix[n_id].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[n_id][i_face];
+          for (size_t i_node = 0; i_node < face_to_node_matrix[face_id].size(); ++i_node) {
+            NodeId node_id = face_to_node_matrix[face_id][i_node];
+            if ((node_id == n_id) or (is_fixed[node_id])) {
+              continue;
+            } else {
+              non_zeros_fixed[local_fixed_id] += 1;
+            }
+          }
+        }
+        local_fixed_id++;
+      } else {
+        gid_node_free[local_free_id] = n_id;
+        for (size_t i_face = 0; i_face < node_to_face_matrix[n_id].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[n_id][i_face];
+          for (size_t i_node = 0; i_node < face_to_node_matrix[face_id].size(); ++i_node) {
+            NodeId node_id = face_to_node_matrix[face_id][i_node];
+            if ((node_id == n_id) or (is_fixed[node_id])) {
+              continue;
+            } else {
+              non_zeros_free[local_free_id] += 1;
+            }
+          }
+        }
+        local_free_id++;
+      }
+    }
+  }
+  void
+  _fillMatrix(CRSMatrixDescriptor<double>& Afree,
+              CRSMatrixDescriptor<double>& Afixed,
+              const NodeValue<bool>& is_fixed,
+              const NodeValue<int>& id_free,
+              const NodeValue<int>& id_fixed) const
+  // ,
+  //           const Array<NodeId>& gid_node_free,
+  //           const Array<NodeId>& gid_node_fixed) const
+  {
+    const auto& node_to_face_matrix = m_given_mesh.connectivity().nodeToFaceMatrix();
+    const auto& face_to_node_matrix = m_given_mesh.connectivity().faceToNodeMatrix();
+    int local_free_id               = 0;
+    for (NodeId n_id = 0; n_id < m_given_mesh.numberOfNodes(); n_id++) {
+      if (is_fixed[n_id]) {
+        continue;
+      } else {
+        Assert(id_free[n_id] == local_free_id, "bad matrix definition");
+        for (size_t i_face = 0; i_face < node_to_face_matrix[n_id].size(); ++i_face) {
+          FaceId face_id = node_to_face_matrix[n_id][i_face];
+          for (size_t i_node = 0; i_node < face_to_node_matrix[face_id].size(); ++i_node) {
+            NodeId node_id = face_to_node_matrix[face_id][i_node];
+            if (node_id == n_id) {
+              continue;
+            } else if ((is_fixed[node_id])) {
+              Afree(local_free_id, local_free_id) += 1;
+              Afixed(local_free_id, id_fixed[node_id]) -= 1;
+            } else {
+              Afree(local_free_id, local_free_id) += 1;
+              Afree(local_free_id, id_free[node_id]) -= 1;
+            }
+          }
+        }
+        local_free_id++;
+      }
+    }
+  }
+
+  NodeValue<Rd>
+  _getPosition() const
+  {
+    const ConnectivityType& connectivity = m_given_mesh.connectivity();
+    NodeValue<const Rd> given_xr         = m_given_mesh.xr();
+
+    NodeValue<Rd> pos_r{connectivity};
+    NodeValue<bool> is_fixed{connectivity};
+    is_fixed.fill(false);
+    parallel_for(
+      m_given_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) { pos_r[node_id] = given_xr[node_id]; });
+    _browseBC(is_fixed);
+
+    NodeValue<int> node_dof_id{connectivity};
+    size_t nb_free, nb_fixed;
+    NodeValue<int> id_free{connectivity};
+    NodeValue<int> id_fixed{connectivity};
+    _computeMatrixSize(is_fixed, id_fixed, id_free, nb_free, nb_fixed);
+    // std::cout << " is_fixed: "
+    //           << "\n";
+    // std::cout << is_fixed << "\n";
+    // std::cout << " id_fixed: "
+    //           << "\n";
+    // std::cout << id_fixed << "\n";
+    // std::cout << " is_free: "
+    //           << "\n";
+    // std::cout << id_free << "\n";
+    std::cout << "nb_free " << nb_free << " nb_fixed " << nb_fixed << "\n";
+    Array<int> non_zeros_free{nb_free};
+    Array<int> non_zeros_fixed{nb_fixed};
+    Array<NodeId> gid_node_free{nb_free};
+    Array<NodeId> gid_node_fixed{nb_fixed};
+    // size_t local_free_id  = 0;
+    // size_t local_fixed_id = 0;
+    _findLocalIds(is_fixed, gid_node_free, gid_node_fixed, non_zeros_free, non_zeros_fixed);
+    // for (NodeId n_id = 0; n_id < m_given_mesh.numberOfNodes(); n_id++) {
+    //   if (is_fixed[n_id]) {
+    //     gid_node_fixed[local_fixed_id] = n_id;
+    //     local_fixed_id++;
+    //   } else {
+    //     gid_node_free[local_free_id] = n_id;
+    //     local_free_id++;
+    //   }
+    // }
+    std::cout << " gid_node_fixed: "
+              << "\n";
+    std::cout << gid_node_fixed << "\n";
+    std::cout << " gid_node_free: "
+              << "\n";
+    std::cout << gid_node_free << "\n";
+
+    CRSMatrixDescriptor<double> Afree(nb_free, nb_free, non_zeros_free);
+    CRSMatrixDescriptor<double> Afixed(nb_free, nb_fixed, non_zeros_fixed);
+    LinearSolver solver;
+    _fillMatrix(Afree, Afixed, is_fixed, id_free, id_fixed);
+    std::cout << " Afree :"
+              << "\n";
+    std::cout << Afree << "\n";
+    std::cout << " Afixed :"
+              << "\n";
+    std::cout << Afixed << "\n";
+
+    Vector<double> F{nb_fixed};
+    CRSMatrix Mfree{Afree.getCRSMatrix()};
+    CRSMatrix Mfixed{Afixed.getCRSMatrix()};
+    for (size_t dir = 0; dir < Dimension; ++dir) {
+      for (size_t lid_node = 0; lid_node < nb_fixed; lid_node++) {
+        F[lid_node] = -given_xr[gid_node_fixed[lid_node]][dir];
+      }
+      Vector<double> X{nb_free};
+      Vector<double> b{nb_free};
+      b = Mfixed * F;
+      std::cout << " F " << F << "\n";
+      std::cout << " b " << b << "\n";
+      solver.solveLocalSystem(Mfree, X, b);
+      parallel_for(
+        m_given_mesh.numberOfNodes(), PUGS_LAMBDA(NodeId node_id) {
+          if (!is_fixed[node_id]) {
+            pos_r[node_id][dir] = X[id_free[node_id]];
+          }
+        });
+    }
+    return pos_r;
+  }
+
+ public:
+  std::shared_ptr<const IMesh>
+  getSmoothedMesh() const
+  {
+    NodeValue<const Rd> given_xr = m_given_mesh.xr();
+
+    NodeValue<Rd> xr = this->_getPosition();
+
+    return std::make_shared<MeshType>(m_given_mesh.shared_connectivity(), xr);
+  }
+
+  ImplicitMeshSmoother(const ImplicitMeshSmoother&) = delete;
+  ImplicitMeshSmoother(ImplicitMeshSmoother&&)      = delete;
+
+  ImplicitMeshSmoother(const MeshType& given_mesh,
+                       const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
+    : m_given_mesh(given_mesh), m_boundary_condition_list(this->_getBCList(given_mesh, bc_descriptor_list))
+  {}
+
+  ~ImplicitMeshSmoother() = default;
+};
+
+// template <size_t Dimension>
+// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::AxisBoundaryCondition
+// {
+//  public:
+//   using Rd = TinyVector<Dimension, double>;
+
+//  private:
+//   const MeshLineNodeBoundary<Dimension> m_mesh_line_node_boundary;
+
+//  public:
+//   const Rd&
+//   direction() const
+//   {
+//     return m_mesh_line_node_boundary.direction();
+//   }
+
+//   const Array<const NodeId>&
+//   nodeList() const
+//   {
+//     return m_mesh_line_node_boundary.nodeList();
+//   }
+
+//   AxisBoundaryCondition(MeshLineNodeBoundary<Dimension>&& mesh_line_node_boundary)
+//     : m_mesh_line_node_boundary(mesh_line_node_boundary)
+//   {
+//     ;
+//   }
+
+//   ~AxisBoundaryCondition() = default;
+// };
+
+// template <>
+// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<1>::AxisBoundaryCondition
+// {
+//  public:
+//   AxisBoundaryCondition()  = default;
+//   ~AxisBoundaryCondition() = default;
+// };
+
+template <size_t Dimension>
+class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::FixedBoundaryCondition
+{
+ private:
+  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+
+ public:
+  const Array<const NodeId>&
+  nodeList() const
+  {
+    return m_mesh_node_boundary.nodeList();
+  }
+
+  FixedBoundaryCondition(MeshNodeBoundary<Dimension>&& mesh_node_boundary) : m_mesh_node_boundary{mesh_node_boundary} {}
+
+  ~FixedBoundaryCondition() = default;
+};
+
+// template <size_t Dimension>
+// class ImplicitMeshSmootherHandler::ImplicitMeshSmoother<Dimension>::SymmetryBoundaryCondition
+// {
+//  public:
+//   using Rd = TinyVector<Dimension, double>;
+
+//  private:
+//   const MeshFlatNodeBoundary<Dimension> m_mesh_flat_node_boundary;
+
+//  public:
+//   const Rd&
+//   outgoingNormal() const
+//   {
+//     return m_mesh_flat_node_boundary.outgoingNormal();
+//   }
+
+//   const Array<const NodeId>&
+//   nodeList() const
+//   {
+//     return m_mesh_flat_node_boundary.nodeList();
+//   }
+
+//   SymmetryBoundaryCondition(MeshFlatNodeBoundary<Dimension>&& mesh_flat_node_boundary)
+//     : m_mesh_flat_node_boundary(mesh_flat_node_boundary)
+//   {
+//     ;
+//   }
+
+//   ~SymmetryBoundaryCondition() = default;
+// };
+
+std::shared_ptr<const IMesh>
+ImplicitMeshSmootherHandler::getSmoothedMesh(
+  const std::shared_ptr<const IMesh>& mesh,
+  const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const
+{
+  switch (mesh->dimension()) {
+  case 1: {
+    throw NotImplementedError("ImplicitMeshSmoother not implemented in 1D");
+    break;
+  }
+  case 2: {
+    constexpr size_t Dimension = 2;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh();
+  }
+  case 3: {
+    constexpr size_t Dimension = 3;
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    ImplicitMeshSmoother smoother(dynamic_cast<const MeshType&>(*mesh), bc_descriptor_list);
+    return smoother.getSmoothedMesh();
+  }
+  default: {
+    throw UnexpectedError("invalid mesh dimension");
+  }
+  }
+}
diff --git a/src/mesh/ImplicitMeshSmoother.hpp b/src/mesh/ImplicitMeshSmoother.hpp
new file mode 100644
index 000000000..b5ca016bc
--- /dev/null
+++ b/src/mesh/ImplicitMeshSmoother.hpp
@@ -0,0 +1,65 @@
+#ifndef IMPLICIT_MESH_SMOOTHER_HPP
+#define IMPLICIT_MESH_SMOOTHER_HPP
+#include <algebra/CRSMatrix.hpp>
+#include <algebra/CRSMatrixDescriptor.hpp>
+#include <algebra/LinearSolver.hpp>
+#include <algebra/TinyVector.hpp>
+#include <algebra/Vector.hpp>
+#include <language/utils/InterpolateItemValue.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/MeshFaceBoundary.hpp>
+#include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/SubItemValuePerItem.hpp>
+#include <scheme/DirichletBoundaryConditionDescriptor.hpp>
+#include <scheme/FourierBoundaryConditionDescriptor.hpp>
+#include <scheme/NeumannBoundaryConditionDescriptor.hpp>
+#include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+
+#include <mesh/IMesh.hpp>
+#include <scheme/IBoundaryConditionDescriptor.hpp>
+
+#include <memory>
+#include <vector>
+
+class FunctionSymbolId;
+class IZoneDescriptor;
+class DiscreteFunctionVariant;
+
+class ImplicitMeshSmootherHandler
+{
+ private:
+  template <size_t Dimension>
+  class ImplicitMeshSmoother;
+
+ public:
+  std::shared_ptr<const IMesh> getSmoothedMesh(
+    const std::shared_ptr<const IMesh>& mesh,
+    const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list) const;
+
+  // std::shared_ptr<const IMesh> getSmoothedMesh(
+  //   const std::shared_ptr<const IMesh>& mesh,
+  //   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  //   const FunctionSymbolId& function_symbol_id) const;
+
+  // std::shared_ptr<const IMesh> getSmoothedMesh(
+  //   const std::shared_ptr<const IMesh>& mesh,
+  //   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  //   const std::vector<std::shared_ptr<const IZoneDescriptor>>& smoothing_zone_list) const;
+
+  // std::shared_ptr<const IMesh> getSmoothedMesh(
+  //   const std::shared_ptr<const IMesh>& mesh,
+  //   const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list,
+  //   const std::vector<std::shared_ptr<const DiscreteFunctionVariant>>& smoothing_zone_list) const;
+
+  ImplicitMeshSmootherHandler()                              = default;
+  ImplicitMeshSmootherHandler(ImplicitMeshSmootherHandler&&) = default;
+  ~ImplicitMeshSmootherHandler()                             = default;
+};
+
+#endif   // IMPLICIT_MESH_SMOOTHER_HPP
-- 
GitLab