From c61eb5cdec4ee89e014a2da04cb8dc590e640c70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Buchwald?= <joerg.buchwald@ufz.de>
Date: Thu, 4 Nov 2021 22:25:21 +0100
Subject: [PATCH] add integration order 4 for Tets

---
 MathLib/Integration/GaussLegendreTet.cpp      | 42 +++++++++++++++++++
 MathLib/Integration/GaussLegendreTet.h        |  9 ++++
 .../IntegrationGaussLegendrePyramid.h         |  1 +
 .../IntegrationGaussLegendreRegular.h         |  1 +
 .../Integration/IntegrationGaussLegendreTet.h |  5 +++
 .../MathLib/TestGaussLegendreIntegration.cpp  |  6 +--
 6 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/MathLib/Integration/GaussLegendreTet.cpp b/MathLib/Integration/GaussLegendreTet.cpp
index 593b251cb16..afa8e7e4672 100644
--- a/MathLib/Integration/GaussLegendreTet.cpp
+++ b/MathLib/Integration/GaussLegendreTet.cpp
@@ -65,4 +65,46 @@ static const double r = 0.0425460207770812 / 6.;
 
 double const GaussLegendreTet<3>::W[GaussLegendreTet<3>::NPoints] = {
     p, p, p, p, q, q, q, q, r, r, r, r, r, r};
+
+static std::array<std::array<double, 3>, GaussLegendreTet<4>::NPoints>
+initGLTet4X()
+{
+    // Cf. Gellert, M., Harbord, R., 1991. Moderate degree cubature formulas for
+    // 3-D tetrahedral finite-element approximations. Communications in Applied
+    // Numerical Methods 7, 487-495. doi:10.1002/cnm.1630070609
+    const double a = 0.3797582452067875;
+    const double b = 0.1202417547932126;
+
+    return {{{{0.0, 1. / 3, 1. / 3}},
+             {{1. / 3, 0.0, 1. / 3}},
+             {{1. / 3, 1. / 3, 0.0}},
+             {{1. / 3, 1. / 3, 1. / 3}},
+             {{8. / 11, 1. / 11, 1. / 11}},
+             {{1. / 11, 8. / 11, 1. / 11}},
+             {{1. / 11, 1. / 11, 8. / 11}},
+             {{1. / 11, 1. / 11, 1. / 11}},
+             {{0.0, 0.0, 0.5}},
+             {{0.0, 0.5, 0.0}},
+             {{0.0, 0.5, 0.5}},
+             {{0.5, 0.0, 0.0}},
+             {{0.5, 0.0, 0.5}},
+             {{0.5, 0.5, 0.0}},
+             {{a, a, b}},
+             {{a, b, a}},
+             {{a, b, b}},
+             {{b, a, a}},
+             {{b, a, b}},
+             {{b, b, a}}}};
+}
+const std::array<std::array<double, 3>, GaussLegendreTet<4>::NPoints>
+    GaussLegendreTet<4>::X = initGLTet4X();
+
+static const double s = 81. / 2240. / 6.;
+static const double t = 161051. / 2304960. / 6.;
+static const double u = 409. / 31395. / 6.;
+static const double v = 2679769. / 32305455. / 6.;
+
+double const GaussLegendreTet<4>::W[GaussLegendreTet<4>::NPoints] = {
+    s, s, s, s, t, t, t, t, u, u, u, u, u, u, v, v, v, v, v, v};
+
 }  // namespace MathLib
diff --git a/MathLib/Integration/GaussLegendreTet.h b/MathLib/Integration/GaussLegendreTet.h
index 9dd8370ed09..5897905ec63 100644
--- a/MathLib/Integration/GaussLegendreTet.h
+++ b/MathLib/Integration/GaussLegendreTet.h
@@ -44,6 +44,15 @@ struct GaussLegendreTet<3> {
     static MATHLIB_EXPORT const double W[NPoints];
 };
 
+template <>
+struct GaussLegendreTet<4>
+{
+    static MATHLIB_EXPORT const unsigned Order = 4;
+    static MATHLIB_EXPORT const unsigned NPoints = 20;
+    static MATHLIB_EXPORT const std::array<std::array<double, 3>, NPoints> X;
+    static MATHLIB_EXPORT const double W[NPoints];
+};
+
 #ifndef _MSC_VER  // The following explicit instantatiation declaration does not
                   // compile on that particular compiler but is necessary.
 template <>
diff --git a/NumLib/Fem/Integration/IntegrationGaussLegendrePyramid.h b/NumLib/Fem/Integration/IntegrationGaussLegendrePyramid.h
index 2a679dff4d7..088967d5b3b 100644
--- a/NumLib/Fem/Integration/IntegrationGaussLegendrePyramid.h
+++ b/NumLib/Fem/Integration/IntegrationGaussLegendrePyramid.h
@@ -10,6 +10,7 @@
 
 #pragma once
 
