From 8aaf552aea9f409e5dab14ff705b98ebd7f4bfe8 Mon Sep 17 00:00:00 2001
From: Dmitri Naumov <github@naumov.de>
Date: Fri, 24 Jul 2020 19:39:22 +0200
Subject: [PATCH] Deduplicate code. Use computeShapeMatrices().

---
 NumLib/Function/Interpolation.h               | 38 +++++++-----
 .../ComponentTransportFEM.h                   | 23 +++----
 ProcessLib/HT/HTFEM.h                         | 15 ++---
 .../LiquidFlowLocalAssembler-impl.h           | 15 ++---
 .../SteadyStateDiffusionFEM.h                 | 15 ++---
 Tests/NumLib/NaturalNodeCoordinates.cpp       | 31 +++++-----
 Tests/NumLib/TestFe.cpp                       | 25 +++++---
 Tests/NumLib/TestGradShapeFunction.cpp        | 62 ++++++++-----------
 8 files changed, 106 insertions(+), 118 deletions(-)

diff --git a/NumLib/Function/Interpolation.h b/NumLib/Function/Interpolation.h
index 06408b1939b..0d6bb7b6ae6 100644
--- a/NumLib/Function/Interpolation.h
+++ b/NumLib/Function/Interpolation.h
@@ -15,6 +15,7 @@
 
 #include "NumLib/Fem/CoordinatesMapping/NaturalNodeCoordinates.h"
 #include "NumLib/Fem/FiniteElement/TemplateIsoparametric.h"
