From 1fb2ac94b5b4e69a2a8e478efbf4fae43d4e9b97 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Del=20Pino?= <stephane.delpino44@gmail.com>
Date: Mon, 29 Nov 2021 18:34:02 +0100
Subject: [PATCH] Allow LineTransformation in higher dimensions: ]-1,1[ -> R^d,
 (d>=1)

For higher dimensions, the Jacobian determinant make now sense but the
relevant quantity (to compute integrals) is the norm of the velocity
of the curvilinear abscissa.
---
 src/geometry/LineTransformation.hpp     | 50 ++++++++++++++++++---
 src/language/utils/IntegrateOnCells.hpp |  4 +-
 tests/test_LineTransformation.cpp       | 60 +++++++++++++++++++++----
 3 files changed, 98 insertions(+), 16 deletions(-)

diff --git a/src/geometry/LineTransformation.hpp b/src/geometry/LineTransformation.hpp
index 641821809..ae0b00a8a 100644
--- a/src/geometry/LineTransformation.hpp
+++ b/src/geometry/LineTransformation.hpp
@@ -1,16 +1,19 @@
 #ifndef LINE_TRANSFORMATION_HPP
 #define LINE_TRANSFORMATION_HPP
 
-#include <algebra/TinyMatrix.hpp>
 #include <algebra/TinyVector.hpp>
 
-class LineTransformation
+template <size_t GivenDimension>
+class LineTransformation;
+
+template <>
+class LineTransformation<1>
 {
  public:
   constexpr static size_t Dimension = 1;
 
  private:
-  TinyMatrix<Dimension> m_jacobian;
+  double m_jacobian;
   TinyVector<Dimension> m_shift;
 
  public:
@@ -24,14 +27,49 @@ class LineTransformation
   double
   jacobianDeterminant() const
   {
-    return m_jacobian(0, 0);
+    return m_jacobian;
+  }
+
+  PUGS_INLINE
+  LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
+  {
+    m_jacobian = 0.5 * (b[0] - a[0]);
+    m_shift    = 0.5 * (a + b);
+  }
+
+  ~LineTransformation() = default;
+};
+
+template <size_t GivenDimension>
+class LineTransformation
+{
+ public:
+  constexpr static size_t Dimension = GivenDimension;
+
+ private:
+  TinyVector<Dimension> m_velocity;
+  const double m_velocity_norm;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<1>& x) const
+  {
+    return x[0] * m_velocity + m_shift;
+  }
+
+  double
+  velocityNorm() const
+  {
+    return m_velocity_norm;
   }
 
   PUGS_INLINE
   LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
+    : m_velocity{0.5 * (b - a)}, m_velocity_norm{l2Norm(m_velocity)}, m_shift{0.5 * (a + b)}
   {
-    m_jacobian(0, 0) = 0.5 * (b[0] - a[0]);
-    m_shift[0]       = 0.5 * (a[0] + b[0]);
+    static_assert(Dimension > 1);
   }
 
   ~LineTransformation() = default;
diff --git a/src/language/utils/IntegrateOnCells.hpp b/src/language/utils/IntegrateOnCells.hpp
index 180008c15..e833fd5fb 100644
--- a/src/language/utils/IntegrateOnCells.hpp
+++ b/src/language/utils/IntegrateOnCells.hpp
@@ -140,7 +140,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
       if constexpr (Dimension == 1) {
         switch (cell_type[cell_id]) {
         case CellType::Line: {
-          const LineTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
 
           for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
             const auto xi = qf.point(i_point);
@@ -380,7 +380,7 @@ class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<Outpu
       if constexpr (Dimension == 1) {
         switch (cell_type[cell_id]) {
         case CellType::Line: {
-          const LineTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
           const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
 
           for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
diff --git a/tests/test_LineTransformation.cpp b/tests/test_LineTransformation.cpp
index f9c9620af..f7acac50f 100644
--- a/tests/test_LineTransformation.cpp
+++ b/tests/test_LineTransformation.cpp
@@ -7,16 +7,60 @@
 
 TEST_CASE("LineTransformation", "[geometry]")
 {
-  using R1 = TinyVector<1>;
+  SECTION("1D")
+  {
+    using R1 = TinyVector<1>;
 
-  const R1 a = 0.3;
-  const R1 b = 2.7;
+    const R1 a = 0.3;
+    const R1 b = 2.7;
 
-  const LineTransformation t(a, b);
+    const LineTransformation<1> t(a, b);
 
-  REQUIRE(t(-1)[0] == Catch::Approx(0.3));
-  REQUIRE(t(0)[0] == Catch::Approx(1.5));
-  REQUIRE(t(1)[0] == Catch::Approx(2.7));
+    REQUIRE(t(-1)[0] == Catch::Approx(0.3));
+    REQUIRE(t(0)[0] == Catch::Approx(1.5));
+    REQUIRE(t(1)[0] == Catch::Approx(2.7));
 
-  REQUIRE(t.jacobianDeterminant() == Catch::Approx(1.2));
+    REQUIRE(t.jacobianDeterminant() == Catch::Approx(1.2));
+  }
+
+  SECTION("2D")
+  {
+    using R2 = TinyVector<2>;
+
+    const R2 a = {0.3, 1.2};
+    const R2 b = {2.7, 0.7};
+
+    const LineTransformation<2> t(a, b);
+
+    REQUIRE(t(-1)[0] == Catch::Approx(0.3));
+    REQUIRE(t(-1)[1] == Catch::Approx(1.2));
+    REQUIRE(t(0)[0] == Catch::Approx(1.5));
+    REQUIRE(t(0)[1] == Catch::Approx(0.95));
+    REQUIRE(t(1)[0] == Catch::Approx(2.7));
+    REQUIRE(t(1)[1] == Catch::Approx(0.7));
+
+    REQUIRE(t.velocityNorm() == Catch::Approx(l2Norm(0.5 * (b - a))));
+  }
+
+  SECTION("3D")
+  {
+    using R3 = TinyVector<3>;
+
+    const R3 a = {0.3, 1.2, -1};
+    const R3 b = {2.7, 0.7, 0.3};
+
+    const LineTransformation<3> t(a, b);
+
+    REQUIRE(t(-1)[0] == Catch::Approx(0.3));
+    REQUIRE(t(-1)[1] == Catch::Approx(1.2));
+    REQUIRE(t(-1)[2] == Catch::Approx(-1.));
+    REQUIRE(t(0)[0] == Catch::Approx(1.5));
+    REQUIRE(t(0)[1] == Catch::Approx(0.95));
+    REQUIRE(t(0)[2] == Catch::Approx(-0.35));
+    REQUIRE(t(1)[0] == Catch::Approx(2.7));
+    REQUIRE(t(1)[1] == Catch::Approx(0.7));
+    REQUIRE(t(1)[2] == Catch::Approx(0.3));
+
+    REQUIRE(t.velocityNorm() == Catch::Approx(l2Norm(0.5 * (b - a))));
+  }
 }
-- 
GitLab