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