OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
CubicSpline.cpp
Go to the documentation of this file.
1//
2// Cubic Spline Interpolation to replace GSL spline
3//
4// Copyright (c) 2023, Paul Scherrer Institute, Villigen PSI, Switzerland
5// All rights reserved
6//
7// This file is part of OPAL.
8//
9// OPAL is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// You should have received a copy of the GNU General License
15// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
16//
17#include "CubicSpline.h"
18#include <stdexcept>
19
20CubicSpline::CubicSpline(const std::vector<double>& x, const std::vector<double>& y) {
22}
23
24void CubicSpline::init(const std::vector<double>& x, const std::vector<double>& y) {
25 // Base class handles the points
27 // Natural cubic spline: second derivatives at endpoints are zero
29 // And the integrals over each interval
31}
32
33double CubicSpline::eval(const double x, Accelerator& accel) const {
34 double result{};
35 if (x_.empty()) {
36 throw std::runtime_error("CubicSpline: not initialized");
37 }
38 if (x <= x_.front()) {
39 result = extrapolateLeft(x);
40 } else if (x >= x_.back()) {
41 result = extrapolateRight(x);
42 } else {
43 const size_t i = findInterval(x, accel.last_index_);
44 const double dx = x - x_[i];
45 result = y_[i] + b_[i] * dx + c_[i] * dx * dx + d_[i] * dx * dx * dx;
46 }
47 return result;
48}
49
50double CubicSpline::evalIntegral(double xa, double xb, Accelerator& accel) const {
51 double result = 0.0;
52 if (x_.empty()) {
53 throw std::runtime_error("CubicSpline: not initialized");
54 }
55 if (xb < xa) {
56 std::swap(xa, xb);
57 }
58 // Linear extrapolation below the defined intervals
59 if (xa < x_.front()) {
60 const auto y = extrapolateLeft(xa);
61 const auto dx = x_.front() - xa;
62 const auto dy = y_.front() - y;
63 result += dx * y_.front() - dx * dy / 2.0;
64 xa = x_.front();
65 }
66 // Linear extrapolation above the defined intervals
67 if (xb > x_.back()) {
68 const auto y = extrapolateRight(xb);
69 const auto dx = xb - x_.back();
70 const auto dy = y - y_.back();
71 result += dx * y_.back() + dx * dy / 2.0;
72 xb = x_.back();
73 }
74 // Find the intervals
75 const size_t ia = findInterval(xa, accel.last_index_);
76 const size_t ib = findInterval(xb, accel.last_upper_index_);
77 // The first partial interval
78 result -= integral(ia, xa - x_[ia]);
79 // All the full interval integrals
80 for (size_t m = ia; m < ib; ++m) {
81 result += integrals_[m];
82 }
83 // The last partial interval
84 result += integral(ib, xb - x_[ib]);
85 return result;
86}
87
89 // Size the coefficients
90 const size_t n = x_.size();
91 b_.resize(n);
92 c_.resize(n);
93 d_.resize(n);
94 // Compute differences
95 std::vector<double> h(n - 1);
96 std::vector<double> alpha(n - 1);
97 for (size_t i = 0; i < n - 1; ++i) {
98 h[i] = x_[i + 1] - x_[i];
99 alpha[i] =
100 3.0 * ((y_[i + 1] - y_[i]) / h[i] - (i > 0 ? (y_[i] - y_[i - 1]) / h[i - 1] : 0.0));
101 }
102 // Natural spline: second derivatives at endpoints are zero
103 std::vector l(n, 1.0);
104 std::vector mu(n, 0.0);
105 std::vector z(n, 0.0);
106 // Forward elimination
107 for (size_t i = 1; i < n - 1; ++i) {
108 l[i] = 2.0 * (x_[i + 1] - x_[i - 1]) - h[i - 1] * mu[i - 1];
109 mu[i] = h[i] / l[i];
110 z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l[i];
111 }
112 // Back substitution
113 c_[n - 1] = 0.0; // Natural spline
114 for (int i = static_cast<int>(n) - 2; i >= 0; --i) {
115 c_[i] = z[i] - mu[i] * c_[i + 1];
116 b_[i] = (y_[i + 1] - y_[i]) / h[i] - h[i] * (c_[i + 1] + 2.0 * c_[i]) / 3.0;
117 d_[i] = (c_[i + 1] - c_[i]) / (3.0 * h[i]);
118 }
119 // Set last coefficients
120 b_[n - 1] = 0.0;
121 d_[n - 1] = 0.0;
122}
123
125 integrals_.resize(x_.size() - 1);
126 for (size_t i = 0; i < x_.size() - 1; ++i) {
127 integrals_[i] = integral(i, x_[i + 1] - x_[i]);
128 }
129}
130
131double CubicSpline::integral(const size_t i, const double dx) const {
132 return y_[i] * dx + b_[i] * dx * dx / 2.0 + c_[i] * dx * dx * dx / 3.0
133 + d_[i] * dx * dx * dx * dx / 4.0;
134}
135
136double CubicSpline::extrapolateLeft(double x) const {
137 double dx = x - x_[0];
138 return y_[0] + b_[0] * dx;
139}
140
141double CubicSpline::extrapolateRight(double x) const {
142 size_t n = x_.size();
143 double dx = x - x_[n - 1];
144 return y_[n - 1] + b_[n - 2] * dx;
145}
Accelerator caching last interval indices.
virtual void init(const std::vector< double > &x, const std::vector< double > &y)
Initialize from tabulated data (natural spline).
size_t findInterval(double x, size_t &intervalCache) const
Return the interval index for the given x-coordinate, using the supplied cached value if possible.
std::vector< double > x_
std::vector< double > y_
void computeIntegrals()
Compute the integrals of the intervals.
double evalIntegral(double xa, double xb, Accelerator &accel) const override
Evaluate the integral of the spline at x.
void init(const std::vector< double > &x, const std::vector< double > &y) override
Initialize from tabulated data (natural spline).
void computeCoefficients()
Compute natural spline coefficients.
double extrapolateLeft(double x) const
Linear extrapolation to the left of the data range.
double extrapolateRight(double x) const
Linear extrapolation to the right of the data range.
CubicSpline()=default
Default constructor.
std::vector< double > b_
Definition CubicSpline.h:79
double eval(double x, Accelerator &accel) const override
Evaluate the spline at x using an accelerator.
double integral(size_t i, double dx) const
Calculate the integral from x_[i] to x_[i]+dx.
std::vector< double > c_
Definition CubicSpline.h:80
std::vector< double > d_
Definition CubicSpline.h:81
std::vector< double > integrals_
Definition CubicSpline.h:82