hummingbird
Solving the self-adjoint angular flux transport equation using spectral elements on Cartesian geometry
Loading...
Searching...
No Matches
legendre_polynomials.cc
Go to the documentation of this file.
2
3#include <cmath>
4#include <iomanip>
5#include <limits>
6#include <numeric>
7#include <sstream>
8#include <stdexcept>
9
10#include "utils/constants.h"
11#include "utils/misc.h"
12
13namespace hummingbird {
14double LegendrePolynomial(const int n, const double x) {
15 switch (n) {
16 case 0:
17 return 1.0;
18
19 case 1:
20 return x;
21
22 default:
23 double previous = 1.0;
24 double current = x;
25 for (double j = 1; j < n; j++) {
26 double next =
27 ((2.0 * j + 1.0) * x * current - j * previous) / (j + 1.0);
28 previous = current;
29 current = next;
30 }
31 return current;
32 }
33}
34
35double LegendrePolynomialPrime(const int n, const double x) {
36 if (DoubleEqual(x, -1.0))
37 return std::pow(-1, n - 1) * n * (n + 1) / 2.0;
38 else if (DoubleEqual(x, 1.0))
39 return n * (n + 1) / 2.0;
40
41 switch (n) {
42 case 0:
43 return 0;
44
45 case 1:
46 return 1.0;
47
48 default:
49 return (static_cast<double>(n) * LegendrePolynomial(n - 1, x) -
50 static_cast<double>(n) * x * LegendrePolynomial(n, x)) /
51 (1.0 - x * x);
52 }
53}
54
55double LegendrePolynomialPrimePrime(const int n, const double x) {
56 return (2 * x * LegendrePolynomialPrime(n, x) -
57 n * (n + 1) * LegendrePolynomial(n, x)) /
58 (1.0 - x * x);
59}
60
61std::vector<double> AllLegendreRoots(const int n) {
62 unsigned int n_even =
63 static_cast<unsigned int>(std::floor(static_cast<double>(n) / 2.0));
64 std::vector<double> computed_roots(n);
65
66 for (auto i = 0; i < n_even; i++) {
67 computed_roots[n - 1 - i] = LegendreRoot(n, i);
68 computed_roots[i] = -computed_roots[n - 1 - i];
69 }
70 return computed_roots;
71}
72
73double LegendreRoot(const int n, const int k) {
74 double x_old = ApproximateLegendreRoot(n, k);
75 unsigned int safety = 10000;
76 for (auto i = 0; i < safety; i++) {
77 double x_new = x_old - LegendrePolynomial(n, x_old) /
79 double relative_error = std::abs((x_new - x_old) / std::max(1.0, x_new));
80 double backward_error = std::abs(LegendrePolynomial(n, x_new));
81
82 if (backward_error < 1e-8 && relative_error < TOLERANCE) return x_new;
83 x_old = x_new;
84 }
85 throw std::runtime_error("LegendreRoot did not converge.");
86}
87
88double ApproximateLegendreRoot(const int n, const int k) {
89 if (k > n) throw std::invalid_argument("k must be less than n.");
90
91 return std::cos((4.0 * static_cast<double>(k) + 3.0) /
92 (4.0 * static_cast<double>(n) + 2.0) * M_PI);
93}
94
95std::vector<double> AllLegendrePrimeRoots(const int n) {
96 unsigned int n_even =
97 static_cast<unsigned int>(std::floor(static_cast<double>(n - 1) / 2.0));
98 std::vector<double> computed_roots(n - 1);
99
100 for (auto i = 0; i < n_even; i++) {
101 computed_roots[i] = LegendrePrimeRoot(n, i);
102 computed_roots[n - 2 - i] = -computed_roots[i];
103 }
104 return computed_roots;
105}
106
107double LegendrePrimeRoot(const int n, const int k) {
108 double x_old = ApproximateLegendrePrimeRoot(n, k);
109 unsigned int safety = 100;
110
111 double relative_error = std::numeric_limits<double>::infinity();
112 double backward_error = std::numeric_limits<double>::infinity();
113 for (auto i = 0; i < safety; i++) {
114 double x_new = x_old - LegendrePolynomialPrime(n, x_old) /
116 relative_error = std::abs((x_new - x_old) / std::max(1.0, x_new));
117 backward_error = std::abs(LegendrePolynomialPrime(n, x_new));
118
119 if (backward_error < 1e-8 && relative_error < TOLERANCE) return x_new;
120 x_old = x_new;
121 }
122 std::ostringstream error_message;
123 error_message << std::scientific << std::setprecision(6)
124 << "LegendrePrimeRoot did not converge. "
125 << "Backward Error: " << backward_error
126 << ", Relative Error: " << relative_error;
127
128 throw std::runtime_error(error_message.str());
129}
130
131double ApproximateLegendrePrimeRoot(const int n, const int k) {
132 if (k >= n - 1) throw std::invalid_argument("k must be less than n-1.");
133 return -(ApproximateLegendreRoot(n, k) + ApproximateLegendreRoot(n, k + 1)) /
134 2.0;
135}
136
137} // namespace hummingbird
std::vector< double > AllLegendrePrimeRoots(const int n)
Computes all roots of the polynomial. A total of n-1 roots will be computed and returned.
double LegendreRoot(const int n, const int k)
Computes the k-th root of the n-th order Legendre polynomial by first approximating the root with App...
double LegendrePolynomialPrime(const int n, const double x)
Computes the first derivative of the Legendre polynomial of degree n at a point x using the recurrenc...
double LegendrePrimeRoot(const int n, const int k)
Computes the k-th root of the n-th order first derivative of the Legendre polynomial by first approxi...
double LegendrePolynomialPrimePrime(const int n, const double x)
Computes the second derivative of the Legendre polynomial of degree n using Legendre's differential e...
bool DoubleEqual(const double first, const double second, const double tolerance=TOLERANCE)
Tests if two double-type numbers are equal using the TOLERANCE value. Taken from https://github....
Definition misc.cc:9
double ApproximateLegendrePrimeRoot(const int n, const int k)
Approximates the k-th root of the first derivative of the n-th degree Legendre polynomial using the a...
double LegendrePolynomial(const int n, const double x)
Computes the Legendre polynomial of degree n at a point x using the formula:
std::vector< double > AllLegendreRoots(const int n)
Compute all roots of the Legendre polynomial of order n.
constexpr double TOLERANCE
General tolerance value for double comparisons.
Definition constants.h:10
double ApproximateLegendreRoot(const int n, const int k)
Approximates the k-th root of the n-th order Legendre polynomial with: