-
Claudio Scafuri authoredc5b7245b
spline.cpp 6.62 KiB
/*
* class Spline - implementation
*
* $Author: claudio $
*
* $Revision: 1.2 $
*
* $Log: spline.cpp,v $
* Revision 1.2 2019-12-06 13:03:02 claudio
* gcc 7.4.0
*
* Revision 1.1.1.1 2012-09-05 08:17:22 claudio
* Interpolator utility classes
*
*/
#include "spline.h"
#include <cmath>
#include <cstdlib>
#include <limits>
namespace Interpolator{
Spline::Spline (doubleVector& xvalues, doubleVector& yvalues, double yp0 , double ypN ):Interpolator(){
if(xvalues.size() != yvalues.size()) throw std::length_error("Spline(): data vectors must be same length");
if(xvalues.size() < 2) throw std::length_error("Spline(): data vector length must be >=2");
n_base=xvalues.size();
x_base= static_cast<double*>(malloc(n_base*sizeof(double))); //get one element more the needed - just for testing bug
y_base= static_cast<double*>(malloc(n_base*sizeof(double)));
y2 = static_cast<double*>(malloc((n_base+1)*sizeof(double)));
u = static_cast<double*>(malloc((n_base-1)*sizeof(double)));
//copy the base values into the just allocated arrays
//and check that xvalues are sorted correctly
double lowbound=-HUGE_VAL;
for(unsigned int i=0;i < n_base;i++){
if(xvalues[i]>lowbound){
lowbound=xvalues[i];
}
else{
free(u);
free(y2);
free(y_base);
free(x_base);
throw std::domain_error("Spline(): x_values must be in ascending order");
}
x_base[i]=xvalues[i];
y_base[i]=yvalues[i];
}
//record range limits
_min=x_base[0];
_max=x_base[n_base-1];
// do the initial calulations for the spline algorithm
// got from an old Pascal based text about Numerical Algorithms
{
double p,sig,qn,un;
unsigned int i;
int k;
//set boundary conditions for intial point
if(std::isnan(yp0)){
u[0]=0.0; y2[0]=0.0;//natural spline condition
}
else{
u[0]=(3.0/(x_base[1]-x_base[0]))*((y_base[1]-y_base[0])/(x_base[1]-x_base[0])-yp0);
y2[0]=-0.5;
}
for (i=1;i<n_base-1;i++) {
sig=(x_base[i]-x_base[i-1])/(x_base[i+1]-x_base[i-1]);
p=sig*y2[i-1]+2.0;
y2[i]=(sig-1.0)/p;
u[i]=(y_base[i+1]-y_base[i])/(x_base[i+1]-x_base[i]) - (y_base[i]-y_base[i-1])/(x_base[i]-x_base[i-1]);
u[i]=(6.0*u[i]/(x_base[i+1]-x_base[i-1])-sig*u[i-1])/p;
}
//set boundary conditions for intial point
if(std::isnan(ypN)){
qn=un=0.0; //natural spline condition
}
else{
un=(3.0/(x_base[n_base-1]-x_base[n_base-2]))* (ypN-(y_base[n_base-1]-y_base[n_base-2])/(x_base[n_base-1]-x_base[n_base-2]));
qn=0.5;
}
y2[n_base-1]=(un-qn*u[n_base-2])/(qn*y2[n_base-2]+1.0);
for (k=n_base-2;k>=0;k--)
y2[k]=y2[k]*y2[k+1]+u[k];
}
}
Spline::Spline(Spline& src):Interpolator()
{
if( src.n_base < 2 ) throw std::length_error("Spline(): data vector length must be >=2");
n_base=src.n_base;
x_base= static_cast<double*>(malloc(n_base*sizeof(double)));
y_base= static_cast<double*>(malloc(n_base*sizeof(double)));
y2 = static_cast<double*>(malloc((n_base+1)*sizeof(double)));
u = static_cast<double*>(malloc((n_base-1)*sizeof(double)));
_min=src._min;
_max=src._max;
for(unsigned int i=0; i<n_base;i++){
x_base[i]=src.x_base[i];
y_base[i]=src.y_base[i];
y2[i]=src.y2[i];
}
y2[n_base]=src.y2[n_base];
for(unsigned int i=0; i<n_base-1;i++){
u[i]=src.u[i];
}
}
Spline& Spline::operator=(const Spline& src)
{
n_base=src.n_base;
x_base= static_cast<double*>(malloc(n_base*sizeof(double)));
y_base= static_cast<double*>(malloc(n_base*sizeof(double)));
y2 = static_cast<double*>(malloc((n_base+1)*sizeof(double)));
u = static_cast<double*>(malloc((n_base+1)*sizeof(double)));
_min=src._min;
_max=src._max;
for(unsigned int i=0; i<n_base;i++){
x_base[i]=src.x_base[i];
y_base[i]=src.y_base[i];
y2[i]=src.y2[i];
}
y2[n_base]=src.y2[n_base];
for(unsigned int i=0; i<n_base-1;i++){
u[i]=src.u[i];
}
return *this;
}
Spline::~Spline ( )
{
if(n_base){
free(u);
//free(pp);
free(y2);
free(y_base);
free(x_base);
}
}
/**
* @param xvalues returns the abscissae of the base point of the spline
* @param yvalues returns the ordinates of the base point of the spline
*/
void Spline::get_base_points (doubleVector& xvalues, doubleVector& yvalues ) const
{
xvalues.clear();
yvalues.clear();
for(unsigned int i=0;i<n_base;i++){
xvalues.push_back(x_base[i]);
yvalues.push_back(y_base[i]);
}
return;
}
double Spline::evaluate (double v )
{
int klo,khi,k;
double h,b,a;
if (v < _min || v > _max ) throw std::range_error("input value out of range");
klo=0;
khi=n_base-1; //era n_base !!!!!
while (khi-klo > 1) {
k=(khi+klo) >> 1;//k=(khi+klo)/2;
if (x_base[k] > v) khi=k;
else klo=k;
}
h=x_base[khi]-x_base[klo];
if (h == 0.0) throw std::range_error("input value out of range");
a=(x_base[khi]-v)/h;
b=(v-x_base[klo])/h;
return a*y_base[klo]+b*y_base[khi]+((a*a*a-a)*y2[klo]+(b*b*b-b)*y2[khi])*(h*h)/6.0;
}
doubleVector Spline::evaluate (doubleVector in_val )
{
//simplicistic implementation. Should make better use of the indexing
doubleVector outval;
for(unsigned int i=0; i < in_val.size(); i++) outval.push_back(evaluate(in_val[i]));
return outval;
}
// bool Spline::bracket(double goal,double initial,double tol, double& xlow, double& xhigh)
// {
// if(tol<=std::numeric_limits<double>::epsilon()) throw std::range_error("bracket: tol too small");
// const double fact=1.618;
// const unsigned int ntry=50;
// double x1,x2;
// x1=initial-tol; if(x1 < _min) x1=_min;
// x2=initial+tol; if(x2 > _max) x2=_max;
// double f1,f2;
// f1=goal - evaluate(x1);
// f2=goal - evaluate(x2);
// for(unsigned int j=0; j < ntry; j++){
// if(f1*f2 <0.0){
// xlow=x1;
// xhigh=x2;
// return true;
// }
// if(fabs(f1) < fabs(f2)){
// x1 += fact*(x1-x2);
// if(x1 < _min) x1=_min;
// if(x1 > _max) x1=_max;
// f1=goal - evaluate(x1);
// }
// else{
// x2 += fact *(x2-x1);
// if(x2 < _min) x2=_min;
// if(x2 > _max) x2=_max;
// f2=goal - evaluate(x2);
// }
// }
// return false;
// }
//
//
// double Spline::solve(double goal,double x1,double x2,double tol,unsigned int maxiter)
// {
// double fl,fh,xl,xh,del,f,rtf,dx;
// fl = goal - evaluate(x1);
// fh = goal - evaluate(x2);
// if( fl*fh > 0.0) throw std::range_error("solve: initial interval does not braket solution");
// if(fl < 0.0 ){
// xl=x1;
// xh=x2;
// }
// else{
// xl=x2;
// xh=x1;
// double swap=fl;
// fl=fh;
// fh=swap;
// }
// dx=xh-xl;
// for(unsigned int j=0; j < maxiter; j++){
// rtf=xl+dx*fl/(fl-fh);
// f= goal - evaluate(rtf);
// if(f < 0.0){
// del=xl-rtf;
// xl=rtf;
// fl=f;
// }
// else{
// del=xh-rtf;
// xh=rtf;
// fh=f;
// }
// dx=xh-xl;
// if(fabs(del) < tol || f == 0.0) return rtf;
// }
// return NAN; //maxiter exceeded
// }
} //close namespace Interpolator