interp.h

00001 /*
00002   -------------------------------------------------------------------
00003   
00004   Copyright (C) 2006, 2007, 2008, Andrew W. Steiner
00005   
00006   This file is part of O2scl.
00007   
00008   O2scl is free software; you can redistribute it and/or modify
00009   it under the terms of the GNU General Public License as published by
00010   the Free Software Foundation; either version 3 of the License, or
00011   (at your option) any later version.
00012   
00013   O2scl is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016   GNU General Public License for more details.
00017   
00018   You should have received a copy of the GNU General Public License
00019   along with O2scl. If not, see <http://www.gnu.org/licenses/>.
00020 
00021   -------------------------------------------------------------------
00022 */
00023 #ifndef O2SCL_INTERP_H
00024 #define O2SCL_INTERP_H
00025 
00026 #include <iostream>
00027 #include <string>
00028 
00029 // for gsl_linalg_solve_symm_tridiag for cspline_interp
00030 #include <gsl/gsl_linalg.h>
00031 
00032 #include <o2scl/err_hnd.h>
00033 #include <o2scl/collection.h>
00034 #include <o2scl/ovector_tlate.h>
00035 #include <o2scl/search_vec.h>
00036 
00037 #ifndef DOXYGENP
00038 namespace o2scl {
00039 #endif
00040   
00041   /** 
00042       \brief Base low-level interpolation class
00043 
00044       The descendants of this class are intended to be fast
00045       interpolation routines for increasing functions, leaving the
00046       some error handling, user-friendliness, and other more 
00047       complicated improvements for other classes.
00048 
00049       For any pair of vectors x and y into which you would like to
00050       interpolate, you need to call allocate() and init() first, and then 
00051       the interpolation functions, and then free(). If the next pair
00052       of vectors has the same size, then you need only to call init()
00053       before the next call to an interpolation function. If the vectors
00054       do not change, then you may call the interpolation functions in
00055       succession.
00056 
00057       All of the descendants are based on the GSL interpolation 
00058       routines and give identical results.
00059 
00060       \future These might work for decreasing functions by just
00061       replacing calls to search_vec::bsearch_inc() with
00062       search_vec::bsearch_dec(). If this is the case, then this should
00063       be rewritten accordingly. (I think I might have removed the
00064       acceleration)
00065   */
00066   template<class vec_t> class base_interp {
00067 
00068 #ifndef DOXYGEN_INTERNAL
00069 
00070   protected:
00071 
00072     /// The binary search object
00073     search_vec<vec_t> sv;
00074 
00075     /** 
00076         \brief An internal function to assist in computing the 
00077         integral for both the cspline and Akima types
00078     */
00079     double integ_eval(double ai, double bi, double ci, double di, double xi, 
00080                       double a, double b) {
00081       const double r1 = a - xi;
00082       const double r2 = b - xi;
00083       const double r12 = r1 + r2;
00084       const double bterm = 0.5 * bi * r12;
00085       const double cterm = (1.0 / 3.0) * ci * (r1 * r1 + r2 * r2 + r1 * r2);
00086       const double dterm = 0.25 * di * r12 * (r1 * r1 + r2 * r2);
00087       
00088       return (b - a) * (ai + bterm + cterm + dterm);
00089     }
00090 
00091 #endif
00092 
00093   public:
00094 
00095     virtual ~base_interp() {}
00096 
00097     /** 
00098         \brief The minimum size of the vectors to interpolate between
00099 
00100         This needs to be set in the constructor of the children for
00101         access by the class user
00102     */
00103     size_t min_size;
00104 
00105     /// Allocate memory, assuming x and y have size \c size
00106     virtual int allocate(size_t size) { return 0; }
00107 
00108     /// Free allocated memory
00109     virtual int free() { return 0; }
00110 
00111     /// Initialize interpolation routine
00112     virtual int init(const vec_t &x, const vec_t &y, size_t size) {
00113       return 0;
00114     }
00115     
00116     /// Give the value of the function \f$ y(x=x_0) \f$ .
00117     virtual int interp(const vec_t &x, const vec_t &y, size_t size, 
00118                        double x0, double &y0) {
00119       return 0;
00120     }
00121     
00122     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
00123     virtual int deriv(const vec_t &x, const vec_t &y, size_t size,
00124                       double x0, double &dydx) {
00125       return 0;
00126     }
00127     
00128     /** \brief Give the value of the second derivative 
00129         \f$ y^{\prime \prime}(x=x_0) \f$ .
00130     */
00131     virtual int deriv2(const vec_t &x, const vec_t &y, size_t size, 
00132                        double x0, double &d2ydx2) {
00133       return 0;
00134     }
00135     
00136     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
00137     virtual int integ(const vec_t &x, const vec_t &y, size_t size, 
00138                       double a, double b, double &result) {
00139       return 0;
00140     }
00141   };
00142   
00143   /** 
00144       \brief Linear interpolation (GSL)
00145 
00146       Linear interpolation requires no calls to allocate(), free() or
00147       init(), as there is no internal storage required.
00148   */
00149   template<class vec_t> class linear_interp : public base_interp<vec_t> {
00150     
00151   public:
00152 
00153     linear_interp() {
00154       this->min_size=2;
00155     }
00156 
00157     /// Give the value of the function \f$ y(x=x_0) \f$ .
00158     virtual int interp(const vec_t &x_array, const vec_t &y_array, 
00159                        size_t size, double x, double &y) {
00160       double x_lo, x_hi;
00161       double y_lo, y_hi;
00162       double dx;
00163       size_t index;
00164 
00165       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00166       
00167       /* evaluate */
00168       x_lo = x_array[index];
00169       x_hi = x_array[index + 1];
00170       y_lo = y_array[index];
00171       y_hi = y_array[index + 1];
00172       dx = x_hi - x_lo;
00173       if (dx > 0.0)
00174         {
00175           y = y_lo + (x - x_lo) / dx * (y_hi - y_lo);
00176           return gsl_success;
00177         }
00178       else
00179         {
00180           y = 0.0;
00181           return GSL_EINVAL;
00182         }
00183     }
00184     
00185     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
00186     virtual int deriv(const vec_t &x_array, const vec_t &y_array, 
00187                       size_t size,
00188                       double x, double &dydx) {
00189       
00190       double x_lo, x_hi;
00191       double y_lo, y_hi;
00192       double dx;
00193       double dy;
00194       size_t index;
00195       
00196       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00197       
00198       /* evaluate */
00199       x_lo = x_array[index];
00200       x_hi = x_array[index + 1];
00201       y_lo = y_array[index];
00202       y_hi = y_array[index + 1];
00203       dx = x_hi - x_lo;
00204       dy = y_hi - y_lo;
00205       if (dx > 0.0)
00206         {
00207           dydx = dy / dx;;
00208           return gsl_success;
00209         }
00210       else
00211         {
00212           dydx = 0.0;
00213           return GSL_EINVAL;
00214         }
00215     }
00216 
00217     /** \brief Give the value of the second derivative  
00218         \f$ y^{\prime \prime}(x=x_0) \f$ .
00219     */
00220     virtual int deriv2(const vec_t &x, const vec_t &y, size_t size, 
00221                        double x0, double &d2ydx2) {
00222       d2ydx2=0.0;
00223       return gsl_success;
00224     }
00225 
00226 
00227     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
00228     virtual int integ(const vec_t &x_array, const vec_t &y_array, 
00229                       size_t size, double a, double b, 
00230                       double &result) {
00231       size_t i, index_a, index_b;
00232       
00233       index_a=this->sv.bsearch_inc(a,x_array,0,size-1);
00234       index_b=this->sv.bsearch_inc(b,x_array,0,size-1);
00235       
00236       /* endpoints span more than one interval */
00237       
00238       result = 0.0;
00239       
00240       /* interior intervals */
00241       for(i=index_a; i<=index_b; i++) {
00242         const double x_hi = x_array[i + 1];
00243         const double x_lo = x_array[i];
00244         const double y_lo = y_array[i];
00245         const double y_hi = y_array[i + 1];
00246         const double dx = x_hi - x_lo;
00247         
00248         if(dx != 0.0) {
00249           if (i == index_a || i == index_b)
00250             {
00251               double x1 = (i == index_a) ? a : x_lo;
00252               double x2 = (i == index_b) ? b : x_hi;
00253               const double D = (y_hi-y_lo)/dx;
00254               result += (x2-x1) * (y_lo + 0.5*D*((x2-x_lo)+(x1-x_lo)));
00255             }
00256           else
00257             {
00258               result += 0.5 * dx * (y_lo + y_hi);
00259             }
00260         }
00261       }
00262       
00263       return gsl_success;
00264     }
00265   };
00266   
00267   /** 
00268       \brief Cubic spline interpolation (GSL)
00269   */
00270   template<class vec_t> class cspline_interp : public base_interp<vec_t> {
00271     
00272 #ifndef DOXYGEN_INTERNAL
00273     
00274   protected:
00275 
00276     /// \name Storage for cubic spline interpolation
00277     //@{
00278     double *c;
00279     double *g;
00280     double *diag;
00281     double *offdiag;
00282     //@}
00283 
00284     /// True for periodic boundary conditions
00285     bool peri;
00286 
00287     /// Compute coefficients for cubic spline interpolation
00288     void coeff_calc(const double c_array[], double dy, double dx, 
00289                     size_t index,  double * b, double * c2, double * d) {
00290       const double c_i = c_array[index];
00291       const double c_ip1 = c_array[index + 1];
00292       *b = (dy / dx) - dx * (c_ip1 + 2.0 * c_i) / 3.0;
00293       *c2 = c_i;
00294       *d = (c_ip1 - c_i) / (3.0 * dx);
00295     }
00296 
00297 #endif
00298     
00299   public:
00300 
00301     /** \brief Create a base interpolation object with natural or
00302         periodic boundary conditions
00303     */
00304     cspline_interp(bool periodic=false) {
00305       
00306       peri=periodic;
00307       if (peri) this->min_size=2;
00308       else this->min_size=3;
00309       
00310     }
00311 
00312     virtual ~cspline_interp() {}
00313     
00314     /// Allocate memory, assuming x and y have size \c size
00315     virtual int allocate(size_t size) {
00316       
00317       c = (double *) malloc (size * sizeof (double));
00318       
00319       if ( c == NULL)
00320         {
00321           GSL_ERROR_NULL("failed to allocate space for c", GSL_ENOMEM);
00322         }
00323       
00324       g = (double *) malloc (size * sizeof (double));
00325       
00326       if ( g == NULL)
00327         {
00328           std::free(c);
00329           GSL_ERROR_NULL("failed to allocate space for g", GSL_ENOMEM);
00330         }
00331       
00332       diag = (double *) malloc (size * sizeof (double));
00333       
00334       if ( diag == NULL)
00335         {
00336           std::free(g);
00337           std::free(c);
00338           GSL_ERROR_NULL("failed to allocate space for diag", GSL_ENOMEM);
00339         }
00340       
00341       offdiag = (double *) malloc (size * sizeof (double));
00342       
00343       if ( offdiag == NULL)
00344         {
00345           std::free(diag);
00346           std::free(g);
00347           std::free(c);
00348           GSL_ERROR_NULL("failed to allocate space for offdiag", GSL_ENOMEM);
00349         }
00350       
00351       return 0;
00352     }
00353     
00354     /// Initialize interpolation routine
00355     virtual int init(const vec_t &xa, const vec_t &ya, size_t size) {
00356 
00357       if (peri) {
00358 
00359         /// Periodic boundary conditions
00360          
00361         size_t i;
00362         size_t num_points = size;
00363         /* Engeln-Mullges + Uhlig "n" */
00364         size_t max_index = num_points - 1;  
00365         /* linear system is sys_size x sys_size */
00366         size_t sys_size = max_index;    
00367         
00368         if (sys_size == 2) {
00369           /* solve 2x2 system */
00370           
00371           const double h0 = xa[1] - xa[0];
00372           const double h1 = xa[2] - xa[1];
00373           const double h2 = xa[3] - xa[2];
00374           const double A = 2.0*(h0 + h1);
00375           const double B = h0 + h1;
00376           double gx[2];
00377           double det;
00378           
00379           gx[0] = 3.0 * ((ya[2] - ya[1]) / h1 - (ya[1] - ya[0]) / h0);
00380           gx[1] = 3.0 * ((ya[1] - ya[2]) / h2 - (ya[2] - ya[1]) / h1);
00381           
00382           det = 3.0 * (h0 + h1) * (h0 + h1);
00383           c[1] = ( A * gx[0] - B * gx[1])/det;
00384           c[2] = (-B * gx[0] + A * gx[1])/det;
00385           c[0] =  c[2];
00386           
00387           return gsl_success;
00388 
00389         } else {
00390           
00391           for (i = 0; i < sys_size-1; i++) {
00392             const double h_i       = xa[i + 1] - xa[i];
00393             const double h_ip1     = xa[i + 2] - xa[i + 1];
00394             const double ydiff_i   = ya[i + 1] - ya[i];
00395             const double ydiff_ip1 = ya[i + 2] - ya[i + 1];
00396             const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0;
00397             const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0;
00398             offdiag[i] = h_ip1;
00399             diag[i] = 2.0 * (h_ip1 + h_i);
00400             g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i);
00401           }
00402           
00403           i = sys_size - 1;
00404           {
00405             const double h_i       = xa[i + 1] - xa[i];
00406             const double h_ip1     = xa[1] - xa[0];
00407             const double ydiff_i   = ya[i + 1] - ya[i];
00408             const double ydiff_ip1 = ya[1] - ya[0];
00409             const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0;
00410             const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0;
00411             offdiag[i] = h_ip1;
00412             diag[i] = 2.0 * (h_ip1 + h_i);
00413             g[i] = 3.0 * (ydiff_ip1 * g_ip1 - ydiff_i * g_i);
00414           }
00415           
00416           {
00417             gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size);
00418             gsl_vector_view diag_vec = 
00419               gsl_vector_view_array( diag,sys_size);
00420             gsl_vector_view offdiag_vec = 
00421               gsl_vector_view_array( offdiag, sys_size);
00422             gsl_vector_view solution_vec = 
00423               gsl_vector_view_array (( c) + 1, sys_size);
00424             int status = 
00425               gsl_linalg_solve_symm_cyc_tridiag(&diag_vec.vector,
00426                                                 &offdiag_vec.vector,
00427                                                 &g_vec.vector,
00428                                                 &solution_vec.vector);
00429             c[0] =  c[max_index];
00430             
00431             return status;
00432           }
00433         }
00434 
00435       } else {
00436 
00437         /// Natural boundary conditions
00438 
00439         size_t i;
00440         size_t num_points = size;
00441         size_t max_index = num_points - 1;
00442         size_t sys_size = max_index - 1;
00443 
00444         c[0] = 0.0;
00445         c[max_index] = 0.0;
00446 
00447         for (i = 0; i < sys_size; i++)
00448           {
00449             const double h_i   = xa[i + 1] - xa[i];
00450             const double h_ip1 = xa[i + 2] - xa[i + 1];
00451             const double ydiff_i   = ya[i + 1] - ya[i];
00452             const double ydiff_ip1 = ya[i + 2] - ya[i + 1];
00453             const double g_i = (h_i != 0.0) ? 1.0 / h_i : 0.0;
00454             const double g_ip1 = (h_ip1 != 0.0) ? 1.0 / h_ip1 : 0.0;
00455             offdiag[i] = h_ip1;
00456             diag[i] = 2.0 * (h_ip1 + h_i);
00457             g[i] = 3.0 * (ydiff_ip1 * g_ip1 -  ydiff_i * g_i);
00458           }
00459 
00460         if (sys_size == 1)
00461           {
00462             c[1] =  g[0] /  diag[0];
00463             return gsl_success;
00464           }
00465         else
00466           {
00467             gsl_vector_view g_vec = gsl_vector_view_array( g, sys_size);
00468             gsl_vector_view diag_vec = gsl_vector_view_array( diag, sys_size);
00469             gsl_vector_view offdiag_vec = 
00470               gsl_vector_view_array( offdiag, sys_size - 1);
00471             gsl_vector_view solution_vec = 
00472               gsl_vector_view_array (( c) + 1, sys_size);
00473             
00474             int status = gsl_linalg_solve_symm_tridiag(&diag_vec.vector, 
00475                                                        &offdiag_vec.vector, 
00476                                                        &g_vec.vector, 
00477                                                        &solution_vec.vector);
00478             return status;
00479           }
00480       }
00481     }
00482       
00483     
00484     /// Free allocated memory
00485     virtual int free() {
00486       std::free(c);
00487       std::free(g);
00488       std::free(diag);
00489       std::free(offdiag);
00490 
00491       return 0;
00492     }
00493 
00494     /// Give the value of the function \f$ y(x=x_0) \f$ .
00495     virtual int interp(const vec_t &x_array, const vec_t &y_array, 
00496                        size_t size, double x, double &y) {
00497       
00498       double x_lo, x_hi;
00499       double dx;
00500       size_t index;
00501       
00502       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00503   
00504       /* evaluate */
00505       x_hi = x_array[index + 1];
00506       x_lo = x_array[index];
00507       dx = x_hi - x_lo;
00508       if (dx > 0.0)
00509         {
00510           const double y_lo = y_array[index];
00511           const double y_hi = y_array[index + 1];
00512           const double dy = y_hi - y_lo;
00513           double delx = x - x_lo;
00514           double b_i, c_i, d_i; 
00515           coeff_calc( c, dy, dx, index,  &b_i, &c_i, &d_i);
00516           y = y_lo + delx * (b_i + delx * (c_i + delx * d_i));
00517           return gsl_success;
00518         }
00519       else
00520         {
00521           y = 0.0;
00522           return GSL_EINVAL;
00523         }
00524     }
00525 
00526 
00527     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
00528     virtual int deriv(const vec_t &x_array, const vec_t &y_array, 
00529                       size_t size, double x, double &dydx) {
00530 
00531       double x_lo, x_hi;
00532       double dx;
00533       size_t index;
00534   
00535       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00536   
00537       /* evaluate */
00538       x_hi = x_array[index + 1];
00539       x_lo = x_array[index];
00540       dx = x_hi - x_lo;
00541       if (dx > 0.0)
00542         {
00543           const double y_lo = y_array[index];
00544           const double y_hi = y_array[index + 1];
00545           const double dy = y_hi - y_lo;
00546           double delx = x - x_lo;
00547           double b_i, c_i, d_i; 
00548           coeff_calc( c, dy, dx, index,  &b_i, &c_i, &d_i);
00549           dydx = b_i + delx * (2.0 * c_i + 3.0 * d_i * delx);
00550           return gsl_success;
00551         }
00552       else
00553         {
00554           dydx = 0.0;
00555           return GSL_FAILURE;
00556         }
00557     }
00558 
00559     /** \brief Give the value of the second derivative  
00560         \f$ y^{\prime \prime}(x=x_0) \f$ .
00561     */
00562     virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 
00563                        size_t size, double x, double &d2ydx2) {
00564 
00565       double x_lo, x_hi;
00566       double dx;
00567       size_t index;
00568   
00569       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00570   
00571       /* evaluate */
00572       x_hi = x_array[index + 1];
00573       x_lo = x_array[index];
00574       dx = x_hi - x_lo;
00575       if (dx > 0.0)
00576         {
00577           const double y_lo = y_array[index];
00578           const double y_hi = y_array[index + 1];
00579           const double dy = y_hi - y_lo;
00580           double delx = x - x_lo;
00581           double b_i, c_i, d_i;
00582           coeff_calc( c, dy, dx, index,  &b_i, &c_i, &d_i);
00583           d2ydx2 = 2.0 * c_i + 6.0 * d_i * delx;
00584           return gsl_success;
00585         }
00586       else
00587         {
00588           d2ydx2 = 0.0;
00589           return GSL_FAILURE;
00590         }
00591     }
00592 
00593   public:
00594     
00595     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
00596     virtual int integ(const vec_t &x_array, const vec_t &y_array, 
00597                       size_t size, double a, double b, 
00598                       double &result) {
00599 
00600       size_t i, index_a, index_b;
00601   
00602       index_a=this->sv.bsearch_inc(a,x_array,0,size-1);
00603       index_b=this->sv.bsearch_inc(b,x_array,0,size-1);
00604 
00605       result = 0.0;
00606   
00607       /* interior intervals */
00608       for(i=index_a; i<=index_b; i++) {
00609         const double x_hi = x_array[i + 1];
00610         const double x_lo = x_array[i];
00611         const double y_lo = y_array[i];
00612         const double y_hi = y_array[i + 1];
00613         const double dx = x_hi - x_lo;
00614         const double dy = y_hi - y_lo;
00615         if(dx != 0.0) {
00616           double b_i, c_i, d_i; 
00617           coeff_calc( c, dy, dx, i,  &b_i, &c_i, &d_i);
00618       
00619           if (i == index_a || i == index_b)
00620             {
00621               double x1 = (i == index_a) ? a : x_lo;
00622               double x2 = (i == index_b) ? b : x_hi;
00623               result += this->integ_eval(y_lo, b_i, c_i, d_i, x_lo, x1, x2);
00624             }
00625           else
00626             {
00627               result += dx * (y_lo + dx*(0.5*b_i + 
00628                                          dx*(c_i/3.0 + 0.25*d_i*dx)));
00629             }
00630         }
00631         else {
00632           result = 0.0;
00633           return GSL_FAILURE;
00634         }
00635       }
00636   
00637       return gsl_success;
00638     }
00639 
00640   };
00641   
00642   /** 
00643       \brief Cubic spline interpolation with periodic 
00644       boundary conditions (GSL)
00645 
00646       This is convenient to allow interpolation objects to
00647       be supplied as template parameters
00648   */
00649   template<class vec_t> class cspline_peri_interp : 
00650   public cspline_interp<vec_t> {
00651 
00652   public:
00653     cspline_peri_interp() : cspline_interp<vec_t>(true) {
00654     }
00655 
00656   };
00657 
00658   /** 
00659       \brief Akima spline interpolation (GSL)
00660 
00661       \future It appears that the interp() function below searches
00662       for indices slightly differently than the original GSL
00663       eval() function. This should be checked, as it might be
00664       slightly non-optimal in terms of speed (shouldn't matter for
00665       the accuracy). 
00666   */
00667   template<class vec_t> class akima_interp : public base_interp<vec_t> {
00668     
00669 #ifndef DOXYGEN_INTERNAL
00670     
00671   protected:
00672 
00673     /// \name Storage for Akima spline interpolation
00674     //@{
00675     double *b;
00676     double *c;
00677     double *d;
00678     double *um;
00679     //@}
00680 
00681     /// True for periodic boundary conditions
00682     bool peri;
00683 
00684     /// For initializing the interpolation
00685     void akima_calc(const vec_t &x_array, size_t size, double m[]) {
00686       size_t i;
00687       
00688       for (i = 0; i < (size - 1); i++) {
00689 
00690         const double NE = fabs (m[i + 1] - m[i]) + 
00691           fabs (m[i - 1] - m[i - 2]);
00692         
00693         if (NE == 0.0) {
00694           b[i] = m[i];
00695           c[i] = 0.0;
00696           d[i] = 0.0;
00697         } else {
00698           const double h_i = x_array[i + 1] - x_array[i];
00699           const double NE_next = fabs (m[i + 2] - m[i + 1]) + 
00700             fabs (m[i] - m[i - 1]);
00701           const double alpha_i = fabs (m[i - 1] - m[i - 2]) / NE;
00702           double alpha_ip1;
00703           double tL_ip1;
00704           if (NE_next == 0.0) {
00705             tL_ip1 = m[i];
00706           } else {
00707             alpha_ip1 = fabs (m[i] - m[i - 1]) / NE_next;
00708             tL_ip1 = (1.0 - alpha_ip1) * m[i] + alpha_ip1 * m[i + 1];
00709           }
00710           b[i] = (1.0 - alpha_i) * m[i - 1] + alpha_i * m[i];
00711           c[i] = (3.0 * m[i] - 2.0 * b[i] - tL_ip1) / h_i;
00712           d[i] = (b[i] + tL_ip1 - 2.0 * m[i]) / (h_i * h_i);
00713         }
00714       }
00715     }
00716     
00717 #endif
00718     
00719   public:
00720 
00721     /** \brief Create a base interpolation object with or without
00722         periodic boundary conditions
00723     */
00724     akima_interp(bool periodic=false) {
00725       peri=periodic;
00726       this->min_size=5;
00727     }
00728 
00729     virtual ~akima_interp() {}
00730     
00731     /// Allocate memory, assuming x and y have size \c size
00732     virtual int allocate(size_t size) {
00733       
00734       b = (double *) malloc (size * sizeof (double));
00735       
00736       if (b==NULL) {
00737           GSL_ERROR_NULL("failed to allocate space for c", GSL_ENOMEM);
00738         }
00739       
00740       c = (double *) malloc (size * sizeof (double));
00741       
00742       if (c==NULL) {
00743           std::free(b);
00744           GSL_ERROR_NULL("failed to allocate space for g", GSL_ENOMEM);
00745         }
00746       
00747       d = (double *) malloc (size * sizeof (double));
00748       
00749       if (d==NULL) {
00750           std::free(b);
00751           std::free(c);
00752           GSL_ERROR_NULL("failed to allocate space for diag", GSL_ENOMEM);
00753         }
00754       
00755       um = (double *) malloc ((size+4) * sizeof (double));
00756       
00757       if (um==NULL) {
00758           std::free(b);
00759           std::free(c);
00760           std::free(d);
00761           GSL_ERROR_NULL("failed to allocate space for offdiag", GSL_ENOMEM);
00762         }
00763       
00764       return 0;
00765     }
00766     
00767     /// Initialize interpolation routine
00768     virtual int init(const vec_t &xa, const vec_t &ya, size_t size) {
00769       
00770       if (peri) {
00771         
00772         /// Periodic boundary conditions
00773 
00774         double *m=um+2;
00775         size_t i;
00776         for (i = 0; i <= size - 2; i++)
00777           {
00778             m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]);
00779           }
00780         
00781         /* periodic boundary conditions */
00782         m[-2] = m[size - 1 - 2];
00783         m[-1] = m[size - 1 - 1];
00784         m[size - 1] = m[0];
00785         m[size] = m[1];
00786         
00787         akima_calc (xa,size, m);
00788         
00789       } else {
00790 
00791         /// Non-periodic boundary conditions
00792 
00793         double *m=um+2;
00794         size_t i;
00795         for (i = 0; i <= size - 2; i++) {
00796           m[i] = (ya[i + 1] - ya[i]) / (xa[i + 1] - xa[i]);
00797         }
00798         
00799         /* non-periodic boundary conditions */
00800         m[-2] = 3.0 * m[0] - 2.0 * m[1];
00801         m[-1] = 2.0 * m[0] - m[1];
00802         m[size - 1] = 2.0 * m[size - 2] - m[size - 3];
00803         m[size] = 3.0 * m[size - 2] - 2.0 * m[size - 3];
00804         
00805         akima_calc (xa, size, m);
00806 
00807       }
00808       return 0;
00809     }
00810       
00811     
00812     /// Free allocated memory
00813     virtual int free() {
00814       std::free(b);
00815       std::free(c);
00816       std::free(d);
00817       std::free(um);
00818 
00819       return 0;
00820     }
00821 
00822     /// Give the value of the function \f$ y(x=x_0) \f$ .
00823     virtual int interp(const vec_t &x_array, const vec_t &y_array, 
00824                        size_t size, double x, double &y) {
00825       
00826       size_t index;
00827       
00828       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00829   
00830       const double x_lo = x_array[index];
00831       const double delx = x - x_lo;
00832       const double bb = b[index];
00833       const double cc = c[index];
00834       const double dd = d[index];
00835       y = y_array[index] + delx * (bb + delx * (cc + dd * delx));
00836       return gsl_success;
00837     }
00838 
00839 
00840     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
00841     virtual int deriv(const vec_t &x_array, const vec_t &y_array, 
00842                       size_t size, double x, double &dydx) {
00843 
00844       size_t index;
00845   
00846       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00847       double x_lo = x_array[index];
00848       double delx = x - x_lo;
00849       double bb = b[index];
00850       double cc = c[index];
00851       double dd = d[index];
00852       dydx = bb + delx * (2.0 * cc + 3.0 * dd * delx);
00853       return gsl_success;
00854 
00855     }
00856 
00857     /** \brief Give the value of the second derivative  
00858         \f$ y^{\prime \prime}(x=x_0) \f$ .
00859     */
00860     virtual int deriv2(const vec_t &x_array, const vec_t &y_array, 
00861                        size_t size, double x, double &d2ydx2) {
00862 
00863       size_t index;
00864   
00865       index=this->sv.bsearch_inc(x,x_array,0,size-1);
00866   
00867       const double x_lo = x_array[index];
00868       const double delx = x - x_lo;
00869       const double cc = c[index];
00870       const double dd = d[index];
00871       d2ydx2 = 2.0 * cc + 6.0 * dd * delx;
00872       return gsl_success;
00873 
00874     }
00875 
00876     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
00877     virtual int integ(const vec_t &x_array, const vec_t &y_array, 
00878                       size_t size, double aa, double bb, 
00879                       double &result) {
00880 
00881       size_t i, index_a, index_b;
00882   
00883       index_a=this->sv.bsearch_inc(aa,x_array,0,size-1);
00884       index_b=this->sv.bsearch_inc(bb,x_array,0,size-1);
00885 
00886       result = 0.0;
00887   
00888       /* interior intervals */
00889 
00890       for(i=index_a; i<=index_b; i++) {
00891 
00892         const double x_hi = x_array[i + 1];
00893         const double x_lo = x_array[i];
00894         const double y_lo = y_array[i];
00895         const double dx = x_hi - x_lo;
00896 
00897         if(dx != 0.0) {
00898           
00899           if (i==index_a || i==index_b) {
00900             double x1 = (i==index_a) ? aa : x_lo;
00901             double x2 = (i==index_b) ? bb : x_hi;
00902             result += this->integ_eval(y_lo,b[i],c[i],d[i],x_lo,x1,x2);
00903           } else {
00904             result+=dx*(y_lo+dx*(0.5*b[i]+dx*(c[i]/3.0+0.25*d[i]*dx)));
00905           }
00906 
00907         } else {
00908           result = 0.0;
00909           return GSL_FAILURE;
00910         }
00911       }
00912   
00913       return gsl_success;
00914     }
00915 
00916   };
00917   
00918   /** 
00919       \brief Akima spline interpolation with periodic 
00920       boundary conditions (GSL)
00921       
00922       This is convenient to allow interpolation objects to
00923       be supplied as template parameters
00924   */
00925   template<class vec_t> class akima_peri_interp : 
00926   public akima_interp<vec_t> {
00927 
00928   public:
00929     akima_peri_interp() : akima_interp<vec_t>(true) {
00930     }
00931 
00932   };
00933 
00934   /** 
00935       \brief Interpolation class
00936       
00937       This interpolation class is intended to be sufficiently general
00938       to handle any vector type. Interpolation of \ref ovector like
00939       objects is performed with the default template parameters, and
00940       \ref array_interp is provided for simple interpolation on
00941       C-style arrays.
00942 
00943       The type of interpolation to be performed can be specified using
00944       the set_type() function or in the constructor.  The default is
00945       cubic splines with natural boundary conditions.
00946 
00947       The class automatically handles decreasing arrays by converting
00948       from an object of type \c vec_t to an object ov type \c rvec_t.
00949 
00950       While \c vec_t may be any vector type which allows indexing via
00951       \c [], \c rvec_t must be a vector type which allows indexing
00952       and has a constructor with one of the two forms
00953       \verbatim
00954       rvec_t::rvec_t(vec_t &v);
00955       rvec_t::rvec_t(vec_t v);
00956       \endverbatim
00957       so that o2scl_interp can automatically "reverse" a vector if 
00958       necessary.
00959       
00960       It is important that different instances of o2scl_interp_vec
00961       and o2scl_interp not be given the same interpolation objects,
00962       as they will clash.
00963       
00964   */
00965   template<class vec_t=ovector_view, class rvec_t=ovector_const_reverse> 
00966     class o2scl_interp {
00967       
00968 #ifndef DOXYGEN_INTERNAL
00969       
00970     protected:
00971       
00972     /// Pointer to base interpolation object
00973     base_interp<vec_t> *itp;
00974       
00975     /// Pointer to base interpolation object for reversed vectors
00976     base_interp<rvec_t> *ritp;
00977       
00978 #endif
00979       
00980     public:
00981       
00982     /// Create with base interpolation objects \c it and \c rit
00983     o2scl_interp(base_interp<vec_t> &it, base_interp<rvec_t> &rit) {
00984       itp=&it;
00985       ritp=&rit;
00986     }
00987 
00988     /** \brief Create with base interpolation object \c it and use
00989         \ref def_ritp for reverse interpolation if necessary
00990     */
00991     o2scl_interp(base_interp<vec_t> &it) {
00992       itp=&it;
00993       ritp=&def_ritp;
00994     }
00995     
00996     /** \brief Create an interpolator using \ref def_itp and 
00997         \ref def_ritp
00998     */
00999     o2scl_interp() {
01000       itp=&def_itp;
01001       ritp=&def_ritp;
01002     }
01003 
01004     virtual ~o2scl_interp() {}
01005 
01006     /// Give the value of the function \f$ y(x=x_0) \f$ .
01007     virtual double interp(const double x0, size_t n, const vec_t &x, 
01008                           const vec_t &y) 
01009     {
01010         
01011       double ret=0.0;
01012       
01013       err_hnd->reset();
01014       
01015       if (n<itp->min_size) {
01016         set_err("Vector size too small in interp().",gsl_edom);
01017       }
01018       
01019       if (x[0]>x[n-1]) {
01020 
01021         // Decreasing case
01022 
01023         rvec_t ax(x);
01024         rvec_t ay(y);
01025 
01026         int r1=ritp->allocate(n);
01027         if (r1==0) {
01028           int r2=ritp->init(ax,ay,n);
01029           if (r2==0) {
01030             ritp->interp(ax,ay,n,x0,ret);
01031             ritp->free();
01032           }
01033         }
01034 
01035       } else {
01036 
01037         // Increasing case
01038           
01039         int r1=itp->allocate(n);
01040         if (r1==0) {
01041           int r2=itp->init(x,y,n);
01042           if (r2==0) {
01043             itp->interp(x,y,n,x0,ret);
01044             itp->free();
01045           }
01046         }
01047           
01048       }
01049       
01050       return ret;
01051     }                 
01052     
01053     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
01054     virtual double deriv(const double x0, size_t n, const vec_t &x, 
01055                          const vec_t &y) 
01056     {
01057       
01058       double ret=0.0;
01059       
01060       err_hnd->reset();
01061       
01062       if (n<itp->min_size) {
01063         set_err("Vector size too small in interp().",gsl_edom);
01064       }
01065       
01066       if (x[0]>x[n-1]) {
01067           
01068         rvec_t ax(x);
01069         rvec_t ay(y);
01070 
01071         int r1=ritp->allocate(n);
01072         if (r1==0) {
01073           int r2=ritp->init(ax,ay,n);
01074           if (r2==0) {
01075             ritp->deriv(ax,ay,n,x0,ret);
01076             ritp->free();
01077           }
01078         }
01079           
01080       } else {
01081 
01082         // Increasing case
01083           
01084         int r1=itp->allocate(n);
01085         if (r1==0) {
01086           int r2=itp->init(x,y,n);
01087           if (r2==0) {
01088             itp->deriv(x,y,n,x0,ret);
01089             itp->free();
01090           }
01091         }
01092       }
01093       
01094       return ret;
01095     }                 
01096     
01097     /** \brief Give the value of the second derivative  
01098         \f$ y^{\prime \prime}(x=x_0) \f$ .
01099     */
01100     virtual double deriv2(const double x0, size_t n, const vec_t &x, 
01101                           const vec_t &y) 
01102     {
01103       
01104       double ret=0.0;
01105       
01106       err_hnd->reset();
01107       
01108       if (n<itp->min_size) {
01109 
01110         set_err("Vector size too small in deriv2().",gsl_edom);
01111 
01112       }
01113       
01114       if (x[0]>x[n-1]) {
01115           
01116         // Decreasing case
01117             
01118         rvec_t ax(x);
01119         rvec_t ay(y);
01120 
01121         int r1=ritp->allocate(n);
01122         if (r1==0) {
01123           int r2=ritp->init(ax,ay,n);
01124           if (r2==0) {
01125             ritp->deriv2(ax,ay,n,x0,ret);
01126             ritp->free();
01127           }
01128         }
01129 
01130       } else {
01131 
01132         // Increasing case
01133 
01134         int r1=itp->allocate(n);
01135         if (r1==0) {
01136           int r2=itp->init(x,y,n);
01137           if (r2==0) {
01138             itp->deriv2(x,y,n,x0,ret);
01139             itp->free();
01140           }
01141         }
01142 
01143       }
01144       
01145       return ret;
01146     }                 
01147     
01148     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
01149     virtual double integ(const double x1, const double x2, size_t n, 
01150                          const vec_t &x, const vec_t &y) 
01151     {
01152       
01153       double ret=0.0;
01154       
01155       err_hnd->reset();
01156       
01157       if (n<itp->min_size) {
01158 
01159         set_err("Vector size too small in integ().",gsl_edom);
01160 
01161       }
01162       
01163       if (x[0]>x[n-1]) {
01164           
01165         // Decreasing case
01166             
01167         rvec_t ax(x);
01168         rvec_t ay(y);
01169 
01170         int r1=ritp->allocate(n);
01171         if (r1==0) {
01172           int r2=ritp->init(ax,ay,n);
01173           if (r2==0) {
01174             ritp->integ(ax,ay,n,x1,x2,ret);
01175             ritp->free();
01176           }
01177         }
01178 
01179       } else {
01180 
01181         // Increasing case
01182             
01183         int r1=itp->allocate(n);
01184         if (r1==0) {
01185           int r2=itp->init(x,y,n);
01186           if (r2==0) {
01187             itp->integ(x,y,n,x1,x2,ret);
01188             itp->free();
01189           }
01190         }
01191 
01192       }
01193       
01194       return ret;
01195     }                 
01196     
01197     /// Set base interpolation object
01198     int set_type(base_interp<vec_t> &it, base_interp<rvec_t> &rit) {
01199       itp=&it;
01200       ritp=&rit;
01201       return 0;
01202     }
01203       
01204     /** \brief Default base interpolation object (cubic spline with natural 
01205         boundary conditions)
01206     */
01207     cspline_interp<vec_t> def_itp;
01208 
01209     /** \brief Default base interpolation object for reversed vectors
01210         (cubic spline with natural boundary conditions)
01211     */
01212     cspline_interp<rvec_t> def_ritp;
01213       
01214   };
01215   
01216   /** 
01217       \brief Interpolation class for pre-specified vector
01218       
01219       This interpolation class is intended to be sufficiently general
01220       to handle any vector type. Interpolation of \ref ovector like
01221       objects is performed with the default template parameters, and
01222       \ref array_interp_vec is provided for simple interpolation on
01223       C-style arrays.
01224 
01225       The class automatically handles decreasing arrays by copying the
01226       old array to a reversed version. For several interpolations on
01227       the same data, copying the initial array can be faster than
01228       overloading operator[].
01229 
01230       The type of interpolation to be performed can be specified 
01231       using the set_type() function. The default is cubic splines
01232       with natural boundary conditions. 
01233       
01234       It is important that different instances of o2scl_interp_vec
01235       and o2scl_interp not be given the same interpolation objects,
01236       as they will clash.
01237 
01238       \todo Need to fix constructor to behave properly if init() fails.
01239       It should free the memory and set \c ln to zero.
01240   */
01241   template<class vec_t=ovector_view, class alloc_vec_t=ovector, 
01242     class alloc_t=ovector_alloc> class o2scl_interp_vec {
01243       
01244 #ifndef DOXYGEN_INTERNAL
01245       
01246     protected:
01247       
01248     /// Memory allocator for objects of type \c alloc_vec_t
01249     alloc_t ao;
01250 
01251     /// Pointer to base interpolation object
01252     base_interp<vec_t> *itp;
01253       
01254     /// True if the original array was increasing
01255     bool inc;
01256 
01257     /// Pointer to the user-specified x vector
01258     const vec_t *lx;
01259 
01260     /// Pointer to the user-specified x vector
01261     const vec_t *ly;
01262 
01263     /// Reversed version of x
01264     alloc_vec_t lrx;
01265 
01266     /// Reversed version of y
01267     alloc_vec_t lry;
01268 
01269     /// Size of user-specified vectors
01270     size_t ln;
01271 
01272 #endif
01273       
01274     public:
01275     
01276     /// Create with base interpolation object \c it
01277     o2scl_interp_vec(base_interp<vec_t> &it, 
01278                      size_t n, const vec_t &x, const vec_t &y) {
01279       itp=&it;
01280       ln=0;
01281         
01282       if (n<itp->min_size) {
01283         set_err("Vector size too small in o2scl_interp_vec().",gsl_edom);
01284       } else {
01285         size_t iend=n-1;
01286           
01287         if (x[0]>x[iend]) {
01288             
01289           ao.allocate(lrx,n);
01290           ao.allocate(lry,n);
01291 
01292           for(size_t i=0;i<n;i++) {
01293             lrx[iend-i]=x[i];
01294             lry[iend-i]=y[i];
01295           }
01296             
01297           int r1=itp->allocate(n);
01298           if (r1==0) {
01299             itp->init(lrx,lry,n);
01300             ln=n;
01301             inc=false;
01302           }
01303             
01304         } else {
01305             
01306           int r1=itp->allocate(n);
01307           if (r1==0) {
01308             itp->init(x,y,n);
01309             ln=n;
01310             lx=&x;
01311             ly=&y;
01312             inc=true;
01313           }
01314             
01315         }
01316 
01317       } 
01318         
01319     }
01320       
01321     virtual ~o2scl_interp_vec() {
01322       if (ln>0) {
01323         itp->free();
01324       }
01325       if (inc==false) {
01326         ao.free(lrx);
01327         ao.free(lry);
01328       }
01329     }
01330     
01331     /// Give the value of the function \f$ y(x=x_0) \f$ .
01332     virtual double interp(const double x0) {
01333       double ret=0.0;
01334       if (ln>0) {
01335         if (inc) itp->interp(*lx,*ly,ln,x0,ret);
01336         else itp->interp(lrx,lry,ln,x0,ret);
01337       }
01338       return ret;
01339     }                 
01340     
01341     /// Give the value of the derivative \f$ y^{\prime}(x=x_0) \f$ .
01342     virtual double deriv(const double x0) {
01343       double ret=0.0;
01344       if (ln>0) {
01345         if (inc) itp->deriv(*lx,*ly,ln,x0,ret);
01346         else itp->deriv(lrx,lry,ln,x0,ret);
01347       }
01348       return ret;
01349     }                 
01350     
01351     /** \brief Give the value of the second derivative  
01352         \f$ y^{\prime \prime}(x=x_0) \f$ .
01353     */
01354     virtual double deriv2(const double x0) {
01355       double ret=0.0;
01356       if (ln>0) {
01357         if (inc) itp->deriv2(*lx,*ly,ln,x0,ret);
01358         else itp->deriv2(lrx,lry,ln,x0,ret);
01359       }
01360       return ret;
01361     }                 
01362     
01363     /// Give the value of the integral \f$ \int_a^{b}y(x)~dx \f$ .
01364     virtual double integ(const double x1, const double x2) {
01365       double ret=0.0;
01366       if (ln>0) {
01367         if (inc) itp->integ(*lx,*ly,ln,x1,x2,ret);
01368         else itp->integ(lrx,lry,ln,x1,x2,ret);
01369       }
01370       return ret;
01371     }                 
01372     
01373     /// Set base interpolation object
01374     int set_type(base_interp<vec_t> &it) {
01375       itp=&it;
01376       return 0;
01377     }
01378       
01379     /** \brief Default base interpolation object (cubic spline with natural 
01380         boundary conditions)
01381     */
01382     cspline_interp<vec_t> def_itp;
01383       
01384   };
01385   
01386   /** \brief A specialization of o2scl_interp for C-style double arrays
01387    */
01388   template<size_t n> class array_interp :
01389   public o2scl_interp<double[n],array_const_reverse<n> > {
01390     
01391   public:
01392     
01393     /// Create with base interpolation objects \c it and \c rit
01394     array_interp(base_interp<double[n]> &it, 
01395                  base_interp<array_const_reverse<n> > &rit)
01396       : o2scl_interp<double[n],array_const_reverse<n> >(it,rit) {
01397     }
01398     
01399       /** \brief Create with base interpolation object \c it and use
01400           the default base interpolation object for reversed arrays
01401       */
01402       array_interp(base_interp<double[n]> &it) : 
01403         o2scl_interp<double[n],array_const_reverse<n> >(it) {
01404       }
01405       
01406         /** \brief Create an interpolator using the default base
01407             interpolation objects
01408         */
01409         array_interp() : 
01410           o2scl_interp<double[n],array_const_reverse<n> >() {
01411         }
01412 
01413   };
01414 
01415   /** 
01416       \brief A specialization of o2scl_interp_vec for C-style arrays
01417   */
01418   template<class arr_t> class array_interp_vec : 
01419   public o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> > {
01420     
01421   public:
01422     
01423     /// Create with base interpolation object \c it
01424     array_interp_vec(base_interp<arr_t> &it, 
01425                      size_t nv, const arr_t &x, const arr_t &y) :
01426       o2scl_interp_vec<arr_t,arr_t,array_alloc<arr_t> >(it,nv,x,y) {
01427     }
01428     
01429   };
01430   
01431 #ifndef DOXYGENP
01432 }
01433 #endif
01434 
01435 #endif

Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.

Project hosting provided by SourceForge.net Logo, O2scl Sourceforge Project Page