+#include "NumLib/Fem/InitShapeMatrices.h"
 
 namespace NumLib
 {
@@ -108,11 +109,7 @@ void interpolateToHigherOrderNodes(
 
     using SF = LowerOrderShapeFunction;
     using ShapeMatricesType = ShapeMatrixPolicyType<SF, GlobalDim>;
-    using ShapeMatrices = typename ShapeMatricesType::ShapeMatrices;
 
-    auto const fe =
-        NumLib::createIsoparametricFiniteElement<SF, ShapeMatricesType>(
-            element);
     int const number_base_nodes = element.getNumberOfBaseNodes();
     int const number_all_nodes = element.getNumberOfNodes();
 
@@ -123,21 +120,32 @@ void interpolateToHigherOrderNodes(
         interpolated_values_global_vector[global_index] = node_values[n];
     }
 
-    // Shape matrices storage reused in the interpolation loop.
-    ShapeMatrices shape_matrices{SF::DIM, GlobalDim, SF::NPOINTS};
-
+    //
     // Interpolate values for higher order nodes.
-    for (int n = number_base_nodes; n < number_all_nodes; ++n)
+    //
+
+    int const number_higher_order_nodes = number_all_nodes - number_base_nodes;
+    std::vector<MathLib::Point3d> higher_order_nodes;
+    higher_order_nodes.reserve(number_higher_order_nodes);
+    for (int n = 0; n < number_higher_order_nodes; ++n)
     {
-        // Evaluated at higher order nodes' coordinates.
-        fe.template computeShapeFunctions<ShapeMatrixType::N>(
-            NaturalCoordinates<HigherOrderMeshElementType>::coordinates[n]
-                .data(),
-            shape_matrices, GlobalDim, is_axially_symmetric);
+        higher_order_nodes.emplace_back(
+            NaturalCoordinates<HigherOrderMeshElementType>::coordinates
+                [number_base_nodes + n]);
+    }
 
-        std::size_t const global_index = element.getNodeIndex(n);
+    // Shape matrices evaluated at higher order nodes' coordinates.
+    auto const shape_matrices =
+        computeShapeMatrices<SF, ShapeMatricesType, GlobalDim,
+                             ShapeMatrixType::N>(element, is_axially_symmetric,
+                                                 higher_order_nodes);
+
+    for (int n = 0; n < number_higher_order_nodes; ++n)
+    {
+        std::size_t const global_index =
+            element.getNodeIndex(number_base_nodes + n);
         interpolated_values_global_vector[global_index] =
-            shape_matrices.N * node_values;
+            shape_matrices[n].N * node_values;
     }
 }
 
diff --git a/ProcessLib/ComponentTransport/ComponentTransportFEM.h b/ProcessLib/ComponentTransport/ComponentTransportFEM.h
index 2c3b5f5d516..235e8d0f147 100644
--- a/ProcessLib/ComponentTransport/ComponentTransportFEM.h
+++ b/ProcessLib/ComponentTransport/ComponentTransportFEM.h
@@ -854,21 +854,14 @@ public:
         auto const local_C = Eigen::Map<const NodalVectorType>(
             &local_x[first_concentration_index], concentration_size);
 
-        // eval shape matrices at given point
-        auto const shape_matrices = [&]() {
-            auto const fe = NumLib::createIsoparametricFiniteElement<
-                ShapeFunction, ShapeMatricesType>(_element);
-
-            typename ShapeMatricesType::ShapeMatrices sm(
-                ShapeFunction::DIM, GlobalDim, ShapeFunction::NPOINTS);
-
-            // Note: Axial symmetry is set to false here, because we only need
-            // dNdx here, which is not affected by axial symmetry.
-            fe.template computeShapeFunctions<NumLib::ShapeMatrixType::DNDX>(
-                pnt_local_coords.getCoords(), sm, GlobalDim, false);
-
-            return sm;
-        }();
+        // Eval shape matrices at given point
+        // Note: Axial symmetry is set to false here, because we only need dNdx
+        // here, which is not affected by axial symmetry.
+        auto const shape_matrices =
+            NumLib::computeShapeMatrices<ShapeFunction, ShapeMatricesType,
+                                         GlobalDim>(
+                _element, false /*is_axially_symmetric*/,
+                std::array{pnt_local_coords})[0];
 
         ParameterLib::SpatialPosition pos;
         pos.setElementID(_element.getID());
diff --git a/ProcessLib/HT/HTFEM.h b/ProcessLib/HT/HTFEM.h
index 701d448e400..643a114e917 100644
--- a/ProcessLib/HT/HTFEM.h
+++ b/ProcessLib/HT/HTFEM.h
@@ -95,17 +95,14 @@ public:
                             double const t,
                             std::vector<double> const& local_x) const override
     {
-        // eval dNdx and invJ at given point
-        auto const fe = NumLib::createIsoparametricFiniteElement<
-            ShapeFunction, ShapeMatricesType>(_element);
-
-        typename ShapeMatricesType::ShapeMatrices shape_matrices(
-            ShapeFunction::DIM, GlobalDim, ShapeFunction::NPOINTS);
-
+        // Eval shape matrices at given point
         // Note: Axial symmetry is set to false here, because we only need dNdx
         // here, which is not affected by axial symmetry.
-        fe.computeShapeFunctions(pnt_local_coords.getCoords(), shape_matrices,
-                                 GlobalDim, false);
+        auto const shape_matrices =
+            NumLib::computeShapeMatrices<ShapeFunction, ShapeMatricesType,
+                                         GlobalDim>(
+                _element, false /*is_axially_symmetric*/,
+                std::array{pnt_local_coords})[0];
 
         ParameterLib::SpatialPosition pos;
         pos.setElementID(this->_element.getID());
diff --git a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
index 5b4922097f5..56e483bea27 100644
--- a/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
+++ b/ProcessLib/LiquidFlow/LiquidFlowLocalAssembler-impl.h
@@ -76,18 +76,13 @@ LiquidFlowLocalAssembler<ShapeFunction, IntegrationMethod, GlobalDim>::getFlux(
     // extension of getFlux interface
     double const dt = std::numeric_limits<double>::quiet_NaN();
 
-    // eval dNdx and invJ at p
-    auto const fe =
-        NumLib::createIsoparametricFiniteElement<ShapeFunction,
-                                                 ShapeMatricesType>(_element);
-
-    typename ShapeMatricesType::ShapeMatrices shape_matrices(
-        ShapeFunction::DIM, GlobalDim, ShapeFunction::NPOINTS);
-
     // Note: Axial symmetry is set to false here, because we only need dNdx
     // here, which is not affected by axial symmetry.
-    fe.computeShapeFunctions(p_local_coords.getCoords(), shape_matrices,
-                             GlobalDim, false);
+    auto const shape_matrices =
+        NumLib::computeShapeMatrices<ShapeFunction, ShapeMatricesType,
+                                     GlobalDim>(_element,
+                                                false /*is_axially_symmetric*/,
+                                                std::array{p_local_coords})[0];
 
     // create pos object to access the correct media property
     ParameterLib::SpatialPosition pos;
diff --git a/ProcessLib/SteadyStateDiffusion/SteadyStateDiffusionFEM.h b/ProcessLib/SteadyStateDiffusion/SteadyStateDiffusionFEM.h
index 2bcbabbc3b8..4cb5664408e 100644
--- a/ProcessLib/SteadyStateDiffusion/SteadyStateDiffusionFEM.h
+++ b/ProcessLib/SteadyStateDiffusion/SteadyStateDiffusionFEM.h
@@ -136,17 +136,14 @@ public:
         // extension of getFlux interface
         double const dt = std::numeric_limits<double>::quiet_NaN();
 
-        // eval dNdx and invJ at p
-        auto const fe = NumLib::createIsoparametricFiniteElement<
-            ShapeFunction, ShapeMatricesType>(_element);
-
-        typename ShapeMatricesType::ShapeMatrices shape_matrices(
-            ShapeFunction::DIM, GlobalDim, ShapeFunction::NPOINTS);
-
+        // Eval shape matrices at given point
         // Note: Axial symmetry is set to false here, because we only need dNdx
         // here, which is not affected by axial symmetry.
-        fe.computeShapeFunctions(p_local_coords.getCoords(), shape_matrices,
-                                 GlobalDim, false);
+        auto const shape_matrices =
+            NumLib::computeShapeMatrices<ShapeFunction, ShapeMatricesType,
+                                         GlobalDim>(
+                _element, false /*is_axially_symmetric*/,
+                std::array{p_local_coords})[0];
 
         // fetch hydraulic conductivity
         ParameterLib::SpatialPosition pos;
diff --git a/Tests/NumLib/NaturalNodeCoordinates.cpp b/Tests/NumLib/NaturalNodeCoordinates.cpp
index 6166382d347..93d44ad9310 100644
--- a/Tests/NumLib/NaturalNodeCoordinates.cpp
+++ b/Tests/NumLib/NaturalNodeCoordinates.cpp
@@ -7,15 +7,16 @@
  *
  */
 
+#include "NumLib/Fem/CoordinatesMapping/NaturalNodeCoordinates.h"
+
 #include <gtest/gtest.h>
 
 #include <vector>
 
 #include "MeshLib/ElementCoordinatesMappingLocal.h"
-
-#include "NumLib/Fem/CoordinatesMapping/NaturalNodeCoordinates.h"
 #include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h"
 #include "NumLib/Fem/FiniteElement/C0IsoparametricElements.h"
+#include "NumLib/Fem/InitShapeMatrices.h"
 #include "NumLib/Fem/Integration/GaussLegendreIntegrationPolicy.h"
 #include "NumLib/Fem/ShapeMatrixPolicy.h"
 
@@ -25,8 +26,6 @@ template <typename ShapeFunction, int GlobalDim>
 bool test(MeshLib::Element const& element)
 {
     using ShapeMatricesType = ShapeMatrixPolicyType<ShapeFunction, GlobalDim>;
-    using FemType =
-        NumLib::TemplateIsoparametric<ShapeFunction, ShapeMatricesType>;
 
     // Test if the cast is possible.
     bool const dynamic_cast_failed =
@@ -37,29 +36,31 @@ bool test(MeshLib::Element const& element)
     {
         return false;
     }
-    FemType fe{
-        static_cast<typename ShapeFunction::MeshElement const&>(element)};
-
-    bool result = true;
 
     // For each node evaluate the shape functions at natural coordinates and
     // test if only the corresponding shape function has value 1 and all others
     // must return 0.
     int const number_nodes = element.getNumberOfNodes();
+    std::vector<MathLib::Point3d> points;
+    points.reserve(number_nodes);
     for (int n = 0; n < number_nodes; ++n)
     {
         // Evaluate shape matrices at natural coordinates.
-        typename ShapeMatricesType::ShapeMatrices shape_matrices{
-            ShapeFunction::DIM, GlobalDim, ShapeFunction::NPOINTS};
         // Compute only N, because for pyramid the detJ becomes zero at the
         // apex, and we only use N in the following test anyway.
-        fe.template computeShapeFunctions<NumLib::ShapeMatrixType::N>(
+        points.emplace_back(
             NumLib::NaturalCoordinates<
-                typename ShapeFunction::MeshElement>::coordinates[n]
-                .data(),
-            shape_matrices, GlobalDim, false /* axial symmetry */);
+                typename ShapeFunction::MeshElement>::coordinates[n]);
+    }
+    auto const shape_matrices =
+        NumLib::computeShapeMatrices<ShapeFunction, ShapeMatricesType,
+                                     GlobalDim, NumLib::ShapeMatrixType::N>(
+            element, false /*is_axially_symmetric*/, points);
 
-        auto const& N = shape_matrices.N;
+    bool result = true;
+    for (int n = 0; n < number_nodes; ++n)
+    {
+        auto const& N = shape_matrices[n].N;
         for (int p = 0; p < static_cast<int>(ShapeFunction::NPOINTS); ++p)
         {
             if (p == n)
diff --git a/Tests/NumLib/TestFe.cpp b/Tests/NumLib/TestFe.cpp
index 395f0b36950..d7b93505796 100644
--- a/Tests/NumLib/TestFe.cpp
+++ b/Tests/NumLib/TestFe.cpp
@@ -265,22 +265,22 @@ TYPED_TEST(NumLibFemIsoTest, CheckMassLaplaceMatrices)
 TYPED_TEST(NumLibFemIsoTest, CheckGaussLegendreIntegrationLevel)
 {
     // Refer to typedefs in the fixture
-    using FeType = typename TestFixture::FeType;
     using NodalMatrix = typename TestFixture::NodalMatrix;
-    using ShapeMatricesType = typename TestFixture::ShapeMatricesType;
 
-    // create a finite element object with gauss quadrature level 2
-    FeType fe(*this->mesh_element);
+    auto shape_matrices =
+        NumLib::initShapeMatrices<typename TestFixture::ShapeFunction,
+                                  typename TestFixture::ShapeMatrixTypes,
+                                  TestFixture::global_dim>(
+            *this->mesh_element, false /*is_axially_symmetric*/,
+            this->integration_method);
 
     // evaluate a mass matrix
     NodalMatrix M(this->e_nnodes, this->e_nnodes);
     M.setZero();
-    ShapeMatricesType shape(this->dim, this->global_dim, this->e_nnodes);
     ASSERT_EQ(TestFixture::n_sample_pt_order2, this->integration_method.getNumberOfPoints());
     for (std::size_t i=0; i < this->integration_method.getNumberOfPoints(); i++) {
-        shape.setZero();
+        auto const& shape = shape_matrices[i];
         auto wp = this->integration_method.getWeightedPoint(i);
-        fe.computeShapeFunctions(wp.getCoords(), shape);
         M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight();
     }
     //std::cout << "M=\n" << M << std::endl;
@@ -290,10 +290,17 @@ TYPED_TEST(NumLibFemIsoTest, CheckGaussLegendreIntegrationLevel)
     this->integration_method.setIntegrationOrder(3);
     M *= .0;
     ASSERT_EQ(TestFixture::n_sample_pt_order3, this->integration_method.getNumberOfPoints());
+
+    shape_matrices =
+        NumLib::initShapeMatrices<typename TestFixture::ShapeFunction,
+                                  typename TestFixture::ShapeMatrixTypes,
+                                  TestFixture::global_dim>(
+            *this->mesh_element, false /*is_axially_symmetric*/,
+            this->integration_method);
+
     for (std::size_t i=0; i < this->integration_method.getNumberOfPoints(); i++) {
-        shape.setZero();
+        auto const& shape = shape_matrices[i];
         auto wp = this->integration_method.getWeightedPoint(i);
-        fe.computeShapeFunctions(wp.getCoords(), shape);
         M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight();
     }
     //std::cout << "M=\n" << M << std::endl;
diff --git a/Tests/NumLib/TestGradShapeFunction.cpp b/Tests/NumLib/TestGradShapeFunction.cpp
index d8457ecc3fe..daaf40b3f50 100644
--- a/Tests/NumLib/TestGradShapeFunction.cpp
+++ b/Tests/NumLib/TestGradShapeFunction.cpp
@@ -14,36 +14,33 @@
 
 #include <gtest/gtest.h>
 
-#include <vector>
-#include <cmath>
-
 #include <Eigen/Eigen>
+#include <cmath>
+#include <vector>
 
-#include "MeshLib/ElementCoordinatesMappingLocal.h"
-#include "MeshLib/CoordinateSystem.h"
-
-#include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h"
-#include "NumLib/Fem/FiniteElement/C0IsoparametricElements.h"
-#include "NumLib/Fem/Integration/GaussLegendreIntegrationPolicy.h"
-#include "NumLib/Fem/ShapeMatrixPolicy.h"
-
+#include "FeTestData/TestFeHEX20.h"
+#include "FeTestData/TestFeHEX8.h"
 #include "FeTestData/TestFeLINE2.h"
 #include "FeTestData/TestFeLINE2Y.h"
 #include "FeTestData/TestFeLINE3.h"
-#include "FeTestData/TestFeTRI3.h"
-#include "FeTestData/TestFeTRI6.h"
+#include "FeTestData/TestFePRISM15.h"
+#include "FeTestData/TestFePRISM6.h"
+#include "FeTestData/TestFePYRA13.h"
+#include "FeTestData/TestFePYRA5.h"
 #include "FeTestData/TestFeQUAD4.h"
 #include "FeTestData/TestFeQUAD8.h"
 #include "FeTestData/TestFeQUAD9.h"
-#include "FeTestData/TestFeHEX8.h"
-#include "FeTestData/TestFeHEX20.h"
-#include "FeTestData/TestFeTET4.h"
 #include "FeTestData/TestFeTET10.h"
-#include "FeTestData/TestFePRISM6.h"
-#include "FeTestData/TestFePRISM15.h"
-#include "FeTestData/TestFePYRA5.h"
-#include "FeTestData/TestFePYRA13.h"
-
+#include "FeTestData/TestFeTET4.h"
+#include "FeTestData/TestFeTRI3.h"
+#include "FeTestData/TestFeTRI6.h"
+#include "MeshLib/CoordinateSystem.h"
+#include "MeshLib/ElementCoordinatesMappingLocal.h"
+#include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h"
+#include "NumLib/Fem/FiniteElement/C0IsoparametricElements.h"
+#include "NumLib/Fem/InitShapeMatrices.h"
+#include "NumLib/Fem/Integration/GaussLegendreIntegrationPolicy.h"
+#include "NumLib/Fem/ShapeMatrixPolicy.h"
 #include "Tests/TestTools.h"
 
 using namespace NumLib;
@@ -110,11 +107,6 @@ public:
     // Finite element type
     template <typename X>
     using ShapeMatrixPolicy = typename T::template ShapeMatrixPolicy<X>;
-    using FeType =
-        typename TestFeType::template FeType<ShapeMatrixPolicy>::type;
-
-    // Shape matrix data type
-    using ShapeMatricesType = typename ShapeMatrixTypes::ShapeMatrices;
     using MeshElementType = typename TestFeType::MeshElementType;
 
     static const unsigned dim = TestFeType::dim;
@@ -182,23 +174,21 @@ TYPED_TEST_CASE(GradShapeFunctionTest, TestTypes);
 TYPED_TEST(GradShapeFunctionTest,
            CheckGradShapeFunctionByComputingElementVolume)
 {
-    // Refer to typedefs in the fixture
-    using FeType = typename TestFixture::FeType;
-    using ShapeMatricesType = typename TestFixture::ShapeMatricesType;
-
-    // create a finite element object
-    FeType fe(*this->mesh_element);
+    auto const shape_matrices =
+        NumLib::initShapeMatrices<typename TestFixture::ShapeFunction,
+                                  typename TestFixture::ShapeMatrixTypes,
+                                  TestFixture::global_dim,
+                                  ShapeMatrixType::N_J>(
+            *this->mesh_element, false /*is_axially_symmetric*/,
+            this->integration_method);
 
     // Compute element volume numerically as V_e = int {1}dA_e
     double computed_element_volume = 0.;
-    ShapeMatricesType shape(this->dim, this->global_dim, this->e_nnodes);
     for (std::size_t i = 0; i < this->integration_method.getNumberOfPoints();
          i++)
     {
-        shape.setZero();
+        auto const& shape = shape_matrices[i];
         auto wp = this->integration_method.getWeightedPoint(i);
-        fe.template computeShapeFunctions<ShapeMatrixType::N_J>(
-            wp.getCoords(), shape, this->global_dim, false);
         computed_element_volume += shape.detJ * wp.getWeight();
     }
 
-- 
GitLab