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

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).

Get Object-oriented Scientific Computing
Lib at SourceForge.net. Fast, secure and Free Open Source software
downloads.