ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/branches/development/src/math/CubicSpline.cpp
Revision: 1767
Committed: Fri Jul 6 22:01:58 2012 UTC (12 years, 9 months ago) by gezelter
File size: 8840 byte(s)
Log Message:
Various fixes required to compile OpenMD with the MS Visual C++ compiler

File Contents

# Content
1 /*
2 * Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3 *
4 * The University of Notre Dame grants you ("Licensee") a
5 * non-exclusive, royalty free, license to use, modify and
6 * redistribute this software in source and binary code form, provided
7 * that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * This software is provided "AS IS," without a warranty of any
18 * kind. All express or implied conditions, representations and
19 * warranties, including any implied warranty of merchantability,
20 * fitness for a particular purpose or non-infringement, are hereby
21 * excluded. The University of Notre Dame and its licensors shall not
22 * be liable for any damages suffered by licensee as a result of
23 * using, modifying or distributing the software or its
24 * derivatives. In no event will the University of Notre Dame or its
25 * licensors be liable for any lost revenue, profit or data, or for
26 * direct, indirect, special, consequential, incidental or punitive
27 * damages, however caused and regardless of the theory of liability,
28 * arising out of the use of or inability to use software, even if the
29 * University of Notre Dame has been advised of the possibility of
30 * such damages.
31 *
32 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
33 * research, please cite the appropriate papers when you publish your
34 * work. Good starting points are:
35 *
36 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
37 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
38 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 24107 (2008).
39 * [4] Kuang & Gezelter, J. Chem. Phys. 133, 164101 (2010).
40 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
41 */
42
43 #include "math/CubicSpline.hpp"
44 #include "utils/simError.h"
45 #include <cmath>
46 #include <cstdio>
47 #include <algorithm>
48
49 using namespace OpenMD;
50 using namespace std;
51
52 CubicSpline::CubicSpline() : generated(false), isUniform(true) {
53 data_.clear();
54 }
55
56 void CubicSpline::addPoint(const RealType xp, const RealType yp) {
57 data_.push_back(make_pair(xp, yp));
58 }
59
60 void CubicSpline::addPoints(const vector<RealType>& xps,
61 const vector<RealType>& yps) {
62
63 if (xps.size() != yps.size()) {
64 printf( painCave.errMsg,
65 "CubicSpline::addPoints was passed vectors of different length!\n");
66 painCave.severity = OPENMD_ERROR;
67 painCave.isFatal = 1;
68 simError();
69 }
70
71 for (unsigned int i = 0; i < xps.size(); i++)
72 data_.push_back(make_pair(xps[i], yps[i]));
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, h, p;
88
89 // make sure the sizes match
90
91 n = data_.size();
92 b.resize(n);
93 c.resize(n);
94 d.resize(n);
95
96 // make sure we are monotonically increasing in x:
97
98 bool sorted = true;
99
100 for (int i = 1; i < n; i++) {
101 if ( (data_[i].first - data_[i-1].first ) <= 0.0 ) sorted = false;
102 }
103
104 // sort if necessary
105
106 if (!sorted) sort(data_.begin(), data_.end());
107
108 // Calculate coefficients for the tridiagonal system: store
109 // sub-diagonal in B, diagonal in D, difference quotient in C.
110
111 b[0] = data_[1].first - data_[0].first;
112 c[0] = (data_[1].second - data_[0].second) / b[0];
113
114 if (n == 2) {
115
116 // Assume the derivatives at both endpoints are zero. Another
117 // assumption could be made to have a linear interpolant between
118 // the two points. In that case, the b coefficients below would be
119 // (data_[1].second - data_[0].second) / (data_[1].first - data_[0].first)
120 // and the c and d coefficients would both be zero.
121 b[0] = 0.0;
122 c[0] = -3.0 * pow((data_[1].second - data_[0].second) /
123 (data_[1].first-data_[0].first), 2);
124 d[0] = -2.0 * pow((data_[1].second - data_[0].second) /
125 (data_[1].first-data_[0].first), 3);
126 b[1] = b[0];
127 c[1] = 0.0;
128 d[1] = 0.0;
129 dx = 1.0 / (data_[1].first - data_[0].first);
130 isUniform = true;
131 generated = true;
132 return;
133 }
134
135 d[0] = 2.0 * b[0];
136
137 for (int i = 1; i < n-1; i++) {
138 b[i] = data_[i+1].first - data_[i].first;
139 if ( fabs( b[i] - b[0] ) / b[0] > 1.0e-5) isUniform = false;
140 c[i] = (data_[i+1].second - data_[i].second) / b[i];
141 d[i] = 2.0 * (b[i] + b[i-1]);
142 }
143
144 d[n-1] = 2.0 * b[n-2];
145
146 // Calculate estimates for the end slopes using polynomials
147 // that interpolate the data_ nearest the end.
148
149 fp1 = c[0] - b[0]*(c[1] - c[0])/(b[0] + b[1]);
150 if (n > 3) fp1 = fp1 + b[0]*((b[0] + b[1]) * (c[2] - c[1]) /
151 (b[1] + b[2]) -
152 c[1] + c[0]) / (data_[3].first - data_[0].first);
153
154 fpn = c[n-2] + b[n-2]*(c[n-2] - c[n-3])/(b[n-3] + b[n-2]);
155
156 if (n > 3) fpn = fpn + b[n-2] *
157 (c[n-2] - c[n-3] - (b[n-3] + b[n-2]) *
158 (c[n-3] - c[n-4])/(b[n-3] + b[n-4]))/(data_[n-1].first - data_[n-4].first);
159
160
161 // Calculate the right hand side and store it in C.
162
163 c[n-1] = 3.0 * (fpn - c[n-2]);
164 for (int i = n-2; i > 0; i--)
165 c[i] = 3.0 * (c[i] - c[i-1]);
166 c[0] = 3.0 * (c[0] - fp1);
167
168 // Solve the tridiagonal system.
169
170 for (int k = 1; k < n; k++) {
171 p = b[k-1] / d[k-1];
172 d[k] = d[k] - p*b[k-1];
173 c[k] = c[k] - p*c[k-1];
174 }
175
176 c[n-1] = c[n-1] / d[n-1];
177
178 for (int k = n-2; k >= 0; k--)
179 c[k] = (c[k] - b[k] * c[k+1]) / d[k];
180
181 // Calculate the coefficients defining the spline.
182
183 for (int i = 0; i < n-1; i++) {
184 h = data_[i+1].first - data_[i].first;
185 d[i] = (c[i+1] - c[i]) / (3.0 * h);
186 b[i] = (data_[i+1].second - data_[i].second)/h - h * (c[i] + h * d[i]);
187 }
188
189 b[n-1] = b[n-2] + h * (2.0 * c[n-2] + h * 3.0 * d[n-2]);
190
191 if (isUniform) dx = 1.0 / (data_[1].first - data_[0].first);
192
193 generated = true;
194 return;
195 }
196
197 RealType CubicSpline::getValueAt(RealType t) {
198 // Evaluate the spline at t using coefficients
199 //
200 // Input parameters
201 // t = point where spline is to be evaluated.
202 // Output:
203 // value of spline at t.
204
205 if (!generated) generate();
206 RealType dt;
207
208 if ( t < data_[0].first || t > data_[n-1].first ) {
209 sprintf( painCave.errMsg,
210 "CubicSpline::getValueAt was passed a value outside the range of the spline!\n");
211 painCave.severity = OPENMD_ERROR;
212 painCave.isFatal = 1;
213 simError();
214 }
215
216 // Find the interval ( x[j], x[j+1] ) that contains or is nearest
217 // to t.
218
219 int j;
220
221 if (isUniform) {
222
223 j = max(0, min(n-1, int((t - data_[0].first) * dx)));
224
225 } else {
226
227 j = n-1;
228
229 for (int i = 0; i < n; i++) {
230 if ( t < data_[i].first ) {
231 j = i-1;
232 break;
233 }
234 }
235 }
236
237 // Evaluate the cubic polynomial.
238
239 dt = t - data_[j].first;
240 return data_[j].second + dt*(b[j] + dt*(c[j] + dt*d[j]));
241
242 }
243
244
245 pair<RealType, RealType> CubicSpline::getValueAndDerivativeAt(RealType t) {
246 // Evaluate the spline and first derivative at t using coefficients
247 //
248 // Input parameters
249 // t = point where spline is to be evaluated.
250 // Output:
251 // pair containing value of spline at t and first derivative at t
252
253 if (!generated) generate();
254 RealType dt;
255
256 if ( t < data_.front().first || t > data_.back().first ) {
257 sprintf( painCave.errMsg,
258 "CubicSpline::getValueAndDerivativeAt was passed a value outside the range of the spline!\n");
259 painCave.severity = OPENMD_ERROR;
260 painCave.isFatal = 1;
261 simError();
262 }
263
264 // Find the interval ( x[j], x[j+1] ) that contains or is nearest
265 // to t.
266
267 int j;
268
269 if (isUniform) {
270
271 j = max(0, min(n-1, int((t - data_[0].first) * dx)));
272
273 } else {
274
275 j = n-1;
276
277 for (int i = 0; i < n; i++) {
278 if ( t < data_[i].first ) {
279 j = i-1;
280 break;
281 }
282 }
283 }
284
285 // Evaluate the cubic polynomial.
286
287 dt = t - data_[j].first;
288
289 RealType yval = data_[j].second + dt*(b[j] + dt*(c[j] + dt*d[j]));
290 RealType dydx = b[j] + dt*(2.0 * c[j] + 3.0 * dt * d[j]);
291
292 return make_pair(yval, dydx);
293 }

Properties

Name Value
svn:eol-style native