00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 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_EQI_DERIV_H 00024 #define O2SCL_EQI_DERIV_H 00025 00026 #include <cmath> 00027 #include <o2scl/deriv.h> 00028 #include <o2scl/collection.h> 00029 00030 #ifndef DOXYGENP 00031 namespace o2scl { 00032 #endif 00033 00034 /** 00035 \brief Derivatives for equally-spaced abscissas 00036 00037 This is an implementation of the formulas for equally-spaced 00038 abscissas as indicated below. The level of approximation is 00039 specified in set_npoints(). The value of \f$ p \times h \f$ 00040 can be specified in \c xoff (default is zero). 00041 00042 \note The derivatives given, for example, from the 00043 five-point formula can sometimes be more accurate 00044 than computing the derivative from the interpolation class. 00045 This is especially true near the boundaries of the interpolated 00046 region. 00047 00048 \todo The uncertainties in the derivatives are not yet computed 00049 and the second and third derivative formulas are not yet finished. 00050 00051 Two-point formula (note that this is independent of p). 00052 \f[ 00053 f^{\prime}(x_0+p h)=\frac{1}{h}\left[ 00054 f_{1}-f_{0} \right] 00055 \f] 00056 Three-point formula from Abramowitz and Stegun 00057 \f[ 00058 f^{\prime}(x_0+p h)=\frac{1}{h}\left[ 00059 \frac{2p-1}{2}f_{-1}-2 p f_{0}+\frac{2p+1}{2}f_{1}\right] 00060 \f] 00061 Four-point formula from Abramowitz and Stegun 00062 \f[ 00063 f^{\prime}(x_0+p h)=\frac{1}{h}\left[ 00064 -\frac{3 p^2-6 p+2}{6}f_{-1} 00065 +\frac{3 p^2-4 p -1}{2}f_{0} 00066 -\frac{3 p^2-2 p-2}{2}f_{1} 00067 +\frac{3 p^2-1}{6}f_{2} 00068 \right] 00069 \f] 00070 Five-point formula from Abramowitz and Stegun 00071 \f{eqnarray*} 00072 f^{\prime}(x_0+p h)&=&\frac{1}{h}\left[ 00073 \frac{2 p^3-3 p^2-p+1}{12}f_{-2} 00074 -\frac{4 p^3-3p^2-8p+4}{6}f_{-1} 00075 \right. \\ && \left. 00076 +\frac{2p^3-5p}{2}f_{0} 00077 -\frac{4p^3+3p^2-8p-4}{6}f_{1} 00078 \right. \\ && \left. 00079 +\frac{2p^3+3p^2-p-1}{12}f_{2} 00080 \right] 00081 \f} 00082 00083 The relations above can be confined to give formulas 00084 for second derivative formulas: 00085 Three-point formula 00086 \f[ 00087 f^{\prime}(x_0+p h)=\frac{1}{h^2} 00088 \left[f_{-1}-2 f_0+f_1\right] 00089 \f] 00090 Four-point formula: 00091 \f[ 00092 f^{\prime}(x_0+p h)=\frac{1}{2 h^2} 00093 \left[\left(1-2p\right)f_{-1}-\left(1-6p\right)f_0 00094 -\left(1+6p\right)f_1+\left(1+2p\right)f_2\right] 00095 \f] 00096 Five-point formula: 00097 \f[ 00098 f^{\prime}(x_0+p h)=\frac{1}{4 h^2} 00099 \left[\left(1-2p\right)^2f_{-2} 00100 +\left(8p-16 p^2\right)f_{-1} 00101 -\left(2-24 p^2\right)f_0 00102 -\left(8p+16p^2\right)f_1 00103 +\left(1+2p\right)^2 f_2\right] 00104 \f] 00105 Six-point formula: 00106 \f{eqnarray*} 00107 f^{\prime}(x_0+p h)&=&\frac{1}{12 h^2}\left[ 00108 \left(2-10p+15 p^2-6p^3\right)f_{-2} 00109 +\left(3+14p-57p^2+30p^3\right)f_{-1} 00110 \right. \\ && \left. 00111 +\left(-8+20p+78 p^2-60p^3\right)f_0 00112 +\left(-2-44p-42p^2+60p^3\right)f_1 00113 \right. \\ && \left. 00114 +\left(6+22p+3p^2-30p^3\right)f_2 00115 +\left(-1-2p+3p^2+6p^3\right)f_3 00116 \right] 00117 \f} 00118 Seven-point formula: 00119 \f{eqnarray*} 00120 f^{\prime}(x_0+p h)&=&\frac{1}{36 h^2}\left[ 00121 \left(4-24p+48p^2-36p^3+9p^4\right)f_{-3} 00122 +\left(12+12p-162p^2+180p^3-54p^4\right)f_{-2} 00123 \right. \\ && \left. 00124 +\left(-15+120p+162p^2-360p^3+135p^4\right)f_{-1} 00125 -4\left(8+48p-3p^2-90p^3+45p^4\right)f_0 00126 \right. \\ && \left. 00127 +3\left(14+32p-36p^2-60p^3+45p^4\right)f_1 00128 +\left(-12-12p+54p^2+36p^3-54p^4\right)f_2 00129 \right. \\ && \left. 00130 +\left(1-6p^2+9p^4\right)f_3 00131 \right] 00132 \f} 00133 */ 00134 template<class param_t, class func_t, class vec_t=ovector_view> 00135 class eqi_deriv : public deriv<param_t, func_t> { 00136 public: 00137 00138 eqi_deriv() { 00139 h=1.0e-4; 00140 xoff=0.0; 00141 cap=&eqi_deriv::calc_array5; 00142 cp=&eqi_deriv::calcp5; 00143 c2p=&eqi_deriv::calc2p5; 00144 } 00145 00146 /// Stepsize (Default \f$ 10^{-4} \f$ ). 00147 double h; 00148 00149 /// Offset (default 0.0) 00150 double xoff; 00151 00152 /** \brief Set the number of points to use for first derivatives 00153 (default 5) 00154 00155 Acceptable values are 2-5 (see above). 00156 */ 00157 int set_npoints(int npoints) { 00158 if (npoints==2) { 00159 cap=&eqi_deriv::calc_array3; 00160 cp=&eqi_deriv::calcp2; 00161 } else if (npoints==3) { 00162 cap=&eqi_deriv::calc_array3; 00163 cp=&eqi_deriv::calcp3; 00164 } else if (npoints==4) { 00165 cap=&eqi_deriv::calc_array4; 00166 cp=&eqi_deriv::calcp4; 00167 } else { 00168 cap=&eqi_deriv::calc_array5; 00169 cp=&eqi_deriv::calcp5; 00170 } 00171 if (npoints<=1 || npoints>5) { 00172 set_err_ret("Invalid # of points in set_npoints(). Using default", 00173 gsl_einval); 00174 } 00175 return 0; 00176 } 00177 00178 /** \brief Set the number of points to use for second derivatives 00179 (default 5) 00180 00181 Acceptable values are 3-5 (see above). 00182 */ 00183 int set_npoints2(int npoints) { 00184 if (npoints==3) { 00185 c2p=&eqi_deriv::calc2p3; 00186 } else if (npoints==4) { 00187 c2p=&eqi_deriv::calc2p4; 00188 } else { 00189 c2p=&eqi_deriv::calc2p5; 00190 } 00191 if (npoints<=2 || npoints>5) { 00192 set_err_ret("Invalid # of points in set_npoints2(). Using default", 00193 gsl_einval); 00194 } 00195 return 0; 00196 } 00197 00198 /** 00199 \brief Calculate the first derivative of \c func w.r.t. x 00200 */ 00201 virtual double calc(double x, void *pa, func_t &func) { 00202 double df, p=xoff/h; 00203 df=(this->*cp)(x,p,pa,func)/h; 00204 return df; 00205 } 00206 00207 /** \brief Calculate the second derivative of \c func w.r.t. x 00208 */ 00209 virtual double calc2(double x, void *pa, func_t &func) { 00210 double df, p=xoff/h; 00211 df=(this->*c2p)(x,p,pa,func)/h/h; 00212 return df; 00213 } 00214 00215 /** \brief Calculate the third derivative of \c func w.r.t. x 00216 */ 00217 virtual double calc3(double x, void *pa, func_t &func) { 00218 double df, p=xoff/h; 00219 df=(this->*c3p)(x,h,p,pa,func)/h; 00220 return df; 00221 } 00222 00223 /** 00224 \brief Calculate the derivative at \c x given an array 00225 00226 This calculates the derivative at \c x given a func_tion 00227 specified in an array \c y of size \c nx with equally spaced 00228 abscissas. The first abscissa should be given as \c x0 00229 and the distance between adjacent abscissas should be 00230 given as \c dx. The value \c x need not be one of the 00231 abscissas (i.e. it can lie in between an interval). The 00232 appropriate offset is calculated automatically. 00233 */ 00234 double calc_array(double x, double x0, double dx, 00235 size_t nx, const vec_t &y) { 00236 size_t ix=(size_t)((x-x0+dx/100.0)/dx); 00237 return (this->*cap)(x,x0,dx,nx,y,ix)/dx; 00238 } 00239 00240 /** 00241 \brief Calculate the second derivative at \c x given an array 00242 00243 This calculates the second derivative at \c x given a func_tion 00244 specified in an array \c y of size \c nx with equally spaced 00245 abscissas. The first abscissa should be given as \c x0 00246 and the distance between adjacent abscissas should be 00247 given as \c dx. The value \c x need not be one of the 00248 abscissas (i.e. it can lie in between an interval). The 00249 appropriate offset is calculated automatically. 00250 */ 00251 double calc2_array(double x, double x0, double dx, 00252 size_t nx, const vec_t &y) 00253 { 00254 size_t ix=(size_t)((x-x0+dx/100.0)/dx); 00255 return (this->*c2ap)(x,x0,dx,nx,y,ix)/dx; 00256 } 00257 00258 /** 00259 \brief Calculate the third derivative at \c x given an array 00260 00261 This calculates the third derivative at \c x given a function 00262 specified in an array \c y of size \c nx with equally spaced 00263 abscissas. The first abscissa should be given as \c x0 and the 00264 distance between adjacent abscissas should be given as \c 00265 dx. The value \c x need not be one of the abscissas (i.e. it 00266 can lie in between an interval). The appropriate offset is 00267 calculated automatically. 00268 */ 00269 double calc3_array(double x, double x0, double dx, 00270 size_t nx, const vec_t &y) 00271 { 00272 size_t ix=(size_t)((x-x0+dx/100.0)/dx); 00273 return (this->*c3ap)(x,x0,dx,nx,y,ix)/dx; 00274 } 00275 00276 /** 00277 \brief Calculate the derivative of an entire array 00278 00279 Right now this uses np=5. 00280 00281 \todo generalize to other values of npoints. 00282 */ 00283 int deriv_array(size_t nv, double dx, const vec_t &y, 00284 vec_t &dydx) 00285 { 00286 dydx[0]=(-25.0/12.0*y[0]+4.0*y[1]-3.0*y[2]+4.0/3.0*y[3]-0.25*y[4])/dx; 00287 dydx[1]=(-0.25*y[0]-5.0/6.0*y[1]+1.5*y[2]-0.5*y[3]+1.0/12.0*y[4])/dx; 00288 for(size_t i=2;i<nv-2;i++) { 00289 dydx[i]=(1.0/12.0*y[i-2]-2.0/3.0*y[i-1]+2.0/3.0*y[i+1]- 00290 1.0/12.0*y[i+2])/dx; 00291 } 00292 dydx[nv-2]=(-1.0/12.0*y[nv-5]+0.5*y[nv-4]-1.5*y[nv-3]+ 00293 5.0/6.0*y[nv-2]+0.25*y[nv-1])/dx; 00294 dydx[nv-1]=(0.25*y[nv-5]-4.0/3.0*y[nv-4]+3.0*y[nv-3]- 00295 4.0*y[nv-2]+25.0/12.0*y[nv-1])/dx; 00296 return 0; 00297 } 00298 00299 /// Return string denoting type ("eqi_deriv") 00300 virtual const char *type() { return "eqi_deriv"; } 00301 00302 #ifndef DOXYGENP 00303 00304 protected: 00305 00306 /// Two-point first derivative 00307 double calcp2(double x, double p, void *pa, 00308 func_t &func) { 00309 return (func(x+h,pa)-func(x,pa)); 00310 } 00311 00312 /// Three-point first derivative 00313 double calcp3(double x, double p, void *pa, 00314 func_t &func) { 00315 if (p==0.0) { 00316 return ((-0.5)*func(x-h,pa)+(0.5)*func(x+h,pa)); 00317 } 00318 return ((p-0.5)*func(x-h,pa)-2.0*p*func(x,pa)+(p+0.5)*func(x+h,pa)); 00319 } 00320 00321 /// Four-point first derivative 00322 double calcp4(double x, double p, void *pa, 00323 func_t &func) { 00324 double p2=p*p; 00325 return (-(3.0*p2-6.0*p+2.0)/6.0*func(x-h,pa)+ 00326 (1.5*p2-2.0*p-0.5)*func(x,pa)- 00327 (1.5*p2-p-1.0)*func(x+h,pa)+ 00328 (3.0*p2-1.0)/6.0*func(x+2.0*h,pa)); 00329 } 00330 00331 /// Five-point first derivative 00332 double calcp5(double x, double p, void *pa, 00333 func_t &func) { 00334 double p2=p*p, p3=p*p*p; 00335 if (p==0.0) { 00336 return ((1.0)/12.0*func(x-2.0*h,pa)- 00337 (4.0)/6.0*func(x-h,pa)- 00338 (-4.0)/6.0*func(x+h,pa)+ 00339 (-1.0)/12.0*func(x+2.0*h,pa)); 00340 } 00341 return ((2.0*p3-3.0*p2-p+1.0)/12.0*func(x-2.0*h,pa)- 00342 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*func(x-h,pa)+ 00343 (p3-2.5*p)*func(x,pa)- 00344 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*func(x+h,pa)+ 00345 (2.0*p3+3.0*p2-p-1.0)/12.0*func(x+2.0*h,pa)); 00346 } 00347 00348 00349 /// Three-point first derivative for arrays 00350 double calc_array3(double x, double x0, double dx, size_t nx, 00351 const vec_t &y, size_t ix) { 00352 double p; 00353 if (ix>0 && ix<nx-1) { 00354 p=x-(x0+ix*dx); 00355 return ((p-0.5)*y[ix-1]-2.0*p*y[ix]+(p+0.5)*y[ix+1]); 00356 } else if (ix==0) { 00357 p=x-(x0+dx); 00358 return ((p-0.5)*y[0]-2.0*p*y[1]+(p+0.5)*y[2]); 00359 } 00360 p=x-(x0+(nx-2)*dx); 00361 return ((p-0.5)*y[nx-3]-2.0*p*y[nx-2]+(p+0.5)*y[nx-1]); 00362 } 00363 00364 /// Four-point first derivative for arrays 00365 double calc_array4(double x, double x0, double dx, size_t nx, 00366 const vec_t &y, size_t ix) { 00367 double p, p2; 00368 if (ix>0 && ix<nx-2) { 00369 p=x-(x0+ix*dx); 00370 p2=p*p; 00371 return (-(3.0*p2-6.0*p+2.0)/6.0*y[ix-1]+ 00372 (1.5*p2-2.0*p-0.5)*y[ix]- 00373 (1.5*p2-p-1.0)*y[ix+1]+ 00374 (3.0*p2-1.0)/6.0*y[ix+2]); 00375 } else if (ix==0) { 00376 p=x-(x0+dx); 00377 p2=p*p; 00378 return (-(3.0*p2-6.0*p+2.0)/6.0*y[0]+ 00379 (1.5*p2-2.0*p-0.5)*y[1]- 00380 (1.5*p2-p-1.0)*y[2]+ 00381 (3.0*p2-1.0)/6.0*y[3]); 00382 } 00383 p=x-(x0+(nx-3)*dx); 00384 p2=p*p; 00385 return (-(3.0*p2-6.0*p+2.0)/6.0*y[nx-4]+ 00386 (1.5*p2-2.0*p-0.5)*y[nx-3]- 00387 (1.5*p2-p-1.0)*y[nx-2]+ 00388 (3.0*p2-1.0)/6.0*y[nx-1]); 00389 } 00390 00391 /// Five-point first derivative for arrays 00392 double calc_array5(double x, double x0, 00393 double dx, size_t nx, 00394 const vec_t &y, size_t ix) { 00395 double p, p2, p3; 00396 if (ix>1 && ix<nx-2) { 00397 p=x-(x0+ix*dx); 00398 p2=p*p, p3=p*p*p; 00399 return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[ix-2]- 00400 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[ix-1]+ 00401 (p3-2.5*p)*y[ix]- 00402 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[ix+1]+ 00403 (2.0*p3+3.0*p2-p-1.0)/12.0*y[ix+2]); 00404 } else if (ix<=1) { 00405 p=x-(x0+2*dx); 00406 p2=p*p, p3=p*p*p; 00407 return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[0]- 00408 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[1]+ 00409 (p3-2.5*p)*y[2]- 00410 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[3]+ 00411 (2.0*p3+3.0*p2-p-1.0)/12.0*y[4]); 00412 } 00413 p=x-(x0+(nx-3)*dx); 00414 p2=p*p, p3=p*p*p; 00415 return ((2.0*p3-3.0*p2-p+1.0)/12.0*y[nx-5]- 00416 (4.0*p3-3.0*p2-8.0*p+4.0)/6.0*y[nx-4]+ 00417 (p3-2.5*p)*y[nx-3]- 00418 (4.0*p3+3.0*p2-8.0*p-4.0)/6.0*y[nx-2]+ 00419 (2.0*p3+3.0*p2-p-1.0)/12.0*y[nx-1]); 00420 } 00421 00422 /// Three-point second derivative 00423 double calc2p3(double x, double p, void *pa, 00424 func_t &func) { 00425 return (func(x-h,pa)-2*func(x,pa)+func(x+h,pa)); 00426 } 00427 00428 /// Four-point second derivative 00429 double calc2p4(double x, double p, void *pa, 00430 func_t &func) { 00431 return ((1.0-2.0*p)*func(x-h,pa)-(1.0-6.0*p)*func(x,pa) 00432 -(1.0-6.0*p)*func(x+h,pa)+(1.0+2.0*p)*func(x+2.0*h,pa))/2.0; 00433 } 00434 00435 /// Five-point second derivative 00436 double calc2p5(double x, double p, void *pa, 00437 func_t &func) { 00438 return ((1.0-2.0*p)*(1.0-2.0*p)*func(x-2.0*h,pa) 00439 +(8.0*p-16.0*p*p)*func(x-h,pa) 00440 -(2.0-24.0*p*p)*func(x,pa) 00441 -(8.0*p+16.0*p*p)*func(x+h,pa) 00442 +(1.0+2.0*p)*(1.0+2.0*p)*func(x+2.0*h,pa))/4.0; 00443 } 00444 00445 /// Pointer to the first derivative function 00446 double (eqi_deriv::*cp)(double x, double p, void *pa, 00447 func_t &func); 00448 00449 /// Pointer to the first derivative for arrays function 00450 double (eqi_deriv::*cap)(double x, double x0, 00451 double dx, size_t nx, 00452 const vec_t &y, size_t ix); 00453 00454 /// Pointer to the second derivative function 00455 double (eqi_deriv::*c2p)(double x, double p, void *pa, 00456 func_t &func); 00457 00458 /// Pointer to the second derivative for arrays function 00459 double (eqi_deriv::*c2ap)(double x, double x0, 00460 double dx, size_t nx, 00461 const vec_t &y, size_t ix); 00462 00463 /// Pointer to the third derivative function 00464 double (eqi_deriv::*c3p)(double x, double h, double p, void *pa, 00465 func_t &func); 00466 00467 /// Pointer to the third derivative for arrays function 00468 double (eqi_deriv::*c3ap)(double x, double x0, 00469 double dx, size_t nx, 00470 const vec_t &y, size_t ix); 00471 00472 #endif 00473 00474 }; 00475 00476 #ifndef DOXYGENP 00477 } 00478 #endif 00479 00480 #endif
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