+#include "BaseLib/Error.h"
 #include "MathLib/Integration/GaussLegendrePyramid.h"
 #include "MathLib/TemplateWeightedPoint.h"
 
diff --git a/NumLib/Fem/Integration/IntegrationGaussLegendreRegular.h b/NumLib/Fem/Integration/IntegrationGaussLegendreRegular.h
index a9b70ba7bd1..b7c4d9cbc5f 100644
--- a/NumLib/Fem/Integration/IntegrationGaussLegendreRegular.h
+++ b/NumLib/Fem/Integration/IntegrationGaussLegendreRegular.h
@@ -15,6 +15,7 @@
 #include <array>
 #include <cmath>
 
+#include "BaseLib/Error.h"
 #include "MathLib/Integration/GaussLegendre.h"
 #include "MathLib/TemplateWeightedPoint.h"
 
diff --git a/NumLib/Fem/Integration/IntegrationGaussLegendreTet.h b/NumLib/Fem/Integration/IntegrationGaussLegendreTet.h
index ebbbcd887c8..1d911f427cf 100644
--- a/NumLib/Fem/Integration/IntegrationGaussLegendreTet.h
+++ b/NumLib/Fem/Integration/IntegrationGaussLegendreTet.h
@@ -10,6 +10,7 @@
 
 #pragma once
 
+#include "BaseLib/Error.h"
 #include "MathLib/Integration/GaussLegendreTet.h"
 #include "MathLib/TemplateWeightedPoint.h"
 
@@ -74,6 +75,8 @@ public:
                 return getWeightedPoint<MathLib::GaussLegendreTet<2>>(igp);
             case 3:
                 return getWeightedPoint<MathLib::GaussLegendreTet<3>>(igp);
+            case 4:
+                return getWeightedPoint<MathLib::GaussLegendreTet<4>>(igp);
         }
         OGS_FATAL("Integration order {:d} not implemented for tetrahedrals.",
                   order);
@@ -101,6 +104,8 @@ public:
                 return MathLib::GaussLegendreTet<2>::NPoints;
             case 3:
                 return MathLib::GaussLegendreTet<3>::NPoints;
+            case 4:
+                return MathLib::GaussLegendreTet<4>::NPoints;
         }
         OGS_FATAL("Integration order {:d} not implemented for tetrahedrals.",
                   order);
diff --git a/Tests/MathLib/TestGaussLegendreIntegration.cpp b/Tests/MathLib/TestGaussLegendreIntegration.cpp
index 5e6ff1b0e8c..d648130f5c6 100644
--- a/Tests/MathLib/TestGaussLegendreIntegration.cpp
+++ b/Tests/MathLib/TestGaussLegendreIntegration.cpp
@@ -447,7 +447,7 @@ OGS_DONT_TEST_THIS_IF_PETSC(MathLib, IntegrationGaussLegendreTet)
         MeshLib::IO::VtuInterface::readVTUFile(
             TestInfoLib::TestInfo::data_path + "/MathLib/unit_cube_tet.vtu"));
 
-    for (unsigned integration_order : {1, 2, 3})
+    for (unsigned integration_order : {1, 2, 3, 4})
     {
         DBUG("\n==== integration order: {:d}.\n", integration_order);
         GaussLegendreTest::IntegrationTestProcess pcs_tet(*mesh_tet,
@@ -508,7 +508,7 @@ OGS_DONT_TEST_THIS_IF_PETSC(
         MeshLib::IO::VtuInterface::readVTUFile(
             TestInfoLib::TestInfo::data_path + "/MathLib/unit_cube_tet.vtu"));
 
-    for (unsigned integration_order : {1, 2, 3})
+    for (unsigned integration_order : {1, 2, 3, 4})
     {
         DBUG("\n==== integration order: {:d}.\n", integration_order);
         GaussLegendreTest::IntegrationTestProcess pcs_tet(*mesh_tet,
@@ -564,7 +564,7 @@ OGS_DONT_TEST_THIS_IF_PETSC(MathLib,
         MeshLib::IO::VtuInterface::readVTUFile(
             TestInfoLib::TestInfo::data_path + "/MathLib/unit_cube_tet.vtu"));
 
-    for (unsigned integration_order : {1, 2, 3})
+    for (unsigned integration_order : {1, 2, 3, 4})
     {
         DBUG("\n==== integration order: {:d}.\n", integration_order);
         GaussLegendreTest::IntegrationTestProcess pcs_tet(*mesh_tet,
-- 
GitLab