![]() |
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_CERN_ADAPT_H 00024 #define O2SCL_CERN_ADAPT_H 00025 00026 #include <o2scl/inte.h> 00027 #include <o2scl/cern_gauss56.h> 00028 #include <o2scl/string_conv.h> 00029 00030 #ifndef DOXYGENP 00031 namespace o2scl { 00032 #endif 00033 00034 /** \brief Adaptive integration (CERNLIB) 00035 00036 Uses a base integration object (default is \ref cern_gauss56) to 00037 perform adaptive integration by automatically subdividing the 00038 integration interval. At each step, the interval with the 00039 largest absolute uncertainty is divided in half. The routine 00040 succeeds if the absolute tolerance is less than \ref tol_abs or 00041 if the relative tolerance is less than \ref tol_rel, i.e. 00042 \f[ 00043 \mathrm{err}\leq\mathrm{tol\_abs}~\mathrm{or}~ 00044 \mathrm{err}\leq\mathrm{tol\_rel}\cdot|I| 00045 \f] 00046 where \f$ I \f$ is the current estimate for the integral and \f$ 00047 \mathrm{err} \f$ is the current estimate for the uncertainty. If 00048 the number of subdivisions exceeds the template parameter \c 00049 nsub, the error handler is called, since the integration may not 00050 have been successful. The number of subdivisions used in the 00051 last integration can be obtained from get_nsubdivisions(). 00052 00053 The template parameter \c nsub, is the maximum number of 00054 subdivisions. It is automatically set to 100 in the original 00055 CERNLIB routine, and defaults to 100 here. The default base 00056 integration object is of type \ref cern_gauss56. This is the 00057 CERNLIB default, but can be modified by calling set_inte(). 00058 00059 This class is based on the CERNLIB routines RADAPT and 00060 DADAPT which are documented at 00061 http://wwwasdoc.web.cern.ch/wwwasdoc/shortwrupsdir/d102/top.html 00062 00063 \future 00064 - Allow user to set the initial subdivisions? 00065 - It might be interesting to directly compare the performance 00066 of this class to \ref gsl_inte_qag . 00067 - There is a fixme entry in the code which could be resolved. 00068 - Output the point where most subdividing was required? 00069 */ 00070 template<class func_t=funct, size_t nsub=100> 00071 class cern_adapt : public inte<func_t> { 00072 00073 #ifndef DOXYGEN_INTERNAL 00074 00075 protected: 00076 00077 /// Lower end of subdivision 00078 double xlo[nsub]; 00079 00080 /// High end of subdivision 00081 double xhi[nsub]; 00082 00083 /// Value of integral for subdivision 00084 double tval[nsub]; 00085 00086 /// Squared error for subdivision 00087 double ters[nsub]; 00088 00089 /// Previous number of subdivisions 00090 int prev_subdiv; 00091 00092 /// The base integration object 00093 inte<func_t> *it; 00094 00095 #endif 00096 00097 public: 00098 00099 cern_adapt() { 00100 nsubdiv=1; 00101 prev_subdiv=0; 00102 00103 it=&def_inte; 00104 } 00105 00106 /// \name Basic usage 00107 //@{ 00108 /** \brief Integrate function \c func from \c a to \c b 00109 giving result \c res and error \c err 00110 */ 00111 virtual int integ_err(func_t &func, double a, double b, 00112 double &res, double &err) { 00113 00114 double tvals=0.0, terss, xlob, xhib, yhib=0.0, te, root=0.0; 00115 int i, nsubdivd; 00116 00117 this->last_conv=0; 00118 00119 if (nsubdiv==0) { 00120 if (prev_subdiv==0) { 00121 // If the previous binning was requested, but 00122 // there is no previous binning stored, then 00123 // just shift to automatic binning 00124 nsubdivd=1; 00125 } else { 00126 tvals=0.0; 00127 terss=0.0; 00128 for(i=0;i<prev_subdiv;i++) { 00129 it->integ_err(func,xlo[i],xhi[i],tval[i],te); 00130 ters[i]=te*te; 00131 tvals+=tval[i]; 00132 terss+=ters[i]; 00133 } 00134 err=sqrt(2.0*terss); 00135 res=tvals; 00136 return 0; 00137 } 00138 } 00139 00140 // Ensure we're not asking for too many divisions 00141 if (nsub<nsubdiv) { 00142 nsubdivd=nsub; 00143 } else { 00144 nsubdivd=nsubdiv; 00145 } 00146 00147 // Compute the initial set of intervals and integral values 00148 xhib=a; 00149 double bin=(b-a)/((double)nsubdivd); 00150 for(i=0;i<nsubdivd;i++) { 00151 xlo[i]=xhib; 00152 xlob=xlo[i]; 00153 xhi[i]=xhib+bin; 00154 if (i==nsubdivd-1) xhi[i]=b; 00155 xhib=xhi[i]; 00156 it->integ_err(func,xlob,xhib,tval[i],te); 00157 ters[i]=te*te; 00158 } 00159 prev_subdiv=nsubdivd; 00160 00161 for(size_t iter=1;iter<=nsub;iter++) { 00162 00163 // Compute the total value of the integrand 00164 // and the squared uncertainty 00165 tvals=tval[0]; 00166 terss=ters[0]; 00167 for(i=1;i<prev_subdiv;i++) { 00168 tvals+=tval[i]; 00169 terss+=ters[i]; 00170 } 00171 00172 // Output iteration information 00173 if (this->verbose>0) { 00174 std::cout << "cern_adapt Iter: " << iter; 00175 std::cout.setf(std::ios::showpos); 00176 std::cout << " Res: " << tvals; 00177 std::cout.unsetf(std::ios::showpos); 00178 std::cout << " Err: " << sqrt(2.0*terss); 00179 if (this->tol_abs>this->tol_rel*fabs(tvals)) { 00180 std::cout << " Tol: " << this->tol_abs << std::endl; 00181 } else { 00182 std::cout << " Tol: " << this->tol_rel*fabs(tvals) << std::endl; 00183 } 00184 if (this->verbose>1) { 00185 char ch; 00186 std::cout << "Press a key and type enter to continue. " ; 00187 std::cin >> ch; 00188 } 00189 } 00190 00191 // See if we're finished 00192 root=sqrt(2.0*terss); 00193 if (root<=this->tol_abs || root<=this->tol_rel*fabs(tvals)) { 00194 res=tvals; 00195 err=root; 00196 this->last_iter=iter; 00197 return 0; 00198 } 00199 00200 // Test if we've run out of intervals 00201 if (prev_subdiv==nsub) { 00202 res=tvals; 00203 err=root; 00204 this->last_iter=iter; 00205 std::string s="Reached maximum number of "+itos(nsub)+ 00206 "subdivisions in cern_adapt::integ_err()."; 00207 this->last_conv=gsl_etable; 00208 O2SCL_CONV_RET(s.c_str(),gsl_etable,this->err_nonconv); 00209 } 00210 00211 // Find the subdivision with the largest error 00212 double bige=ters[0]; 00213 int ibig=0; 00214 for(i=1;i<prev_subdiv;i++) { 00215 if (ters[i]>bige) { 00216 bige=ters[i]; 00217 ibig=i; 00218 } 00219 } 00220 00221 // Subdivide that subdivision further 00222 xhi[prev_subdiv]=xhi[ibig]; 00223 double xnew=(xlo[ibig]+xhi[ibig])/2.0; 00224 xhi[ibig]=xnew; 00225 xlo[prev_subdiv]=xnew; 00226 it->integ_err(func,xlo[ibig],xhi[ibig],tval[ibig],te); 00227 ters[ibig]=te*te; 00228 it->integ_err(func,xlo[prev_subdiv], 00229 xhi[prev_subdiv],tval[prev_subdiv],te); 00230 ters[prev_subdiv]=te*te; 00231 prev_subdiv++; 00232 00233 } 00234 00235 // FIXME: Should we set an error here, or does this 00236 // only happen if we happen to need exactly nsub 00237 // intervals? 00238 res=tvals; 00239 err=root; 00240 return 0; 00241 } 00242 //@} 00243 00244 /// \name Integration object 00245 //@{ 00246 /// Set the base integration object to use 00247 int set_inte(inte<func_t> &i) { 00248 it=&i; 00249 return 0; 00250 } 00251 00252 /// Default integration object 00253 cern_gauss56<func_t> def_inte; 00254 //@} 00255 00256 /// \name Subdivisions 00257 //@{ 00258 /** \brief Number of subdivisions 00259 00260 The options are 00261 - 0: Use previous binning and do not subdivide further \n 00262 - 1: Automatic - adapt until tolerance is attained (default) \n 00263 - n: (n>1) split first in n equal subdivisions, then adapt 00264 until tolerance is obtained. 00265 */ 00266 size_t nsubdiv; 00267 00268 /// Return the number of subdivisions used in the last integration 00269 size_t get_nsubdivisions() { 00270 return prev_subdiv; 00271 } 00272 00273 /// Return the ith subdivision 00274 int get_ith_subdivision(size_t i, double &xlow, double &xhigh, 00275 double &value, double &errsq) { 00276 if (i<prev_subdiv) { 00277 xlow=xlo[i]; 00278 xhigh=xhi[i]; 00279 value=tval[i]; 00280 errsq=ters[i]; 00281 } 00282 return 0; 00283 } 00284 00285 /// Return all of the subdivisions 00286 template<class vec_t> 00287 int get_subdivisions(vec_t &xlow, vec_t &xhigh, vec_t &value, 00288 vec_t &errsq) { 00289 00290 for(int i=0;i<prev_subdiv;i++) { 00291 xlow[i]=xlo[i]; 00292 xhigh[i]=xhi[i]; 00293 value[i]=tval[i]; 00294 errsq[i]=ters[i]; 00295 } 00296 return 0; 00297 } 00298 //@} 00299 00300 }; 00301 00302 #ifndef DOXYGENP 00303 } 00304 #endif 00305 00306 #endif
Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).