Isis 3 Programmer Reference
NumericalApproximation.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "NumericalApproximation.h"
8
9#include <cmath>
10#include <iostream>
11#include <sstream>
12#include <string>
13#include <vector>
14#include <algorithm>
15
16#include "SpecialPixel.h"
17#include "IException.h"
18#include "IString.h"
19
20using namespace std;
21namespace Isis {
23
42 // Validate parameter and initialize class variables
43 try {
44 Init(itype);
45 p_dataValidated = false;
46 }
47 catch(IException &e) { // catch exception from Init()
48 throw IException(e, e.errorType(),
49 "NumericalApproximation() - Unable to construct NumericalApproximation object",
50 _FILEINFO_);
51 }
52 }
53
87 NumericalApproximation::NumericalApproximation(unsigned int n, double *x, double *y,
89 try {
90 Init(itype);
91 AddData(n, x, y);
93 }
94 catch(IException &e) { // catch exception from Init(), AddData(), ValidateDataSet()
95 throw IException(e, e.errorType(),
96 "NumericalApproximation() - Unable to construct object using the given arrays, size and interpolation type",
97 _FILEINFO_);
98 }
99 }
100
131 NumericalApproximation::NumericalApproximation(const vector <double> &x, const vector <double> &y,
133 try {
134 Init(itype);
135 AddData(x, y); // size of x = size of y validated in this method
137 }
138 catch(IException &e) { // catch exception from Init(), AddData(), ValidateDataSet()
139 throw IException(e, e.errorType(),
140 "NumericalApproximation() - Unable to construct an object using the given vectors and interpolation type",
141 _FILEINFO_);
142 }
143 }
144
171 try {
172 // initialize new object and set type to that of old object
173 Init(oldObject.p_itype);
174 // fill data set of new object
175 p_x = oldObject.p_x;
176 p_y = oldObject.p_y;
177 // if this data was previously validated, no need to do this again
178 p_dataValidated = oldObject.p_dataValidated;
179 // copy values for interpolation-specific variables
180 p_clampedComputed = oldObject.p_clampedComputed;
181 p_clampedEndptsSet = oldObject.p_clampedEndptsSet;
182 p_clampedSecondDerivs = oldObject.p_clampedSecondDerivs;
183 p_clampedDerivFirstPt = oldObject.p_clampedDerivFirstPt;
184 p_clampedDerivLastPt = oldObject.p_clampedDerivLastPt;
185 p_polyNevError = oldObject.p_polyNevError;
186 p_fprimeOfx = oldObject.p_fprimeOfx;
187 }
188 catch(IException &e) { // catch exception from Init()
189 throw IException(e,
190 e.errorType(),
191 "NumericalApproximation() - Unable to copy the given object",
192 _FILEINFO_);
193 }
194 }
195
221 try {
222 if(&oldObject != this) {
224 SetInterpType(oldObject.p_itype);
225 // set class variables
226 p_x = oldObject.p_x;
227 p_y = oldObject.p_y;
228 p_dataValidated = oldObject.p_dataValidated;
229 p_clampedSecondDerivs = oldObject.p_clampedSecondDerivs;
230 p_clampedDerivFirstPt = oldObject.p_clampedDerivFirstPt;
231 p_clampedDerivLastPt = oldObject.p_clampedDerivLastPt;
232 p_polyNevError = oldObject.p_polyNevError;
233 p_fprimeOfx = oldObject.p_fprimeOfx;
234 }
235 return (*this);
236 }
237 catch(IException &e) { // catch exception from SetInterpType()
238 throw IException(e,
239 e.errorType(),
240 "operator=() - Unable to copy the given object",
241 _FILEINFO_);
242 }
243
244 }
245
259
284 return "cspline-neighborhood";
285 }
287 return "cspline-clamped";
288 }
290 return "polynomial-Neville's";
291 }
293 return "cspline-Hermite";
294 }
295 try {
296 string name = (string(GslFunctor(p_itype)->name));
298 return name + "-natural";
299 }
300 else return name;
301 }
302 catch(IException &e) { // catch exception from GslFunctor()
303 throw IException(e,
304 e.errorType(),
305 "Name() - GSL interpolation type not found",
306 _FILEINFO_);
307 }
308 }
309
335 return 4;
336 }
338 return 3;
339 }
341 return 3;
342 }
344 return 2;
345 }
346 try {
347 return (GslFunctor(p_itype)->min_size);
348 }
349 catch(IException &e) { // catch exception from GslFunctor()
350 throw IException(e,
351 e.errorType(),
352 "MinPoints() - GSL interpolation not found",
353 _FILEINFO_);
354 }
355 }
356
385 //Validates the parameter
386 if(GslInterpType(itype)) {
387 try {
388 //GslFunctor() Validates parameter for Gsl interp types
389 NumericalApproximation nam(itype);
390 return (nam.GslFunctor(itype)->min_size);
391 }
392 catch(IException &e) { // catch exception from GslFunctor()
393 throw IException(e,
394 e.errorType(),
395 "MinPoints() - GSL interpolation type not found",
396 _FILEINFO_);
397 }
398 }
400 return 4;
401 }
402 else if(itype == NumericalApproximation::CubicClamped) {
403 return 3;
404 }
406 return 3;
407 }
408 else if(itype == NumericalApproximation::CubicHermite) {
409 return 2;
410 }
412 "MinPoints() - Invalid argument. Unknown interpolation type: "
414 _FILEINFO_);
415 }
416
440 void NumericalApproximation::AddData(const double x, const double y) {
441 p_x.push_back(x);
442 p_y.push_back(y);
443 p_clampedComputed = false;
444 p_clampedEndptsSet = false;
445 p_dataValidated = false;
446 p_clampedSecondDerivs.clear();
449 p_polyNevError.clear();
450 p_interp = 0;
451 p_acc = 0;
452 return;
453 }
454
473 void NumericalApproximation::AddData(unsigned int n, double *x, double *y) {
474 for(unsigned int i = 0; i < n; i++) {
475 p_x.push_back(x[i]);
476 p_y.push_back(y[i]);
477 }
478 p_clampedComputed = false;
479 p_clampedEndptsSet = false;
480 p_dataValidated = false;
481 p_clampedSecondDerivs.clear();
484 p_polyNevError.clear();
485 p_interp = 0;
486 p_acc = 0;
487 return;
488 }
489
512 void NumericalApproximation::AddData(const vector <double> &x,
513 const vector <double> &y)
514 {
515 int n = x.size();
516 int m = y.size();
517 if(n != m) {
519 "Invalid arguments. The sizes of the input vectors do "
520 "not match",
521 _FILEINFO_);
522 }
523
524 // Avoid push_back if at all possible. These calls were consuming 10% of
525 // cam2map's run time on a line scan camera.
526 if(!p_x.empty() || !p_y.empty()) {
527 for(int i = 0; i < n; i++) {
528 p_x.push_back(x[i]);
529 p_y.push_back(y[i]);
530 }
531 }
532 else {
533 p_x = x;
534 p_y = y;
535 }
536
537 p_clampedComputed = false;
538 p_clampedEndptsSet = false;
539 p_dataValidated = false;
540 p_clampedSecondDerivs.clear();
543 p_polyNevError.clear();
544 p_interp = 0;
545 p_acc = 0;
546 return;
547 }
548
567 ReportException(IException::Programmer, "SetCubicClampedEndptDeriv()",
568 "This method is only valid for cspline-clamped interpolation, may not be used for "
569 + Name() + " interpolation",
570 _FILEINFO_);
571 }
574 p_clampedEndptsSet = true;
575 return;
576 }
577
592 void NumericalApproximation::AddCubicHermiteDeriv(unsigned int n, double *fprimeOfx) {
594 ReportException(IException::Programmer, "SetCubicHermiteDeriv()",
595 "This method is only valid for cspline-Hermite interpolation, may not be used for "
596 + Name() + " interpolation",
597 _FILEINFO_);
598 }
599 for(unsigned int i = 0; i < n; i++) {
600 p_fprimeOfx.push_back(fprimeOfx[i]);
601 }
602 return;
603 }
618 const vector <double> &fprimeOfx) {
620 ReportException(IException::Programmer, "SetCubicHermiteDeriv()",
621 "This method is only valid for cspline-Hermite interpolation, may not be used for "
622 + Name() + " interpolation",
623 _FILEINFO_);
624 }
625
626 // Avoid push_back if at all possible.
627 if(!p_fprimeOfx.empty()) {
628 for(unsigned int i = 0; i < fprimeOfx.size(); i++) {
629 p_fprimeOfx.push_back(fprimeOfx[i]);
630 }
631 }
632 else {
633 p_fprimeOfx = fprimeOfx;
634 }
635
636 return;
637 }
652 double fprimeOfx) {
654 ReportException(IException::Programmer, "SetCubicHermiteDeriv()",
655 "This method is only valid for cspline-Hermite interpolation, may not be used for "
656 + Name() + " interpolation",
657 _FILEINFO_);
658 }
659 p_fprimeOfx.push_back(fprimeOfx);
660 return;
661 }
662
688 try {
690 ReportException(IException::Programmer, "CubicClampedSecondDerivatives()",
691 "This method is only valid for cspline-clamped interpolation type may not be used for "
692 + Name() + " interpolation",
693 _FILEINFO_);
696 }
697 catch(IException &e) { // catch exception from ComputeCubicClamped()
698 throw IException(e,
699 e.errorType(),
700 "CubicClampedSecondDerivatives() - Unable to compute clamped cubic spline interpolation",
701 _FILEINFO_);
702 }
703 }
704
724 try {
726 // if GSL interpolation type, we need to compute, if not already done
727 if(!GslComputed()) ComputeGsl();
728 return (p_interp->interp->xmin);
729 }
730 else {
732 return *min_element(p_x.begin(), p_x.end());
733 }
734 }
735 catch(IException &e) { // catch exception from ComputeGsl() or ValidateDataSet()
736 throw IException(e,
737 e.errorType(),
738 "DomainMinimum() - Unable to calculate the domain minimum for the data set",
739 _FILEINFO_);
740 }
741 }
742
761 try {
763 // if GSL interpolation type, we need to compute, if not already done
764 if(!GslComputed()) ComputeGsl();
765 return (p_interp->interp->xmax);
766 }
767 else {
769 return *max_element(p_x.begin(), p_x.end());
770 }
771 }
772 catch(IException &e) { // catch exception from ComputeGsl() or ValidateDataSet()
773 throw IException(e,
774 e.errorType(),
775 "DomainMaximum() - Unable to calculate the domain maximum for the data set",
776 _FILEINFO_);
777 }
778 }
779
794 return binary_search(p_x.begin(), p_x.end(), x);
795 }
796
836 double NumericalApproximation::Evaluate(const double a, const ExtrapType &etype) {
837 try {
838 // a is const, so we must set up temporary variable in case value needs to be changed
839 double a0;
840 if(InsideDomain(a)) // this will validate data set, if not already done
841 a0 = a;
842 else a0 = ValueToExtrapolate(a, etype);
843 // perform interpolation/extrapoltion
845 return EvaluateCubicNeighborhood(a0);
846 }
848 p_polyNevError.clear();
849 return EvaluatePolynomialNeville(a0);
850 }
852 // if cubic clamped, we need to compute, if not already done
854 return EvaluateCubicClamped(a0);
855 }
857 return EvaluateCubicHermite(a0);
858 }
859 // if GSL interpolation type we need to compute, if not already done
860 if(!GslComputed()) ComputeGsl();
861 double result;
862 GslIntegrityCheck(gsl_spline_eval_e(p_interp, a0, p_acc, &result), _FILEINFO_);
863 return (result);
864 }
865 catch(IException &e) { // catch exception from EvaluateCubicNeighborhood(), EvaluateCubicClamped(), EvaluatePolynomialNeville(), GslIntegrityCheck()
866 throw IException(e,
867 e.errorType(),
868 "Evaluate() - Unable to evaluate the function at the point a = "
869 + IString(a),
870 _FILEINFO_);
871 }
872 }
873
912 vector <double> NumericalApproximation::Evaluate(const vector <double> &a, const ExtrapType &etype) {
913 try {
915 // cubic neighborhood has it's own method that will take entire vector
916 // this is faster than looping through values calling Evaluate(double)
917 // for each component of the passed in vector
918 return EvaluateCubicNeighborhood(a, etype);
919 }
920 vector <double> result(a.size());
922 // cannot loop through values calling Evaluate(double)
923 // because it will clear the p_polyNevError vector each time
924 p_polyNevError.clear();
925 for(unsigned int i = 0; i < result.size(); i++) {
926 double a0;
927 if(InsideDomain(a[i]))
928 a0 = a[i];
929 else a0 = ValueToExtrapolate(a[i], etype);
930 result[i] = EvaluatePolynomialNeville(a0);
931 }
932 return result;
933 }
934 // cubic-clamped, cubic-Hermite and gsl types can be done by calling Evaluate(double)
935 // for each value of the passed in vector
936 for(unsigned int i = 0; i < result.size(); i++) {
937 result[i] = Evaluate(a[i], etype);
938 }
939 return result;
940 }
941 catch(IException &e) { // catch exception from EvaluateCubicNeighborhood(), EvaluateCubicClamped(), EvaluatePolynomialNeville(), GslIntegrityCheck()
942 throw IException(e,
943 e.errorType(),
944 "Evaluate() - Unable to evaluate the function at the given vector of points",
945 _FILEINFO_);
946 }
947 }
948
972 ReportException(IException::Programmer, "PolynomialNevilleErrorEstimate()",
973 "This method is only valid for polynomial-Neville's, may not be used for "
974 + Name() + " interpolation",
975 _FILEINFO_);
976 }
977 if(p_polyNevError.empty()) {
978 ReportException(IException::Programmer, "PolynomialNevilleErrorEstimate()",
979 "Error not calculated. This method only valid after Evaluate() has been called",
980 _FILEINFO_);
981 }
982 return p_polyNevError;
983 }
984
1021 try { // we need to compute, if not already done
1022 if(!GslComputed()) ComputeGsl();
1023 }
1024 catch(IException &e) { // catch exception from ComputeGsl()
1025 throw IException(e,
1026 e.errorType(),
1027 "GslFirstDerivative() - Unable to compute the first derivative at a = "
1028 + IString(a) + " using the GSL interpolation",
1029 _FILEINFO_);
1030 }
1031 if(!InsideDomain(a)) {
1032 ReportException(IException::Programmer, "GslFirstDerivative()",
1033 "Invalid argument. Value entered, a = "
1034 + IString(a) + ", is outside of domain = ["
1035 + IString(DomainMinimum()) + ", "
1036 + IString(DomainMaximum()) + "]",
1037 _FILEINFO_);
1038 }
1039 if(!GslInterpType(p_itype)) {
1040 ReportException(IException::Programmer, "GslFirstDerivative()",
1041 "Method only valid for GSL interpolation types, may not be used for "
1042 + Name() + " interpolation",
1043 _FILEINFO_);
1044 }
1045 try {
1046 double value;
1047 GslIntegrityCheck(gsl_spline_eval_deriv_e(p_interp, a, p_acc, &value), _FILEINFO_);
1048 return (value);
1049 }
1050 catch(IException &e) { // catch exception from GslIntegrityCheck()
1051 throw IException(e,
1052 e.errorType(),
1053 "GslFirstDerivative() - Unable to compute the first derivative at a = "
1054 + IString(a) + ". GSL integrity check failed",
1055 _FILEINFO_);
1056 }
1057 }
1058
1072 if(p_itype != NumericalApproximation::CubicHermite) { //??? is this necessary??? create single derivative method with GSL?
1073 ReportException(IException::User, "EvaluateCubicHermiteFirstDeriv()",
1074 "This method is only valid for cspline-Hermite interpolation, may not be used for "
1075 + Name() + " interpolation",
1076 _FILEINFO_);
1077 }
1078 if(p_fprimeOfx.size() != Size()) {
1079 ReportException(IException::User, "EvaluateCubicHermiteFirstDeriv()",
1080 "Invalid arguments. The size of the first derivative vector does not match the number of (x,y) data points.",
1081 _FILEINFO_);
1082 }
1083 // find the interval in which "a" exists
1084 int lowerIndex = FindIntervalLowerIndex(a);
1085
1086 // we know that "a" is within the domain since this is verified in
1087 // Evaluate() before this method is called, thus n <= Size()
1088 if(a == p_x[lowerIndex]) {
1089 return p_fprimeOfx[lowerIndex];
1090 }
1091 if(a == p_x[lowerIndex+1]) {
1092 return p_fprimeOfx[lowerIndex+1];
1093 }
1094
1095 double x0, x1, y0, y1, m0, m1;
1096 // a is contained within the interval (x0,x1)
1097 x0 = p_x[lowerIndex];
1098 x1 = p_x[lowerIndex+1];
1099 // the corresponding known y-values for x0 and x1
1100 y0 = p_y[lowerIndex];
1101 y1 = p_y[lowerIndex+1];
1102 // the corresponding known tangents (slopes) at (x0,y0) and (x1,y1)
1103 m0 = p_fprimeOfx[lowerIndex];
1104 m1 = p_fprimeOfx[lowerIndex+1];
1105
1106 double h, t;
1107 h = x1 - x0;
1108 t = (a - x0) / h;
1109 if(h != 0.) {
1110 return ((6 * t * t - 6 * t) * y0 + (3 * t * t - 4 * t + 1) * h * m0 + (-6 * t * t + 6 * t) * y1 + (3 * t * t - 2 * t) * h * m1) / h;
1111 }
1112 else {
1113 return 0; // Should never happen
1114 }
1115 }
1116
1159 double NumericalApproximation::BackwardFirstDifference(const double a, const unsigned int n, const double h) {
1160 if(!InsideDomain(a)) {
1161 ReportException(IException::Programmer, "BackwardFirstDifference()",
1162 "Invalid argument. Value entered, a = "
1163 + IString(a) + ", is outside of domain = ["
1164 + IString(DomainMinimum()) + ", "
1165 + IString(DomainMaximum()) + "]",
1166 _FILEINFO_);
1167 }
1168 if(!InsideDomain(a - (n - 1)*h)) {
1169 ReportException(IException::Programmer, "BackwardFirstDifference()",
1170 "Formula steps outside of domain. For "
1171 + IString((int) n) + "-point backward difference, a-(n-1)h = "
1172 + IString(a - (n - 1)*h) + " is smaller than domain min = "
1174 + ". Try forward difference or use smaller value for h or n",
1175 _FILEINFO_);
1176 }
1178 vector <double> f;
1179 double xi;
1180 try {
1181 for(double i = 0; i < n; i++) {
1182 xi = a + h * (i - (n - 1));
1183 f.push_back(Evaluate(xi)); // allow ExtrapType = ThrowError (default)
1184 }
1185 }
1186 catch(IException &e) { // catch exception from Evaluate()
1187 throw IException(e,
1188 e.errorType(),
1189 "BackwardFirstDifference() - Unable to calculate backward first difference for (a, n, h) = ("
1190 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1191 _FILEINFO_);
1192 }
1193 switch(n) {
1194 case 2:
1195 return (-f[0] + f[1]) / h; //2pt backward
1196 case 3:
1197 return (3 * f[2] - 4 * f[1] + f[0]) / (2 * h); //3pt backward
1198 default:
1200 "BackwardFirstDifference() - Invalid argument. There is no "
1201 + IString((int) n) + "-point backward difference formula in use",
1202 _FILEINFO_);
1203 }
1204 }
1205
1206
1248 double NumericalApproximation::ForwardFirstDifference(const double a, const unsigned int n, const double h) {
1249 if(!InsideDomain(a)) {
1250 ReportException(IException::Programmer, "ForwardFirstDifference()",
1251 "Invalid argument. Value entered, a = "
1252 + IString(a) + ", is outside of domain = ["
1253 + IString(DomainMinimum()) + ", "
1254 + IString(DomainMaximum()) + "]",
1255 _FILEINFO_);
1256 }
1257 if(!InsideDomain(a + (n - 1)*h)) {
1258 ReportException(IException::Programmer, "ForwardFirstDifference()",
1259 "Formula steps outside of domain. For "
1260 + IString((int) n) + "-point forward difference, a+(n-1)h = "
1261 + IString(a + (n - 1)*h) + " is greater than domain max = "
1263 + ". Try backward difference or use smaller value for h or n",
1264 _FILEINFO_);
1265 }
1267 vector <double> f;
1268 double xi;
1269 try {
1270 for(double i = 0; i < n; i++) {
1271 xi = a + h * i;
1272 f.push_back(Evaluate(xi));// allow ExtrapType = ThrowError (default)
1273 }
1274 }
1275 catch(IException &e) { // catch exception from Evaluate()
1276 throw IException(e,
1277 e.errorType(),
1278 "ForwardFirstDifference() - Unable to calculate forward first difference for (a, n, h) = ("
1279 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1280 _FILEINFO_);
1281 }
1282 switch(n) {
1283 case 2:
1284 return (-f[0] + f[1]) / h; //2pt forward
1285 case 3:
1286 return (-3 * f[0] + 4 * f[1] - f[2]) / (2 * h); //3pt forward
1287 default:
1289 "ForwardFirstDifference() - Invalid argument. There is no "
1290 + IString((int) n) + "-point forward difference formula in use",
1291 _FILEINFO_);
1292 }
1293 }
1294
1295
1338 double NumericalApproximation::CenterFirstDifference(const double a, const unsigned int n, const double h) {
1339 if(!InsideDomain(a)) {
1340 ReportException(IException::Programmer, "CenterFirstDifference()",
1341 "Invalid argument. Value entered, a = "
1342 + IString(a) + ", is outside of domain = ["
1343 + IString(DomainMinimum()) + ", "
1344 + IString(DomainMaximum()) + "]",
1345 _FILEINFO_);
1346 }
1347 if(!InsideDomain(a + (n - 1)*h) || !InsideDomain(a - (n - 1)*h)) {
1348 ReportException(IException::Programmer, "CenterFirstDifference()",
1349 "Formula steps outside of domain. For "
1350 + IString((int) n) + "-point center difference, a-(n-1)h = "
1351 + IString(a - (n - 1)*h) + " or a+(n-1)h = "
1352 + IString(a + (n - 1)*h) + " is out of domain = ["
1354 + "]. Use smaller value for h or n",
1355 _FILEINFO_);
1356 }
1358 vector <double> f;
1359 double xi;
1360 try {
1361 for(double i = 0; i < n; i++) {
1362 xi = a + h * (i - (n - 1) / 2);
1363 f.push_back(Evaluate(xi));// allow ExtrapType = ThrowError (default)
1364 }
1365 }
1366 catch(IException &e) { // catch exception from Evaluate()
1367 throw IException(e,
1368 e.errorType(),
1369 "CenterFirstDifference() - Unable to calculate center first difference for (a, n, h) = ("
1370 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1371 _FILEINFO_);
1372 }
1373 switch(n) {
1374 case 3:
1375 return (-f[0] + f[2]) / (2 * h); //3pt center
1376 case 5:
1377 return (f[0] - 8 * f[1] + 8 * f[3] - f[4]) / (12 * h); //5pt center
1378 default:
1380 "CenterFirstDifference() - Invalid argument. There is no "
1381 + IString((int) n) + "-point center difference formula in use",
1382 _FILEINFO_);
1383 }
1384 }
1385
1423 try { // we need to compute, if not already done
1424 if(!GslComputed()) ComputeGsl();
1425 }
1426 catch(IException &e) { // catch exception from ComputeGsl()
1427 throw IException(e,
1428 e.errorType(),
1429 "GslSecondDerivative() - Unable to compute the second derivative at a = "
1430 + IString(a) + " using the GSL interpolation",
1431 _FILEINFO_);
1432 }
1433 if(!InsideDomain(a))
1434 ReportException(IException::Programmer, "GslSecondDerivative()",
1435 "Invalid argument. Value entered, a = "
1436 + IString(a) + ", is outside of domain = ["
1437 + IString(DomainMinimum()) + ", "
1438 + IString(DomainMaximum()) + "]",
1439 _FILEINFO_);
1441 ReportException(IException::Programmer, "GslSecondDerivative()",
1442 "Method only valid for GSL interpolation types, may not be used for "
1443 + Name() + " interpolation",
1444 _FILEINFO_);
1445 try {
1446 // we need to compute, if not already done
1447 if(!GslComputed()) ComputeGsl();
1448 double value;
1449 GslIntegrityCheck(gsl_spline_eval_deriv2_e(p_interp, a, p_acc, &value), _FILEINFO_);
1450 return (value);
1451 }
1452 catch(IException &e) { // catch exception from GslIntegrityCheck()
1453 throw IException(e,
1454 e.errorType(),
1455 "GslSecondDerivative() - Unable to compute the second derivative at a = "
1456 + IString(a) + ". GSL integrity check failed",
1457 _FILEINFO_);
1458 }
1459 }
1460
1461
1476 ReportException(IException::User, "EvaluateCubicHermiteSecDeriv()",
1477 "This method is only valid for cspline-Hermite interpolation, may not be used for "
1478 + Name() + " interpolation",
1479 _FILEINFO_);
1480 }
1481 if(p_fprimeOfx.size() != Size()) {
1482 ReportException(IException::User, "EvaluateCubicHermiteSecDeriv()",
1483 "Invalid arguments. The size of the first derivative vector does not match the number of (x,y) data points.",
1484 _FILEINFO_);
1485 }
1486 // find the interval in which "a" exists
1487 int lowerIndex = FindIntervalLowerIndex(a);
1488 double x0, x1, y0, y1, m0, m1;
1489 // a is contained within the interval (x0,x1)
1490 x0 = p_x[lowerIndex];
1491 x1 = p_x[lowerIndex+1];
1492 // the corresponding known y-values for x0 and x1
1493 y0 = p_y[lowerIndex];
1494 y1 = p_y[lowerIndex+1];
1495 // the corresponding known tangents (slopes) at (x0,y0) and (x1,y1)
1496 m0 = p_fprimeOfx[lowerIndex];
1497 m1 = p_fprimeOfx[lowerIndex+1];
1498
1499 double h, t;
1500 h = x1 - x0;
1501 t = (a - x0) / h;
1502 if(h != 0.) {
1503 return ((12 * t - 6) * y0 + (6 * t - 4) * h * m0 + (-12 * t + 6) * y1 + (6 * t - 2) * h * m1) / h;
1504 }
1505 else {
1506 return 0; // Should never happen
1507 }
1508 }
1509
1546 double NumericalApproximation::BackwardSecondDifference(const double a, const unsigned int n, const double h) {
1547 if(!InsideDomain(a)) {
1548 ReportException(IException::Programmer, "BackwardSecondDifference()",
1549 "Invalid argument. Value entered, a = "
1550 + IString(a) + ", is outside of domain = ["
1551 + IString(DomainMinimum()) + ", "
1552 + IString(DomainMaximum()) + "]",
1553 _FILEINFO_);
1554 }
1555 if(!InsideDomain(a - (n - 1)*h)) {
1556 ReportException(IException::Programmer, "BackwardSecondDifference()",
1557 "Formula steps outside of domain. For "
1558 + IString((int) n) + "-point backward difference, a-(n-1)h = "
1559 + IString(a - (n - 1)*h) + " is smaller than domain min = "
1561 + ". Try forward difference or use smaller value for h or n",
1562 _FILEINFO_);
1563 }
1565 vector <double> f;
1566 double xi;
1567 try {
1568 for(double i = 0; i < n; i++) {
1569 xi = a + h * (i - (n - 1));
1570 f.push_back(Evaluate(xi));// allow ExtrapType = ThrowError (default)
1571 }
1572 }
1573 catch(IException &e) { // catch exception from Evaluate()
1574 throw IException(e,
1575 e.errorType(),
1576 "BackwardSecondDifference() - Unable to calculate backward second difference for (a, n, h) = ("
1577 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1578 _FILEINFO_);
1579 }
1580 switch(n) {
1581 case 3:
1582 return (f[0] - 2 * f[1] + f[2]) / (h * h); //3pt backward
1583 default:
1585 "BackwardSecondDifference() - Invalid argument. There is no "
1586 + IString((int) n) + "-point backward second difference formula in use",
1587 _FILEINFO_);
1588 }
1589 }
1590
1591
1628 double NumericalApproximation::ForwardSecondDifference(const double a, const unsigned int n, const double h) {
1629 if(!InsideDomain(a)) {
1630 ReportException(IException::Programmer, "ForwardSecondDifference()",
1631 "Invalid argument. Value entered, a = "
1632 + IString(a) + ", is outside of domain = ["
1633 + IString(DomainMinimum()) + ", "
1634 + IString(DomainMaximum()) + "]",
1635 _FILEINFO_);
1636 }
1637 if(!InsideDomain(a + (n - 1)*h)) {
1638 ReportException(IException::Programmer, "ForwardSecondDifference()",
1639 "Formula steps outside of domain. For "
1640 + IString((int) n) + "-point forward difference, a+(n-1)h = "
1641 + IString(a + (n - 1)*h) + " is greater than domain max = "
1643 + ". Try backward difference or use smaller value for h or n",
1644 _FILEINFO_);
1645 }
1647 vector <double> f;
1648 double xi;
1649 try {
1650 for(double i = 0; i < n; i++) {
1651 xi = a + h * i;
1652 f.push_back(Evaluate(xi));// allow ExtrapType = ThrowError (default)
1653 }
1654 }
1655 catch(IException &e) { // catch exception from Evaluate()
1656 throw IException(e,
1657 e.errorType(),
1658 "ForwardSecondDifference() - Unable to calculate forward second difference for (a, n, h) = ("
1659 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1660 _FILEINFO_);
1661 }
1662 switch(n) {
1663 case 3:
1664 return (f[0] - 2 * f[1] + f[2]) / (h * h); //3pt forward
1665 default:
1667 "ForwardSecondDifference() - Invalid argument. There is no "
1668 + IString((int) n) + "-point forward second difference formula in use",
1669 _FILEINFO_);
1670 }
1671 }
1672
1673
1716 double NumericalApproximation::CenterSecondDifference(const double a, const unsigned int n, const double h) {
1717 if(!InsideDomain(a)) {
1718 ReportException(IException::Programmer, "CenterSecondDifference()",
1719 "Invalid argument. Value entered, a = "
1720 + IString(a) + ", is outside of domain = ["
1721 + IString(DomainMinimum()) + ", "
1722 + IString(DomainMaximum()) + "]",
1723 _FILEINFO_);
1724 }
1725 if(!InsideDomain(a + (n - 1)*h) || !InsideDomain(a - (n - 1)*h)) {
1726 ReportException(IException::Programmer, "CenterSecondDifference()",
1727 "Formula steps outside of domain. For "
1728 + IString((int) n) + "-point center difference, a-(n-1)h = "
1729 + IString(a - (n - 1)*h) + " or a+(n-1)h = "
1730 + IString(a + (n - 1)*h) + " is out of domain = ["
1732 + "]. Use smaller value for h or n",
1733 _FILEINFO_);
1734 }
1736 vector <double> f;
1737 double xi;
1738 try {
1739 for(double i = 0; i < n; i++) {
1740 xi = a + h * (i - (n - 1) / 2);
1741 f.push_back(Evaluate(xi));// allow ExtrapType = ThrowError (default)
1742 }
1743 }
1744 catch(IException &e) { // catch exception from Evaluate()
1745 throw IException(e,
1746 e.errorType(),
1747 "CenterSecondDifference() - Unable to calculate center second difference for (a, n, h) = ("
1748 + IString(a) + ", " + IString((int) n) + ", " + IString(h) + ")",
1749 _FILEINFO_);
1750 }
1751 switch(n) {
1752 case 3:
1753 return (f[0] - 2 * f[1] + f[2]) / (h * h); //3pt center
1754 case 5:
1755 return (-f[0] + 16 * f[1] - 30 * f[2] + 16 * f[3] - f[4]) / (12 * h * h); //5pt center
1756 default:
1758 "CenterSecondDifference() - Invalid argument. There is no "
1759 + IString((int) n) + "-point center second difference formula in use",
1760 _FILEINFO_);
1761 }
1762 }
1763
1797 double NumericalApproximation::GslIntegral(const double a, const double b) {
1798 try { // we need to compute, if not already done
1799 if(!GslComputed()) ComputeGsl();
1800 }
1801 catch(IException &e) { // catch exception from ComputeGsl()
1802 throw IException(e,
1803 e.errorType(),
1804 "GslIntegral() - Unable to compute the integral on the interval (a,b) = ("
1805 + IString(a) + ", " + IString(b)
1806 + ") using the GSL interpolation",
1807 _FILEINFO_);
1808 }
1809 if(a > b) {
1810 ReportException(IException::Programmer, "GslIntegral()",
1811 "Invalid interval entered: [a,b] = ["
1812 + IString(a) + ", " + IString(b) + "]",
1813 _FILEINFO_);
1814 }
1815 if(!InsideDomain(a) || !InsideDomain(b)) {
1816 ReportException(IException::Programmer, "GslIntegral()",
1817 "Invalid arguments. Interval entered ["
1818 + IString(a) + ", " + IString(b)
1819 + "] is not contained within domain ["
1820 + IString(DomainMinimum()) + ", "
1821 + IString(DomainMaximum()) + "]",
1822 _FILEINFO_);
1823 }
1824 if(!GslInterpType(p_itype)) {
1825 ReportException(IException::Programmer, "GslIntegral()",
1826 "Method only valid for GSL interpolation types, may not be used for "
1827 + Name() + " interpolation",
1828 _FILEINFO_);
1829 }
1830 try {
1831 double value;
1832 GslIntegrityCheck(gsl_spline_eval_integ_e(p_interp, a, b, p_acc, &value), _FILEINFO_);
1833 return (value);
1834 }
1835 catch(IException &e) { // catch exception from GslIntegrityCheck()
1836 throw IException(e,
1837 e.errorType(),
1838 "GslIntegral() - Unable to compute the integral on the interval (a,b) = ("
1839 + IString(a) + ", " + IString(b)
1840 + "). GSL integrity check failed",
1841 _FILEINFO_);
1842 }
1843 }
1844
1868 double NumericalApproximation::TrapezoidalRule(const double a, const double b) {
1869 try {
1870 //n is the number of points used in the formula of the integration type chosen
1871 unsigned int n = 2;
1872 double result = 0;
1873 vector <double> f = EvaluateForIntegration(a, b, n);
1874 double h = f.back();
1875 f.pop_back();
1876 //Compute the integral using composite trapezoid formula
1877 int ii;
1878 for(unsigned int i = 0; i < (f.size() - 1) / (n - 1); i++) {
1879 ii = (i + 1) * (n - 1);
1880 result += (f[ii-1] + f[ii]) * h / 2;
1881 }
1882 return result;
1883 }
1884 catch(IException &e) { // catch exception from EvaluateForIntegration()
1885 throw IException(e,
1886 e.errorType(),
1887 "TrapezoidalRule() - Unable to calculate the integral on the interval (a,b) = ("
1888 + IString(a) + ", " + IString(b)
1889 + ") using the trapeziodal rule",
1890 _FILEINFO_);
1891 }
1892 }
1893
1920 double NumericalApproximation::Simpsons3PointRule(const double a, const double b) {
1921 try {
1922 //n is the number of points used in the formula of the integration type chosen
1923 unsigned int n = 3;
1924 double result = 0;
1925 vector <double> f = EvaluateForIntegration(a, b, n);
1926 double h = f.back();
1927 f.pop_back();
1928 int ii;
1929 for(unsigned int i = 0; i < (f.size() - 1) / (n - 1); i++) {
1930 ii = (i + 1) * (n - 1);
1931 result += (f[ii-2] + 4 * f[ii-1] + f[ii]) * h / 3;
1932 }
1933 return result;
1934 }
1935 catch(IException &e) { // catch exception from EvaluateForIntegration()
1936 throw IException(e,
1937 e.errorType(),
1938 "Simpsons3PointRule() - Unable to calculate the integral on the interval (a,b) = ("
1939 + IString(a) + ", " + IString(b)
1940 + ") using Simpson's 3 point rule",
1941 _FILEINFO_);
1942 }
1943 }
1944
1945
1972 double NumericalApproximation::Simpsons4PointRule(const double a, const double b) {
1973 try {
1974 //n is the number of points used in the formula of the integration type chosen
1975 unsigned int n = 4;
1976 double result = 0;
1977 vector <double> f = EvaluateForIntegration(a, b, n);
1978 double h = f.back();
1979 f.pop_back();
1980 int ii;
1981 for(unsigned int i = 0; i < (f.size() - 1) / (n - 1); i++) {
1982 ii = (i + 1) * (n - 1);
1983 result += (f[ii-3] + 3 * f[ii-2] + 3 * f[ii-1] + f[ii]) * h * 3 / 8;
1984 }
1985 return result;
1986 }
1987 catch(IException &e) { // catch exception from EvaluateForIntegration()
1988 throw IException(e,
1989 e.errorType(),
1990 "Simpsons4PointRule() - Unable to calculate the integral on the interval (a,b) = ("
1991 + IString(a) + ", " + IString(b)
1992 + ") using Simpson's 4 point rule",
1993 _FILEINFO_);
1994 }
1995 }
1996
1997
2027 double NumericalApproximation::BoolesRule(const double a, const double b) {
2028 try {
2029 //n is the number of points used in the formula of the integration type chosen
2030 unsigned int n = 5;
2031 double result = 0;
2032 vector <double> f = EvaluateForIntegration(a, b, n);
2033 double h = f.back();
2034 f.pop_back();
2035 int ii;
2036 for(unsigned int i = 0; i < (f.size() - 1) / (n - 1); i++) {
2037 ii = (i + 1) * (n - 1);
2038 result += (7 * f[ii-4] + 32 * f[ii-3] + 12 * f[ii-2] + 32 * f[ii-1] + 7 * f[ii]) * h * 2 / 45;
2039 }
2040 return result;
2041 }
2042 catch(IException &e) { // catch exception from EvaluateForIntegration()
2043 throw IException(e,
2044 e.errorType(),
2045 "BoolesRule() - Unable to calculate the integral on the interval (a,b) = ("
2046 + IString(a) + ", " + IString(b)
2047 + ") using Boole's rule",
2048 _FILEINFO_);
2049 }
2050 }
2051
2096 double NumericalApproximation::RefineExtendedTrap(double a, double b, double s, unsigned int n) {
2097 // This method was derived from an algorithm in the text
2098 // Numerical Recipes in C: The Art of Scientific Computing
2099 // Section 4.2 by Flannery, Press, Teukolsky, and Vetterling
2100 try {
2101 if(n == 1) {
2102 double begin, end;
2104 // if a or b are outside the domain, return y-value of nearest endpoint
2107 }
2108 else {
2109 // if a or b are outside the domain, return extrapolated y-value
2112 }
2113 return (0.5 * (b - a) * (begin + end));
2114 }
2115 else {
2116 int it;
2117 double delta, tnm, x, sum;
2118 it = (int)(pow(2.0, (double)(n - 2)));
2119 tnm = it;
2120 delta = (b - a) / tnm; // spacing of the points to be added
2121 x = a + 0.5 * delta;
2122 sum = 0.0;
2123 for(int i = 0; i < it; i++) {
2126 }
2127 else {
2129 }
2130 x = x + delta;
2131 }
2132 return (0.5 * (s + (b - a) * sum / tnm));// return refined value of s
2133 }
2134 }
2135 catch(IException &e) { // catch exception from Evaluate()
2136 throw IException(e,
2137 e.errorType(),
2138 "RefineExtendedTrap() - Unable to calculate the integral on the interval (a,b) = ("
2139 + IString(a) + ", " + IString(b)
2140 + ") using the extended trapeziodal rule",
2141 _FILEINFO_);
2142 }
2143 }
2144
2179 double NumericalApproximation::RombergsMethod(double a, double b) {
2180 // This method was derived from an algorithm in the text
2181 // Numerical Recipes in C: The Art of Scientific Computing
2182 // Section 4.3 by Flannery, Press, Teukolsky, and Vetterling
2183 int maxits = 20;
2184 double dss = 0; // error estimate for
2185 double h[maxits+1]; // relative stepsizes for trap
2186 double trap[maxits+1]; // successive trapeziodal approximations
2187 double epsilon; // desired fractional accuracy
2188 double epsilon2;// desired fractional accuracy
2189 double ss; // result
2190
2191 epsilon = 1.0e-4;
2192 epsilon2 = 1.0e-6;
2193
2194 h[0] = 1.0;
2195 try {
2197 for(int i = 0; i < maxits; i++) {
2198 // i will determine number of trapezoidal partitions of area
2199 // under curve for "integration" using refined trapezoidal rule
2200 trap[i] = RefineExtendedTrap(a, b, trap[i], i + 1); // validates data here
2201 if(i >= 4) {
2202 interp.AddData(5, &h[i-4], &trap[i-4]);
2203 // PolynomialNeville can extrapolate data outside of domain
2204 ss = interp.Evaluate(0.0, NumericalApproximation::Extrapolate);
2205 dss = interp.PolynomialNevilleErrorEstimate()[0];
2206 interp.Reset();
2207 // we work only until our necessary accuracy is achieved
2208 if(fabs(dss) <= epsilon * fabs(ss)) return ss;
2209 if(fabs(dss) <= epsilon2) return ss;
2210 }
2211 trap[i+1] = trap[i];
2212 h[i+1] = 0.25 * h[i];
2213 // This is a key step: the factor is 0.25d0 even though
2214 // the stepsize is decreased by 0.5d0. This makes the
2215 // extraplolation a polynomial in h-squared as allowed
2216 // by the equation from Numerical Recipes 4.2.1 pg.132,
2217 // not just a polynomial in h.
2218 }
2219 }
2220 catch(IException &e) { // catch error from RefineExtendedTrap, Constructor, Evaluate, PolynomialNevilleErrorEstimate
2221 throw IException(e,
2222 e.errorType(),
2223 "RombergsMethod() - Unable to calculate the integral on the interval (a,b) = ("
2224 + IString(a) + ", " + IString(b)
2225 + ") using Romberg's method",
2226 _FILEINFO_);
2227 }
2229 "RombergsMethod() - Unable to calculate the integral using RombergsMethod() - Failed to converge in "
2230 + IString(maxits) + " iterations",
2231 _FILEINFO_);
2232 }
2233
2253 p_clampedComputed = false;
2254 p_clampedEndptsSet = false;
2255 p_dataValidated = false;
2256 p_x.clear();
2257 p_y.clear();
2258 p_clampedSecondDerivs.clear();
2261 p_polyNevError.clear();
2262 p_fprimeOfx.clear();
2263 return;
2264 }
2265
2284 try {
2285 Reset();
2286 SetInterpType(itype);
2287 return;
2288 }
2289 catch(IException &e) { // catch exception from SetInterpType()
2290 throw IException(e,
2291 e.errorType(),
2292 "Reset() - Unable to reset interpolation type",
2293 _FILEINFO_);
2294 }
2295 }
2296
2320 // Validates the parameter
2321 if(GslInterpType(itype)) {
2322 try {
2323 GslFunctor(itype);
2324 }
2325 catch(IException &e) { // catch exception from GslFunctor()
2326 throw IException(e,
2327 e.errorType(),
2328 "SetInterpType() - Unable to set interpolation type",
2329 _FILEINFO_);
2330 }
2331 }
2332 else if(itype > 9) { // there are currently 9 interpolation types
2333 ReportException(IException::Programmer, "SetInterpType()",
2334 "Invalid argument. Unknown interpolation type: "
2336 _FILEINFO_);
2337 }
2338 // p_x, p_y are kept and p_itype is replaced
2339 p_itype = itype;
2340 // reset state of class variables that are InterpType dependent //??? should we keep some of this info?
2341 p_dataValidated = false;
2342 p_clampedComputed = false;
2343 p_clampedEndptsSet = false;
2344 p_clampedSecondDerivs.clear();
2347 p_polyNevError.clear();
2348 p_fprimeOfx.clear();
2349 }
2350
2376 if(p_interpFunctors.empty()) {
2377 p_interpFunctors.insert(make_pair(Linear, gsl_interp_linear));
2378 p_interpFunctors.insert(make_pair(Polynomial, gsl_interp_polynomial));
2379 p_interpFunctors.insert(make_pair(CubicNatural, gsl_interp_cspline));
2380 p_interpFunctors.insert(make_pair(CubicNatPeriodic, gsl_interp_cspline_periodic));
2381 p_interpFunctors.insert(make_pair(Akima, gsl_interp_akima));
2382 p_interpFunctors.insert(make_pair(AkimaPeriodic, gsl_interp_akima_periodic));
2383 }
2384
2385 p_acc = 0;
2386 p_interp = 0;
2387 try {
2388 SetInterpType(itype);
2389 }
2390 catch(IException &e) { // catch exception from SetInterpType()
2391 throw IException(e,
2392 e.errorType(),
2393 "Init() - Unable to initialize NumericalApproximation object",
2394 _FILEINFO_);
2395 }
2396 // Turn all GSL error handling off...repeatedly, every time this routine is
2397 // called.
2398 gsl_set_error_handler_off();
2399 }
2400
2419 if(itype == NumericalApproximation::Linear) return true;
2420 if(itype == NumericalApproximation::Polynomial) return true;
2421 if(itype == NumericalApproximation::CubicNatural) return true;
2422 if(itype == NumericalApproximation::CubicNatPeriodic) return true;
2423 if(itype == NumericalApproximation::Akima) return true;
2424 if(itype == NumericalApproximation::AkimaPeriodic) return true;
2425 return false;
2426 }
2427
2444 void NumericalApproximation::GslAllocation(unsigned int npoints) {
2446 //get pointer to accelerator object (iterator for interpolation lookups)
2447 p_acc = gsl_interp_accel_alloc();
2448 //get pointer to interpolation object of interp type given for npoints datapoints
2449 p_interp = gsl_spline_alloc(GslFunctor(p_itype), npoints);
2450 return;
2451 }
2452
2467 if(p_interp) gsl_spline_free(p_interp);
2468 if(p_acc) gsl_interp_accel_free(p_acc);
2469 p_acc = 0;
2470 p_interp = 0;
2471 return;
2472 }
2473
2493 const {
2494 FunctorConstIter fItr = p_interpFunctors.find(itype);
2495 if(fItr == p_interpFunctors.end()) {
2497 "Invalid argument. Unable to find GSL interpolator with id = "
2499 _FILEINFO_);
2500 }
2501 return (fItr->second);
2502 }
2503
2523 void NumericalApproximation::GslIntegrityCheck(int gsl_status, const char *src, int line)
2524 {
2525 if(gsl_status != GSL_SUCCESS) {
2526 if(gsl_status != GSL_EDOM) {
2527 ReportException(IException::Programmer, "GslIntegrityCheck(int,char,int)",
2528 "GslIntegrityCheck(): GSL error occured: "
2529 + string(gsl_strerror(gsl_status)), src, line);
2530 }
2531 }
2532 return;
2533 }
2534
2564 if((int) Size() < MinPoints()) {
2565 ReportException(IException::Programmer, "ValidateDataSet()",
2566 Name() + " interpolation requires a minimum of "
2567 + IString(MinPoints()) + " data points - currently have "
2568 + IString((int) Size()),
2569 _FILEINFO_);
2570 }
2571 for(unsigned int i = 1; i < Size(); i++) {
2572 // Check for uniqueness -- this applies to all interpolation types
2573 if(p_x[i-1] == p_x[i]) {
2574 ReportException(IException::Programmer, "ValidateDataSet()",
2575 "Invalid data set, x-values must be unique: \n\t\tp_x["
2576 + IString((int) i - 1) + "] = " + IString(p_x[i-1])
2577 + " = p_x[" + IString((int) i) + "]",
2578 _FILEINFO_);
2579 }
2580 if(p_x[i-1] > p_x[i]) {
2581 // Verify that data set is in ascending order --
2582 // this does not apply to PolynomialNeville, which appears to get the same results with unsorted data
2584 ReportException(IException::Programmer, "ValidateDataSet()",
2585 "Invalid data set, x-values must be in ascending order for "
2586 + Name() + " interpolation: \n\t\tx["
2587 + IString((int) i - 1) + "] = " + IString(p_x[i-1]) + " > x["
2588 + IString((int) i) + "] = " + IString(p_x[i]),
2589 _FILEINFO_);
2590 }
2591 }
2592 }
2594 if(p_y[0] != p_y[Size()-1]) {
2595 ReportException(IException::Programmer, "ValidateDataSet()",
2596 "First and last points of the data set must have the same y-value for "
2597 + Name() + "interpolation to prevent discontinuity at the boundary",
2598 _FILEINFO_);
2599 }
2600 }
2601 p_dataValidated = true;
2602 return;
2603 }
2604
2605
2620 try {
2621 if(a + DBL_EPSILON < DomainMinimum()) {
2622 return false;
2623 }
2624 if(a - DBL_EPSILON > DomainMaximum()) {
2625 return false;
2626 }
2627 }
2628 catch(IException &e) { // catch exception from DomainMinimum(), DomainMaximum()
2629 throw IException(e,
2630 e.errorType(),
2631 "InsideDomain() - Unable to compute domain boundaries",
2632 _FILEINFO_);
2633 }
2634 return true;
2635 }
2636
2654 if(GslInterpType(p_itype)) return ((p_interp) && (p_acc));
2655 else
2657 "GslComputed() - Method only valid for GSL interpolation types, may not be used for "
2658 + Name() + " interpolation",
2659 _FILEINFO_);
2660 }
2661
2684 try {
2685 if(GslComputed()) return;
2688 GslIntegrityCheck(gsl_spline_init(p_interp, &p_x[0], &p_y[0], Size()), _FILEINFO_);
2689 return;
2690 }
2691 catch(IException &e) { // catch exception from ValidateDataSet(), GslIntegrityCheck()
2692 throw IException(e,
2693 e.errorType(),
2694 "ComputeGsl() - Unable to compute GSL interpolation",
2695 _FILEINFO_);
2696 }
2697 }
2698
2736 // This method was derived from an algorithm in the text
2737 // Numerical Recipes in C: The Art of Scientific Computing
2738 // Section 3.3 by Flannery, Press, Teukolsky, and Vetterling
2739
2740 if(!p_dataValidated) {
2741 try {
2743 }
2744 catch(IException &e) { // catch exception from ValidateDataSet()
2745 throw IException(e,
2746 e.errorType(),
2747 "ComputeCubicClamped() - Unable to compute cubic clamped interpolation",
2748 _FILEINFO_);
2749 }
2750 }
2751 if(!p_clampedEndptsSet) {
2752 ReportException(IException::Programmer, "ComputeCubicClamped()",
2753 "Must set endpoint derivative values after adding data in order to compute cubic spline with clamped boundary conditions",
2754 _FILEINFO_);
2755 }
2756 int n = Size();
2757 p_clampedSecondDerivs.resize(n);
2758 double u[n];
2759 double p, sig, qn, un;
2760
2761 if(p_clampedDerivFirstPt > 0.99e30) {
2762 p_clampedSecondDerivs[0] = 0.0;//natural boundary conditions are used if deriv of first value is greater than 10^30
2763 u[0] = 0.0;
2764 }
2765 else {
2766 p_clampedSecondDerivs[0] = -0.5;// clamped conditions are used
2767 u[0] = (3.0 / (p_x[1] - p_x[0])) * ((p_y[1] - p_y[0]) / (p_x[1] - p_x[0]) - p_clampedDerivFirstPt);
2768 }
2769 for(int i = 1; i < n - 1; i++) { // decomposition loop of the tridiagonal algorithm
2770 sig = (p_x[i] - p_x[i-1]) / (p_x[i+1] - p_x[i-1]);
2771 p = sig * p_clampedSecondDerivs[i-1] + 2.0;
2772 p_clampedSecondDerivs[i] = (sig - 1.0) / p;
2773 u[i] = (6.0 * ((p_y[i+1] - p_y[i]) / (p_x[i+1] - p_x[i]) - (p_y[i] - p_y[i-1]) /
2774 (p_x[i] - p_x[i-1])) / (p_x[i+1] - p_x[i-1]) - sig * u[i-1]) / p;
2775 }
2776 if(p_clampedDerivLastPt > 0.99e30) { // upper boundary is natural
2777 qn = 0.0;
2778 un = 0.0;
2779 }
2780 else {// upper boundary is clamped
2781 qn = 0.5;
2782 un = (3.0 / (p_x[n-1] - p_x[n-2])) * (p_clampedDerivLastPt - (p_y[n-1] - p_y[n-2]) /
2783 (p_x[n-1] - p_x[n-2]));
2784 }
2785 p_clampedSecondDerivs[n-1] = (un - qn * u[n-2]) / (qn * p_clampedSecondDerivs[n-2] + 1.0);
2786 for(int i = n - 2; i >= 0; i--) { // backsubstitution loop of the tridiagonal algorithm
2788 }
2789 p_clampedComputed = true;
2790 return;
2791 }
2792
2830 double NumericalApproximation::ValueToExtrapolate(const double a, const ExtrapType &etype) {
2833 "Invalid argument. Value entered, a = "
2834 + IString(a) + ", is outside of domain = ["
2835 + IString(DomainMinimum()) + ", "
2836 + IString(DomainMaximum()) + "]",
2837 _FILEINFO_);
2838 }
2840 if(a + DBL_EPSILON < DomainMinimum()) {
2841 return DomainMinimum();
2842 }
2843 else {
2844 return DomainMaximum(); // (a > DomainMaximum())
2845 }
2846 }
2847 else { // gsl interpolations and CubicNeighborhood cannot extrapolate
2851 "Invalid argument. Cannot extrapolate for type "
2852 + Name() + ", must choose to throw error or return nearest neighbor",
2853 _FILEINFO_);
2854 }
2855 return a;
2856 }
2857 }
2858
2894 try {
2895 int s = 0, s_old, s0;
2896 vector <double> x0(4), y0(4);
2898 for(unsigned int n = 0; n < Size(); n++) {
2899 if(p_x[n] < a) s = n;
2900 else break;
2901 }
2902 if(s < 1) s = 1;
2903 else if(s > (int) Size() - 3) s = Size() - 3;
2904 s_old = -1;
2905 s0 = s - 1;
2906 if(s_old != s0) {
2907 for(int n = 0; n < 4; n++) {
2908 x0[n] = p_x[n+s0];
2909 y0[n] = p_y[n+s0];
2910 spline.AddData(x0[n], y0[n]);
2911 }
2912 s_old = s0;
2913 }
2914 // use nearest endpoint extrapolation method for neighborhood spline
2915 // since CubicNatural can do no other
2916 return spline.Evaluate(a, NumericalApproximation::NearestEndpoint);
2917 }
2918 catch(IException &e) { // catch exception from Constructor, ComputeGsl(), Evaluate()
2919 throw IException(e,
2920 e.errorType(),
2921 "EvaluateCubicNeighborhood() - Unable to evaluate cubic neighborhood interpolation at a = "
2922 + IString(a),
2923 _FILEINFO_);
2924 }
2925 }
2926
2973 vector <double> NumericalApproximation::EvaluateCubicNeighborhood(const vector <double> &a,
2975 vector <double> result(a.size());
2976 int s_old, s0;
2977 vector <double> x0(4), y0(4);
2978 vector <int> s;
2979 s.clear();
2980 s.resize(a.size());
2981 for(unsigned int i = 0; i < a.size(); i++) {
2982 for(unsigned int n = 0; n < Size(); n++) {
2983 if(p_x[n] < a[i]) {
2984 s[i] = n;
2985 }
2986 }
2987 if(s[i] < 1) {
2988 s[i] = 1;
2989 }
2990 if(s[i] > ((int) Size()) - 3) {
2991 s[i] = Size() - 3;
2992 }
2993 }
2994 s_old = -1;
2995 try {
2997 for(unsigned int i = 0; i < a.size(); i++) {
2998 s0 = s[i] - 1;
2999 if(s_old != s0) {
3000 spline.Reset();
3001 for(int n = 0; n < 4; n++) {
3002 x0[n] = p_x[n+s0];
3003 y0[n] = p_y[n+s0];
3004 spline.AddData(x0[n], y0[n]);
3005 }
3006 s_old = s0;
3007 }
3008 double a0;
3009 // checks whether this value is in domain of the main spline
3010 if(InsideDomain(a[i]))
3011 a0 = a[i];
3012 else a0 = ValueToExtrapolate(a[i], etype);
3013 // since neighborhood spline is CubicNatural, the only extrapolation possible is NearestEndpoint
3014 result[i] = spline.Evaluate(a0, NumericalApproximation::NearestEndpoint);
3015 }
3016 return result;
3017 }
3018 catch(IException &e) { // catch exception from Constructor, ComputeGsl(), Evaluate()
3019 throw IException(e,
3020 e.errorType(),
3021 "EvaluateCubicNeighborhood() - Unable to evaluate the function at the given vector of points using cubic neighborhood interpolation",
3022 _FILEINFO_);
3023 }
3024 }
3025
3061 // This method was derived from an algorithm in the text
3062 // Numerical Recipes in C: The Art of Scientific Computing
3063 // Section 3.3 by Flannery, Press, Teukolsky, and Vetterling
3064 int n = Size();
3065 double result = 0;
3066
3067 int k;
3068 int k_Lo;
3069 int k_Hi;
3070 double h;
3071 double A;
3072 double B;
3073
3074 k_Lo = 0;
3075 k_Hi = n - 1;
3076 while(k_Hi - k_Lo > 1) {
3077 k = (k_Hi + k_Lo) / 2;
3078 if(p_x[k] > a) {
3079 k_Hi = k;
3080 }
3081 else {
3082 k_Lo = k;
3083 }
3084 }
3085
3086 h = p_x[k_Hi] - p_x[k_Lo];
3087 A = (p_x[k_Hi] - a) / h;
3088 B = (a - p_x[k_Lo]) / h;
3089 result = A * p_y[k_Lo] + B * p_y[k_Hi] + ((pow(A, 3.0) - A) *
3090 p_clampedSecondDerivs[k_Lo] + (pow(B, 3.0) - B) * p_clampedSecondDerivs[k_Hi]) * pow(h, 2.0) / 6.0;
3091 return result;
3092 }
3093
3122 // algorithm was found at en.wikipedia.org/wiki/Cubic_Hermite_spline
3123 // it seems to produce same answers, as the NumericalAnalysis book
3124
3125 if(p_fprimeOfx.size() != Size()) {
3126 ReportException(IException::User, "EvaluateCubicHermite()",
3127 "Invalid arguments. The size of the first derivative vector does not match the number of (x,y) data points.",
3128 _FILEINFO_);
3129 }
3130 // find the interval in which "a" exists
3131 int lowerIndex = FindIntervalLowerIndex(a);
3132
3133 // we know that "a" is within the domain since this is verified in
3134 // Evaluate() before this method is called, thus n <= Size()
3135 if(a == p_x[lowerIndex]) {
3136 return p_y[lowerIndex];
3137 }
3138 if(a == p_x[lowerIndex+1]) {
3139 return p_y[lowerIndex+1];
3140 }
3141
3142 double x0, x1, y0, y1, m0, m1;
3143 // a is contained within the interval (x0,x1)
3144 x0 = p_x[lowerIndex];
3145 x1 = p_x[lowerIndex+1];
3146 // the corresponding known y-values for x0 and x1
3147 y0 = p_y[lowerIndex];
3148 y1 = p_y[lowerIndex+1];
3149 // the corresponding known tangents (slopes) at (x0,y0) and (x1,y1)
3150 m0 = p_fprimeOfx[lowerIndex];
3151 m1 = p_fprimeOfx[lowerIndex+1];
3152
3153
3154 // following algorithm found at en.wikipedia.org/wiki/Cubic_Hermite_spline
3155 // seems to produce same answers, is it faster?
3156
3157 double h, t;
3158 h = x1 - x0;
3159 t = (a - x0) / h;
3160 return (2 * t * t * t - 3 * t * t + 1) * y0 + (t * t * t - 2 * t * t + t) * h * m0 + (-2 * t * t * t + 3 * t * t) * y1 + (t * t * t - t * t) * h * m1;
3161 }
3162
3184 if(InsideDomain(a)) {
3185 // find the interval in which "a" exists
3186 std::vector<double>::iterator pos;
3187 // find position in vector that is greater than or equal to "a"
3188 pos = upper_bound(p_x.begin(), p_x.end(), a);
3189 int upperIndex = 0;
3190 if(pos != p_x.end()) {
3191 upperIndex = distance(p_x.begin(), pos);
3192 }
3193 else {
3194 upperIndex = Size() - 1;
3195 }
3196 return upperIndex - 1;
3197 }
3198 else if((a + DBL_EPSILON) < DomainMinimum()) {
3199 return 0;
3200 }
3201 else {
3202 return Size() - 2;
3203 }
3204 }
3205
3206
3243 // This method was derived from an algorithm in the text
3244 // Numerical Recipes in C: The Art of Scientific Computing
3245 // Section 3.1 by Flannery, Press, Teukolsky, and Vetterling
3246 int n = Size();
3247 double y;
3248
3249 int ns;
3250 double den, dif, dift, c[n], d[n], ho, hp, w;
3251 double *err = 0;
3252 ns = 1;
3253 dif = fabs(a - p_x[0]);
3254 for(int i = 0; i < n; i++) { // Get the index ns of the closest table entry
3255 dift = fabs(a - p_x[i]);
3256 if(dift < dif) {
3257 ns = i + 1;
3258 dif = dift;
3259 }
3260 c[i] = p_y[i]; // initialize c and d
3261 d[i] = p_y[i];
3262 }
3263 ns = ns - 1;
3264 y = p_y[ns]; // initial approximation for y
3265 for(int m = 1; m < n; m++) {
3266 for(int i = 1; i <= n - m; i++) { // loop over c and d and update them
3267 ho = p_x[i-1] - a;
3268 hp = p_x[i+m-1] - a;
3269 w = c[i] - d[i-1];
3270 den = ho - hp;
3271 den = w / den;
3272 d[i-1] = hp * den; // update c and d
3273 c[i-1] = ho * den;
3274 }
3275 if(2 * ns < n - m) { // After each column in the tableau is completed, we decide
3276 err = &c[ns]; // which correction, c or d, we want to add to our accumulating
3277 } // value of y, i.e., which path to take through the tableau|
3278 else { // forking up or down. We do this in such a way as to take the
3279 ns = ns - 1; // most "straight line" route through the tableau to its apex,
3280 err = &d[ns]; // updating ns accordingly to keep track of where we are. This
3281 } // route keeps the partial approximations centered (insofar as possible)
3282 y = y + *err; // on the target x. The last err added is thus the error indication.
3283 }
3284 p_polyNevError.push_back(*err);
3285 return y;
3286 }
3287
3306 vector <double> NumericalApproximation::EvaluateForIntegration(const double a, const double b, const unsigned int n) {
3307 if(a > b) {
3308 ReportException(IException::Programmer, "EvaluatedForIntegration()",
3309 "Invalid interval entered: [a,b] = ["
3310 + IString(a) + ", " + IString(b) + "]",
3311 _FILEINFO_);
3312 }
3313 if(!InsideDomain(a) || !InsideDomain(b)) {
3314 ReportException(IException::Programmer, "EvaluateForIntegration()",
3315 "Invalid arguments. Interval entered ["
3316 + IString(a) + ", " + IString(b)
3317 + "] is not contained within domain ["
3318 + IString(DomainMinimum()) + ", "
3319 + IString(DomainMaximum()) + "]",
3320 _FILEINFO_);
3321 }
3322 vector <double> f;
3323 //need total number of segments to be divisible by n-1
3324 // This way we can use the formula for each interval: 0 to n, n to 2n, 2n to 3n, etc.
3325 // Notice interval endpoints will overlap for these composite formulas
3326 int xSegments = Size() - 1;
3327 while(xSegments % (n - 1) != 0) {
3328 xSegments++;
3329 }
3330 // x must be sorted and unique
3331 double xMin = a;
3332 double xMax = b;
3333 //Uniform step size.
3334 double h = (xMax - xMin) / xSegments;
3335 //Compute the interpolates using spline.
3336 try {
3337 for(double i = 0; i < (xSegments + 1); i++) {
3338 double xi = h * i + xMin;
3339 f.push_back(Evaluate(xi)); // validate data set here, allow default ThrowError extrap type
3340 }
3341 f.push_back(h);
3342 return f;
3343 }
3344 catch(IException &e) { // catch exception from Evaluate()
3345 throw IException(e,
3346 e.errorType(),
3347 "EvaluateForIntegration() - Unable to evaluate the data set for integration",
3348 _FILEINFO_);
3349 }
3350 }// for integration using composite of spline (creates evenly spaced points)
3351
3372 void NumericalApproximation::ReportException(IException::ErrorType type, const string &methodName, const string &message,
3373 const char *filesrc, int lineno)
3374 const {
3375 string msg = methodName + " - " + message;
3376 throw IException(type, msg.c_str(), filesrc, lineno);
3377 return;
3378 }
3379
3380}
Isis exception class.
Definition IException.h:91
ErrorType
Contains a set of exception error types.
Definition IException.h:111
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
Adds specific functionality to C++ strings.
Definition IString.h:165
NumericalApproximation provides various numerical analysis methods of interpolation,...
void ValidateDataSet()
Validates the data set before computing interpolation.
gsl_interp_accel * p_acc
Lookup accelorator.
void GslDeallocation()
Deallocate GSL interpolator resources, if used.
void GslAllocation(unsigned int npoints)
Allocates GSL interpolation functions.
double EvaluatePolynomialNeville(const double a)
Performs polynomial interpolation using Neville's algorithm.
vector< double > p_x
List of X values.
vector< double > p_fprimeOfx
List of first derivatives corresponding to each x value in the data set (i.e. each value in p_x)
void ReportException(IException::ErrorType type, const string &method, const string &message, const char *filesrc, int lineno) const
Generalized error report generator.
double GslFirstDerivative(const double a)
Approximates the first derivative of the data set function evaluated at the given domain value for GS...
double p_clampedDerivLastPt
First derivative of last x-value, p_x[n-1]. This is only used for the CubicClamped interpolation type...
int FindIntervalLowerIndex(const double a)
Find the index of the x-value in the data set that is just below the input value, a.
vector< double > CubicClampedSecondDerivatives()
Retrieves the second derivatives of the data set.
double DomainMinimum()
Input data domain minimum value.
bool GslInterpType(NumericalApproximation::InterpType itype) const
Returns whether an interpolation type is adapted from the GSL library.
InterpFunctor GslFunctor(NumericalApproximation::InterpType itype) const
Search for a GSL interpolation function.
bool p_clampedComputed
Flag variable to determine whether ComputeCubicClamped() has been called.
double Evaluate(const double a, const ExtrapType &etype=ThrowError)
Calculates interpolated or extrapolated value of tabulated data set for given domain value.
void Init(NumericalApproximation::InterpType itype)
Initializes the object upon instantiation.
bool InsideDomain(const double a)
Returns whether the passed value is greater than or equal to the domain minimum and less than or equa...
bool p_dataValidated
Flag variable to determine whether ValidateDataSet() has been called.
void GslIntegrityCheck(int gsl_status, const char *src, int line)
Checks the status of the GSL interpolation operations.
void AddCubicHermiteDeriv(unsigned int n, double *fprimeOfx)
Adds values for the first derivatives of the data points.
const gsl_interp_type * InterpFunctor
GSL Interpolation specs.
void ComputeGsl()
Computes the GSL interpolation for a set of (x,y) data points.
string Name() const
Get name of interpolating function assigned to object.
map< InterpType, InterpFunctor > FunctorList
Set up a std::map of GSL interpolator functors. List of function types.
double RombergsMethod(double a, double b)
Uses Romberg's method to approximate the integral of the interpolated data set function on the interv...
InterpType
This enum defines the types of interpolation supported in this class.
@ CubicClamped
Cubic Spline interpolation with clamped boundary conditions.
@ CubicNatPeriodic
Cubic Spline interpolation with periodic boundary conditions.
@ CubicHermite
Cubic Spline interpolation using the Hermite cubic polynomial.
@ CubicNeighborhood
Cubic Spline interpolation using 4-pt Neighborhoods with natural boundary conditions.
@ PolynomialNeville
Polynomial interpolation using Neville's algorithm.
@ CubicNatural
Cubic Spline interpolation with natural boundary conditions.
@ AkimaPeriodic
Non-rounded Akima Spline interpolation with periodic boundary conditions.
@ Polynomial
Polynomial interpolation.
@ Akima
Non-rounded Akima Spline interpolation with natural boundary conditions.
NumericalApproximation & operator=(const NumericalApproximation &numApMeth)
NumericalApproximation assigmment operator sets this object "equal to" another.
double TrapezoidalRule(const double a, const double b)
Uses the trapezoidal rule to approximate the integral of the interpolated data set function on the in...
double RefineExtendedTrap(double a, double b, double s, unsigned int n)
Calculates refinements extended trapezoidal rule to approximate the integral of the interpolated data...
double ValueToExtrapolate(const double a, const ExtrapType &etype)
Returns the domain value at which to evaluate.
double GslSecondDerivative(const double a)
Approximates the second derivative of the interpolated data set function evaluated at the given domai...
void ComputeCubicClamped()
Computes the cubic clamped interpolation for a set of (x,y) data points, given the first derivatives ...
bool Contains(double x)
Returns whether the passed value is an element of the set of x-values in the data set.
void SetInterpType(NumericalApproximation::InterpType itype)
Sets interpolation type.
double Simpsons4PointRule(const double a, const double b)
Uses Simpson's 4-point rule to approximate the integral of the interpolated data set function on the ...
double BackwardFirstDifference(const double a, const unsigned int n=3, const double h=0.1)
Uses an n point backward first difference formula to approximate the first derivative evaluated at a ...
double EvaluateCubicHermite(const double a)
Performs interpolation using the Hermite cubic polynomial.
bool GslComputed() const
Returns whether a GSL interpolation computation of the data set has been performed.
gsl_spline * p_interp
Currently active interpolator.
double GslIntegral(const double a, const double b)
Approximates the integral of the data set function evaluated on the given interval for GSL supported ...
void Reset()
Resets the state of the object.
FunctorList::const_iterator FunctorConstIter
GSL Iterator.
vector< double > p_clampedSecondDerivs
List of second derivatives evaluated at p_x values. This is only used for the CubicClamped interpolat...
bool p_clampedEndptsSet
Flag variable to determine whether SetCubicClampedEndptDeriv() has been called after all data was add...
NumericalApproximation(const NumericalApproximation::InterpType &itype=CubicNatural)
Default constructor creates NumericalApproximation object.
double CenterSecondDifference(const double a, const unsigned int n=5, const double h=0.1)
Uses an n point center second difference formula to approximate the second derivative evaluated at a ...
double p_clampedDerivFirstPt
First derivative of first x-value, p_x[0]. This is only used for the CubicClamped interpolation type.
InterpType p_itype
Interpolation type.
double EvaluateCubicHermiteSecDeriv(const double a)
Approximates the second derivative of the data set function evaluated at the given domain value for C...
double Simpsons3PointRule(const double a, const double b)
Uses Simpson's 3-point rule to approximate the integral of the interpolated data set function on the ...
static FunctorList p_interpFunctors
Maintains list of interpolator options.
double DomainMaximum()
Input data domain maximum value.
double EvaluateCubicClamped(const double a)
Performs cubic spline interpolation with clamped boundary conditions, if possible.
virtual ~NumericalApproximation()
Destructor deallocates memory being used.
double BackwardSecondDifference(const double a, const unsigned int n=3, const double h=0.1)
Uses an n point backward second difference formula to approximate the second derivative evaluated at ...
double ForwardFirstDifference(const double a, const unsigned int n=3, const double h=0.1)
Uses an n point forward first difference formula to approximate the first derivative evaluated at a g...
double BoolesRule(const double a, const double b)
Uses Boole's Rule to approximate the integral of the interpolated data set function on the interval (...
vector< double > p_polyNevError
Estimate of error for interpolation evaluated at x. This is only used for the PolynomialNeville inter...
double EvaluateCubicNeighborhood(const double a)
Performs cubic spline interpolation for a neighborhood about a.
double CenterFirstDifference(const double a, const unsigned int n=5, const double h=0.1)
Uses an n point center first difference formula to approximate the first derivative evaluated at a gi...
double EvaluateCubicHermiteFirstDeriv(const double a)
Approximates the first derivative of the data set function evaluated at the given domain value for Cu...
ExtrapType
This enum defines the manner in which a value outside of the domain should be handled if passed to th...
@ NearestEndpoint
Evaluate() returns the y-value of the nearest endpoint if a is outside of the domain.
@ ThrowError
Evaluate() throws an error if a is outside of the domain.
@ Extrapolate
Evaluate() attempts to extrapolate if a is outside of the domain. This is only valid for NumericalApp...
unsigned int Size()
Returns the number of the coordinates added to the data set.
int MinPoints()
Minimum number of points required by interpolating function.
double ForwardSecondDifference(const double a, const unsigned int n=3, const double h=0.1)
Uses an n point forward second difference formula to approximate the second derivative evaluated at a...
vector< double > PolynomialNevilleErrorEstimate()
Retrieves the error estimate for the Neville's polynomial interpolation type.
vector< double > p_y
List of Y values.
void SetCubicClampedEndptDeriv(const double yp1, const double ypn)
Sets the values for the first derivatives of the endpoints of the data set.
void AddData(const double x, const double y)
Add a datapoint to the set.
vector< double > EvaluateForIntegration(const double a, const double b, const unsigned int n)
Evaluates data set in order to have enough data points to approximate the function to be integrated.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.