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