LehrFEM++ 1.0.0
A simple Finite Element Library for teaching
Loading...
Searching...
No Matches
gauss_quadrature.cc
1
10#include "gauss_quadrature.h"
11
12#include <boost/math/special_functions/gamma.hpp>
13#include <boost/multiprecision/cpp_bin_float.hpp>
14
15namespace lf::quad {
16std::tuple<Eigen::VectorXd, Eigen::VectorXd> GaussLegendre(
17 unsigned num_points) {
18 LF_ASSERT_MSG(num_points > 0, "num_points must be positive.");
19
20 using scalar_t =
21 boost::multiprecision::number<boost::multiprecision::cpp_bin_float<
22 57, boost::multiprecision::digit_base_2>>;
23
24 static const scalar_t kPi = boost::math::constants::pi<scalar_t>();
25
26 Eigen::VectorXd points(num_points);
27 Eigen::VectorXd weights(num_points);
28
29 // the roots are symmetric in the interval, so we only have to find half of
30 // them
31 const unsigned int m = (num_points + 1) / 2;
32
33 // approximation for the roots:
34 for (unsigned int i = 0; i < m; ++i) {
35 // initial guess for the i-th root:
36 scalar_t z = cos(kPi * (i + 0.75) / (num_points + 0.5));
37 scalar_t z1;
38 scalar_t pp;
39
40 // start of newton
41 do {
42 // calculate value of legendre polynomial at z using recurrence relation:
43 scalar_t p1 = 1.0;
44 scalar_t p2 = 0.0;
45 scalar_t p3;
46
47 for (unsigned int j = 0; j < num_points; ++j) {
48 p3 = p2;
49 p2 = p1;
50 p1 = ((2.0 * j + 1.) * z * p2 - j * p3) / (j + 1.);
51 }
52
53 // p1 is now the value of the legendre polynomial at z. Next we compute
54 // its derivative using a standard relation involving also p2, the
55 // polynomial of one lower order:
56 pp = num_points * (z * p1 - p2) / (z * z - 1.0);
57
58 z1 = z;
59 z = z1 - p1 / pp;
60 } while (abs(z - z1) > 1e-17);
61
62 points(i) = (0.5 * (1 - z)).convert_to<double>();
63 points(num_points - 1 - i) = (0.5 * (1 + z)).convert_to<double>();
64 weights(i) = (1. / ((1.0 - z * z) * pp * pp)).convert_to<double>();
65 weights(num_points - 1 - i) = weights(i);
66 }
67
68 return {std::move(points), std::move(weights)};
69}
70
71std::tuple<Eigen::VectorXd, Eigen::VectorXd> GaussJacobi(
72 quadDegree_t num_points, double alpha, double beta) {
73 // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
74 LF_ASSERT_MSG(num_points > 0, "num_points must be positive.");
75 LF_ASSERT_MSG(alpha > -1, "alpha > -1 required");
76 LF_ASSERT_MSG(beta > -1, "beta > -1 required.");
77
78 using boost::math::lgamma;
79 using scalar_t =
80 boost::multiprecision::number<boost::multiprecision::cpp_bin_float<
81 57, boost::multiprecision::digit_base_2>>;
82
83 const int MAX_IT = 10;
84
85 scalar_t alfbet;
86 scalar_t an;
87 scalar_t bn;
88 scalar_t r1;
89 scalar_t r2;
90 scalar_t r3;
91 scalar_t a;
92 scalar_t b;
93 scalar_t c;
94 scalar_t p1;
95 scalar_t p2;
96 scalar_t p3;
97 scalar_t pp;
98 scalar_t temp;
99 scalar_t z;
100 scalar_t z1;
101
102 std::vector<scalar_t> points(num_points);
103 Eigen::VectorXd weights(num_points);
104
105 // Make an initial guess for the zeros:
106 for (quadDegree_t i = 0; i < num_points; ++i) {
107 if (i == 0) {
108 // initial guess for the largest root
109 an = alpha / num_points;
110 bn = beta / num_points;
111 r1 = (1.0 + alpha) *
112 (2.78 / (4.0 + num_points * num_points) + 0.768 * an / num_points);
113 r2 = 1.0 + 1.48 * an + 0.96 * bn + 0.452 * an * an + 0.83 * an * bn;
114 z = 1.0 - r1 / r2;
115 } else if (i == 1) {
116 // initial guess for the second largest root
117 r1 = (4.1 + alpha) / ((1.0 + alpha) * (1.0 + 0.156 * alpha));
118 r2 = 1.0 + 0.06 * (num_points - 8.0) * (1.0 + 0.12 * alpha) / num_points;
119 r3 = 1.0 + 0.012 * beta * (1.0 + 0.25 * std::abs(alpha)) / num_points;
120 z -= (1.0 - z) * r1 * r2 * r3;
121 } else if (i == 2) {
122 // initial guess for the third largest root
123 r1 = (1.67 + 0.28 * alpha) / (1.0 + 0.37 * alpha);
124 r2 = 1.0 + 0.22 * (num_points - 8.0) / num_points;
125 r3 = 1.0 + 8.0 * beta / ((6.28 + beta) * num_points * num_points);
126 z -= (points[0] - z) * r1 * r2 * r3;
127 } else if (i == num_points - 2) {
128 // initial guess for the second smallest root
129 r1 = (1.0 + 0.235 * beta) / (0.766 + 0.119 * beta);
130 r2 = 1.0 / (1.0 + 0.639 * (num_points - 4.0) /
131 (1.0 + 0.71 * (num_points - 4.0)));
132 r3 = 1.0 /
133 (1.0 + 20.0 * alpha / ((7.5 + alpha) * num_points * num_points));
134 z += (z - points[num_points - 4]) * r1 * r2 * r3;
135 } else if (i == num_points - 1) {
136 // initial guess for the smallest root
137 r1 = (1.0 + 0.37 * beta) / (1.67 + 0.28 * beta);
138 r2 = 1.0 / (1.0 + 0.22 * (num_points - 8.0) / num_points);
139 r3 = 1.0 /
140 (1.0 + 8.0 * alpha / ((6.28 + alpha) * num_points * num_points));
141 z += (z - points[num_points - 3]) * r1 * r2 * r3;
142 } else {
143 // initial guess for the other points
144 z = 3.0 * points[i - 1] - 3.0 * points[i - 2] + points[i - 3];
145 }
146 alfbet = alpha + beta;
147 quadDegree_t its;
148 for (its = 1; its <= MAX_IT; ++its) {
149 // refinement by Newton's method
150 temp = 2.0 + alfbet;
151
152 // Start the recurrence with P_0 and P1 to avoid a division by zero when
153 // alpha * beta = 0 or -1
154 p1 = (alpha - beta + temp * z) / 2.0;
155 p2 = 1.0;
156 for (quadDegree_t j = 2; j <= num_points; ++j) {
157 p3 = p2;
158 p2 = p1;
159 temp = 2 * j + alfbet;
160 a = 2 * j * (j + alfbet) * (temp - 2.0);
161 b = (temp - 1.0) *
162 (alpha * alpha - beta * beta + temp * (temp - 2.0) * z);
163 c = 2.0 * (j - 1 + alpha) * (j - 1 + beta) * temp;
164 p1 = (b * p2 - c * p3) / a;
165 }
166 pp = (num_points * (alpha - beta - temp * z) * p1 +
167 2.0 * (num_points + alpha) * (num_points + beta) * p2) /
168 (temp * (1.0 - z * z));
169 // p1 is now the desired jacobia polynomial. We next compute pp, its
170 // derivative, by a standard relation involving p2, the polynomial of one
171 // lower order
172 z1 = z;
173 z = z1 - p1 / pp; // Newtons Formula
174 if (abs(z - z1) <= 1e-17) {
175 break;
176 }
177 }
178 LF_VERIFY_MSG(its <= MAX_IT, "too many iterations.");
179
180 points[i] = z;
181
182 weights(i) = (exp(lgamma(alpha + num_points) + lgamma(beta + num_points) -
183 lgamma(num_points + 1.) -
184 lgamma(static_cast<double>(num_points) + alfbet + 1.0)) *
185 temp * pow(2.0, alfbet) / (pp * p2))
186 .convert_to<double>();
187 }
188
189 Eigen::VectorXd points_result(num_points);
190 for (quadDegree_t i = 0; i < num_points; ++i) {
191 points_result(i) = points[i].convert_to<double>();
192 }
193 // NOLINTEND(clang-analyzer-core.StackAddressEscape)
194
195 return {points_result, weights};
196}
197} // namespace lf::quad
Rules for numerical quadrature on reference entity shapes.
std::tuple< Eigen::VectorXd, Eigen::VectorXd > GaussJacobi(quadDegree_t num_points, double alpha, double beta)
Computes the quadrature points and weights for the interval [-1,1] of a Gauss-Jacobi quadrature rule ...
std::tuple< Eigen::VectorXd, Eigen::VectorXd > GaussLegendre(unsigned num_points)
unsigned int quadDegree_t
Definition quad_rule.h:19