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