votca 2024-dev
Loading...
Searching...
No Matches
akimaspline.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Standard includes
19#include <iostream>
20
21// Local VOTCA includes
23#include "votca/tools/linalg.h"
24
25namespace votca {
26namespace tools {
27
28using namespace std;
29
30void AkimaSpline::Interpolate(const Eigen::VectorXd &x,
31 const Eigen::VectorXd &y) {
32 if (x.size() != y.size()) {
33 throw std::invalid_argument(
34 "error in AkimaSpline::Interpolate : sizes of vectors x and y do not "
35 "match");
36 }
37
38 // Akima splines require at least 4 points
39 if (x.size() < 4) {
40 throw std::invalid_argument(
41 "error in AkimaSpline::Interpolate : vectors x and y have to contain "
42 "at least 4 points");
43 }
44
45 const Index N = x.size();
46
47 // copy the grid points into f
48 r_ = x;
49
50 // initialize vectors p1,p2,p3,p4 and t
51 p0 = Eigen::VectorXd::Zero(N);
52 p1 = Eigen::VectorXd::Zero(N);
53 p2 = Eigen::VectorXd::Zero(N);
54 p3 = Eigen::VectorXd::Zero(N);
55 t = Eigen::VectorXd::Zero(N);
56
57 double m1, m2, m3, m4;
58
59 double temp, g0, g1, g2, x1, x2, y1, y2, x4, x5, y4, y5, m5;
60
61 // boundary conditions
62 // >> determine t(0), t(1) and t(N-2), t(N-1)
63 switch (boundaries_) {
64 case splineNormal:
65 // Akima method: estimation of two more points on each side using a
66 // degree two polyomial
67 // resulting slopes t(0), t(1), t(N-2), t(N-1) are directly calculated
68
69 // left side: t(0), t(1)
70 temp = (x(1) - x(0)) / (x(2) - x(0));
71 temp = temp * temp;
72 g0 = y(0);
73 g1 = ((y(1) - y(0)) - temp * (y(2) - y(0))) /
74 ((x(1) - x(0)) - temp * (x(2) - x(0)));
75 g2 = ((y(2) - y(0)) - g1 * (x(2) - x(0))) /
76 ((x(2) - x(0)) * (x(2) - x(0)));
77 x1 = x(0) - (x(2) - x(0));
78 x2 = x(1) - (x(2) - x(0));
79 y1 = g0 + g1 * (x1 - x(0)) + g2 * (x1 - x(0)) * (x1 - x(0));
80 y2 = g0 + g1 * (x2 - x(0)) + g2 * (x2 - x(0)) * (x2 - x(0));
81 m1 = (y2 - y1) / (x2 - x1);
82 m2 = (y(0) - y2) / (x(0) - x2);
83 m3 = (y(1) - y(0)) / (x(1) - x(0));
84 m4 = (y(2) - y(1)) / (x(2) - x(1));
85 t(0) = getSlope(m1, m2, m3, m4);
86 m5 = (y(3) - y(2)) / (x(3) - x(2));
87 t(1) = getSlope(m2, m3, m4, m5);
88
89 // right side: t(N-2), t(N-1)
90 temp = (x(N - 2) - x(N - 1)) / (x(N - 3) - x(N - 1));
91 temp = temp * temp;
92 g0 = y(N - 1);
93 g1 = ((y(N - 2) - y(N - 1)) - temp * (y(N - 3) - y(N - 1))) /
94 ((x(N - 2) - x(N - 1)) - temp * (x(N - 3) - x(N - 1)));
95 g2 = ((y(N - 3) - y(N - 1)) - g1 * (x(N - 3) - x(N - 1))) /
96 ((x(N - 3) - x(N - 1)) * (x(N - 3) - x(N - 1)));
97 x4 = x(N - 2) + (x(N - 1) - x(N - 3));
98 x5 = x(N - 1) + (x(N - 1) - x(N - 3));
99 y4 = g0 + g1 * (x4 - x(N - 1)) + g2 * (x4 - x(N - 1)) * (x4 - x(N - 1));
100 y5 = g0 + g1 * (x5 - x(N - 1)) + g2 * (x5 - x(N - 1)) * (x5 - x(N - 1));
101 m1 = (y(N - 3) - y(N - 4)) / (x(N - 3) - x(N - 4));
102 m2 = (y(N - 2) - y(N - 3)) / (x(N - 2) - x(N - 3));
103 m3 = (y(N - 1) - y(N - 2)) / (x(N - 1) - x(N - 2));
104 m4 = (y4 - y(N - 1)) / (x4 - x(N - 1));
105 m5 = (y5 - y4) / (x5 - x4);
106 t(N - 2) = getSlope(m1, m2, m3, m4);
107 t(N - 1) = getSlope(m2, m3, m4, m5);
108 break;
109 case splinePeriodic:
110 // left: last two points determine the slopes t(0), t(1)
111 m1 = (y(N - 1) - y(N - 2)) / (x(N - 1) - x(N - 2));
112 m2 = (y(0) - y(N - 1)) / (x(0) - x(N - 1));
113 m3 = (y(1) - y(0)) / (x(1) - x(0));
114 m4 = (y(2) - y(1)) / (x(2) - x(1));
115 m5 = (y(3) - y(2)) / (x(3) - x(2));
116 t(0) = getSlope(m1, m2, m3, m4);
117 t(1) = getSlope(m2, m3, m4, m5);
118 // right: first two points determine the slopes t(N-2), t(N-1)
119 m1 = (y(N - 3) - y(N - 4)) / (x(N - 3) - x(N - 4));
120 m2 = (y(N - 2) - y(N - 3)) / (x(N - 2) - x(N - 3));
121 m3 = (y(N - 1) - y(N - 2)) / (x(N - 1) - x(N - 2));
122 m4 = (y(0) - y(N - 1)) / (x(0) - x(N - 1));
123 m5 = (y(1) - y(0)) / (x(1) - x(0));
124 t(N - 2) = getSlope(m1, m2, m3, m4);
125 t(N - 1) = getSlope(m2, m3, m4, m5);
126 break;
128 throw std::runtime_error(
129 "erro in AkimaSpline::Interpolate: case splineDerivativeZero not "
130 "implemented yet");
131 break;
132 }
133
134 // calculate t's for all inner points [2,N-3]
135 for (Index i = 2; i < N - 2; i++) {
136 m1 = (y(i - 1) - y(i - 2)) / (x(i - 1) - x(i - 2));
137 m2 = (y(i) - y(i - 1)) / (x(i) - x(i - 1));
138 m3 = (y(i + 1) - y(i)) / (x(i + 1) - x(i));
139 m4 = (y(i + 2) - y(i + 1)) / (x(i + 2) - x(i + 1));
140 t(i) = getSlope(m1, m2, m3, m4);
141 }
142
143 // calculate p0,p1,p2,p3 for all intervals 0..(N-2), where interval
144 // [x(i),x(i+1)] shall have number i (this means that the last interval
145 // has number N-2)
146 for (Index i = 0; i < N - 1; i++) {
147 p0(i) = y(i);
148 p1(i) = t(i);
149 p2(i) =
150 (3.0 * (y(i + 1) - y(i)) / (x(i + 1) - x(i)) - 2.0 * t(i) - t(i + 1)) /
151 (x(i + 1) - x(i));
152 p3(i) = (t(i) + t(i + 1) - 2.0 * (y(i + 1) - y(i)) / (x(i + 1) - x(i))) /
153 ((x(i + 1) - x(i)) * (x(i + 1) - x(i)));
154 }
155}
156
157void AkimaSpline::Fit(const Eigen::VectorXd &, const Eigen::VectorXd &) {
158 throw std::runtime_error("Akima fit not implemented.");
159}
160
161double AkimaSpline::Calculate(double r) {
162 Index interval = getInterval(r);
163 double z = r - r_[interval];
164 return p0(interval) + p1(interval) * z + p2(interval) * z * z +
165 p3(interval) * z * z * z;
166}
167
169 Index interval = getInterval(r);
170 double z = r - r_[interval];
171 return +p1(interval) + 2.0 * p2(interval) * z + 3.0 * p3(interval) * z * z;
172}
173
174double AkimaSpline::getSlope(double m1, double m2, double m3, double m4) {
175 if (isApproximatelyEqual(m1, m2, 1E-15) &&
176 isApproximatelyEqual(m3, m4, 1E-15)) {
177 return (m2 + m3) / 2.0;
178 } else {
179 return (std::fabs(m4 - m3) * m2 + std::fabs(m2 - m1) * m3) /
180 (std::fabs(m4 - m3) + std::fabs(m2 - m1));
181 }
182}
183
184} // namespace tools
185} // namespace votca
double getSlope(double m1, double m2, double m3, double m4)
Calculate the slope according to the original Akima paper ("A New Method of Interpolation and Smooth ...
double CalculateDerivative(double r) override
Calculate y value for a given x value on the derivative of the spline created by function Interpolate...
void Fit(const Eigen::VectorXd &x, const Eigen::VectorXd &y) override
Fit spline through noisy (x,y) values. Points on resulting fitted spline can be obtained via Calculat...
void Interpolate(const Eigen::VectorXd &x, const Eigen::VectorXd &y) override
Calculate interpolating spline for given (x,y) values. Points on resulting spline can be obtained via...
double Calculate(double r) override
Calculate spline function value for a given x value on the spline created by Interpolate() or Fit()
Eigen::VectorXd r_
Definition spline.h:173
eBoundary boundaries_
Get the spline data f_.
Definition spline.h:171
Index getInterval(double r)
Determine the index of the interval containing value r.
Definition spline.cc:62
@ splineDerivativeZero
derivatives and end-points are zero.
Definition spline.h:76
@ splinePeriodic
periodic boundary conditions:
Definition spline.h:75
@ splineNormal
normal boundary conditions:
Definition spline.h:74
STL namespace.
static bool isApproximatelyEqual(T a, T b, T tolerance)
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26