eqi_deriv.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_EQI_DERIV_H
00024 #define O2SCL_EQI_DERIV_H
00025 
00026 #include <cmath>
00027 #include <o2scl/deriv.h>
00028 #include <o2scl/collection.h>
00029 
00030 #ifndef DOXYGENP
00031 namespace o2scl {
00032 #endif
00033 
00034   /** 
00035       \brief Derivatives for equally-spaced abscissas
00036 
00037       This is an implementation of the formulas for equally-spaced
00038       abscissas as indicated below. The level of approximation is
00039       specified in set_npoints(). The value of \f$ p \times h \f$ 
00040       can be specified in \c xoff (default is zero).
00041 
00042       \note The derivatives given, for example, from the
00043       five-point formula can sometimes be more accurate
00044       than computing the derivative from the interpolation class.
00045       This is especially true near the boundaries of the interpolated
00046       region.
00047 
00048       \todo The uncertainties in the derivatives are not yet computed
00049       and the second and third derivative formulas are not yet finished.
00050 
00051       Two-point formula (note that this is independent of p).
00052       \f[
00053       f^{\prime}(x_0+p h)=\frac{1}{h}\left[
00054       f_{1}-f_{0} \right]
00055       \f]
00056       Three-point formula from Abramowitz and Stegun
00057       \f[
00058       f^{\prime}(x_0+p h)=\frac{1}{h}\left[
00059       \frac{2p-1}{2}f_{-1}-2 p f_{0}+\frac{2p+1}{2}f_{1}\right]
00060       \f]
00061       Four-point formula from Abramowitz and Stegun
00062       \f[
00063       f^{\prime}(x_0+p h)=\frac{1}{h}\left[
00064       -\frac{3 p^2-6 p+2}{6}f_{-1}
00065       +\frac{3 p^2-4 p -1}{2}f_{0}
00066       -\frac{3 p^2-2 p-2}{2}f_{1}
00067       +\frac{3 p^2-1}{6}f_{2}
00068       \right]
00069       \f]
00070       Five-point formula from Abramowitz and Stegun
00071       \f{eqnarray*}
00072       f^{\prime}(x_0+p h)&=&\frac{1}{h}\left[
00073       \frac{2 p^3-3 p^2-p+1}{12}f_{-2}
00074       -\frac{4 p^3-3p^2-8p+4}{6}f_{-1}
00075       \right. \\ && \left. 
00076       +\frac{2p^3-5p}{2}f_{0}
00077       -\frac{4p^3+3p^2-8p-4}{6}f_{1}
00078       \right. \\ && \left. 
00079       +\frac{2p^3+3p^2-p-1}{12}f_{2}
00080       \right]
00081       \f}
00082 
00083       The relations above can be confined to give formulas
00084       for second derivative formulas:
00085       Three-point formula 
00086       \f[
00087       f^{\prime}(x_0+p h)=\frac{1}{h^2}
00088       \left[f_{-1}-2 f_0+f_1\right]
00089       \f]
00090       Four-point formula:
00091       \f[
00092       f^{\prime}(x_0+p h)=\frac{1}{2 h^2}
00093       \left[\left(1-2p\right)f_{-1}-\left(1-6p\right)f_0
00094       -\left(1+6p\right)f_1+\left(1+2p\right)f_2\right]
00095       \f]
00096       Five-point formula:
00097       \f[
00098       f^{\prime}(x_0+p h)=\frac{1}{4 h^2}
00099       \left[\left(1-2p\right)^2f_{-2}
00100       +\left(8p-16 p^2\right)f_{-1}
00101       -\left(2-24 p^2\right)f_0
00102       -\left(8p+16p^2\right)f_1
00103       +\left(1+2p\right)^2 f_2\right]
00104       \f]
00105       Six-point formula:
00106       \f{eqnarray*}
00107       f^{\prime}(x_0+p h)&=&\frac{1}{12 h^2}\left[
00108       \left(2-10p+15 p^2-6p^3\right)f_{-2}
00109       +\left(3+14p-57p^2+30p^3\right)f_{-1}
00110       \right. \\ && \left. 
00111       +\left(-8+20p+78 p^2-60p^3\right)f_0
00112       +\left(-2-44p-42p^2+60p^3\right)f_1
00113       \right. \\ && \left. 
00114       +\left(6+22p+3p^2-30p^3\right)f_2
00115       +\left(-1-2p+3p^2+6p^3\right)f_3
00116       \right]
00117       \f}
00118       Seven-point formula:
00119       \f{eqnarray*}
00120       f^{\prime}(x_0+p h)&=&\frac{1}{36 h^2}\left[
00121       \left(4-24p+48p^2-36p^3+9p^4\right)f_{-3}
00122       +\left(12+12p-162p^2+180p^3-54p^4\right)f_{-2}
00123       \right. \\ && \left. 
00124       +\left(-15+120p+162p^2-360p^3+135p^4\right)f_{-1} 
00125       -4\left(8+48p-3p^2-90p^3+45p^4\right)f_0
00126       \right. \\ && \left. 
00127       +3\left(14+32p-36p^2-60p^3+45p^4\right)f_1
00128       +\left(-12-12p+54p^2+36p^3-54p^4\right)f_2 
00129       \right. \\ && \left. 
00130       +\left(1-6p^2+9p^4\right)f_3 
00131       \right]
00132       \f}
00133   */
00134   template<class param_t, class func_t, class vec_t=ovector_view> 
00135     class eqi_deriv : public deriv<param_t, func_t> {
00136     public:
00137 
00138     eqi_deriv() {
00139       h=1.0e-4;
00140       xoff=0.0;
00141       cap=&eqi_deriv::calc_array5;
00142       cp=&eqi_deriv::calcp5;
00143       c2p=&eqi_deriv::calc2p5;
00144     }
00145 
00146     /// Stepsize (Default  \f$ 10^{-4} \f$ ).
00147     double h;
00148 
00149     /// Offset (default 0.0)
00150     double xoff;
00151 
00152     /** \brief Set the number of points to use for first derivatives 
00153         (default 5)
00154 
00155         Acceptable values are 2-5 (see above).
00156     */
00157     int set_npoints(int npoints) {
00158       if (npoints==2) {
00159         cap=&eqi_deriv::calc_array3;
00160         cp=&eqi_deriv::calcp2;
00161       } else if (npoints==3) {
00162         cap=&eqi_deriv::calc_array3;
00163         cp=&eqi_deriv::calcp3;
00164       } else if (npoints==4) {
00165         cap=&eqi_deriv::calc_array4;
00166         cp=&eqi_deriv::calcp4;
00167       } else {
00168         cap=&eqi_deriv::calc_array5;
00169         cp=&eqi_deriv::calcp5;
00170       }
00171       if (npoints<=1 || npoints>5) {
00172         set_err_ret("Invalid # of points in set_npoints(). Using default",
00173                     gsl_einval);
00174       }
00175       return 0;
00176     }
00177 
00178     /** \brief Set the number of points to use for second derivatives
00179         (default 5)
00180 
00181         Acceptable values are 3-5 (see above).
00182     */
00183     int set_npoints2(int npoints) {
00184       if (npoints==3) {
00185         c2p=&eqi_deriv::calc2p3;
00186       } else if (npoints==4) {
00187         c2p=&eqi_deriv::calc2p4;
00188       } else {
00189         c2p=&eqi_deriv::calc2p5;
00190       }
00191       if (npoints<=2 || npoints>5) {
00192         set_err_ret("Invalid # of points in set_npoints2(). Using default",
00193                     gsl_einval);
00194       }
00195       return 0;
00196     }
00197 
00198     /** 
00199         \brief Calculate the first derivative of \c func w.r.t. x
00200     */
00201     virtual double calc(double x, void *pa, func_t &func) {
00202       double df, p=xoff/h;
00203       df=(this->*cp)(x,p,pa,func)/h;
00204       return df;
00205     }
00206     
00207     /** \brief Calculate the second derivative of \c func w.r.t. x
00208      */
00209     virtual double calc2(double x, void *pa, func_t &func) {
00210       double df, p=xoff/h;
00211       df=(this->*c2p)(x,p,pa,func)/h/h;
00212       return df;
00213     }
00214     
00215     /** \brief Calculate the third derivative of \c func w.r.t. x
00216      */
00217     virtual double calc3(double x, void *pa, func_t &func) {
00218       double df, p=xoff/h;
00219       df=(this->*c3p)(x,h,p,pa,func)/h;
00220       return df;
00221     }
00222     
00223     /** 
00224         \brief Calculate the derivative at \c x given an array
00225 
00226         This calculates the derivative at \c x given a func_tion
00227         specified in an array \c y of size \c nx with equally spaced
00228         abscissas. The first abscissa should be given as \c x0
00229         and the distance between adjacent abscissas should be
00230         given as \c dx. The value \c x need not be one of the
00231         abscissas (i.e. it can lie in between an interval). The 
00232         appropriate offset is calculated automatically.
00233     */
00234     double calc_array(double x, double x0, double dx, 
00235                       size_t nx, const vec_t &y) {
00236       size_t ix=(size_t)((x-x0+dx/100.0)/dx);
00237       return (this->*cap)(x,x0,dx,nx,y,ix)/dx;
00238     }
00239 
00240     /** 
00241         \brief Calculate the second derivative at \c x given an array
00242 
00243         This calculates the second derivative at \c x given a func_tion
00244         specified in an array \c y of size \c nx with equally spaced
00245         abscissas. The first abscissa should be given as \c x0
00246         and the distance between adjacent abscissas should be
00247         given as \c dx. The value \c x need not be one of the
00248         abscissas (i.e. it can lie in between an interval). The 
00249         appropriate offset is calculated automatically.
00250     */
00251     double calc2_array(double x, double x0, double dx, 
00252                        size_t nx, const vec_t &y) 
00253     {
00254       size_t ix=(size_t)((x-x0+dx/100.0)/dx);
00255       return (this->*c2ap)(x,x0,dx,nx,y,ix)/dx;
00256     }
00257 
00258     /** 
00259         \brief Calculate the third derivative at \c x given an array
00260 
00261         This calculates the third derivative at \c x given a function
00262         specified in an array \c y of size \c nx with equally spaced
00263         abscissas. The first abscissa should be given as \c x0 and the
00264         distance between adjacent abscissas should be given as \c
00265         dx. The value \c x need not be one of the abscissas (i.e. it
00266         can lie in between an interval). The appropriate offset is
00267         calculated automatically.
00268     */
00269     double calc3_array(double x, double x0, double dx, 
00270                        size_t nx, const vec_t &y) 
00271     {
00272       size_t ix=(size_t)((x-x0+dx/100.0)/dx);
00273       return (this->*c3ap)(x,x0,dx,nx,y,ix)/dx;
00274     }
00275 
00276     /** 
00277         \brief Calculate the derivative of an entire array
00278 
00279         Right now this uses np=5.
00280 
00281         \todo generalize to other values of npoints.
00282     */
00283     int deriv_array(size_t nv, double dx, const vec_t &y, 
00284                     vec_t &dydx) 
00285     {
00286       dydx[0]=(-25.0/12.0*y[0]+4.0*y[1]-3.0*y[2]+4.0/3.0*y[3]-0.25*y[4])/dx;
00287       dydx[1]=(-0.25*y[0]-5.0/6.0*y[1]+1.5*y[2]-0.5*y[3]+1.0/12.0*y[4])/dx;
00288       for(size_t i=2;i<nv-2;i++) {
00289         dydx[i]=(1.0/12.0*y[i-2]-2.0/3.0*y[i-1]+2.0/3.0*y[i+1]-
00290                  1.0/12.0*y[i+2])/dx;
00291       }
00292       dydx[nv-2]=(-1.0/12.0*y[nv-5]+0.5*y[nv-4]-1.5*y[nv-3]+
00293                   5.0/6.0*y[nv-2]+0.25*y[nv-1])/dx;
00294       dydx[nv-1]=(0.25*y[nv-5]-4.0/3.0*y[nv-4]+3.0*y[nv-3]-
00295                   4.0*y[nv-2]+25.0/12.0*y[nv-1])/dx;
00296       return 0;
00297     }
00298 
00299     /// Return string denoting type ("eqi_deriv")
00300     virtual const char *type() { return "eqi_deriv"; }
00301 
00302 #ifndef DOXYGENP  
00303 
00304     protected:
00305 
00306     /// Two-point first derivative
00307     double calcp2(double x, double p, void *pa,
00308                   func_t &func) {
00309       return (func(x+h,pa)-func(x,pa));
00310     }
00311       
00312     /// Three-point first derivative
00313     double calcp3(double x, double p, void *pa,
00314                   func_t &func)    {
00315       if (p==0.0) {
00316         return ((-0.5)*func(x-h,pa)+(0.5)*func(x+h,pa));
00317       }
00318       return ((p-0.5)*func(x-h,pa)-2.0*p*func(x,pa)+(p+0.5)*func(x+h,pa));
00319     }
00320 
00321     /// Four-point first derivative
00322     double calcp4(double x, double p, void *pa,
00323                   func_t &func) {
00324       double p2=p*p;
00325       return (-(3.0*p2-6.0*p+2.0)/6.0*func(x-h,pa)+
00326               (1.5*p2-2.0*p-0.5)*func(x,pa)-
00327               (1.5*p2-p-1.0)*func(x+h,pa)+
00328               (3.0*p2-1.0)/6.0*func(x+2.0*h,pa));
00329     }
00330 
00331     /// Five-point first derivative
00332     double calcp5(double x, double p, void *pa,
00333                   func_t &func) {
00334       double p2=p*p, p3=p*p*p;
00335       if (p==0.0) {
00336         return ((1.0)/12.0*func(x-2.0*h,pa)-
00337                 (4.0)/6.0*func(x-h,pa)-
00338                 (-4.0)/6.0*func(x+h,pa)+
00339                 (-1.0)/12.0*func(x+2.0*h,pa));
00340       }
00341       return ((2.0*p3-3.0*p2-p+1.0)/12.0*func(x-2.0*h,pa)-
00342               (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*func(x-h,pa)+
00343               (p3-2.5*p)*func(x,pa)-
00344               (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*func(x+h,pa)+
00345               (2.0*p3+3.0*p2-p-1.0)/12.0*func(x+2.0*h,pa));
00346     }
00347 
00348     
00349     /// Three-point first derivative for arrays
00350     double calc_array3(double x, double x0, double dx, size_t nx,
00351                        const vec_t &y, size_t ix) {
00352       double p;
00353       if (ix>0 && ix<nx-1) {
00354         p=x-(x0+ix*dx);
00355         return ((p-0.5)*y[ix-1]-2.0*p*y[ix]+(p+0.5)*y[ix+1]);
00356       } else if (ix==0) {
00357         p=x-(x0+dx);
00358         return ((p-0.5)*y[0]-2.0*p*y[1]+(p+0.5)*y[2]);
00359       } 
00360       p=x-(x0+(nx-2)*dx);
00361       return ((p-0.5)*y[nx-3]-2.0*p*y[nx-2]+(p+0.5)*y[nx-1]);
00362     }
00363 
00364     /// Four-point first derivative for arrays
00365     double calc_array4(double x, double x0, double dx, size_t nx,
00366                        const vec_t &y, size_t ix) {
00367       double p, p2;
00368       if (ix>0 && ix<nx-2) {
00369         p=x-(x0+ix*dx);
00370         p2=p*p;
00371         return (-(3.0*p2-6.0*p+2.0)/6.0*y[ix-1]+
00372                 (1.5*p2-2.0*p-0.5)*y[ix]-
00373                 (1.5*p2-p-1.0)*y[ix+1]+
00374                 (3.0*p2-1.0)/6.0*y[ix+2]);
00375       } else if (ix==0) {
00376         p=x-(x0+dx);
00377         p2=p*p;
00378         return (-(3.0*p2-6.0*p+2.0)/6.0*y[0]+
00379                 (1.5*p2-2.0*p-0.5)*y[1]-
00380                 (1.5*p2-p-1.0)*y[2]+
00381                 (3.0*p2-1.0)/6.0*y[3]);
00382       }
00383       p=x-(x0+(nx-3)*dx);
00384       p2=p*p;
00385       return (-(3.0*p2-6.0*p+2.0)/6.0*y[nx-4]+
00386               (1.5*p2-2.0*p-0.5)*y[nx-3]-
00387               (1.5*p2-p-1.0)*y[nx-2]+
00388               (3.0*p2-1.0)/6.0*y[nx-1]);
00389     }
00390 
00391     /// Five-point first derivative for arrays
00392     double calc_array5(double x, double x0, 
00393                        double dx, size_t nx,
00394                        const vec_t &y, size_t ix) {
00395       double p, p2, p3;
00396       if (ix>1 && ix<nx-2) {
00397         p=x-(x0+ix*dx);
00398         p2=p*p, p3=p*p*p;
00399         return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[ix-2]-
00400                 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[ix-1]+
00401                 (p3-2.5*p)*y[ix]-
00402                 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[ix+1]+
00403                 (2.0*p3+3.0*p2-p-1.0)/12.0*y[ix+2]);
00404       } else if (ix<=1) {
00405         p=x-(x0+2*dx);
00406         p2=p*p, p3=p*p*p;
00407         return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[0]-
00408                 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[1]+
00409                 (p3-2.5*p)*y[2]-
00410                 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[3]+
00411                 (2.0*p3+3.0*p2-p-1.0)/12.0*y[4]);
00412       }
00413       p=x-(x0+(nx-3)*dx);
00414       p2=p*p, p3=p*p*p;
00415       return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[nx-5]-
00416               (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[nx-4]+
00417               (p3-2.5*p)*y[nx-3]-
00418               (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[nx-2]+
00419               (2.0*p3+3.0*p2-p-1.0)/12.0*y[nx-1]);
00420     }
00421 
00422     /// Three-point second derivative
00423     double calc2p3(double x, double p, void *pa,
00424                    func_t &func) {
00425       return (func(x-h,pa)-2*func(x,pa)+func(x+h,pa));
00426     }
00427 
00428     /// Four-point second derivative
00429     double calc2p4(double x, double p, void *pa,
00430                    func_t &func) {
00431       return ((1.0-2.0*p)*func(x-h,pa)-(1.0-6.0*p)*func(x,pa)
00432               -(1.0-6.0*p)*func(x+h,pa)+(1.0+2.0*p)*func(x+2.0*h,pa))/2.0;
00433     }
00434 
00435     /// Five-point second derivative
00436     double calc2p5(double x, double p, void *pa,
00437                    func_t &func) {
00438       return ((1.0-2.0*p)*(1.0-2.0*p)*func(x-2.0*h,pa)
00439               +(8.0*p-16.0*p*p)*func(x-h,pa)
00440               -(2.0-24.0*p*p)*func(x,pa)
00441               -(8.0*p+16.0*p*p)*func(x+h,pa)
00442               +(1.0+2.0*p)*(1.0+2.0*p)*func(x+2.0*h,pa))/4.0;
00443     }
00444 
00445     /// Pointer to the first derivative function
00446     double (eqi_deriv::*cp)(double x, double p, void *pa,
00447                             func_t &func);
00448 
00449     /// Pointer to the first derivative for arrays function
00450     double (eqi_deriv::*cap)(double x, double x0, 
00451                              double dx, size_t nx,
00452                              const vec_t &y, size_t ix);
00453 
00454     /// Pointer to the second derivative function
00455     double (eqi_deriv::*c2p)(double x, double p, void *pa,
00456                              func_t &func);
00457 
00458     /// Pointer to the second derivative for arrays function
00459     double (eqi_deriv::*c2ap)(double x, double x0, 
00460                               double dx, size_t nx,
00461                               const vec_t &y, size_t ix);
00462 
00463     /// Pointer to the third derivative function
00464     double (eqi_deriv::*c3p)(double x, double h, double p, void *pa,
00465                              func_t &func);
00466 
00467     /// Pointer to the third derivative for arrays function
00468     double (eqi_deriv::*c3ap)(double x, double x0, 
00469                               double dx, size_t nx,
00470                               const vec_t &y, size_t ix);
00471 
00472 #endif
00473 
00474   };
00475 
00476 #ifndef DOXYGENP
00477 }
00478 #endif
00479 
00480 #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