/home/runner/work/kynema/kynema/kynema/src/math/least_squares_fit.hpp Source File

Kynema API: /home/runner/work/kynema/kynema/kynema/src/math/least_squares_fit.hpp Source File
Kynema API
A flexible multibody structural dynamics code for wind turbines
Loading...
Searching...
No Matches
least_squares_fit.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <ranges>
5#include <span>
6#include <stdexcept>
7#include <vector>
8
9#include <Kokkos_Core.hpp>
10
11#include "Kynema_config.h"
12
14namespace lapack {
15#ifdef Kynema_ENABLE_MKL
16#include <mkl_lapacke.h>
17#else
18#include <lapacke.h>
19#endif
20} // namespace lapack
22
23#include "interpolation.hpp"
24
25namespace kynema::math {
26
34inline std::vector<double> MapGeometricLocations(std::span<const double> geom_locations) {
35 // Get first and last points of the input domain (assumed to be sorted)
36 const double domain_start = geom_locations.front();
37 const double domain_end = geom_locations.back();
38 if (domain_end == domain_start) {
39 throw std::invalid_argument(
40 "Invalid geometric locations: domain start and end points are equal."
41 );
42 }
43
44 // Map each point from domain -> [-1, 1]
45 std::vector<double> mapped_locations(geom_locations.size());
46 const auto domain_span = domain_end - domain_start;
47 std::ranges::transform(
48 geom_locations, std::begin(mapped_locations),
49 [domain_start, domain_span](auto geom_location) {
50 return 2. * (geom_location - domain_start) / domain_span - 1.;
51 }
52 );
53 return mapped_locations;
54}
55
65inline std::vector<std::vector<double>> ComputeShapeFunctionValues(
66 std::span<const double> input_points, std::span<const double> output_points
67) {
68 // Number of points in input and output arrays
69 const auto num_input_points = input_points.size();
70 const auto num_output_points = output_points.size();
71
72 // Compute weights for the shape functions and its derivatives at the evaluation points
73 std::vector<double> weights(num_output_points, 0.);
74
75 // Create shape function interpolation matrix
76 auto shape_functions = std::vector<std::vector<double>>(
77 num_output_points, std::vector<double>(num_input_points, 0.)
78 );
79 for (auto input_point : std::views::iota(0U, num_input_points)) {
80 math::LagrangePolynomialInterpWeights(input_points[input_point], output_points, weights);
81 for (auto output_point : std::views::iota(0U, num_output_points)) {
82 shape_functions[output_point][input_point] = weights[output_point];
83 }
84 }
85
86 return shape_functions;
87}
88
98inline std::vector<std::vector<double>> ComputeShapeFunctionDerivatives(
99 std::span<const double> input_points, std::span<const double> output_points
100) {
101 // Number of points in input and output arrays
102 const auto num_input_points = input_points.size();
103 const auto num_output_points = output_points.size();
104
105 // Compute weights for the shape functions and its derivatives at the evaluation points
106 std::vector<double> weights(num_output_points, 0.);
107
108 // Create shape function derivative matrix
109 auto derivative_functions = std::vector<std::vector<double>>(
110 num_output_points, std::vector<double>(num_input_points, 0.)
111 );
112 for (auto input_point : std::views::iota(0U, num_input_points)) {
113 math::LagrangePolynomialDerivWeights(input_points[input_point], output_points, weights);
114 for (auto output_point : std::views::iota(0U, num_output_points)) {
115 derivative_functions[output_point][input_point] = weights[output_point];
116 }
117 }
118
119 return derivative_functions;
120}
121
134inline std::vector<std::array<double, 3>> PerformLeastSquaresFitting(
135 size_t p, std::span<const std::vector<double>> shape_functions,
136 std::span<const std::array<double, 3>> points_to_fit
137) {
138 if (shape_functions.size() != p) {
139 throw std::invalid_argument("shape_functions rows do not match order p.");
140 }
141 const size_t n = shape_functions[0].size();
142 if (std::any_of(shape_functions.begin(), shape_functions.end(), [n](const auto& row) {
143 return row.size() != n;
144 })) {
145 throw std::invalid_argument("Inconsistent number of columns in shape_functions.");
146 }
147
148 // Construct matrix A in LHS (p x p)
149 const auto A = Kokkos::View<double**, Kokkos::LayoutLeft, Kokkos::HostSpace>("A", p, p);
150 A(0, 0) = 1.;
151 A(p - 1, p - 1) = 1.;
152 for (auto j : std::views::iota(0U, p)) {
153 for (auto i : std::views::iota(1U, p - 1U)) {
154 A(i, j) = std::transform_reduce(
155 std::begin(shape_functions[i]), std::end(shape_functions[i]),
156 std::begin(shape_functions[j]), 0., std::plus<>(), std::multiplies<>()
157 );
158 }
159 }
160
161 // Construct matrix B in RHS (p x 3)
162 const auto B = Kokkos::View<double* [3], Kokkos::LayoutLeft, Kokkos::HostSpace>("B", p);
163 for (auto dim : std::views::iota(0U, 3U)) {
164 B(0, dim) = points_to_fit[0][dim];
165 B(p - 1, dim) = points_to_fit[n - 1][dim];
166 }
167 for (auto i : std::views::iota(1U, p - 1U)) {
168 for (auto k : std::views::iota(0U, n)) {
169 for (auto dim : std::views::iota(0U, 3U)) {
170 B(i, dim) += shape_functions[i][k] * points_to_fit[k][dim];
171 }
172 }
173 }
174
175 // Solve the least squares problem for each dimension of B
176#ifdef Kynema_ENABLE_MKL
177 using index_type = MKL_INT;
178#else
179 using index_type = int;
180#endif
181 const auto IPIV =
182 Kokkos::View<index_type*, Kokkos::LayoutLeft, Kokkos::HostSpace>("IPIV", B.extent(0));
183 const auto rows = static_cast<index_type>(p);
184
185 lapack::LAPACKE_dgesv(LAPACK_COL_MAJOR, rows, 3, A.data(), rows, IPIV.data(), B.data(), rows);
186
187 auto result = std::vector<std::array<double, 3>>(B.extent(0));
188
189 for (auto i : std::views::iota(0U, result.size())) {
190 for (auto j : std::views::iota(0U, result.front().size())) {
191 result[i][j] = B(i, j);
192 }
193 }
194
195 return result;
196}
197
198} // namespace kynema::math
Definition gll_quadrature.hpp:7
std::vector< double > MapGeometricLocations(std::span< const double > geom_locations)
Maps input geometric locations -> normalized domain using linear mapping.
Definition least_squares_fit.hpp:34
void LagrangePolynomialInterpWeights(double x, std::span< const double > xs, std::vector< double > &weights)
Computes weights for Lagrange polynomial interpolation.
Definition interpolation.hpp:69
void LagrangePolynomialDerivWeights(double x, std::span< const double > xs, std::vector< double > &weights)
Computes weights for Lagrange polynomial derivative interpolation.
Definition interpolation.hpp:97
std::vector< std::array< double, 3 > > PerformLeastSquaresFitting(size_t p, std::span< const std::vector< double > > shape_functions, std::span< const std::array< double, 3 > > points_to_fit)
Performs least squares fitting to determine interpolation coefficients.
Definition least_squares_fit.hpp:134
std::vector< std::vector< double > > ComputeShapeFunctionValues(std::span< const double > input_points, std::span< const double > output_points)
Computes shape function matrices ϕg relating points ξb to ξg At least two input points are required a...
Definition least_squares_fit.hpp:65
std::vector< std::vector< double > > ComputeShapeFunctionDerivatives(std::span< const double > input_points, std::span< const double > output_points)
Computes shape function derivatives dϕg relating points ξb to ξg At least two input points are requir...
Definition least_squares_fit.hpp:98