/********************************************************** * * Class CubicSpline * * Class for performing an interpolation using a cubic spline * setTabulatedArrays and interpolate adapted, with modification to * an object-oriented approach, from Numerical Recipes in C (http://www.nr.com/) * * * WRITTEN BY: Dr Michael Thomas Flanagan * * DATE: May 2002 * UPDATE: 29 April 2005, 17 February 2006, 21 September 2006, 4 December 2007 * 24 March 2008 (Thanks to Peter Neuhaus, Florida Institute for Human and Machine Cognition) * 21 September 2008 * 14 January 2009 - point deletion and check for 3 points reordered (Thanks to Jan Sacha, Vrije Universiteit Amsterdam) * 31 October 2009, 11-14 January 2010 * 31 August 2010 - overriding natural spline error, introduced in earlier update, corrected (Thanks to Dagmara Oszkiewicz, University of Helsinki) * 2 February 2011 * * DOCUMENTATION: * See Michael Thomas Flanagan's Java library on-line web page: * http://www.ee.ucl.ac.uk/~mflanaga/java/CubicSpline.html * http://www.ee.ucl.ac.uk/~mflanaga/java/ * * Copyright (c) 2002 - 2010 Michael Thomas Flanagan * * PERMISSION TO COPY: * * Permission to use, copy and modify this software and its documentation for NON-COMMERCIAL purposes is granted, without fee, * provided that an acknowledgement to the author, Dr Michael Thomas Flanagan at www.ee.ucl.ac.uk/~mflanaga, appears in all copies * and associated documentation or publications. * * Redistributions of the source code of this source code, or parts of the source codes, must retain the above copyright notice, * this list of conditions and the following disclaimer and requires written permission from the Michael Thomas Flanagan: * * Redistribution in binary form of all or parts of this class must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials provided with the distribution and requires written permission * from the Michael Thomas Flanagan: * * Dr Michael Thomas Flanagan makes no representations about the suitability or fitness of the software for any or for a particular purpose. * Dr Michael Thomas Flanagan shall not be liable for any damages suffered as a result of using, modifying or distributing this software * or its derivatives. * ***************************************************************************************/ package flanagan.interpolation; import flanagan.math.Fmath; import flanagan.math.Conv; public class CubicSpline{ private int nPoints = 0; // no. of tabulated points private int nPointsOriginal = 0; // no. of tabulated points after any deletions of identical points private double[] y = null; // y=f(x) tabulated function private double[] x = null; // x in tabulated function f(x) private double yy = Double.NaN; // interpolated value of y private double dydx = Double.NaN; // interpolated value of the first derivative, dy/dx private int[]newAndOldIndices; // record of indices on ordering x into ascending order private double xMin = Double.NaN; // minimum x value private double xMax = Double.NaN; // maximum x value private double range = Double.NaN; // xMax - xMin private double[] d2ydx2 = null; // second derivatives of y private double yp1 = Double.NaN; // second derivative at point one // default value = NaN (natural spline) private double ypn = Double.NaN; // second derivative at point n // default value = NaN (natural spline) private boolean derivCalculated = false; // = true when the derivatives have been calculated private boolean checkPoints = false; // = true when points checked for identical values private static boolean supress = false; // if true: warning messages supressed private static boolean averageIdenticalAbscissae = false; // if true: the the ordinate values for identical abscissae are averaged // if false: the abscissae values are separated by 0.001 of the total abscissae range; private static double potentialRoundingError = 5e-15; // potential rounding error used in checking wheter a value lies within the interpolation bounds private static boolean roundingCheck = true; // = true: points outside the interpolation bounds by less than the potential rounding error rounded to the bounds limit // Constructors // Constructor with data arrays initialised to arrays x and y public CubicSpline(double[] x, double[] y){ this.nPoints=x.length; this.nPointsOriginal = this.nPoints; if(this.nPoints!=y.length)throw new IllegalArgumentException("Arrays x and y are of different length "+ this.nPoints + " " + y.length); if(this.nPoints<3)throw new IllegalArgumentException("A minimum of three data points is needed"); this.x = new double[nPoints]; this.y = new double[nPoints]; this.d2ydx2 = new double[nPoints]; for(int i=0; ithis.y[1]){ if(this.y[1]>this.y[2]){ check = stay(ii, jj, sepn); } else{ check = swap(ii, jj, sepn); } } else{ if(this.y[2]<=this.y[1]){ check = swap(ii, jj, sepn); } else{ check = stay(ii, jj, sepn); } } } if(jj==this.nPoints-1){ if(x[nP-2]-x[nP-3]<=sepn)sepn = (x[nP-2]-x[nP-3])/2.0D; if(this.y[ii]<=this.y[jj]){ if(this.y[ii-1]<=this.y[ii]){ check = stay(ii, jj, sepn); } else{ check = swap(ii, jj, sepn); } } else{ if(this.y[ii-1]<=this.y[ii]){ check = swap(ii, jj, sepn); } else{ check = stay(ii, jj, sepn); } } } if(ii!=0 && jj!=this.nPoints-1){ if(x[ii]-x[ii-1]<=sepn)sepn = (x[ii]-x[ii-1])/2; if(x[jj+1]-x[jj]<=sepn)sepn = (x[jj+1]-x[jj])/2; if(this.y[ii]>this.y[ii-1]){ if(this.y[jj]>this.y[ii]){ if(this.y[jj]>this.y[jj+1]){ if(this.y[ii-1]<=this.y[jj+1]){ check = stay(ii, jj, sepn); } else{ check = swap(ii, jj, sepn); } } else{ check = stay(ii, jj, sepn); } } else{ if(this.y[jj+1]>this.y[jj]){ if(this.y[jj+1]>this.y[ii-1] && this.y[jj+1]>this.y[ii-1]){ check = stay(ii, jj, sepn); } } else{ check = swap(ii, jj, sepn); } } } else{ if(this.y[jj]>this.y[ii]){ if(this.y[jj+1]>this.y[jj]){ check = stay(ii, jj, sepn); } } else{ if(this.y[jj+1]>this.y[ii-1]){ check = stay(ii, jj, sepn); } else{ check = swap(ii, jj, sepn); } } } } if(check==false){ check = stay(ii, jj, sepn); } if(!CubicSpline.supress)System.out.println(", the two abscissae have been separated by a distance " + sepn); jj++; } } if((this.nPoints-1)==ii)test2 = false; } else{ jj++; } if(jj>=this.nPoints)test2 = false; } ii++; if(ii>=this.nPoints-1)test1 = false; } if(this.nPoints<3)throw new IllegalArgumentException("Removal of duplicate points has reduced the number of points to less than the required minimum of three data points"); this.checkPoints = true; } // Swap method for checkForIdenticalPoints procedure private boolean swap(int ii, int jj, double sepn){ this.x[ii] += sepn; this.x[jj] -= sepn; double hold = this.x[ii]; this.x[ii] = this.x[jj]; this.x[jj] = hold; hold = this.y[ii]; this.y[ii] = this.y[jj]; this.y[jj] = hold; return true; } // Stay method for checkForIdenticalPoints procedure private boolean stay(int ii, int jj, double sepn){ this.x[ii] -= sepn; this.x[jj] += sepn; return true; } // Supress warning messages in the identifiaction of duplicate points public static void supress(){ CubicSpline.supress = true; } // Unsupress warning messages in the identifiaction of duplicate points public static void unsupress(){ CubicSpline.supress = false; } // Returns a new CubicSpline setting array lengths to n and all array values to zero with natural spline default // Primarily for use in BiCubicSpline public static CubicSpline zero(int n){ if(n<3)throw new IllegalArgumentException("A minimum of three data points is needed"); CubicSpline aa = new CubicSpline(n); return aa; } // Create a one dimensional array of cubic spline objects of length n each of array length m // Primarily for use in BiCubicSpline public static CubicSpline[] oneDarray(int n, int m){ if(m<3)throw new IllegalArgumentException("A minimum of three data points is needed"); CubicSpline[] a =new CubicSpline[n]; for(int i=0; i=0;k--){ this.d2ydx2[k]=this.d2ydx2[k]*this.d2ydx2[k+1]+u[k]; } this.derivCalculated = true; } // INTERPOLATE // Returns an interpolated value of y for a value of x from a tabulated function y=f(x) // after the data has been entered via a constructor. // The derivatives are calculated, bt calcDeriv(), on the first call to this method ands are // then stored for use on all subsequent calls public double interpolate(double xx){ // Check for violation of interpolation bounds if (xxthis.x[this.nPoints-1]){ if(CubicSpline.roundingCheck && Math.abs(xx-this.x[this.nPoints-1])<=Math.pow(10, Math.floor(Math.log10(Math.abs(this.x[this.nPoints-1]))))*CubicSpline.potentialRoundingError){ xx = this.x[this.nPoints-1]; } else{ throw new IllegalArgumentException("x ("+xx+") is outside the range of data points ("+x[0]+" to "+x[this.nPoints-1] + ")"); } } double h=0.0D,b=0.0D,a=0.0D, yy=0.0D; int k=0; int klo=0; int khi=this.nPoints-1; while (khi-klo > 1){ k=(khi+klo) >> 1; if(this.x[k] > xx){ khi=k; } else{ klo=k; } } h=this.x[khi]-this.x[klo]; if (h == 0.0){ throw new IllegalArgumentException("Two values of x are identical: point "+klo+ " ("+this.x[klo]+") and point "+khi+ " ("+this.x[khi]+")" ); } else{ a=(this.x[khi]-xx)/h; b=(xx-this.x[klo])/h; this.yy = a*this.y[klo]+b*this.y[khi]+((a*a*a-a)*this.d2ydx2[klo]+(b*b*b-b)*this.d2ydx2[khi])*(h*h)/6.0; this.dydx = (y[khi] - y[klo])/h - ((3*a*a - 1.0)*this.d2ydx2[klo] - (3*b*b - 1.0)*this.d2ydx2[khi])*h/6.0; } return this.yy; } // Returns an interpolated value of y and of the first derivative dy/dx for a value of x from a tabulated function y=f(x) // after the data has been entered via a constructor. public double[] interpolate_for_y_and_dydx(double xx){ this.interpolate(xx); double[] ret = {this.yy, this.dydx}; return ret; } // Returns an interpolated value of y for a value of x (xx) from a tabulated function y=f(x) // after the derivatives (deriv) have been calculated independently of calcDeriv(). public static double interpolate(double xx, double[] x, double[] y, double[] deriv){ if(((x.length != y.length) || (x.length != deriv.length)) || (y.length != deriv.length)){ throw new IllegalArgumentException("array lengths are not all equal"); } int n = x.length; double h=0.0D, b=0.0D, a=0.0D, yy = 0.0D; int k=0; int klo=0; int khi=n-1; while(khi-klo > 1){ k=(khi+klo) >> 1; if(x[k] > xx){ khi=k; } else{ klo=k; } } h=x[khi]-x[klo]; if (h == 0.0){ throw new IllegalArgumentException("Two values of x are identical"); } else{ a=(x[khi]-xx)/h; b=(xx-x[klo])/h; yy=a*y[klo]+b*y[khi]+((a*a*a-a)*deriv[klo]+(b*b*b-b)*deriv[khi])*(h*h)/6.0; } return yy; } }