/home/runner/work/kynema/kynema/kynema/src/elements/beams/beam_quadrature.hpp Source File

Kynema API: /home/runner/work/kynema/kynema/kynema/src/elements/beams/beam_quadrature.hpp Source File
Kynema API
A flexible multibody structural dynamics code for wind turbines
Loading...
Searching...
No Matches
beam_quadrature.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <limits>
7#include <numbers>
8#include <ranges>
9#include <span>
10#include <stdexcept>
11#include <vector>
12
15
16namespace kynema::beams {
17
28inline std::vector<std::array<double, 2>> CreateTrapezoidalQuadrature(std::span<const double> grid) {
29 const auto n{grid.size()};
30 const auto [grid_min, grid_max] = std::ranges::minmax(grid);
31 const auto grid_range{grid_max - grid_min};
32 auto quadrature = std::vector<std::array<double, 2>>{
33 {-1., (grid[1] - grid[0]) / grid_range},
34 };
35 std::ranges::transform(
36 std::views::iota(1U, n - 1), std::back_inserter(quadrature),
37 [grid, gm = grid_min, grid_range](auto i) {
38 return std::array{
39 2. * (grid[i] - gm) / grid_range - 1., (grid[i + 1] - grid[i - 1]) / grid_range
40 };
41 }
42 );
43 quadrature.push_back({1., (grid[n - 1] - grid[n - 2]) / grid_range});
44 return quadrature;
45}
46
47inline std::vector<std::array<double, 2>> CreateGaussLegendreLobattoQuadrature(
48 std::span<const double> grid, size_t order
49) {
50 const auto n{grid.size()};
51 const auto [grid_min, grid_max] = std::ranges::minmax(grid);
52 const auto grid_range{grid_max - grid_min};
53 const auto sectional_weights = math::GetGllWeights(order);
54 const auto sectional_num_nodes = sectional_weights.size();
55 const auto num_sections = (n - 1) / (sectional_num_nodes - 1);
56
57 auto quadrature = std::vector<std::array<double, 2>>{};
58 std::ranges::transform(
59 grid, std::back_inserter(quadrature),
60 [gm = grid_min, grid_range](auto grid_location) {
61 return std::array{2. * (grid_location - gm) / grid_range - 1., 0.};
62 }
63 );
64
65 auto section_index = 0UL;
66 for ([[maybe_unused]] auto section : std::views::iota(0U, num_sections)) {
67 const auto section_range =
68 grid[section_index + sectional_num_nodes - 1] - grid[section_index];
69 const auto weight_scaling = section_range / grid_range;
70 for (auto node : std::views::iota(0U, sectional_num_nodes)) {
71 quadrature[section_index + node][1] += sectional_weights[node] * weight_scaling;
72 }
73 section_index += sectional_num_nodes - 1UL;
74 }
75 return quadrature;
76}
77
88inline std::vector<std::array<double, 2>> CreateGaussLegendreQuadrature(const size_t order) {
89 constexpr auto max_iterations = 1000U;
90 constexpr auto convergence_tolerance = std::numeric_limits<double>::epsilon();
91
92 // If order is 0, return the base case
93 if (order < 1) {
94 return {{0., 2.}}; // point -> 0, weight->2
95 }
96
97 const size_t n_points{order}; // GL has n points for order n
98 const size_t n_unique{(n_points + 1) / 2}; // Unique points due to symmetry
99 std::vector<double> points(n_points, 0.); // Quadrature points
100 std::vector<double> weights(n_points, 0.); // Quadrature weights
101
102 // Find Gauss-Legendre (GL) points using Newton-Raphson method
103 for (auto i_GL_point : std::views::iota(0U, n_unique)) {
104 // Initial guess
105 auto x_it = std::cos(
106 std::numbers::pi * (static_cast<double>(i_GL_point) + 0.75) /
107 (static_cast<double>(n_points) + 0.5)
108 );
109
110 // Newton-Raphson iteration to find root of P_n(x)
111 bool converged = false;
112 for ([[maybe_unused]] auto iteration : std::views::iota(0U, max_iterations)) {
113 const auto x_old = x_it; // Store old value for convergence check
114
115 // Evaluate Legendre polynomials P_{n-1}(x) and P_n(x)
116 const auto p_n_minus_1 = kynema::math::LegendrePolynomial(n_points - 1, x_it);
117 const auto p_n = kynema::math::LegendrePolynomial(n_points, x_it);
118
119 // Compute derivative P'_n(x) using recurrence relation
120 const auto p_n_prime =
121 static_cast<double>(n_points) * (x_it * p_n - p_n_minus_1) / (x_it * x_it - 1.);
122
123 // Newton update: x_{n+1} = x_n - f(x_n)/f'(x_n)
124 x_it -= p_n / p_n_prime;
125
126 // Check convergence
127 if (std::abs(x_it - x_old) <= convergence_tolerance) {
128 converged = true;
129 break;
130 }
131 }
132 if (!converged) {
133 throw std::runtime_error(
134 "Newton-Raphson failed to converge for GL point " + std::to_string(i_GL_point) +
135 " of order " + std::to_string(n_points)
136 );
137 }
138
139 // Store symmetric points
140 points[i_GL_point] = -x_it; // left side
141 points[n_points - 1 - i_GL_point] = x_it; // right side
142
143 // Compute GL weights: w = 2 / ((1 - x²) * [P'_n(x)]²)
144 const auto p_n_minus_1 = kynema::math::LegendrePolynomial(n_points - 1, x_it);
145 const auto p_n = kynema::math::LegendrePolynomial(n_points, x_it);
146 const auto p_n_prime =
147 static_cast<double>(n_points) * (x_it * p_n - p_n_minus_1) / (x_it * x_it - 1.);
148 const auto weight = 2. / ((1. - x_it * x_it) * p_n_prime * p_n_prime);
149
150 // Store symmetric weights
151 weights[i_GL_point] = weight;
152 weights[n_points - 1 - i_GL_point] = weight;
153 }
154
155 // Build and sort quadrature pairs
156 auto quadrature = std::vector<std::array<double, 2>>(n_points);
157 for (auto i_GL_point : std::views::iota(0U, n_points)) {
158 quadrature[i_GL_point] = {points[i_GL_point], weights[i_GL_point]};
159 }
160 std::ranges::sort(quadrature, [](const auto& a, const auto& b) {
161 return a[0] < b[0];
162 });
163 return quadrature;
164}
165
166} // namespace kynema::beams
Definition beam_quadrature.hpp:16
std::vector< std::array< double, 2 > > CreateTrapezoidalQuadrature(std::span< const double > grid)
Creates a trapezoidal quadrature rule based on a given grid.
Definition beam_quadrature.hpp:28
std::vector< std::array< double, 2 > > CreateGaussLegendreLobattoQuadrature(std::span< const double > grid, size_t order)
Definition beam_quadrature.hpp:47
std::vector< std::array< double, 2 > > CreateGaussLegendreQuadrature(const size_t order)
Creates Gauss-Legendre (GL) quadrature points and weights on [-1, 1].
Definition beam_quadrature.hpp:88
double LegendrePolynomial(const size_t n, const double x)
Calculates the value of Legendre polynomial of order n at point x.
Definition interpolation.hpp:136
std::vector< double > GetGllWeights(size_t order)
Definition gll_quadrature.hpp:168