void testNatural(const Spline &sp, const double *x, const double *y) { // test the common properties of splines testCommon(sp, x, y); static double eps = 1e-5; size_t n = sp.numSamples(); // make sure the second derivatives at both end points are 0 double d0 = sp.evalDerivative(x[0]); double d1 = sp.evalDerivative(x[0] + eps); double d2 = sp.evalDerivative(x[n-1] - eps); double d3 = sp.evalDerivative(x[n-1]); if (std::abs(d1 - d0)/eps > 1000*eps) OPM_THROW(std::runtime_error, "Invalid second derivative at beginning of interval: is " << (d1 - d0)/eps << " ought to be 0"); if (std::abs(d3 - d2)/eps > 1000*eps) OPM_THROW(std::runtime_error, "Invalid second derivative at end of interval: is " << (d3 - d2)/eps << " ought to be 0"); }
void testMonotonic(const Spline &sp, const double *x, const double *y) { // test the common properties of splines testCommon(sp, x, y); size_t n = sp.numSamples(); for (size_t i = 0; i < n - 1; ++ i) { // make sure that the spline is monotonic for each interval // between sampling points if (!sp.monotonic(x[i], x[i + 1])) OPM_THROW(std::runtime_error, "Spline says it is not monotonic in interval " << i << " where it should be"); // test the intersection methods double d = (y[i] + y[i+1])/2; double interX = sp.template intersectInterval<double>(x[i], x[i+1], /*a=*/0, /*b=*/0, /*c=*/0, d); double interY = sp.eval(interX); if (std::abs(interY - d) > 1e-5) OPM_THROW(std::runtime_error, "Spline::intersectInterval() seems to be broken: " << sp.eval(interX) << " - " << d << " = " << sp.eval(interX) - d << "!"); } // make sure the spline says to be monotonic on the (extrapolated) // left and right sides if (!sp.monotonic(x[0] - 1.0, (x[0] + x[1])/2, /*extrapolate=*/true)) OPM_THROW(std::runtime_error, "Spline says it is not monotonic on left side where it should be"); if (!sp.monotonic((x[n - 2]+ x[n - 1])/2, x[n-1] + 1.0, /*extrapolate=*/true)) OPM_THROW(std::runtime_error, "Spline says it is not monotonic on right side where it should be"); for (size_t i = 0; i < n - 2; ++ i) { // make sure that the spline says that it is non-monotonic for // if extrema are within the queried interval if (sp.monotonic((x[i] + x[i + 1])/2, (x[i + 1] + x[i + 2])/2)) OPM_THROW(std::runtime_error, "Spline says it is monotonic in interval " << i << " where it should not be"); } }
void testFull(const Spline &sp, const double *x, const double *y, double m0, double m1) { // test the common properties of splines testCommon(sp, x, y); static double eps = 1e-5; size_t n = sp.numSamples(); // make sure the derivative at both end points is correct double d0 = sp.evalDerivative(x[0]); double d1 = sp.evalDerivative(x[n-1]); if (std::abs(d0 - m0) > eps) OPM_THROW(std::runtime_error, "Invalid derivative at beginning of interval: is " << d0 << " ought to be " << m0); if (std::abs(d1 - m1) > eps) OPM_THROW(std::runtime_error, "Invalid derivative at end of interval: is " << d1 << " ought to be " << m1); }
void testCommon(const Spline &sp, const double *x, const double *y) { static double eps = 1e-10; static double epsFD = 1e-7; size_t n = sp.numSamples(); for (size_t i = 0; i < n; ++i) { // sure that we hit all sampling points double y0 = (i>0)?sp.eval(x[i]-eps):y[0]; double y1 = sp.eval(x[i]); double y2 = (i<n-1)?sp.eval(x[i]+eps):y[n-1]; if (std::abs(y0 - y[i]) > 100*eps || std::abs(y2 - y[i]) > 100*eps) OPM_THROW(std::runtime_error, "Spline seems to be discontinuous at sampling point " << i << "!"); if (std::abs(y1 - y[i]) > eps) OPM_THROW(std::runtime_error, "Spline does not capture sampling point " << i << "!"); // make sure the derivative is continuous (assuming that the // second derivative is smaller than 1000) double d1 = sp.evalDerivative(x[i]); double d0 = (i>0)?sp.evalDerivative(x[i]-eps):d1; double d2 = (i<n-1)?sp.evalDerivative(x[i]+eps):d1; if (std::abs(d1 - d0) > 1000*eps || std::abs(d2 - d0) > 1000*eps) OPM_THROW(std::runtime_error, "Spline seems to exhibit a discontinuous derivative at sampling point " << i << "!"); } // make sure the derivatives are consistent with the curve size_t np = 3*n; for (size_t i = 0; i < np; ++i) { double xMin = sp.xAt(0); double xMax = sp.xAt(sp.numSamples() - 1); double xval = xMin + (xMax - xMin)*i/np; // first derivative double y1 = sp.eval(xval+epsFD); double y0 = sp.eval(xval); double mFD = (y1 - y0)/epsFD; double m = sp.evalDerivative(xval); if (std::abs( mFD - m ) > 1000*epsFD) OPM_THROW(std::runtime_error, "Derivative of spline seems to be inconsistent with cuve" " (" << mFD << " - " << m << " = " << mFD - m << ")!"); // second derivative y1 = sp.evalDerivative(xval+epsFD); y0 = sp.evalDerivative(xval); mFD = (y1 - y0)/epsFD; m = sp.evalSecondDerivative(xval); if (std::abs( mFD - m ) > 1000*epsFD) OPM_THROW(std::runtime_error, "Second derivative of spline seems to be inconsistent with cuve" " (" << mFD << " - " << m << " = " << mFD - m << ")!"); // Third derivative y1 = sp.evalSecondDerivative(xval+epsFD); y0 = sp.evalSecondDerivative(xval); mFD = (y1 - y0)/epsFD; m = sp.evalThirdDerivative(xval); if (std::abs( mFD - m ) > 1000*epsFD) OPM_THROW(std::runtime_error, "Third derivative of spline seems to be inconsistent with cuve" " (" << mFD << " - " << m << " = " << mFD - m << ")!"); } }