Branch data Line data Source code
1 : :
2 : : #include <cmath>
3 : :
4 : : namespace ippl {
5 : : template <typename T, unsigned NumNodes1D, typename ElementType>
6 : 146 : GaussJacobiQuadrature<T, NumNodes1D, ElementType>::GaussJacobiQuadrature(
7 : : const ElementType& ref_element, const T& alpha, const T& beta,
8 : : const size_t& max_newton_iterations, const size_t& min_newton_iterations)
9 : : : Quadrature<T, NumNodes1D, ElementType>(ref_element)
10 : 146 : , alpha_m(alpha)
11 : 146 : , beta_m(beta)
12 : 146 : , max_newton_iterations_m(max_newton_iterations)
13 : 146 : , min_newton_iterations_m(min_newton_iterations) {
14 [ - + ]: 146 : assert(alpha > -1.0 && "alpha >= -1.0 is not satisfied");
15 [ - + ]: 146 : assert(beta > -1.0 && "beta >= -1.0 is not satisfied");
16 [ - + ]: 146 : assert(max_newton_iterations >= 1 && "max_newton_iterations >= 1 is not satisfied");
17 [ - + ]: 146 : assert(min_newton_iterations_m >= 1 && "min_newton_iterations_m >= 1 is not satisfied");
18 [ - + ]: 146 : assert(min_newton_iterations_m <= max_newton_iterations_m
19 : : && "min_newton_iterations_m <= max_newton_iterations_m is not satisfied");
20 : :
21 : 146 : this->degree_m = 2 * NumNodes1D - 1;
22 : :
23 : 146 : this->a_m = -1.0; // start of the domain
24 : 146 : this->b_m = 1.0; // end of the domain
25 : :
26 [ + - ]: 146 : this->integration_nodes_m = Vector<T, NumNodes1D>();
27 [ + - ]: 146 : this->weights_m = Vector<T, NumNodes1D>();
28 : :
29 [ + - ]: 146 : this->computeNodesAndWeights();
30 : 146 : }
31 : :
32 : : template <typename T, unsigned NumNodes1D, typename ElementType>
33 : : typename GaussJacobiQuadrature<T, NumNodes1D, ElementType>::scalar_t
34 : 622 : GaussJacobiQuadrature<T, NumNodes1D, ElementType>::getChebyshevNodes(const size_t& i) const {
35 : 622 : return -Kokkos::cos((2.0 * static_cast<scalar_t>(i) + 1.0) * Kokkos::numbers::pi_v<scalar_t>
36 : 622 : / (2.0 * NumNodes1D));
37 : : }
38 : :
39 : : template <typename T, unsigned NumNodes1D, typename ElementType>
40 : : typename GaussJacobiQuadrature<T, NumNodes1D, ElementType>::scalar_t
41 : 0 : GaussJacobiQuadrature<T, NumNodes1D, ElementType>::getLehrFEMInitialGuess(
42 : : const size_t& i,
43 : : const Vector<GaussJacobiQuadrature<T, NumNodes1D, ElementType>::scalar_t, NumNodes1D>&
44 : : integration_nodes) const {
45 : 0 : const scalar_t alpha = this->alpha_m;
46 : 0 : const scalar_t beta = this->beta_m;
47 : : scalar_t r1;
48 : : scalar_t r2;
49 : : scalar_t r3;
50 [ # # ]: 0 : scalar_t z = (i > 0) ? integration_nodes[i - 1] : 0.0;
51 : :
52 [ # # ]: 0 : if (i == 0) {
53 : : // initial guess for the largest root
54 : 0 : const scalar_t an = alpha / NumNodes1D;
55 : 0 : const scalar_t bn = beta / NumNodes1D;
56 : 0 : r1 = (1.0 + alpha) * (2.78 / (4.0 + NumNodes1D * NumNodes1D) + 0.768 * an / NumNodes1D);
57 : 0 : r2 = 1.0 + 1.48 * an + 0.96 * bn + 0.452 * an * an + 0.83 * an * bn;
58 : 0 : z = 1.0 - r1 / r2;
59 [ # # ]: 0 : } else if (i == 1) {
60 : : // initial guess for the second largest root
61 : 0 : r1 = (4.1 + alpha) / ((1.0 + alpha) * (1.0 + 0.156 * alpha));
62 : 0 : r2 = 1.0 + 0.06 * (NumNodes1D - 8.0) * (1.0 + 0.12 * alpha) / NumNodes1D;
63 : 0 : r3 = 1.0 + 0.012 * beta * (1.0 + 0.25 * Kokkos::abs(alpha)) / NumNodes1D;
64 : 0 : z -= (1.0 - z) * r1 * r2 * r3;
65 [ # # ]: 0 : } else if (i == 2) {
66 : : // initial guess for the third largest root
67 : 0 : r1 = (1.67 + 0.28 * alpha) / (1.0 + 0.37 * alpha);
68 : 0 : r2 = 1.0 + 0.22 * (NumNodes1D - 8.0) / NumNodes1D;
69 : 0 : r3 = 1.0 + 8.0 * beta / ((6.28 + beta) * NumNodes1D * NumNodes1D);
70 : 0 : z -= (integration_nodes[0] - z) * r1 * r2 * r3;
71 [ # # ]: 0 : } else if (i == NumNodes1D - 2) {
72 : : // initial guess for the second smallest root
73 : 0 : r1 = (1.0 + 0.235 * beta) / (0.766 + 0.119 * beta);
74 : 0 : r2 = 1.0 / (1.0 + 0.639 * (NumNodes1D - 4.0) / (1.0 + 0.71 * (NumNodes1D - 4.0)));
75 : 0 : r3 = 1.0 / (1.0 + 20.0 * alpha / ((7.5 + alpha) * NumNodes1D * NumNodes1D));
76 : 0 : z += (z - integration_nodes[NumNodes1D - 4]) * r1 * r2 * r3;
77 [ # # ]: 0 : } else if (i == NumNodes1D - 1) {
78 : : // initial guess for the smallest root
79 : 0 : r1 = (1.0 + 0.37 * beta) / (1.67 + 0.28 * beta);
80 : 0 : r2 = 1.0 / (1.0 + 0.22 * (NumNodes1D - 8.0) / NumNodes1D);
81 : 0 : r3 = 1.0 / (1.0 + 8.0 * alpha / ((6.28 + alpha) * NumNodes1D * NumNodes1D));
82 : 0 : z += (z - integration_nodes[NumNodes1D - 3]) * r1 * r2 * r3;
83 : : } else {
84 : : // initial guess for the other integration_nodes
85 : 0 : z = 3.0 * z - 3.0 * integration_nodes[i - 2] + integration_nodes[i - 3];
86 : : }
87 : 0 : return z;
88 : : }
89 : :
90 : : template <typename T, unsigned NumNodes1D, typename ElementType>
91 : 146 : void GaussJacobiQuadrature<T, NumNodes1D, ElementType>::computeNodesAndWeights() {
92 : : // set the initial guess type
93 : 146 : const InitialGuessType& initial_guess_type = InitialGuessType::Chebyshev;
94 : :
95 : : /**
96 : : * the following algorithm for computing the roots and weights for the Gauss-Jacobi
97 : : * quadrature has been taken from LehrFEM++ (MIT License)
98 : : * https://craffael.github.io/lehrfempp/gauss__quadrature_8cc_source.html
99 : : */
100 : :
101 : 146 : const scalar_t tolerance = 2e-16;
102 : :
103 [ + - ]: 146 : Vector<scalar_t, NumNodes1D> integration_nodes;
104 [ + - ]: 146 : Vector<scalar_t, NumNodes1D> weights;
105 : :
106 : 146 : const scalar_t alpha = this->alpha_m;
107 : 146 : const scalar_t beta = this->beta_m;
108 : :
109 : 146 : const scalar_t alfbet = alpha + beta;
110 : :
111 : : scalar_t a;
112 : : scalar_t b;
113 : : scalar_t c;
114 : : scalar_t p1;
115 : : scalar_t p2;
116 : : scalar_t p3;
117 : : scalar_t pp;
118 : : scalar_t temp;
119 : : scalar_t z;
120 : : scalar_t z1;
121 : :
122 : : // Compute the root of the Jacobi polynomial
123 : :
124 [ + + ]: 712 : for (size_t i = 0; i < NumNodes1D; ++i) {
125 : : // initial guess depending on which root we are computing
126 [ - + ]: 566 : if (initial_guess_type == InitialGuessType::LehrFEM) {
127 [ # # ]: 0 : z = this->getLehrFEMInitialGuess(i, integration_nodes);
128 [ + - ]: 566 : } else if (initial_guess_type == InitialGuessType::Chebyshev) {
129 : 566 : z = -this->getChebyshevNodes(i);
130 : : } else {
131 [ # # # # : 0 : throw IpplException("GaussJacobiQuadrature::computeNodesAndWeights",
# # ]
132 : : "Unknown initial guess type");
133 : : }
134 : :
135 : : // std::cout << NumNodes1D - i - 1 << ", initial guess: " << z << " with "
136 : : // << initial_guess_type << std::endl;
137 : :
138 : 566 : size_t its = 1;
139 : : do {
140 : : // refinement by Newton's method (from LehrFEM++)
141 : 2352 : temp = 2.0 + alfbet;
142 : :
143 : : // Start the recurrence with P_0 and P1 to avoid a division by zero when
144 : : // alpha * beta = 0 or -1
145 : 2352 : p1 = (alpha - beta + temp * z) / 2.0;
146 : 2352 : p2 = 1.0;
147 [ + + ]: 10984 : for (size_t j = 2; j <= NumNodes1D; ++j) {
148 : 8632 : p3 = p2;
149 : 8632 : p2 = p1;
150 : 8632 : temp = 2 * j + alfbet;
151 : 8632 : a = 2 * j * (j + alfbet) * (temp - 2.0);
152 : 8632 : b = (temp - 1.0) * (alpha * alpha - beta * beta + temp * (temp - 2.0) * z);
153 : 8632 : c = 2.0 * (j - 1 + alpha) * (j - 1 + beta) * temp;
154 : 8632 : p1 = (b * p2 - c * p3) / a;
155 : : }
156 : 2352 : pp = (NumNodes1D * (alpha - beta - temp * z) * p1
157 : 2352 : + 2.0 * (NumNodes1D + alpha) * (NumNodes1D + beta) * p2)
158 : 2352 : / (temp * (1.0 - z * z));
159 : : // p1 is now the desired jacobian polynomial. We next compute pp, its
160 : : // derivative, by a standard relation involving p2, the polynomial of one
161 : : // lower order
162 : 2352 : z1 = z;
163 : 2352 : z = z1 - p1 / pp; // Newtons Formula
164 : :
165 : : // std::cout << "it = " << its << ", i = " << i << ", error: " << Kokkos::abs(z -
166 : : // z1)
167 : : // << std::endl;
168 [ + + + + : 2352 : if (its > this->min_newton_iterations_m && Kokkos::abs(z - z1) <= tolerance) {
+ + ]
169 : 566 : break;
170 : : }
171 : 1786 : ++its;
172 [ + - ]: 1786 : } while (its <= this->max_newton_iterations_m);
173 : :
174 [ - + ]: 566 : if (its > this->max_newton_iterations_m) {
175 : : // TODO switch to inform
176 [ # # # # ]: 0 : std::cout << "Root " << NumNodes1D - i - 1
177 [ # # ]: 0 : << " didn't converge. Tolerance may be too high for data type"
178 [ # # ]: 0 : << std::endl;
179 : : }
180 : :
181 : : // std::cout << "i = " << i << ", result after " << its << " iterations: " << z
182 : : // << std::endl;
183 : :
184 : 566 : integration_nodes[i] = z;
185 : :
186 : : // Compute the weight of the Gauss-Jacobi quadrature
187 : 1132 : weights[i] =
188 : 1132 : Kokkos::exp(Kokkos::lgamma(alpha + NumNodes1D) + Kokkos::lgamma(beta + NumNodes1D)
189 : 566 : - Kokkos::lgamma(NumNodes1D + 1.)
190 : 566 : - Kokkos::lgamma(static_cast<double>(NumNodes1D) + alfbet + 1.0))
191 : 566 : * temp * Kokkos::pow(2.0, alfbet) / (pp * p2);
192 : :
193 : : // store the integration nodes and weights in the correct order
194 : 566 : this->integration_nodes_m[i] = static_cast<T>(-integration_nodes[i]);
195 : 566 : this->weights_m[i] = static_cast<T>(weights[i]);
196 : : }
197 : 146 : }
198 : :
199 : : } // namespace ippl
|