OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
CubicSpline.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "math/CubicSpline.hpp"
46
47#include <algorithm>
48#include <cassert>
49#include <cmath>
50#include <cstdio>
51#include <numeric>
52
53namespace OpenMD {
54
55 CubicSpline::CubicSpline() : isUniform(true), generated(false) {
56 x_.clear();
57 y_.clear();
58 }
59
60 void CubicSpline::addPoint(const RealType xp, const RealType yp) {
61 x_.push_back(xp);
62 y_.push_back(yp);
63 }
64
65 void CubicSpline::addPoints(const std::vector<RealType>& xps,
66 const std::vector<RealType>& yps) {
67 assert(xps.size() == yps.size());
68
69 for (unsigned int i = 0; i < xps.size(); i++) {
70 x_.push_back(xps[i]);
71 y_.push_back(yps[i]);
72 }
73 }
74
75 void CubicSpline::generate() {
76 // Calculate coefficients defining a smooth cubic interpolatory spline.
77 //
78 // class values constructed:
79 // n = number of data_ points.
80 // x_ = vector of independent variable values
81 // y_ = vector of dependent variable values
82 // b = vector of S'(x_[i]) values.
83 // c = vector of S"(x_[i])/2 values.
84 // d = vector of S'''(x_[i]+)/6 values (i < n).
85 // Local variables:
86
87 RealType fp1, fpn, p;
88 RealType h(0.0);
89
90 // make sure the sizes match
91
92 n = x_.size();
93 b.resize(n);
94 c.resize(n);
95 d.resize(n);
96
97 // make sure we are monotonically increasing in x:
98
99 bool sorted = true;
100
101 for (int i = 1; i < n; i++) {
102 if ((x_[i] - x_[i - 1]) <= 0.0) sorted = false;
103 }
104
105 // sort if necessary
106
107 if (!sorted) {
108 std::vector<int> p = sort_permutation(x_);
109 x_ = apply_permutation(x_, p);
110 y_ = apply_permutation(y_, p);
111 }
112
113 // Calculate coefficients for the tridiagonal system: store
114 // sub-diagonal in B, diagonal in D, difference quotient in C.
115
116 b[0] = x_[1] - x_[0];
117 c[0] = (y_[1] - y_[0]) / b[0];
118
119 if (n == 2) {
120 // Assume the derivatives at both endpoints are zero. Another
121 // assumption could be made to have a linear interpolant between
122 // the two points. In that case, the b coefficients below would be
123 // (y_[1] - y_[0]) / (x_[1] - x_[0])
124 // and the c and d coefficients would both be zero.
125 b[0] = 0.0;
126 c[0] = -3.0 * pow((y_[1] - y_[0]) / (x_[1] - x_[0]), 2);
127 d[0] = -2.0 * pow((y_[1] - y_[0]) / (x_[1] - x_[0]), 3);
128 b[1] = b[0];
129 c[1] = 0.0;
130 d[1] = 0.0;
131 dx = 1.0 / (x_[1] - x_[0]);
132 isUniform = true;
133 generated = true;
134 return;
135 }
136
137 d[0] = 2.0 * b[0];
138
139 for (int i = 1; i < n - 1; i++) {
140 b[i] = x_[i + 1] - x_[i];
141 if (fabs(b[i] - b[0]) / b[0] > 1.0e-5) isUniform = false;
142 c[i] = (y_[i + 1] - y_[i]) / b[i];
143 d[i] = 2.0 * (b[i] + b[i - 1]);
144 }
145
146 d[n - 1] = 2.0 * b[n - 2];
147
148 // Calculate estimates for the end slopes using polynomials
149 // that interpolate the data_ nearest the end.
150
151 fp1 = c[0] - b[0] * (c[1] - c[0]) / (b[0] + b[1]);
152 if (n > 3)
153 fp1 = fp1 +
154 b[0] *
155 ((b[0] + b[1]) * (c[2] - c[1]) / (b[1] + b[2]) - c[1] + c[0]) /
156 (x_[3] - x_[0]);
157
158 fpn = c[n - 2] + b[n - 2] * (c[n - 2] - c[n - 3]) / (b[n - 3] + b[n - 2]);
159
160 if (n > 3)
161 fpn = fpn + b[n - 2] *
162 (c[n - 2] - c[n - 3] -
163 (b[n - 3] + b[n - 2]) * (c[n - 3] - c[n - 4]) /
164 (b[n - 3] + b[n - 4])) /
165 (x_[n - 1] - x_[n - 4]);
166
167 // Calculate the right hand side and store it in C.
168
169 c[n - 1] = 3.0 * (fpn - c[n - 2]);
170 for (int i = n - 2; i > 0; i--)
171 c[i] = 3.0 * (c[i] - c[i - 1]);
172 c[0] = 3.0 * (c[0] - fp1);
173
174 // Solve the tridiagonal system.
175
176 for (int k = 1; k < n; k++) {
177 p = b[k - 1] / d[k - 1];
178 d[k] = d[k] - p * b[k - 1];
179 c[k] = c[k] - p * c[k - 1];
180 }
181
182 c[n - 1] = c[n - 1] / d[n - 1];
183
184 for (int k = n - 2; k >= 0; k--)
185 c[k] = (c[k] - b[k] * c[k + 1]) / d[k];
186
187 // Calculate the coefficients defining the spline.
188
189 for (int i = 0; i < n - 1; i++) {
190 h = x_[i + 1] - x_[i];
191 d[i] = (c[i + 1] - c[i]) / (3.0 * h);
192 b[i] = (y_[i + 1] - y_[i]) / h - h * (c[i] + h * d[i]);
193 }
194
195 b[n - 1] = b[n - 2] + h * (2.0 * c[n - 2] + h * 3.0 * d[n - 2]);
196
197 if (isUniform) dx = 1.0 / (x_[1] - x_[0]);
198
199 generated = true;
200 return;
201 }
202
203 RealType CubicSpline::getValueAt(const RealType& t) {
204 // Evaluate the spline at t using coefficients
205 //
206 // Input parameters
207 // t = point where spline is to be evaluated.
208 // Output:
209 // value of spline at t.
210
211 if (!generated) generate();
212
213 assert(t >= x_.front());
214 assert(t <= x_.back());
215
216 // Find the interval ( x[j], x[j+1] ) that contains or is nearest
217 // to t.
218
219 if (isUniform) {
220 j = std::max(0, std::min(n - 1, int((t - x_[0]) * dx)));
221
222 } else {
223 j = n - 1;
224
225 for (int i = 0; i < n; i++) {
226 if (t < x_[i]) {
227 j = i - 1;
228 break;
229 }
230 }
231 }
232
233 // Evaluate the cubic polynomial.
234
235 dt = t - x_[j];
236 return y_[j] + dt * (b[j] + dt * (c[j] + dt * d[j]));
237 }
238
239 void CubicSpline::getValueAt(const RealType& t, RealType& v) {
240 // Evaluate the spline at t using coefficients
241 //
242 // Input parameters
243 // t = point where spline is to be evaluated.
244 // Output:
245 // value of spline at t.
246
247 if (!generated) generate();
248
249 assert(t >= x_.front());
250 assert(t <= x_.back());
251
252 // Find the interval ( x[j], x[j+1] ) that contains or is nearest
253 // to t.
254
255 if (isUniform) {
256 j = std::max(0, std::min(n - 1, int((t - x_[0]) * dx)));
257
258 } else {
259 j = n - 1;
260
261 for (int i = 0; i < n; i++) {
262 if (t < x_[i]) {
263 j = i - 1;
264 break;
265 }
266 }
267 }
268
269 // Evaluate the cubic polynomial.
270
271 dt = t - x_[j];
272 v = y_[j] + dt * (b[j] + dt * (c[j] + dt * d[j]));
273 }
274
275 pair<RealType, RealType> CubicSpline::getLimits() {
276 if (!generated) generate();
277 return make_pair(x_.front(), x_.back());
278 }
279
280 RealType CubicSpline::getSpacing() {
281 if (!generated) generate();
282 assert(isUniform);
283 if (isUniform)
284 return 1.0 / dx;
285 else
286 return 0.0;
287 }
288
289 void CubicSpline::getValueAndDerivativeAt(const RealType& t, RealType& v,
290 RealType& dv) {
291 // Evaluate the spline and first derivative at t using coefficients
292 //
293 // Input parameters
294 // t = point where spline is to be evaluated.
295
296 if (!generated) generate();
297
298 assert(t >= x_.front());
299 assert(t <= x_.back());
300
301 // Find the interval ( x[j], x[j+1] ) that contains or is nearest
302 // to t.
303
304 if (isUniform) {
305 j = std::max(0, std::min(n - 1, int((t - x_[0]) * dx)));
306
307 } else {
308 j = n - 1;
309
310 for (int i = 0; i < n; i++) {
311 if (t < x_[i]) {
312 j = i - 1;
313 break;
314 }
315 }
316 }
317
318 // Evaluate the cubic polynomial.
319
320 dt = t - x_[j];
321
322 v = y_[j] + dt * (b[j] + dt * (c[j] + dt * d[j]));
323 dv = b[j] + dt * (2.0 * c[j] + 3.0 * dt * d[j]);
324 }
325
326 std::vector<int> CubicSpline::sort_permutation(std::vector<RealType>& v) {
327 std::vector<int> p(v.size());
328
329 // 6 lines to replace std::iota(p.begin(), p.end(), 0);
330 int value = 0;
331 std::vector<int>::iterator i;
332 for (i = p.begin(); i != p.end(); ++i) {
333 (*i) = value;
334 ++value;
335 }
336
337 std::sort(p.begin(), p.end(), OpenMD::Comparator(v));
338 return p;
339 }
340
341 std::vector<RealType> CubicSpline::apply_permutation(
342 std::vector<RealType> const& v, std::vector<int> const& p) {
343 std::size_t n = p.size();
344 std::vector<RealType> sorted_vec(n);
345 for (std::size_t i = 0; i < n; ++i) {
346 sorted_vec[i] = v[p[i]];
347 }
348 return sorted_vec;
349 }
350} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.