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_GSL_DERIV_H 00024 #define O2SCL_GSL_DERIV_H 00025 00026 #include <iostream> 00027 #include <o2scl/deriv.h> 00028 #include <o2scl/funct.h> 00029 #include <gsl/gsl_deriv.h> 00030 #include <gsl/gsl_errno.h> 00031 00032 #ifndef DOXYGENP 00033 namespace o2scl { 00034 #endif 00035 00036 /** 00037 \brief Numerical differentiation (GSL) 00038 00039 This class computes the numerical derivative of a function. The 00040 stepsize \ref h should be specified before use. If similar 00041 functions are being differentiated in succession, the user may 00042 be able to increase the speed of later derivatives by setting 00043 the new stepsize equal to the optimized stepsize from the 00044 previous differentiation, by setting \ref h to \ref h_opt. 00045 00046 \note Second and third derivatives are computed by naive nested 00047 applications of the formula for the first derivative and the 00048 uncertainty for these will likely be underestimated. 00049 00050 \future Include the forward and backward GSL derivatives 00051 00052 */ 00053 template<class param_t, class func_t=funct<param_t> > 00054 class gsl_deriv : public deriv<param_t,func_t> { 00055 public: 00056 00057 gsl_deriv() { 00058 h=0.0; 00059 h_opt=0.0; 00060 } 00061 00062 virtual ~gsl_deriv() {} 00063 00064 /** 00065 \brief Initial stepsize 00066 00067 This should be specified before a call to calc() or 00068 calc_err(). If it is zero, then \f$ x 10^{-4} \f$ will used, or 00069 if \c x is zero, then \f$ 10^{-4} \f$ will be used. 00070 */ 00071 double h; 00072 00073 /** 00074 \brief The last value of the optimized stepsize 00075 00076 This is initialized to zero in the constructor and 00077 set by calc_err() to the most recent value of the 00078 optimized stepsize. 00079 */ 00080 double h_opt; 00081 00082 /** \brief Calculate the first derivative of \c func w.r.t. x and 00083 uncertainty 00084 */ 00085 virtual int calc_err(double x, param_t &pa, func_t &func, 00086 double &dfdx, double &err) { 00087 00088 double hh; 00089 if (h==0.0) { 00090 if (x==0.0) hh=1.0e-4; 00091 else hh=1.0e-4*x; 00092 } else { 00093 hh=h; 00094 } 00095 00096 double r_0, round, trunc, error; 00097 00098 central_deriv(x,h,r_0,round,trunc,func,pa); 00099 error = round + trunc; 00100 00101 if (round < trunc && (round > 0 && trunc > 0)) { 00102 double r_opt, round_opt, trunc_opt, error_opt; 00103 00104 /* Compute an optimised stepsize to minimize the total error, 00105 using the scaling of the truncation error (O(h^2)) and 00106 rounding error (O(1/h)). */ 00107 00108 h_opt = h * pow (round / (2.0 * trunc), 1.0 / 3.0); 00109 central_deriv(x,h_opt,r_opt,round_opt,trunc_opt,func,pa); 00110 error_opt = round_opt + trunc_opt; 00111 00112 /* Check that the new error is smaller, and that the new derivative 00113 is consistent with the error bounds of the original estimate. */ 00114 00115 if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error) { 00116 r_0 = r_opt; 00117 error = error_opt; 00118 } 00119 } 00120 00121 dfdx=r_0; 00122 err=error; 00123 00124 return 0; 00125 } 00126 00127 /// Return string denoting type ("gsl_deriv") 00128 virtual const char *type() { return "gsl_deriv"; } 00129 00130 #ifndef DOXYGEN_INTERNAL 00131 00132 protected: 00133 00134 /** \brief Internal version of calc_err() for second 00135 and third derivatives 00136 */ 00137 virtual int calc_err_int 00138 (double x, typename deriv<param_t,func_t>::dpars &pa, 00139 funct<typename deriv<param_t,func_t>::dpars> &func, 00140 double &dfdx, double &err) { 00141 00142 err_hnd->reset(); 00143 00144 double hh; 00145 if (h==0.0) { 00146 if (x==0.0) hh=1.0e-4; 00147 else hh=1.0e-4*x; 00148 } else { 00149 hh=h; 00150 } 00151 00152 double r_0, round, trunc, error; 00153 00154 central_deriv(x,h,r_0,round,trunc,func,pa); 00155 error = round + trunc; 00156 00157 if (round < trunc && (round > 0 && trunc > 0)) { 00158 double r_opt, round_opt, trunc_opt, error_opt; 00159 00160 /* Compute an optimised stepsize to minimize the total error, 00161 using the scaling of the truncation error (O(h^2)) and 00162 rounding error (O(1/h)). 00163 */ 00164 00165 // This value 'hopt' is different from 'h_opt' above so that 00166 // the two derivative functions don't get confused about which 00167 // 'hopt' they are using. 00168 double hopt = h * pow (round / (2.0 * trunc), 1.0 / 3.0); 00169 central_deriv(x,hopt,r_opt,round_opt,trunc_opt,func,pa); 00170 error_opt = round_opt + trunc_opt; 00171 00172 /* Check that the new error is smaller, and that the new derivative 00173 is consistent with the error bounds of the original estimate. 00174 */ 00175 00176 if (error_opt < error && fabs (r_opt - r_0) < 4.0 * error) { 00177 r_0 = r_opt; 00178 error = error_opt; 00179 } 00180 } 00181 00182 dfdx=r_0; 00183 err=error; 00184 00185 return 0; 00186 } 00187 00188 /** 00189 \brief Compute derivative using 5-point rule 00190 00191 Compute the derivative using the 5-point rule (x-h, x-h/2, x, 00192 x+h/2, x+h) and the error using the difference between the 00193 5-point and the 3-point rule (x-h,x,x+h). Note that the 00194 central point is not used for either. 00195 00196 This must be a class template because it is used by 00197 both calc_err() and calc_err_int(). 00198 */ 00199 template<class func2_t, class param2_t> 00200 int central_deriv(double x, double hh, double &result, 00201 double &abserr_round, double &abserr_trunc, 00202 func2_t &func, param2_t &pa) { 00203 00204 double fm1, fp1, fmh, fph; 00205 00206 func(x-hh,fm1,pa); 00207 func(x+hh,fp1,pa); 00208 00209 func(x-hh/2,fmh,pa); 00210 func(x+hh/2,fph,pa); 00211 00212 double r3 = 0.5 * (fp1 - fm1); 00213 double r5 = (4.0 / 3.0) * (fph - fmh) - (1.0 / 3.0) * r3; 00214 00215 double e3 = (fabs (fp1) + fabs (fm1)) * GSL_DBL_EPSILON; 00216 double e5 = 2.0 * (fabs (fph) + fabs (fmh)) * GSL_DBL_EPSILON + e3; 00217 00218 /* The next term is due to finite precision in x+h = O (eps * x) */ 00219 00220 double dy=GSL_MAX(fabs(r3/hh),fabs(r5/hh))*fabs(x/hh)*GSL_DBL_EPSILON; 00221 00222 /* The truncation error in the r5 approximation itself is O(h^4). 00223 However, for safety, we estimate the error from r5-r3, which is 00224 O(h^2). By scaling h we will minimise this estimated error, not 00225 the actual truncation error in r5. 00226 */ 00227 00228 result = r5 / hh; 00229 /* Estimated truncation error O(h^2) */ 00230 abserr_trunc = fabs ((r5 - r3) / hh); 00231 /* Rounding error (cancellations) */ 00232 abserr_round = fabs (e5 / hh) + dy; 00233 00234 if (this->verbose>0) { 00235 std::cout << "gsl_deriv: " << std::endl; 00236 std::cout << "step: " << hh << std::endl; 00237 std::cout << "abcissas: " << x-hh/2 << " " << x-hh << " " 00238 << x+hh/2 << " " << x+hh << std::endl; 00239 std::cout << "ordinates: " << fm1 << " " << fmh << " " << fph << " " 00240 << fp1 << std::endl; 00241 std::cout << "res: " << result << " trc: " << abserr_trunc 00242 << " rnd: " << abserr_round << std::endl; 00243 } 00244 00245 return 0; 00246 } 00247 00248 #endif 00249 00250 }; 00251 00252 #ifndef DOXYGENP 00253 } 00254 #endif 00255 00256 #endif 00257 00258 00259
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