Object-oriented Scientific Computing Library: Version 0.910
cern_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_CERN_DERIV_H
00024 #define O2SCL_CERN_DERIV_H
00025 
00026 #include <o2scl/deriv.h>
00027 #include <o2scl/funct.h>
00028 #include <o2scl/string_conv.h>
00029 #include <o2scl/err_hnd.h>
00030 
00031 #ifndef DOXYGENP
00032 namespace o2scl {
00033 #endif
00034   
00035   /** \brief Numerical differentiation routine (CERNLIB)
00036 
00037       This uses Romberg extrapolation to compute the 
00038       derivative with the finite-differencing formula
00039       \f[
00040       f^{\prime}(x) = [f(x+h)-f(x-h)]/(2h)
00041       \f]
00042 
00043       If \ref deriv::verbose is greater than zero, then each iteration
00044       prints out the extrapolation table, and if \ref deriv::verbose
00045       is greater than 1, then a keypress is required at the end of
00046       each iteration.
00047 
00048       For sufficiently difficult functions, the derivative computation 
00049       can fail, and will call the error handler and return zero with
00050       zero error.
00051 
00052       Based on the CERNLIB routine DERIV, which was 
00053       based on \ref Rutishauser63 and is documented at
00054       http://wwwasdoc.web.cern.ch/wwwasdoc/shortwrupsdir/d401/top.html
00055       
00056       An example demonstrating the usage of this class is 
00057       given in <tt>examples/ex_deriv.cpp</tt> and the \ref ex_deriv_sect .
00058 
00059       If \ref deriv::verbose is greater than zero, at each iteration
00060       this class prints something similar to
00061       \verbatim
00062       cern_deriv, iteration: 1
00063       (hh, a[k], derivative) list: 
00064       -4.193459e-05 4.387643e-14 8.775286e-01
00065       -2.995402e-05 4.387792e-14 8.775585e-01
00066       -1.048405e-05 4.387845e-14 8.775690e-01
00067       -7.488654e-06 4.387882e-14 8.775765e-01
00068       -2.621038e-06 4.387895e-14 8.775791e-01
00069       -1.872173e-06 4.387905e-14 8.775810e-01
00070       -6.552611e-07 4.387908e-14 8.775817e-01
00071       -4.680438e-07 4.387910e-14 8.775821e-01
00072       -1.638153e-07 4.387911e-14 8.775823e-01
00073       \endverbatim
00074       If \ref deriv::verbose is greater than 1, a keypress is required
00075       after each iteration.
00076 
00077       \note Second and third derivatives are computed by naive nested
00078       applications of the formula for the first derivative.
00079       No uncertainty for these derivatives is provided. 
00080 
00081       \future All of the coefficients appear to be fractions which
00082       could be replaced with exact representation?
00083       \future Record the number of function calls?
00084       
00085       \comment
00086       - Maybe we should consider moving the table size to a template 
00087       parameter? (1/29/07 - Probably not, as we'd have to re-derive
00088       the coefficients for sizes other than 10)
00089       \endcomment
00090   */
00091   template<class func_t=funct> class cern_deriv : public deriv<func_t> {
00092 
00093     public:
00094   
00095     /// A scaling factor (default 1.0)
00096     double delta;
00097   
00098     /// Extrapolation tolerance (default is \f$ 5 \times 10^{14} \f$)
00099     double eps;
00100 
00101     cern_deriv() {
00102       dx[0]=0.0256;
00103       dx[1]=0.0192;
00104       dx[2]=0.0128;
00105       dx[3]=0.0096;
00106       dx[4]=0.0064;
00107       dx[5]=0.0048;
00108       dx[6]=0.0032;
00109       dx[7]=0.0024;
00110       dx[8]=0.0016;
00111       dx[9]=0.0012;
00112   
00113       w[1][1]=1.3333333333333333;
00114       w[3][1]=1.0666666666666667;
00115       w[5][1]=1.0158730158730159;
00116       w[7][1]=1.0039215686274510;
00117   
00118       w[2][1]=3.3333333333333333e-1;
00119       w[4][1]=6.6666666666666667e-2;
00120       w[6][1]=1.5873015873015873e-2;
00121       w[8][1]=3.9215686274509804e-3;
00122 
00123       w[0][2]=2.2857142857142857;
00124       w[2][2]=1.1636363636363636;
00125       w[4][2]=1.0364372469635628;
00126       w[6][2]=1.0088669950738916;
00127       w[8][2]=1.0022021042329337;
00128 
00129       w[1][2]=1.2857142857142857;
00130       w[3][2]=1.6363636363636364e-1;
00131       w[5][2]=3.6437246963562753e-2;
00132       w[7][2]=8.8669950738916256e-3;
00133       w[9][2]=2.2021042329336922e-3;
00134   
00135       w[0][3]=1.8000000000000000;
00136       w[2][3]=1.1250000000000000;
00137       w[4][3]=1.0285714285714286;
00138       w[6][3]=1.0069930069930070;
00139       w[8][3]=1.0017391304347826;
00140   
00141       w[1][3]=8.0000000000000000e-1;
00142       w[3][3]=1.2500000000000000e-1;
00143       w[5][3]=2.8571428571428571e-2;
00144       w[7][3]=6.9930069930069930e-3;
00145       w[9][3]=1.7391304347826087e-3;
00146   
00147       delta=1.0;
00148       eps=5.0e-14;
00149     }
00150     
00151     /** \brief Calculate the first derivative of \c func w.r.t. x and the
00152         uncertainty
00153     */
00154     virtual int calc_err(double x, func_t &func,
00155                          double &dfdx, double &err) {
00156       return calc_base<func_t>(x,func,dfdx,err);
00157     }
00158 
00159     /// Return string denoting type ("cern_deriv")
00160     virtual const char *type() { return "cern_deriv"; }
00161 
00162   protected:
00163 
00164 #ifndef DOXYGEN_INTERNAL
00165  
00166     /** \brief Internal template version of the derivative function
00167     */
00168     template<class func2_t> int calc_base(double x, func2_t &func,
00169                                           double &dfdx, double &err) {
00170       
00171       double t[10][10], a[10], del, hh;
00172       bool lev[10]={1,0,1,0,1,0,1,0,1,0}, lmt;
00173       int is, k, m;
00174 
00175       del=10.0*fabs(delta);
00176       is=10;
00177   
00178       do {
00179         is--;
00180         del=del/10.0;
00181 
00182         if (is==0 || x+del*dx[9]==x) {
00183           delta=del;
00184           dfdx=0.0;
00185           err=0.0;
00186           std::string str="Calculation of derivative failed (is="+
00187             itos(is)+" and del*dx[9]="+dtos(del*dx[9])+
00188             ") in cern_deriv::calc_base().";
00189           O2SCL_ERR_RET(str.c_str(),gsl_efailed);
00190         }
00191     
00192         for(k=0;k<=9;k++) {
00193           hh=del*dx[k];
00194           t[k][0]=(func(x+hh)-func(x-hh))/(hh+hh);
00195           a[k]=t[k][0];
00196         }
00197     
00198         if (a[0]>=a[9]) {
00199           for(k=0;k<=9;k++) a[k]=-a[k];
00200         }
00201     
00202         lmt=true;
00203         for(k=1;k<=9;k++) {
00204           hh=a[k-1]-a[k];
00205           lmt=(lmt && (hh<=0.0 || fabs(hh)<=eps*fabs(a[k])));
00206         }
00207         
00208         if (this->verbose>0) {
00209           std::cout << "cern_deriv, iteration: " << 10-is << std::endl;
00210           std::cout << "(hh, a[k], derivative) list: " 
00211                     << std::endl;
00212           for(k=1;k<=9;k++) {
00213             std::cout << a[k-1]-a[k] << " " << eps*fabs(a[k]) << " "
00214                       << t[k][0] << std::endl;
00215           }
00216           std::cout << "Converged: " << lmt << std::endl;
00217           if (this->verbose>1) {
00218             char ch;
00219             std::cin >> ch;
00220           }
00221         }
00222 
00223       } while (lmt==false);
00224   
00225       for(m=1;m<=9;m++) {
00226         for(k=0;k<=9-m;k++) {
00227           if (lev[m]) {
00228             t[k][m]=w[m-1][1]*t[k+1][m-1]-w[m][1]*t[k][m-1];
00229           } else if (lev[k]) {
00230             t[k][m]=w[m-1][2]*t[k+1][m-1]-w[m][2]*t[k][m-1];
00231           } else {
00232             t[k][m]=w[m-1][3]*t[k+1][m-1]-w[m][3]*t[k][m-1];
00233           }
00234         }
00235       }
00236       dfdx=t[0][9];
00237       if (dfdx!=0.0) err=(dfdx-t[0][8])/dfdx;
00238       else err=0.0;
00239       delta=del;
00240   
00241       return 0;
00242     }
00243 
00244     /** \brief Calculate the first derivative of \c func w.r.t. x
00245 
00246         This is an internal version of calc() which is used in
00247         computing second and third derivatives
00248      */
00249     virtual int calc_err_int(double x, funct &func, double &dfdx, 
00250                              double &err) {
00251       return calc_base<funct>(x,func,dfdx,err);
00252     }
00253  
00254     /// \name Storage for the fixed coefficients
00255     //@{
00256     double dx[10];
00257     double w[10][4];
00258     //@}
00259 
00260 #endif
00261 
00262   };
00263 
00264 #ifndef DOXYGENP
00265 }
00266 #endif
00267 
00268 #endif
00269 
00270 
 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.