preCICE
Loading...
Searching...
No Matches
Bspline.cpp
Go to the documentation of this file.
1#include "math/Bspline.hpp"
2
3#include <Eigen/Core>
4#include <Eigen/Sparse>
5#include <algorithm>
6#include <cstdlib>
7#include <unsupported/Eigen/Splines>
8
10#include "profiling/Event.hpp"
11#include "utils/assertion.hpp"
12
13namespace precice::math {
14
15Bspline::Bspline(Eigen::VectorXd ts, const Eigen::MatrixXd &xs, int splineDegree)
16{
17
18 PRECICE_ASSERT(ts.size() >= 2, "Interpolation requires at least 2 samples");
19 PRECICE_ASSERT(std::is_sorted(ts.begin(), ts.end()), "Timestamps must be sorted");
20
21 // organize data in columns. Each column represents one sample in time.
22 PRECICE_ASSERT(xs.cols() == ts.size());
23 _ndofs = xs.rows(); // number of dofs. Each dof needs its own interpolant.
24 _tsMin = ts(0);
25 _tsMax = ts(ts.size() - 1);
26 auto relativeTime = [tsMin = _tsMin, tsMax = _tsMax](double t) -> double { return (t - tsMin) / (tsMax - tsMin); };
27 ts = ts.unaryExpr(relativeTime);
28
29 profiling::Event e("bspline.compute");
30
31 // The code for computing the knots and the control points is copied from Eigens bspline interpolation with some modifications
32 // https://gitlab.com/libeigen/eigen/-/blob/master/unsupported/Eigen/src/Splines/SplineFitting.h
33
34 // 1. Compute the knot vector
35 Eigen::KnotAveraging(ts, splineDegree, _knots);
36
37 // 2. Compute the control points
38 // We use a nxn sparse matrix with 2 + (n-2) * (d+1) entries and thus a fill-factor < 0.5.
39 Eigen::DenseIndex n = xs.cols();
40 std::vector<Eigen::Triplet<double>> matrixEntries;
41 matrixEntries.reserve(2 + (n - 2) * (splineDegree + 1));
42
43 // Build matrix entries
44 matrixEntries.emplace_back(0, 0, 1.0);
45 for (Eigen::DenseIndex i = 1; i < n - 1; ++i) {
46 const Eigen::DenseIndex span = Eigen::Spline<double, 1>::Span(ts[i], splineDegree, _knots);
47 auto basisFunc = Eigen::Spline<double, 1>::BasisFunctions(ts[i], splineDegree, _knots);
48
49 for (Eigen::DenseIndex j = 0; j < splineDegree + 1; ++j) {
50 matrixEntries.emplace_back(i, span - splineDegree + j, basisFunc(j));
51 }
52 }
53 matrixEntries.emplace_back(n - 1, n - 1, 1.0);
54 PRECICE_ASSERT(matrixEntries.capacity() == matrixEntries.size(), matrixEntries.capacity(), matrixEntries.size(), n, splineDegree);
55
56 // Create sparse matrix
57 Eigen::SparseMatrix<double> A(n, n);
58 A.setFromTriplets(matrixEntries.begin(), matrixEntries.end());
59 A.makeCompressed();
60
61 // Solve system
62 Eigen::SparseQR<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> qr;
63 qr.analyzePattern(A);
64 qr.factorize(A);
65
66 _ctrls = qr.solve(xs.transpose());
67}
68
69Eigen::VectorXd Bspline::interpolateAt(double t) const
70{
71 // transform t to the relative interval [0; 1]
72 const double tRelative = std::clamp((t - _tsMin) / (_tsMax - _tsMin), 0.0, 1.0);
73
74 Eigen::VectorXd interpolated(_ndofs);
75 constexpr int splineDimension = 1;
76
77 for (int i = 0; i < _ndofs; i++) {
78 interpolated[i] = Eigen::Spline<double, splineDimension>(_knots, _ctrls.col(i))(tRelative)[0];
79 }
80
81 return interpolated;
82}
83} // namespace precice::math
Eigen::Vector2d ts
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
Eigen::MatrixXd _ctrls
Definition Bspline.hpp:31
Eigen::VectorXd _knots
Definition Bspline.hpp:30
Eigen::VectorXd interpolateAt(double t) const
Samples the B-Spline interpolation.
Definition Bspline.cpp:69
Bspline(Eigen::VectorXd ts, const Eigen::MatrixXd &xs, int splineDegree)
Initialises the B-Spline interpolation with the given data (x0,t0), (x1,t1), ..., (xn,...
Definition Bspline.cpp:15
A C++ 11 implementation of the non-owning C++20 std::span type.
Definition span.hpp:284
provides general mathematical constants and functions.
Definition barycenter.cpp:9