Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

Isis 3 Programmer Reference
NumericalApproximation.cpp
1
5
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
179 // copy values for interpolation-specific variables
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;
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 }
411 throw IException(IException::Programmer,
412 "MinPoints() - Invalid argument. Unknown interpolation type: "
413 + IString(NumericalApproximation::InterpType(itype)),
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) {
518 ReportException(IException::Programmer, "AddData()",
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 }
604
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 }
638
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 = "
1173 + IString(DomainMinimum())
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:
1199 throw IException(IException::Programmer,
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 = "
1262 + IString(DomainMaximum())
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:
1288 throw IException(IException::Programmer,
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 = ["
1353 + IString(DomainMinimum()) + ", " + IString(DomainMaximum())
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:
1379 throw IException(IException::Programmer,
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 = "
1560 + IString(DomainMinimum())
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:
1584 throw IException(IException::Programmer,
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 = "
1642 + IString(DomainMaximum())
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:
1666 throw IException(IException::Programmer,
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 = ["
1731 + IString(DomainMinimum()) + ", " + IString(DomainMaximum())
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:
1757 throw IException(IException::Programmer,
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
2181 // This method was derived from an algorithm in the text
2182 // Numerical Recipes in C: The Art of Scientific Computing
2183 // Section 4.3 by Flannery, Press, Teukolsky, and Vetterling
2184 int maxits = 20;
2185 double dss = 0; // error estimate
2186 std::vector<double> h(maxits+1, 0); // relative stepsizes for trap
2187 std::vector<double> trap(maxits+1, 0); // successive trapezoidal approximations
2188 double epsilon = 1.0e-4; // desired fractional accuracy
2189 double epsilon2 = 1.0e-6;// desired fractional accuracy
2190 double ss = -1.0; // result
2191
2192 h[0] = 1.0;
2193 try {
2195 for(int i = 0; i < maxits; i++) {
2196 // i will determine number of trapezoidal partitions of area
2197 // under curve for "integration" using refined trapezoidal rule
2198 trap[i] = RefineExtendedTrap(a, b, trap[i], i + 1); // validates data here
2199 if(i >= 4) {
2200 interp.AddData(5, &h[i-4], &trap[i-4]);
2201 // PolynomialNeville can extrapolate data outside of domain
2203 dss = interp.PolynomialNevilleErrorEstimate()[0];
2204 interp.Reset();
2205 // we work only until our necessary accuracy is achieved
2206 if(fabs(dss) <= epsilon * fabs(ss)) return ss;
2207 if(fabs(dss) <= epsilon2) return ss;
2208 }
2209 trap[i+1] = trap[i];
2210 h[i+1] = 0.25 * h[i];
2211 // This is a key step: the factor is 0.25d0 even though
2212 // the stepsize is decreased by 0.5d0. This makes the
2213 // extrapolation a polynomial in h-squared as allowed
2214 // by the equation from Numerical Recipes 4.2.1 pg.132,
2215 // not just a polynomial in h.
2216 }
2217 } catch (IException &e) { // catch error from RefineExtendedTrap, Constructor, Evaluate, PolynomialNevilleErrorEstimate
2218 throw IException(e,
2219 e.errorType(),
2220 "RombergsMethod() - Unable to calculate the integral on the interval (a,b) = ("
2221 + IString(a) + ", " + IString(b)
2222 + ") using Romberg's method",
2223 _FILEINFO_);
2224 }
2225 throw IException(IException::Programmer,
2226 "RombergsMethod() - Unable to calculate the integral using RombergsMethod() - Failed to converge in "
2227 + IString(maxits) + " iterations",
2228 _FILEINFO_);
2229 }
2230
2250 p_clampedComputed = false;
2251 p_clampedEndptsSet = false;
2252 p_dataValidated = false;
2253 p_x.clear();
2254 p_y.clear();
2255 p_clampedSecondDerivs.clear();
2258 p_polyNevError.clear();
2259 p_fprimeOfx.clear();
2260 return;
2261 }
2262
2281 try {
2282 Reset();
2283 SetInterpType(itype);
2284 return;
2285 }
2286 catch(IException &e) { // catch exception from SetInterpType()
2287 throw IException(e,
2288 e.errorType(),
2289 "Reset() - Unable to reset interpolation type",
2290 _FILEINFO_);
2291 }
2292 }
2293
2317 // Validates the parameter
2318 if(GslInterpType(itype)) {
2319 try {
2320 GslFunctor(itype);
2321 }
2322 catch(IException &e) { // catch exception from GslFunctor()
2323 throw IException(e,
2324 e.errorType(),
2325 "SetInterpType() - Unable to set interpolation type",
2326 _FILEINFO_);
2327 }
2328 }
2329 else if(itype > 9) { // there are currently 9 interpolation types
2330 ReportException(IException::Programmer, "SetInterpType()",
2331 "Invalid argument. Unknown interpolation type: "
2332 + IString(NumericalApproximation::InterpType(itype)),
2333 _FILEINFO_);
2334 }
2335 // p_x, p_y are kept and p_itype is replaced
2336 p_itype = itype;
2337 // reset state of class variables that are InterpType dependent //??? should we keep some of this info?
2338 p_dataValidated = false;
2339 p_clampedComputed = false;
2340 p_clampedEndptsSet = false;
2341 p_clampedSecondDerivs.clear();
2344 p_polyNevError.clear();
2345 p_fprimeOfx.clear();
2346 }
2347
2373 if(p_interpFunctors.empty()) {
2374 p_interpFunctors.insert(make_pair(Linear, gsl_interp_linear));
2375 p_interpFunctors.insert(make_pair(Polynomial, gsl_interp_polynomial));
2376 p_interpFunctors.insert(make_pair(CubicNatural, gsl_interp_cspline));
2377 p_interpFunctors.insert(make_pair(CubicNatPeriodic, gsl_interp_cspline_periodic));
2378 p_interpFunctors.insert(make_pair(Akima, gsl_interp_akima));
2379 p_interpFunctors.insert(make_pair(AkimaPeriodic, gsl_interp_akima_periodic));
2380 }
2381
2382 p_acc = 0;
2383 p_interp = 0;
2384 try {
2385 SetInterpType(itype);
2386 }
2387 catch(IException &e) { // catch exception from SetInterpType()
2388 throw IException(e,
2389 e.errorType(),
2390 "Init() - Unable to initialize NumericalApproximation object",
2391 _FILEINFO_);
2392 }
2393 // Turn all GSL error handling off...repeatedly, every time this routine is
2394 // called.
2395 gsl_set_error_handler_off();
2396 }
2397
2416 if(itype == NumericalApproximation::Linear) return true;
2417 if(itype == NumericalApproximation::Polynomial) return true;
2418 if(itype == NumericalApproximation::CubicNatural) return true;
2419 if(itype == NumericalApproximation::CubicNatPeriodic) return true;
2420 if(itype == NumericalApproximation::Akima) return true;
2421 if(itype == NumericalApproximation::AkimaPeriodic) return true;
2422 return false;
2423 }
2424
2441 void NumericalApproximation::GslAllocation(unsigned int npoints) {
2443 //get pointer to accelerator object (iterator for interpolation lookups)
2444 p_acc = gsl_interp_accel_alloc();
2445 //get pointer to interpolation object of interp type given for npoints datapoints
2446 p_interp = gsl_spline_alloc(GslFunctor(p_itype), npoints);
2447 return;
2448 }
2449
2464 if(p_interp) gsl_spline_free(p_interp);
2465 if(p_acc) gsl_interp_accel_free(p_acc);
2466 p_acc = 0;
2467 p_interp = 0;
2468 return;
2469 }
2470
2490 const {
2491 FunctorConstIter fItr = p_interpFunctors.find(itype);
2492 if(fItr == p_interpFunctors.end()) {
2493 ReportException(IException::Programmer, "GslFunctor()",
2494 "Invalid argument. Unable to find GSL interpolator with id = "
2495 + IString(NumericalApproximation::InterpType(itype)),
2496 _FILEINFO_);
2497 }
2498 return (fItr->second);
2499 }
2500
2520 void NumericalApproximation::GslIntegrityCheck(int gsl_status, const char *src, int line)
2521 {
2522 if(gsl_status != GSL_SUCCESS) {
2523 if(gsl_status != GSL_EDOM) {
2524 ReportException(IException::Programmer, "GslIntegrityCheck(int,char,int)",
2525 "GslIntegrityCheck(): GSL error occured: "
2526 + string(gsl_strerror(gsl_status)), src, line);
2527 }
2528 }
2529 return;
2530 }
2531
2561 if((int) Size() < MinPoints()) {
2562 ReportException(IException::Programmer, "ValidateDataSet()",
2563 Name() + " interpolation requires a minimum of "
2564 + IString(MinPoints()) + " data points - currently have "
2565 + IString((int) Size()),
2566 _FILEINFO_);
2567 }
2568 for(unsigned int i = 1; i < Size(); i++) {
2569 // Check for uniqueness -- this applies to all interpolation types
2570 if(p_x[i-1] == p_x[i]) {
2571 ReportException(IException::Programmer, "ValidateDataSet()",
2572 "Invalid data set, x-values must be unique: \n\t\tp_x["
2573 + IString((int) i - 1) + "] = " + IString(p_x[i-1])
2574 + " = p_x[" + IString((int) i) + "]",
2575 _FILEINFO_);
2576 }
2577 if(p_x[i-1] > p_x[i]) {
2578 // Verify that data set is in ascending order --
2579 // this does not apply to PolynomialNeville, which appears to get the same results with unsorted data
2581 ReportException(IException::Programmer, "ValidateDataSet()",
2582 "Invalid data set, x-values must be in ascending order for "
2583 + Name() + " interpolation: \n\t\tx["
2584 + IString((int) i - 1) + "] = " + IString(p_x[i-1]) + " > x["
2585 + IString((int) i) + "] = " + IString(p_x[i]),
2586 _FILEINFO_);
2587 }
2588 }
2589 }
2591 if(p_y[0] != p_y[Size()-1]) {
2592 ReportException(IException::Programmer, "ValidateDataSet()",
2593 "First and last points of the data set must have the same y-value for "
2594 + Name() + "interpolation to prevent discontinuity at the boundary",
2595 _FILEINFO_);
2596 }
2597 }
2598 p_dataValidated = true;
2599 return;
2600 }
2601
2602
2617 try {
2618 if(a + DBL_EPSILON < DomainMinimum()) {
2619 return false;
2620 }
2621 if(a - DBL_EPSILON > DomainMaximum()) {
2622 return false;
2623 }
2624 }
2625 catch(IException &e) { // catch exception from DomainMinimum(), DomainMaximum()
2626 throw IException(e,
2627 e.errorType(),
2628 "InsideDomain() - Unable to compute domain boundaries",
2629 _FILEINFO_);
2630 }
2631 return true;
2632 }
2633
2651 if(GslInterpType(p_itype)) return ((p_interp) && (p_acc));
2652 else
2653 throw IException(IException::Programmer,
2654 "GslComputed() - Method only valid for GSL interpolation types, may not be used for "
2655 + Name() + " interpolation",
2656 _FILEINFO_);
2657 }
2658
2681 try {
2682 if(GslComputed()) return;
2685 GslIntegrityCheck(gsl_spline_init(p_interp, &p_x[0], &p_y[0], Size()), _FILEINFO_);
2686 return;
2687 }
2688 catch(IException &e) { // catch exception from ValidateDataSet(), GslIntegrityCheck()
2689 throw IException(e,
2690 e.errorType(),
2691 "ComputeGsl() - Unable to compute GSL interpolation",
2692 _FILEINFO_);
2693 }
2694 }
2695
2733 // This method was derived from an algorithm in the text
2734 // Numerical Recipes in C: The Art of Scientific Computing
2735 // Section 3.3 by Flannery, Press, Teukolsky, and Vetterling
2736
2737 if(!p_dataValidated) {
2738 try {
2740 }
2741 catch(IException &e) { // catch exception from ValidateDataSet()
2742 throw IException(e,
2743 e.errorType(),
2744 "ComputeCubicClamped() - Unable to compute cubic clamped interpolation",
2745 _FILEINFO_);
2746 }
2747 }
2748 if(!p_clampedEndptsSet) {
2749 ReportException(IException::Programmer, "ComputeCubicClamped()",
2750 "Must set endpoint derivative values after adding data in order to compute cubic spline with clamped boundary conditions",
2751 _FILEINFO_);
2752 }
2753 int n = Size();
2754 p_clampedSecondDerivs.resize(n);
2755 double u[n];
2756 double p, sig, qn, un;
2757
2758 if(p_clampedDerivFirstPt > 0.99e30) {
2759 p_clampedSecondDerivs[0] = 0.0;//natural boundary conditions are used if deriv of first value is greater than 10^30
2760 u[0] = 0.0;
2761 }
2762 else {
2763 p_clampedSecondDerivs[0] = -0.5;// clamped conditions are used
2764 u[0] = (3.0 / (p_x[1] - p_x[0])) * ((p_y[1] - p_y[0]) / (p_x[1] - p_x[0]) - p_clampedDerivFirstPt);
2765 }
2766 for(int i = 1; i < n - 1; i++) { // decomposition loop of the tridiagonal algorithm
2767 sig = (p_x[i] - p_x[i-1]) / (p_x[i+1] - p_x[i-1]);
2768 p = sig * p_clampedSecondDerivs[i-1] + 2.0;
2769 p_clampedSecondDerivs[i] = (sig - 1.0) / p;
2770 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]) /
2771 (p_x[i] - p_x[i-1])) / (p_x[i+1] - p_x[i-1]) - sig * u[i-1]) / p;
2772 }
2773 if(p_clampedDerivLastPt > 0.99e30) { // upper boundary is natural
2774 qn = 0.0;
2775 un = 0.0;
2776 }
2777 else {// upper boundary is clamped
2778 qn = 0.5;
2779 un = (3.0 / (p_x[n-1] - p_x[n-2])) * (p_clampedDerivLastPt - (p_y[n-1] - p_y[n-2]) /
2780 (p_x[n-1] - p_x[n-2]));
2781 }
2782 p_clampedSecondDerivs[n-1] = (un - qn * u[n-2]) / (qn * p_clampedSecondDerivs[n-2] + 1.0);
2783 for(int i = n - 2; i >= 0; i--) { // backsubstitution loop of the tridiagonal algorithm
2785 }
2786 p_clampedComputed = true;
2787 return;
2788 }
2789
2827 double NumericalApproximation::ValueToExtrapolate(const double a, const ExtrapType &etype) {
2829 ReportException(IException::Programmer, "Evaluate()",
2830 "Invalid argument. Value entered, a = "
2831 + IString(a) + ", is outside of domain = ["
2832 + IString(DomainMinimum()) + ", "
2833 + IString(DomainMaximum()) + "]",
2834 _FILEINFO_);
2835 }
2837 if(a + DBL_EPSILON < DomainMinimum()) {
2838 return DomainMinimum();
2839 }
2840 else {
2841 return DomainMaximum(); // (a > DomainMaximum())
2842 }
2843 }
2844 else { // gsl interpolations and CubicNeighborhood cannot extrapolate
2847 ReportException(IException::Programmer, "Evaluate()",
2848 "Invalid argument. Cannot extrapolate for type "
2849 + Name() + ", must choose to throw error or return nearest neighbor",
2850 _FILEINFO_);
2851 }
2852 return a;
2853 }
2854 }
2855
2891 try {
2892 int s = 0, s_old, s0;
2893 vector <double> x0(4), y0(4);
2895 for(unsigned int n = 0; n < Size(); n++) {
2896 if(p_x[n] < a) s = n;
2897 else break;
2898 }
2899 if(s < 1) s = 1;
2900 else if(s > (int) Size() - 3) s = Size() - 3;
2901 s_old = -1;
2902 s0 = s - 1;
2903 if(s_old != s0) {
2904 for(int n = 0; n < 4; n++) {
2905 x0[n] = p_x[n+s0];
2906 y0[n] = p_y[n+s0];
2907 spline.AddData(x0[n], y0[n]);
2908 }
2909 s_old = s0;
2910 }
2911 // use nearest endpoint extrapolation method for neighborhood spline
2912 // since CubicNatural can do no other
2914 }
2915 catch(IException &e) { // catch exception from Constructor, ComputeGsl(), Evaluate()
2916 throw IException(e,
2917 e.errorType(),
2918 "EvaluateCubicNeighborhood() - Unable to evaluate cubic neighborhood interpolation at a = "
2919 + IString(a),
2920 _FILEINFO_);
2921 }
2922 }
2923
2970 vector <double> NumericalApproximation::EvaluateCubicNeighborhood(const vector <double> &a,
2972 vector <double> result(a.size());
2973 int s_old, s0;
2974 vector <double> x0(4), y0(4);
2975 vector <int> s;
2976 s.clear();
2977 s.resize(a.size());
2978 for(unsigned int i = 0; i < a.size(); i++) {
2979 for(unsigned int n = 0; n < Size(); n++) {
2980 if(p_x[n] < a[i]) {
2981 s[i] = n;
2982 }
2983 }
2984 if(s[i] < 1) {
2985 s[i] = 1;
2986 }
2987 if(s[i] > ((int) Size()) - 3) {
2988 s[i] = Size() - 3;
2989 }
2990 }
2991 s_old = -1;
2992 try {
2994 for(unsigned int i = 0; i < a.size(); i++) {
2995 s0 = s[i] - 1;
2996 if(s_old != s0) {
2997 spline.Reset();
2998 for(int n = 0; n < 4; n++) {
2999 x0[n] = p_x[n+s0];
3000 y0[n] = p_y[n+s0];
3001 spline.AddData(x0[n], y0[n]);
3002 }
3003 s_old = s0;
3004 }
3005 double a0;
3006 // checks whether this value is in domain of the main spline
3007 if(InsideDomain(a[i]))
3008 a0 = a[i];
3009 else a0 = ValueToExtrapolate(a[i], etype);
3010 // since neighborhood spline is CubicNatural, the only extrapolation possible is NearestEndpoint
3011 result[i] = spline.Evaluate(a0, NumericalApproximation::NearestEndpoint);
3012 }
3013 return result;
3014 }
3015 catch(IException &e) { // catch exception from Constructor, ComputeGsl(), Evaluate()
3016 throw IException(e,
3017 e.errorType(),
3018 "EvaluateCubicNeighborhood() - Unable to evaluate the function at the given vector of points using cubic neighborhood interpolation",
3019 _FILEINFO_);
3020 }
3021 }
3022
3058 // This method was derived from an algorithm in the text
3059 // Numerical Recipes in C: The Art of Scientific Computing
3060 // Section 3.3 by Flannery, Press, Teukolsky, and Vetterling
3061 int n = Size();
3062 double result = 0;
3063
3064 int k;
3065 int k_Lo;
3066 int k_Hi;
3067 double h;
3068 double A;
3069 double B;
3070
3071 k_Lo = 0;
3072 k_Hi = n - 1;
3073 while(k_Hi - k_Lo > 1) {
3074 k = (k_Hi + k_Lo) / 2;
3075 if(p_x[k] > a) {
3076 k_Hi = k;
3077 }
3078 else {
3079 k_Lo = k;
3080 }
3081 }
3082
3083 h = p_x[k_Hi] - p_x[k_Lo];
3084 A = (p_x[k_Hi] - a) / h;
3085 B = (a - p_x[k_Lo]) / h;
3086 result = A * p_y[k_Lo] + B * p_y[k_Hi] + ((pow(A, 3.0) - A) *
3087 p_clampedSecondDerivs[k_Lo] + (pow(B, 3.0) - B) * p_clampedSecondDerivs[k_Hi]) * pow(h, 2.0) / 6.0;
3088 return result;
3089 }
3090
3119 // algorithm was found at en.wikipedia.org/wiki/Cubic_Hermite_spline
3120 // it seems to produce same answers, as the NumericalAnalysis book
3121
3122 if(p_fprimeOfx.size() != Size()) {
3123 ReportException(IException::User, "EvaluateCubicHermite()",
3124 "Invalid arguments. The size of the first derivative vector does not match the number of (x,y) data points.",
3125 _FILEINFO_);
3126 }
3127 // find the interval in which "a" exists
3128 int lowerIndex = FindIntervalLowerIndex(a);
3129
3130 // we know that "a" is within the domain since this is verified in
3131 // Evaluate() before this method is called, thus n <= Size()
3132 if(a == p_x[lowerIndex]) {
3133 return p_y[lowerIndex];
3134 }
3135 if(a == p_x[lowerIndex+1]) {
3136 return p_y[lowerIndex+1];
3137 }
3138
3139 double x0, x1, y0, y1, m0, m1;
3140 // a is contained within the interval (x0,x1)
3141 x0 = p_x[lowerIndex];
3142 x1 = p_x[lowerIndex+1];
3143 // the corresponding known y-values for x0 and x1
3144 y0 = p_y[lowerIndex];
3145 y1 = p_y[lowerIndex+1];
3146 // the corresponding known tangents (slopes) at (x0,y0) and (x1,y1)
3147 m0 = p_fprimeOfx[lowerIndex];
3148 m1 = p_fprimeOfx[lowerIndex+1];
3149
3150
3151 // following algorithm found at en.wikipedia.org/wiki/Cubic_Hermite_spline
3152 // seems to produce same answers, is it faster?
3153
3154 double h, t;
3155 h = x1 - x0;
3156 t = (a - x0) / h;
3157 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;
3158 }
3159
3181 if(InsideDomain(a)) {
3182 // find the interval in which "a" exists
3183 std::vector<double>::iterator pos;
3184 // find position in vector that is greater than or equal to "a"
3185 pos = upper_bound(p_x.begin(), p_x.end(), a);
3186 int upperIndex = 0;
3187 if(pos != p_x.end()) {
3188 upperIndex = distance(p_x.begin(), pos);
3189 }
3190 else {
3191 upperIndex = Size() - 1;
3192 }
3193 return upperIndex - 1;
3194 }
3195 else if((a + DBL_EPSILON) < DomainMinimum()) {
3196 return 0;
3197 }
3198 else {
3199 return Size() - 2;
3200 }
3201 }
3202
3203
3240 // This method was derived from an algorithm in the text
3241 // Numerical Recipes in C: The Art of Scientific Computing
3242 // Section 3.1 by Flannery, Press, Teukolsky, and Vetterling
3243 int n = Size();
3244 double y;
3245
3246 int ns;
3247 double den, dif, dift, c[n], d[n], ho, hp, w;
3248 double *err = 0;
3249 ns = 1;
3250 dif = fabs(a - p_x[0]);
3251 for(int i = 0; i < n; i++) { // Get the index ns of the closest table entry
3252 dift = fabs(a - p_x[i]);
3253 if(dift < dif) {
3254 ns = i + 1;
3255 dif = dift;
3256 }
3257 c[i] = p_y[i]; // initialize c and d
3258 d[i] = p_y[i];
3259 }
3260 ns = ns - 1;
3261 y = p_y[ns]; // initial approximation for y
3262 for(int m = 1; m < n; m++) {
3263 for(int i = 1; i <= n - m; i++) { // loop over c and d and update them
3264 ho = p_x[i-1] - a;
3265 hp = p_x[i+m-1] - a;
3266 w = c[i] - d[i-1];
3267 den = ho - hp;
3268 den = w / den;
3269 d[i-1] = hp * den; // update c and d
3270 c[i-1] = ho * den;
3271 }
3272 if(2 * ns < n - m) { // After each column in the tableau is completed, we decide
3273 err = &c[ns]; // which correction, c or d, we want to add to our accumulating
3274 } // value of y, i.e., which path to take through the tableau|
3275 else { // forking up or down. We do this in such a way as to take the
3276 ns = ns - 1; // most "straight line" route through the tableau to its apex,
3277 err = &d[ns]; // updating ns accordingly to keep track of where we are. This
3278 } // route keeps the partial approximations centered (insofar as possible)
3279 y = y + *err; // on the target x. The last err added is thus the error indication.
3280 }
3281 p_polyNevError.push_back(*err);
3282 return y;
3283 }
3284
3303 vector <double> NumericalApproximation::EvaluateForIntegration(const double a, const double b, const unsigned int n) {
3304 if(a > b) {
3305 ReportException(IException::Programmer, "EvaluatedForIntegration()",
3306 "Invalid interval entered: [a,b] = ["
3307 + IString(a) + ", " + IString(b) + "]",
3308 _FILEINFO_);
3309 }
3310 if(!InsideDomain(a) || !InsideDomain(b)) {
3311 ReportException(IException::Programmer, "EvaluateForIntegration()",
3312 "Invalid arguments. Interval entered ["
3313 + IString(a) + ", " + IString(b)
3314 + "] is not contained within domain ["
3315 + IString(DomainMinimum()) + ", "
3316 + IString(DomainMaximum()) + "]",
3317 _FILEINFO_);
3318 }
3319 vector <double> f;
3320 //need total number of segments to be divisible by n-1
3321 // This way we can use the formula for each interval: 0 to n, n to 2n, 2n to 3n, etc.
3322 // Notice interval endpoints will overlap for these composite formulas
3323 int xSegments = Size() - 1;
3324 while(xSegments % (n - 1) != 0) {
3325 xSegments++;
3326 }
3327 // x must be sorted and unique
3328 double xMin = a;
3329 double xMax = b;
3330 //Uniform step size.
3331 double h = (xMax - xMin) / xSegments;
3332 //Compute the interpolates using spline.
3333 try {
3334 for(double i = 0; i < (xSegments + 1); i++) {
3335 double xi = h * i + xMin;
3336 f.push_back(Evaluate(xi)); // validate data set here, allow default ThrowError extrap type
3337 }
3338 f.push_back(h);
3339 return f;
3340 }
3341 catch(IException &e) { // catch exception from Evaluate()
3342 throw IException(e,
3343 e.errorType(),
3344 "EvaluateForIntegration() - Unable to evaluate the data set for integration",
3345 _FILEINFO_);
3346 }
3347 }// for integration using composite of spline (creates evenly spaced points)
3348
3369 void NumericalApproximation::ReportException(IException::ErrorType type, const string &methodName, const string &message,
3370 const char *filesrc, int lineno)
3371 const {
3372 string msg = methodName + " - " + message;
3373 throw IException(type, msg.c_str(), filesrc, lineno);
3374 return;
3375 }
3376
3377}
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.