7 #include "NumericalApproximation.h"
16 #include "SpecialPixel.h"
17 #include "IException.h"
22 NumericalApproximation::FunctorList NumericalApproximation::p_interpFunctors;
45 p_dataValidated =
false;
49 "NumericalApproximation() - Unable to construct NumericalApproximation object",
87 NumericalApproximation::NumericalApproximation(
unsigned int n,
double *x,
double *y,
96 "NumericalApproximation() - Unable to construct object using the given arrays, size and interpolation type",
131 NumericalApproximation::NumericalApproximation(
const vector <double> &x,
const vector <double> &y,
140 "NumericalApproximation() - Unable to construct an object using the given vectors and interpolation type",
191 "NumericalApproximation() - Unable to copy the given object",
222 if(&oldObject !=
this) {
223 if(GslInterpType(p_itype)) GslDeallocation();
224 SetInterpType(oldObject.
p_itype);
240 "operator=() - Unable to copy the given object",
256 NumericalApproximation::~NumericalApproximation() {
257 if(GslInterpType(p_itype)) GslDeallocation();
282 string NumericalApproximation::Name()
const {
283 if(p_itype == NumericalApproximation::CubicNeighborhood) {
284 return "cspline-neighborhood";
286 else if(p_itype == NumericalApproximation::CubicClamped) {
287 return "cspline-clamped";
289 else if(p_itype == NumericalApproximation::PolynomialNeville) {
290 return "polynomial-Neville's";
292 else if(p_itype == NumericalApproximation::CubicHermite) {
293 return "cspline-Hermite";
296 string name = (string(GslFunctor(p_itype)->name));
297 if(p_itype == NumericalApproximation::CubicNatural) {
298 return name +
"-natural";
305 "Name() - GSL interpolation type not found",
333 int NumericalApproximation::MinPoints() {
334 if(p_itype == NumericalApproximation::CubicNeighborhood) {
337 else if(p_itype == NumericalApproximation::CubicClamped) {
340 else if(p_itype == NumericalApproximation::PolynomialNeville) {
343 else if(p_itype == NumericalApproximation::CubicHermite) {
347 return (GslFunctor(p_itype)->min_size);
352 "MinPoints() - GSL interpolation not found",
386 if(GslInterpType(itype)) {
395 "MinPoints() - GSL interpolation type not found",
399 else if(itype == NumericalApproximation::CubicNeighborhood) {
402 else if(itype == NumericalApproximation::CubicClamped) {
405 else if(itype == NumericalApproximation::PolynomialNeville) {
408 else if(itype == NumericalApproximation::CubicHermite) {
412 "MinPoints() - Invalid argument. Unknown interpolation type: "
440 void NumericalApproximation::AddData(
const double x,
const double y) {
443 p_clampedComputed =
false;
444 p_clampedEndptsSet =
false;
445 p_dataValidated =
false;
446 p_clampedSecondDerivs.clear();
447 p_clampedDerivFirstPt = 0;
448 p_clampedDerivLastPt = 0;
449 p_polyNevError.clear();
473 void NumericalApproximation::AddData(
unsigned int n,
double *x,
double *y) {
474 for(
unsigned int i = 0; i < n; i++) {
478 p_clampedComputed =
false;
479 p_clampedEndptsSet =
false;
480 p_dataValidated =
false;
481 p_clampedSecondDerivs.clear();
482 p_clampedDerivFirstPt = 0;
483 p_clampedDerivLastPt = 0;
484 p_polyNevError.clear();
512 void NumericalApproximation::AddData(
const vector <double> &x,
513 const vector <double> &y)
518 ReportException(IException::Programmer,
"AddData()",
519 "Invalid arguments. The sizes of the input vectors do "
526 if(!p_x.empty() || !p_y.empty()) {
527 for(
int i = 0; i < n; i++) {
537 p_clampedComputed =
false;
538 p_clampedEndptsSet =
false;
539 p_dataValidated =
false;
540 p_clampedSecondDerivs.clear();
541 p_clampedDerivFirstPt = 0;
542 p_clampedDerivLastPt = 0;
543 p_polyNevError.clear();
565 void NumericalApproximation::SetCubicClampedEndptDeriv(
double yp1,
double ypn) {
566 if(p_itype != NumericalApproximation::CubicClamped) {
567 ReportException(IException::Programmer,
"SetCubicClampedEndptDeriv()",
568 "This method is only valid for cspline-clamped interpolation, may not be used for "
569 + Name() +
" interpolation",
572 p_clampedDerivFirstPt = yp1;
573 p_clampedDerivLastPt = ypn;
574 p_clampedEndptsSet =
true;
592 void NumericalApproximation::AddCubicHermiteDeriv(
unsigned int n,
double *fprimeOfx) {
593 if(p_itype != NumericalApproximation::CubicHermite) {
594 ReportException(IException::Programmer,
"SetCubicHermiteDeriv()",
595 "This method is only valid for cspline-Hermite interpolation, may not be used for "
596 + Name() +
" interpolation",
599 for(
unsigned int i = 0; i < n; i++) {
600 p_fprimeOfx.push_back(fprimeOfx[i]);
617 void NumericalApproximation::AddCubicHermiteDeriv(
618 const vector <double> &fprimeOfx) {
619 if(p_itype != NumericalApproximation::CubicHermite) {
620 ReportException(IException::Programmer,
"SetCubicHermiteDeriv()",
621 "This method is only valid for cspline-Hermite interpolation, may not be used for "
622 + Name() +
" interpolation",
627 if(!p_fprimeOfx.empty()) {
628 for(
unsigned int i = 0; i < fprimeOfx.size(); i++) {
629 p_fprimeOfx.push_back(fprimeOfx[i]);
633 p_fprimeOfx = fprimeOfx;
651 void NumericalApproximation::AddCubicHermiteDeriv(
653 if(p_itype != NumericalApproximation::CubicHermite) {
654 ReportException(IException::Programmer,
"SetCubicHermiteDeriv()",
655 "This method is only valid for cspline-Hermite interpolation, may not be used for "
656 + Name() +
" interpolation",
659 p_fprimeOfx.push_back(fprimeOfx);
687 vector <double> NumericalApproximation::CubicClampedSecondDerivatives() {
689 if(p_itype != NumericalApproximation::CubicClamped)
690 ReportException(IException::Programmer,
"CubicClampedSecondDerivatives()",
691 "This method is only valid for cspline-clamped interpolation type may not be used for "
692 + Name() +
" interpolation",
694 if(!p_clampedComputed) ComputeCubicClamped();
695 return p_clampedSecondDerivs;
700 "CubicClampedSecondDerivatives() - Unable to compute clamped cubic spline interpolation",
723 double NumericalApproximation::DomainMinimum() {
725 if(GslInterpType(p_itype)) {
727 if(!GslComputed()) ComputeGsl();
728 return (p_interp->interp->xmin);
731 if(!p_dataValidated) ValidateDataSet();
732 return *min_element(p_x.begin(), p_x.end());
738 "DomainMinimum() - Unable to calculate the domain minimum for the data set",
760 double NumericalApproximation::DomainMaximum() {
762 if(GslInterpType(p_itype)) {
764 if(!GslComputed()) ComputeGsl();
765 return (p_interp->interp->xmax);
768 if(!p_dataValidated) ValidateDataSet();
769 return *max_element(p_x.begin(), p_x.end());
775 "DomainMaximum() - Unable to calculate the domain maximum for the data set",
793 bool NumericalApproximation::Contains(
double x) {
794 return binary_search(p_x.begin(), p_x.end(), x);
836 double NumericalApproximation::Evaluate(
const double a,
const ExtrapType &etype) {
842 else a0 = ValueToExtrapolate(a, etype);
844 if(p_itype == NumericalApproximation::CubicNeighborhood) {
845 return EvaluateCubicNeighborhood(a0);
847 else if(p_itype == NumericalApproximation::PolynomialNeville) {
848 p_polyNevError.clear();
849 return EvaluatePolynomialNeville(a0);
851 else if(p_itype == NumericalApproximation::CubicClamped) {
853 if(!p_clampedComputed) ComputeCubicClamped();
854 return EvaluateCubicClamped(a0);
856 else if(p_itype == NumericalApproximation::CubicHermite) {
857 return EvaluateCubicHermite(a0);
860 if(!GslComputed()) ComputeGsl();
862 GslIntegrityCheck(gsl_spline_eval_e(p_interp, a0, p_acc, &result), _FILEINFO_);
868 "Evaluate() - Unable to evaluate the function at the point a = "
912 vector <double> NumericalApproximation::Evaluate(
const vector <double> &a,
const ExtrapType &etype) {
914 if(p_itype == NumericalApproximation::CubicNeighborhood) {
918 return EvaluateCubicNeighborhood(a, etype);
920 vector <double> result(a.size());
921 if(p_itype == NumericalApproximation::PolynomialNeville) {
924 p_polyNevError.clear();
925 for(
unsigned int i = 0; i < result.size(); i++) {
927 if(InsideDomain(a[i]))
929 else a0 = ValueToExtrapolate(a[i], etype);
930 result[i] = EvaluatePolynomialNeville(a0);
936 for(
unsigned int i = 0; i < result.size(); i++) {
937 result[i] = Evaluate(a[i], etype);
944 "Evaluate() - Unable to evaluate the function at the given vector of points",
970 vector <double> NumericalApproximation::PolynomialNevilleErrorEstimate() {
971 if(p_itype != NumericalApproximation::PolynomialNeville) {
972 ReportException(IException::Programmer,
"PolynomialNevilleErrorEstimate()",
973 "This method is only valid for polynomial-Neville's, may not be used for "
974 + Name() +
" interpolation",
977 if(p_polyNevError.empty()) {
978 ReportException(IException::Programmer,
"PolynomialNevilleErrorEstimate()",
979 "Error not calculated. This method only valid after Evaluate() has been called",
982 return p_polyNevError;
1020 double NumericalApproximation::GslFirstDerivative(
const double a) {
1022 if(!GslComputed()) ComputeGsl();
1027 "GslFirstDerivative() - Unable to compute the first derivative at a = "
1028 +
IString(a) +
" using the GSL interpolation",
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()) +
"]",
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",
1047 GslIntegrityCheck(gsl_spline_eval_deriv_e(p_interp, a, p_acc, &value), _FILEINFO_);
1053 "GslFirstDerivative() - Unable to compute the first derivative at a = "
1054 +
IString(a) +
". GSL integrity check failed",
1071 double NumericalApproximation::EvaluateCubicHermiteFirstDeriv(
const double a) {
1072 if(p_itype != NumericalApproximation::CubicHermite) {
1073 ReportException(IException::User,
"EvaluateCubicHermiteFirstDeriv()",
1074 "This method is only valid for cspline-Hermite interpolation, may not be used for "
1075 + Name() +
" interpolation",
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.",
1084 int lowerIndex = FindIntervalLowerIndex(a);
1088 if(a == p_x[lowerIndex]) {
1089 return p_fprimeOfx[lowerIndex];
1091 if(a == p_x[lowerIndex+1]) {
1092 return p_fprimeOfx[lowerIndex+1];
1095 double x0, x1, y0, y1, m0, m1;
1097 x0 = p_x[lowerIndex];
1098 x1 = p_x[lowerIndex+1];
1100 y0 = p_y[lowerIndex];
1101 y1 = p_y[lowerIndex+1];
1103 m0 = p_fprimeOfx[lowerIndex];
1104 m1 = p_fprimeOfx[lowerIndex+1];
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;
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()) +
"]",
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",
1177 if(!p_dataValidated) ValidateDataSet();
1181 for(
double i = 0; i < n; i++) {
1182 xi = a + h * (i - (n - 1));
1183 f.push_back(Evaluate(xi));
1189 "BackwardFirstDifference() - Unable to calculate backward first difference for (a, n, h) = ("
1195 return (-f[0] + f[1]) / h;
1197 return (3 * f[2] - 4 * f[1] + f[0]) / (2 * h);
1200 "BackwardFirstDifference() - Invalid argument. There is no "
1201 +
IString((
int) n) +
"-point backward difference formula in use",
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()) +
"]",
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",
1266 if(!p_dataValidated) ValidateDataSet();
1270 for(
double i = 0; i < n; i++) {
1272 f.push_back(Evaluate(xi));
1278 "ForwardFirstDifference() - Unable to calculate forward first difference for (a, n, h) = ("
1284 return (-f[0] + f[1]) / h;
1286 return (-3 * f[0] + 4 * f[1] - f[2]) / (2 * h);
1289 "ForwardFirstDifference() - Invalid argument. There is no "
1290 +
IString((
int) n) +
"-point forward difference formula in use",
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()) +
"]",
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",
1357 if(!p_dataValidated) ValidateDataSet();
1361 for(
double i = 0; i < n; i++) {
1362 xi = a + h * (i - (n - 1) / 2);
1363 f.push_back(Evaluate(xi));
1369 "CenterFirstDifference() - Unable to calculate center first difference for (a, n, h) = ("
1375 return (-f[0] + f[2]) / (2 * h);
1377 return (f[0] - 8 * f[1] + 8 * f[3] - f[4]) / (12 * h);
1380 "CenterFirstDifference() - Invalid argument. There is no "
1381 +
IString((
int) n) +
"-point center difference formula in use",
1422 double NumericalApproximation::GslSecondDerivative(
const double a) {
1424 if(!GslComputed()) ComputeGsl();
1429 "GslSecondDerivative() - Unable to compute the second derivative at a = "
1430 +
IString(a) +
" using the GSL interpolation",
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()) +
"]",
1440 if(!GslInterpType(p_itype))
1441 ReportException(IException::Programmer,
"GslSecondDerivative()",
1442 "Method only valid for GSL interpolation types, may not be used for "
1443 + Name() +
" interpolation",
1447 if(!GslComputed()) ComputeGsl();
1449 GslIntegrityCheck(gsl_spline_eval_deriv2_e(p_interp, a, p_acc, &value), _FILEINFO_);
1455 "GslSecondDerivative() - Unable to compute the second derivative at a = "
1456 +
IString(a) +
". GSL integrity check failed",
1474 double NumericalApproximation::EvaluateCubicHermiteSecDeriv(
const double a) {
1475 if(p_itype != NumericalApproximation::CubicHermite) {
1476 ReportException(IException::User,
"EvaluateCubicHermiteSecDeriv()",
1477 "This method is only valid for cspline-Hermite interpolation, may not be used for "
1478 + Name() +
" interpolation",
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.",
1487 int lowerIndex = FindIntervalLowerIndex(a);
1488 double x0, x1, y0, y1, m0, m1;
1490 x0 = p_x[lowerIndex];
1491 x1 = p_x[lowerIndex+1];
1493 y0 = p_y[lowerIndex];
1494 y1 = p_y[lowerIndex+1];
1496 m0 = p_fprimeOfx[lowerIndex];
1497 m1 = p_fprimeOfx[lowerIndex+1];
1503 return ((12 * t - 6) * y0 + (6 * t - 4) * h * m0 + (-12 * t + 6) * y1 + (6 * t - 2) * h * m1) / h;
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()) +
"]",
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",
1564 if(!p_dataValidated) ValidateDataSet();
1568 for(
double i = 0; i < n; i++) {
1569 xi = a + h * (i - (n - 1));
1570 f.push_back(Evaluate(xi));
1576 "BackwardSecondDifference() - Unable to calculate backward second difference for (a, n, h) = ("
1582 return (f[0] - 2 * f[1] + f[2]) / (h * h);
1585 "BackwardSecondDifference() - Invalid argument. There is no "
1586 +
IString((
int) n) +
"-point backward second difference formula in use",
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()) +
"]",
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",
1646 if(!p_dataValidated) ValidateDataSet();
1650 for(
double i = 0; i < n; i++) {
1652 f.push_back(Evaluate(xi));
1658 "ForwardSecondDifference() - Unable to calculate forward second difference for (a, n, h) = ("
1664 return (f[0] - 2 * f[1] + f[2]) / (h * h);
1667 "ForwardSecondDifference() - Invalid argument. There is no "
1668 +
IString((
int) n) +
"-point forward second difference formula in use",
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()) +
"]",
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",
1735 if(!p_dataValidated) ValidateDataSet();
1739 for(
double i = 0; i < n; i++) {
1740 xi = a + h * (i - (n - 1) / 2);
1741 f.push_back(Evaluate(xi));
1747 "CenterSecondDifference() - Unable to calculate center second difference for (a, n, h) = ("
1753 return (f[0] - 2 * f[1] + f[2]) / (h * h);
1755 return (-f[0] + 16 * f[1] - 30 * f[2] + 16 * f[3] - f[4]) / (12 * h * h);
1758 "CenterSecondDifference() - Invalid argument. There is no "
1759 +
IString((
int) n) +
"-point center second difference formula in use",
1797 double NumericalApproximation::GslIntegral(
const double a,
const double b) {
1799 if(!GslComputed()) ComputeGsl();
1804 "GslIntegral() - Unable to compute the integral on the interval (a,b) = ("
1806 +
") using the GSL interpolation",
1810 ReportException(IException::Programmer,
"GslIntegral()",
1811 "Invalid interval entered: [a,b] = ["
1815 if(!InsideDomain(a) || !InsideDomain(b)) {
1816 ReportException(IException::Programmer,
"GslIntegral()",
1817 "Invalid arguments. Interval entered ["
1819 +
"] is not contained within domain ["
1820 +
IString(DomainMinimum()) +
", "
1821 +
IString(DomainMaximum()) +
"]",
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",
1832 GslIntegrityCheck(gsl_spline_eval_integ_e(p_interp, a, b, p_acc, &value), _FILEINFO_);
1838 "GslIntegral() - Unable to compute the integral on the interval (a,b) = ("
1840 +
"). GSL integrity check failed",
1868 double NumericalApproximation::TrapezoidalRule(
const double a,
const double b) {
1873 vector <double> f = EvaluateForIntegration(a, b, n);
1874 double h = f.back();
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;
1887 "TrapezoidalRule() - Unable to calculate the integral on the interval (a,b) = ("
1889 +
") using the trapeziodal rule",
1920 double NumericalApproximation::Simpsons3PointRule(
const double a,
const double b) {
1925 vector <double> f = EvaluateForIntegration(a, b, n);
1926 double h = f.back();
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;
1938 "Simpsons3PointRule() - Unable to calculate the integral on the interval (a,b) = ("
1940 +
") using Simpson's 3 point rule",
1972 double NumericalApproximation::Simpsons4PointRule(
const double a,
const double b) {
1977 vector <double> f = EvaluateForIntegration(a, b, n);
1978 double h = f.back();
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;
1990 "Simpsons4PointRule() - Unable to calculate the integral on the interval (a,b) = ("
1992 +
") using Simpson's 4 point rule",
2027 double NumericalApproximation::BoolesRule(
const double a,
const double b) {
2032 vector <double> f = EvaluateForIntegration(a, b, n);
2033 double h = f.back();
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;
2045 "BoolesRule() - Unable to calculate the integral on the interval (a,b) = ("
2047 +
") using Boole's rule",
2096 double NumericalApproximation::RefineExtendedTrap(
double a,
double b,
double s,
unsigned int n) {
2103 if(GslInterpType(p_itype) || p_itype == NumericalApproximation::CubicNeighborhood) {
2105 begin = Evaluate(a, NumericalApproximation::NearestEndpoint);
2106 end = Evaluate(b, NumericalApproximation::NearestEndpoint);
2110 begin = Evaluate(a, NumericalApproximation::Extrapolate);
2111 end = Evaluate(b, NumericalApproximation::Extrapolate);
2113 return (0.5 * (b - a) * (begin + end));
2117 double delta, tnm, x, sum;
2118 it = (int)(pow(2.0, (
double)(n - 2)));
2120 delta = (b - a) / tnm;
2121 x = a + 0.5 * delta;
2123 for(
int i = 0; i < it; i++) {
2124 if(GslInterpType(p_itype) || p_itype == NumericalApproximation::CubicNeighborhood) {
2125 sum = sum + Evaluate(x, NumericalApproximation::NearestEndpoint);
2128 sum = sum + Evaluate(x, NumericalApproximation::Extrapolate);
2132 return (0.5 * (s + (b - a) * sum / tnm));
2138 "RefineExtendedTrap() - Unable to calculate the integral on the interval (a,b) = ("
2140 +
") using the extended trapeziodal rule",
2179 double NumericalApproximation::RombergsMethod(
double a,
double b) {
2186 double trap[maxits+1];
2197 for(
int i = 0; i < maxits; i++) {
2200 trap[i] = RefineExtendedTrap(a, b, trap[i], i + 1);
2202 interp.
AddData(5, &h[i-4], &trap[i-4]);
2204 ss = interp.
Evaluate(0.0, NumericalApproximation::Extrapolate);
2208 if(fabs(dss) <= epsilon * fabs(ss))
return ss;
2209 if(fabs(dss) <= epsilon2)
return ss;
2211 trap[i+1] = trap[i];
2212 h[i+1] = 0.25 * h[i];
2223 "RombergsMethod() - Unable to calculate the integral on the interval (a,b) = ("
2225 +
") using Romberg's method",
2229 "RombergsMethod() - Unable to calculate the integral using RombergsMethod() - Failed to converge in "
2230 +
IString(maxits) +
" iterations",
2251 void NumericalApproximation::Reset() {
2252 if(GslInterpType(p_itype)) GslDeallocation();
2253 p_clampedComputed =
false;
2254 p_clampedEndptsSet =
false;
2255 p_dataValidated =
false;
2258 p_clampedSecondDerivs.clear();
2259 p_clampedDerivFirstPt = 0;
2260 p_clampedDerivLastPt = 0;
2261 p_polyNevError.clear();
2262 p_fprimeOfx.clear();
2286 SetInterpType(itype);
2292 "Reset() - Unable to reset interpolation type",
2321 if(GslInterpType(itype)) {
2328 "SetInterpType() - Unable to set interpolation type",
2332 else if(itype > 9) {
2333 ReportException(IException::Programmer,
"SetInterpType()",
2334 "Invalid argument. Unknown interpolation type: "
2341 p_dataValidated =
false;
2342 p_clampedComputed =
false;
2343 p_clampedEndptsSet =
false;
2344 p_clampedSecondDerivs.clear();
2345 p_clampedDerivFirstPt = 0;
2346 p_clampedDerivLastPt = 0;
2347 p_polyNevError.clear();
2348 p_fprimeOfx.clear();
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));
2388 SetInterpType(itype);
2393 "Init() - Unable to initialize NumericalApproximation object",
2398 gsl_set_error_handler_off();
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;
2444 void NumericalApproximation::GslAllocation(
unsigned int npoints) {
2447 p_acc = gsl_interp_accel_alloc();
2449 p_interp = gsl_spline_alloc(GslFunctor(p_itype), npoints);
2466 void NumericalApproximation::GslDeallocation() {
2467 if(p_interp) gsl_spline_free(p_interp);
2468 if(p_acc) gsl_interp_accel_free(p_acc);
2495 if(fItr == p_interpFunctors.end()) {
2496 ReportException(IException::Programmer,
"GslFunctor()",
2497 "Invalid argument. Unable to find GSL interpolator with id = "
2501 return (fItr->second);
2523 void NumericalApproximation::GslIntegrityCheck(
int gsl_status,
const char *src,
int line)
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);
2563 void NumericalApproximation::ValidateDataSet() {
2564 if((
int) Size() < MinPoints()) {
2565 ReportException(IException::Programmer,
"ValidateDataSet()",
2566 Name() +
" interpolation requires a minimum of "
2567 +
IString(MinPoints()) +
" data points - currently have "
2571 for(
unsigned int i = 1; i < Size(); i++) {
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["
2577 +
" = p_x[" +
IString((
int) i) +
"]",
2580 if(p_x[i-1] > p_x[i]) {
2583 if(p_itype != NumericalApproximation::PolynomialNeville) {
2584 ReportException(IException::Programmer,
"ValidateDataSet()",
2585 "Invalid data set, x-values must be in ascending order for "
2586 + Name() +
" interpolation: \n\t\tx["
2593 if(p_itype == NumericalApproximation::CubicNatPeriodic) {
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",
2601 p_dataValidated =
true;
2619 bool NumericalApproximation::InsideDomain(
const double a) {
2621 if(a + DBL_EPSILON < DomainMinimum()) {
2624 if(a - DBL_EPSILON > DomainMaximum()) {
2631 "InsideDomain() - Unable to compute domain boundaries",
2653 bool NumericalApproximation::GslComputed()
const {
2654 if(GslInterpType(p_itype))
return ((p_interp) && (p_acc));
2657 "GslComputed() - Method only valid for GSL interpolation types, may not be used for "
2658 + Name() +
" interpolation",
2683 void NumericalApproximation::ComputeGsl() {
2685 if(GslComputed())
return;
2686 if(!p_dataValidated) ValidateDataSet();
2687 GslAllocation(Size());
2688 GslIntegrityCheck(gsl_spline_init(p_interp, &p_x[0], &p_y[0], Size()), _FILEINFO_);
2694 "ComputeGsl() - Unable to compute GSL interpolation",
2735 void NumericalApproximation::ComputeCubicClamped() {
2740 if(!p_dataValidated) {
2747 "ComputeCubicClamped() - Unable to compute cubic clamped interpolation",
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",
2757 p_clampedSecondDerivs.resize(n);
2759 double p, sig, qn, un;
2761 if(p_clampedDerivFirstPt > 0.99e30) {
2762 p_clampedSecondDerivs[0] = 0.0;
2766 p_clampedSecondDerivs[0] = -0.5;
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);
2769 for(
int i = 1; i < n - 1; i++) {
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;
2776 if(p_clampedDerivLastPt > 0.99e30) {
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]));
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--) {
2787 p_clampedSecondDerivs[i] = p_clampedSecondDerivs[i] * p_clampedSecondDerivs[i+1] + u[i];
2789 p_clampedComputed =
true;
2830 double NumericalApproximation::ValueToExtrapolate(
const double a,
const ExtrapType &etype) {
2831 if(etype == NumericalApproximation::ThrowError) {
2832 ReportException(IException::Programmer,
"Evaluate()",
2833 "Invalid argument. Value entered, a = "
2834 +
IString(a) +
", is outside of domain = ["
2835 +
IString(DomainMinimum()) +
", "
2836 +
IString(DomainMaximum()) +
"]",
2839 if(etype == NumericalApproximation::NearestEndpoint) {
2840 if(a + DBL_EPSILON < DomainMinimum()) {
2841 return DomainMinimum();
2844 return DomainMaximum();
2848 if(GslInterpType(p_itype)
2849 || p_itype == NumericalApproximation::CubicNeighborhood) {
2850 ReportException(IException::Programmer,
"Evaluate()",
2851 "Invalid argument. Cannot extrapolate for type "
2852 + Name() +
", must choose to throw error or return nearest neighbor",
2893 double NumericalApproximation::EvaluateCubicNeighborhood(
const double a) {
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;
2903 else if(s > (
int) Size() - 3) s = Size() - 3;
2907 for(
int n = 0; n < 4; n++) {
2916 return spline.
Evaluate(a, NumericalApproximation::NearestEndpoint);
2921 "EvaluateCubicNeighborhood() - Unable to evaluate cubic neighborhood interpolation at a = "
2973 vector <double> NumericalApproximation::EvaluateCubicNeighborhood(
const vector <double> &a,
2975 vector <double> result(a.size());
2977 vector <double> x0(4), y0(4);
2981 for(
unsigned int i = 0; i < a.size(); i++) {
2982 for(
unsigned int n = 0; n < Size(); n++) {
2990 if(s[i] > ((
int) Size()) - 3) {
2997 for(
unsigned int i = 0; i < a.size(); i++) {
3001 for(
int n = 0; n < 4; n++) {
3010 if(InsideDomain(a[i]))
3012 else a0 = ValueToExtrapolate(a[i], etype);
3014 result[i] = spline.
Evaluate(a0, NumericalApproximation::NearestEndpoint);
3021 "EvaluateCubicNeighborhood() - Unable to evaluate the function at the given vector of points using cubic neighborhood interpolation",
3060 double NumericalApproximation::EvaluateCubicClamped(
double a) {
3076 while(k_Hi - k_Lo > 1) {
3077 k = (k_Hi + k_Lo) / 2;
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;
3121 double NumericalApproximation::EvaluateCubicHermite(
const double a) {
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.",
3131 int lowerIndex = FindIntervalLowerIndex(a);
3135 if(a == p_x[lowerIndex]) {
3136 return p_y[lowerIndex];
3138 if(a == p_x[lowerIndex+1]) {
3139 return p_y[lowerIndex+1];
3142 double x0, x1, y0, y1, m0, m1;
3144 x0 = p_x[lowerIndex];
3145 x1 = p_x[lowerIndex+1];
3147 y0 = p_y[lowerIndex];
3148 y1 = p_y[lowerIndex+1];
3150 m0 = p_fprimeOfx[lowerIndex];
3151 m1 = p_fprimeOfx[lowerIndex+1];
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;
3183 int NumericalApproximation::FindIntervalLowerIndex(
const double a) {
3184 if(InsideDomain(a)) {
3186 std::vector<double>::iterator pos;
3188 pos = upper_bound(p_x.begin(), p_x.end(), a);
3190 if(pos != p_x.end()) {
3191 upperIndex = distance(p_x.begin(), pos);
3194 upperIndex = Size() - 1;
3196 return upperIndex - 1;
3198 else if((a + DBL_EPSILON) < DomainMinimum()) {
3242 double NumericalApproximation::EvaluatePolynomialNeville(
const double a) {
3250 double den, dif, dift, c[n], d[n], ho, hp, w;
3253 dif = fabs(a - p_x[0]);
3254 for(
int i = 0; i < n; i++) {
3255 dift = fabs(a - p_x[i]);
3265 for(
int m = 1; m < n; m++) {
3266 for(
int i = 1; i <= n - m; i++) {
3268 hp = p_x[i+m-1] - a;
3275 if(2 * ns < n - m) {
3284 p_polyNevError.push_back(*err);
3306 vector <double> NumericalApproximation::EvaluateForIntegration(
const double a,
const double b,
const unsigned int n) {
3308 ReportException(IException::Programmer,
"EvaluatedForIntegration()",
3309 "Invalid interval entered: [a,b] = ["
3313 if(!InsideDomain(a) || !InsideDomain(b)) {
3314 ReportException(IException::Programmer,
"EvaluateForIntegration()",
3315 "Invalid arguments. Interval entered ["
3317 +
"] is not contained within domain ["
3318 +
IString(DomainMinimum()) +
", "
3319 +
IString(DomainMaximum()) +
"]",
3326 int xSegments = Size() - 1;
3327 while(xSegments % (n - 1) != 0) {
3334 double h = (xMax - xMin) / xSegments;
3337 for(
double i = 0; i < (xSegments + 1); i++) {
3338 double xi = h * i + xMin;
3339 f.push_back(Evaluate(xi));
3347 "EvaluateForIntegration() - Unable to evaluate the data set for integration",
3372 void NumericalApproximation::ReportException(
IException::ErrorType type,
const string &methodName,
const string &message,
3373 const char *filesrc,
int lineno)
3375 string msg = methodName +
" - " + message;
3376 throw IException(type, msg.c_str(), filesrc, lineno);