cern_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_CERN_DERIV_H
00024 #define O2SCL_CERN_DERIV_H
00025 
00026 #include <o2scl/deriv.h>
00027 
00028 #ifndef DOXYGENP
00029 namespace o2scl {
00030 #endif
00031 
00032   /** 
00033       \brief Numerical differentiation routine (CERNLIB)
00034 
00035       This uses Romberg extrapolation to compute the 
00036       derivative with the finite-differencing formula
00037       \f[
00038       f^{\prime}(x) = [f(x+h)-f(x-h)]/(2h)
00039       \f]
00040 
00041       If \ref root::verbose is greater than zero, then each iteration
00042       prints out the extrapolation table, and if \ref root::verbose
00043       is greater than 1, then a keypress is required at the 
00044       end of each iteration. 
00045 
00046       Based on the CERNLIB routine DERIV(), which was 
00047       based on \ref Rutishauser63 .
00048       
00049       \note Second and third derivatives are computed by naive nested
00050       applications of the formula for the first derivative and the
00051       uncertainty for these will likely be underestimated.
00052       
00053       \comment
00054       - Maybe we should consider moving the table size to a template 
00055       parameter?
00056       \endcomment
00057   */
00058   template<class param_t, class func_t>
00059     class cern_deriv : public deriv<param_t,func_t> {
00060 
00061     public:
00062   
00063     /// A scaling factor (default 1.0)
00064     double delta;
00065   
00066     /// Extrapolation tolerance (default is \f$ 5 \times 10^{14} \f$)
00067     double eps;
00068 
00069     cern_deriv() {
00070 
00071       dx[0]=0.0256;
00072       dx[1]=0.0192;
00073       dx[2]=0.0128;
00074       dx[3]=0.0096;
00075       dx[4]=0.0064;
00076       dx[5]=0.0048;
00077       dx[6]=0.0032;
00078       dx[7]=0.0024;
00079       dx[8]=0.0016;
00080       dx[9]=0.0012;
00081   
00082       w[1][1]=1.3333333333333333;
00083       w[3][1]=1.0666666666666667;
00084       w[5][1]=1.0158730158730159;
00085       w[7][1]=1.0039215686274510;
00086   
00087       w[2][1]=3.3333333333333333e-1;
00088       w[4][1]=6.6666666666666667e-2;
00089       w[6][1]=1.5873015873015873e-2;
00090       w[8][1]=3.9215686274509804e-3;
00091 
00092       w[0][2]=2.2857142857142857;
00093       w[2][2]=1.1636363636363636;
00094       w[4][2]=1.0364372469635628;
00095       w[6][2]=1.0088669950738916;
00096       w[8][2]=1.0022021042329337;
00097 
00098       w[1][2]=1.2857142857142857;
00099       w[3][2]=1.6363636363636364e-1;
00100       w[5][2]=3.6437246963562753e-2;
00101       w[7][2]=8.8669950738916256e-3;
00102       w[9][2]=2.2021042329336922e-3;
00103   
00104       w[0][3]=1.8000000000000000;
00105       w[2][3]=1.1250000000000000;
00106       w[4][3]=1.0285714285714286;
00107       w[6][3]=1.0069930069930070;
00108       w[8][3]=1.0017391304347826;
00109   
00110       w[1][3]=8.0000000000000000e-1;
00111       w[3][3]=1.2500000000000000e-1;
00112       w[5][3]=2.8571428571428571e-2;
00113       w[7][3]=6.9930069930069930e-3;
00114       w[9][3]=1.7391304347826087e-3;
00115   
00116       delta=1.0;
00117       eps=5.0e-14;
00118     }
00119     
00120     virtual int calc_err(double x, param_t &pa, func_t &func,
00121                          double &dfdx, double &err) {
00122       
00123       double t[10][10], a[10], del, hh;
00124       bool lev[10]={1,0,1,0,1,0,1,0,1,0}, lmt;
00125       int is, k, m;
00126 
00127       err_hnd->reset();
00128 
00129       del=10.0*fabs(delta);
00130       is=10;
00131   
00132       do {
00133         is--;
00134         del=del/10.0;
00135 
00136         if (is==0 || x+del*dx[9]==x) {
00137           delta=del;
00138           dfdx=0.0;
00139           err=0.0;
00140           set_err_ret
00141             ("Calculation of derivative failed in cern_deriv::calc().",
00142              gsl_efailed);
00143         }
00144     
00145         for(k=0;k<=9;k++) {
00146           hh=del*dx[k];
00147           t[k][0]=(func(x+hh,pa)-func(x-hh,pa))/(hh+hh);
00148           a[k]=t[k][0];
00149         }
00150     
00151         if (a[0]>=a[9]) {
00152           for(k=0;k<=9;k++) a[k]=-a[k];
00153         }
00154     
00155         lmt=true;
00156         for(k=1;k<=9;k++) {
00157           hh=a[k-1]-a[k];
00158           lmt=(lmt && (hh<=0.0 || fabs(hh)<=eps*fabs(a[k])));
00159         }
00160         
00161         if (this->verbose>0) {
00162           std::cout << "cern_deriv, iteration: " << 10-is << std::endl;
00163           std::cout << "(hh, a[k], derivative) list: " 
00164                     << std::endl;
00165           for(k=1;k<=9;k++) {
00166             std::cout << a[k-1]-a[k] << " " << eps*fabs(a[k]) << " "
00167                       << t[k][0] << std::endl;
00168           }
00169           std::cout << "Converged: " << lmt << std::endl;
00170           if (this->verbose>1) {
00171             char ch;
00172             std::cin >> ch;
00173           }
00174         }
00175 
00176       } while (lmt==false);
00177   
00178       for(m=1;m<=9;m++) {
00179         for(k=0;k<=9-m;k++) {
00180           if (lev[m]) {
00181             t[k][m]=w[m-1][1]*t[k+1][m-1]-w[m][1]*t[k][m-1];
00182           } else if (lev[k]) {
00183             t[k][m]=w[m-1][2]*t[k+1][m-1]-w[m][2]*t[k][m-1];
00184           } else {
00185             t[k][m]=w[m-1][3]*t[k+1][m-1]-w[m][3]*t[k][m-1];
00186           }
00187         }
00188       }
00189       dfdx=t[0][9];
00190       if (dfdx!=0.0) err=(dfdx-t[0][8])/dfdx;
00191       else err=0.0;
00192       delta=del;
00193   
00194       return 0;
00195     }
00196  
00197     virtual int calc_err_int
00198       (double x, typename deriv<param_t,func_t>::dpars &pa, 
00199        funct<typename deriv<param_t,func_t>::dpars> &func, 
00200        double &dfdx, double &err) {
00201       
00202       double t[10][10], a[10], del, hh;
00203       bool lev[10]={1,0,1,0,1,0,1,0,1,0}, lmt;
00204       int is, k, m;
00205 
00206       err_hnd->reset();
00207 
00208       del=10.0*fabs(delta);
00209       is=10;
00210   
00211       do {
00212         is--;
00213         del=del/10.0;
00214         if (is==0 || x+del*dx[9]==x) {
00215           delta=del;
00216           set_err("Calculation of derivative failed in cern_deriv::calc().",
00217                   gsl_efailed);
00218           dfdx=0.0;
00219           err=0.0;
00220           return 1;
00221         }
00222     
00223         for(k=0;k<=9;k++) {
00224           hh=del*dx[k];
00225           t[k][0]=(func(x+hh,pa)-func(x-hh,pa))/(hh+hh);
00226           a[k]=t[k][0];
00227         }
00228     
00229         if (a[0]>=a[9]) {
00230           for(k=0;k<=9;k++) a[k]=-a[k];
00231         }
00232     
00233         lmt=true;
00234         for(k=1;k<=9;k++) {
00235           hh=a[k-1]-a[k];
00236           lmt=(lmt && (hh<=0.0 || fabs(hh)<=eps*fabs(a[k])));
00237         }
00238 
00239       } while (lmt==false);
00240   
00241       for(m=1;m<=9;m++) {
00242         for(k=0;k<=9-m;k++) {
00243           if (lev[m]) {
00244             t[k][m]=w[m-1][1]*t[k+1][m-1]-w[m][1]*t[k][m-1];
00245           } else if (lev[k]) {
00246             t[k][m]=w[m-1][2]*t[k+1][m-1]-w[m][2]*t[k][m-1];
00247           } else {
00248             t[k][m]=w[m-1][3]*t[k+1][m-1]-w[m][3]*t[k][m-1];
00249           }
00250         }
00251       }
00252       dfdx=t[0][9];
00253       if (dfdx!=0.0) err=(dfdx-t[0][8])/dfdx;
00254       else err=0.0;
00255       delta=del;
00256   
00257       return 0;
00258     }
00259  
00260     /// Return string denoting type ("cern_deriv")
00261     virtual const char *type() { return "cern_deriv"; }
00262 
00263     protected:
00264 
00265 #ifndef DOXYGEN_INTERNAL
00266     
00267     /// \name Storage for the fixed coefficients
00268     //@{
00269     double dx[10];
00270     double w[10][4];
00271     //@}
00272 
00273 #endif
00274 
00275   };
00276 
00277 #ifndef DOXYGENP
00278 }
00279 #endif
00280 
00281 #endif
00282 
00283 

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