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