1 // This code has been mechanically translated from the original FORTRAN 2 // code at http://netlib.org/quadpack. 3 4 /** Authors: Lars Tandle Kyllingstad 5 Copyright: Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved. 6 License: Boost License 1.0 7 */ 8 module scid.ports.quadpack.qawo; 9 10 11 import std.conv; 12 import scid.ports.quadpack.qawoe; 13 14 version(unittest) 15 { 16 import std.math; 17 import scid.core.testing; 18 } 19 20 21 22 23 /// 24 void qawo(Real, Func)(Func f, Real a, Real b, Real omega, int integr, 25 Real epsabs, Real epsrel, out Real result, out Real abserr, 26 out int neval, out int ier, int leniw, int maxp1, int lenw, 27 out int last, int* iwork, Real* work) 28 { 29 //***begin prologue qawo 30 //***date written 800101 (yymmdd) 31 //***revision date 830518 (yymmdd) 32 //***category no. h2a2a1 33 //***keywords automatic integrator, special-purpose, 34 // integrand with oscillatory cos or sin factor, 35 // clenshaw-curtis method, (end point) singularities, 36 // extrapolation, globally adaptive 37 //***author piessens,robert,appl. math. & progr. div. - k.u.leuven 38 // de doncker,elise,appl. math. & progr. div. - k.u.leuven 39 //***purpose the routine calculates an approximation result to a given 40 // definite integral 41 // i = integral of f(x)*w(x) over (a,b) 42 // where w(x) = cos(omega*x) or w(x) = sin(omega*x), 43 // hopefully satisfying following claim for accuracy 44 // abs(i-result).le.max(epsabs,epsrel*abs(i)). 45 //***description 46 // 47 // computation of oscillatory integrals 48 // standard fortran subroutine 49 // real version 50 // 51 // parameters 52 // on entry 53 // f - real 54 // function subprogram defining the function 55 // f(x). the actual name for f needs to be 56 // declared e x t e r n a l in the driver program. 57 // 58 // a - real 59 // lower limit of integration 60 // 61 // b - real 62 // upper limit of integration 63 // 64 // omega - real 65 // parameter in the integrand weight function 66 // 67 // integr - integer 68 // indicates which of the weight functions is used 69 // integr = 1 w(x) = cos(omega*x) 70 // integr = 2 w(x) = sin(omega*x) 71 // if integr.ne.1.and.integr.ne.2, the routine will 72 // end with ier = 6. 73 // 74 // epsabs - real 75 // absolute accuracy requested 76 // epsrel - real 77 // relative accuracy requested 78 // if epsabs.le.0 and 79 // epsrel.lt.max(50*rel.mach.acc.,0.5d-28), 80 // the routine will end with ier = 6. 81 // 82 // on return 83 // result - real 84 // approximation to the integral 85 // 86 // abserr - real 87 // estimate of the modulus of the absolute error, 88 // which should equal or exceed abs(i-result) 89 // 90 // neval - integer 91 // number of integrand evaluations 92 // 93 // ier - integer 94 // ier = 0 normal and reliable termination of the 95 // routine. it is assumed that the requested 96 // accuracy has been achieved. 97 // - ier.gt.0 abnormal termination of the routine. 98 // the estimates for integral and error are 99 // less reliable. it is assumed that the 100 // requested accuracy has not been achieved. 101 // error messages 102 // ier = 1 maximum number of subdivisions allowed 103 // (= leniw/2) has been achieved. one can 104 // allow more subdivisions by increasing the 105 // value of leniw (and taking the according 106 // dimension adjustments into account). 107 // however, if this yields no improvement it 108 // is advised to analyze the integrand in 109 // order to determine the integration 110 // difficulties. if the position of a local 111 // difficulty can be determined (e.g. 112 // singularity, discontinuity within the 113 // interval) one will probably gain from 114 // splitting up the interval at this point 115 // and calling the integrator on the 116 // subranges. if possible, an appropriate 117 // special-purpose integrator should be used 118 // which is designed for handling the type of 119 // difficulty involved. 120 // = 2 the occurrence of roundoff error is 121 // detected, which prevents the requested 122 // tolerance from being achieved. 123 // the error may be under-estimated. 124 // = 3 extremely bad integrand behaviour occurs 125 // at some interior points of the 126 // integration interval. 127 // = 4 the algorithm does not converge. 128 // roundoff error is detected in the 129 // extrapolation table. it is presumed that 130 // the requested tolerance cannot be achieved 131 // due to roundoff in the extrapolation 132 // table, and that the returned result is 133 // the best which can be obtained. 134 // = 5 the integral is probably divergent, or 135 // slowly convergent. it must be noted that 136 // divergence can occur with any other value 137 // of ier. 138 // = 6 the input is invalid, because 139 // (epsabs.le.0 and 140 // epsrel.lt.max(50*rel.mach.acc.,0.5d-28)) 141 // or (integr.ne.1 and integr.ne.2), 142 // or leniw.lt.2 or maxp1.lt.1 or 143 // lenw.lt.leniw*2+maxp1*25. 144 // result, abserr, neval, last are set to 145 // zero. except when leniw, maxp1 or lenw are 146 // invalid, work(limit*2+1), work(limit*3+1), 147 // iwork(1), iwork(limit+1) are set to zero, 148 // work(1) is set to a and work(limit+1) to 149 // b. 150 // 151 // dimensioning parameters 152 // leniw - integer 153 // dimensioning parameter for iwork. 154 // leniw/2 equals the maximum number of subintervals 155 // allowed in the partition of the given integration 156 // interval (a,b), leniw.ge.2. 157 // if leniw.lt.2, the routine will end with ier = 6. 158 // 159 // maxp1 - integer 160 // gives an upper bound on the number of chebyshev 161 // moments which can be stored, i.e. for the 162 // intervals of lengths abs(b-a)*2**(-l), 163 // l=0,1, ..., maxp1-2, maxp1.ge.1 164 // if maxp1.lt.1, the routine will end with ier = 6. 165 // 166 // lenw - integer 167 // dimensioning parameter for work 168 // lenw must be at least leniw*2+maxp1*25. 169 // if lenw.lt.(leniw*2+maxp1*25), the routine will 170 // end with ier = 6. 171 // 172 // last - integer 173 // on return, last equals the number of subintervals 174 // produced in the subdivision process, which 175 // determines the number of significant elements 176 // actually in the work arrays. 177 // 178 // work arrays 179 // iwork - integer 180 // vector of dimension at least leniw 181 // on return, the first k elements of which contain 182 // pointers to the error estimates over the 183 // subintervals, such that work(limit*3+iwork(1)), .. 184 // work(limit*3+iwork(k)) form a decreasing 185 // sequence, with limit = lenw/2 , and k = last 186 // if last.le.(limit/2+2), and k = limit+1-last 187 // otherwise. 188 // furthermore, iwork(limit+1), ..., iwork(limit+ 189 // last) indicate the subdivision levels of the 190 // subintervals, such that iwork(limit+i) = l means 191 // that the subinterval numbered i is of length 192 // abs(b-a)*2**(1-l). 193 // 194 // work - real 195 // vector of dimension at least lenw 196 // on return 197 // work(1), ..., work(last) contain the left 198 // end points of the subintervals in the 199 // partition of (a,b), 200 // work(limit+1), ..., work(limit+last) contain 201 // the right end points, 202 // work(limit*2+1), ..., work(limit*2+last) contain 203 // the integral approximations over the 204 // subintervals, 205 // work(limit*3+1), ..., work(limit*3+last) 206 // contain the error estimates. 207 // work(limit*4+1), ..., work(limit*4+maxp1*25) 208 // provide space for storing the chebyshev moments. 209 // note that limit = lenw/2. 210 // 211 //***references (none) 212 //***routines called qawoe,xerror 213 //***end prologue qawo 214 // 215 int lvl=1,l1=1,l2=1,l3=1,l4=1,limit=1,momcom=1; 216 // 217 //dimension iwork(leniw),work(lenw) 218 // 219 // check validity of leniw, maxp1 and lenw. 220 // 221 //***first executable statement qawo 222 ier = 6; 223 neval = 0; 224 last = 0; 225 result = 0.0; 226 abserr = 0.0; 227 if(leniw < 2 || maxp1 < 1 || lenw < (leniw*2+maxp1*25)) 228 goto l10; 229 // 230 // prepare call for qawoe 231 // 232 limit = leniw/2; 233 l1 = limit; 234 l2 = limit+l1; 235 l3 = limit+l2; 236 l4 = limit+l3; 237 qawoe!(Real,Func)(f,a,b,omega,integr,epsabs,epsrel,limit,1,maxp1,result, 238 abserr,neval,ier,last,work,work+l1,work+l2,work+l3, 239 iwork,iwork+l1,momcom,work+l4); 240 // 241 // call error handler if necessary 242 // 243 lvl = 0; 244 l10: if(ier == 6) lvl = 1; 245 if(ier != 0) 246 throw new Exception("abnormal return from qawo: "~to!string(ier)); 247 return; 248 } 249 250 251 unittest 252 { 253 alias qawo!(float, float delegate(float)) fqawo; 254 alias qawo!(double, double delegate(double)) dqawo; 255 alias qawo!(double, double function(double)) dfqawo; 256 alias qawo!(real, real delegate(real)) rqawo; 257 } 258 259 260 unittest 261 { 262 // From the QUADPACK book 263 double dlog(double x) { return x > 0.0 ? log(x) : 0.0; } 264 265 double a = 0.0, b = 1.0, omega = 10*PI; 266 int integr = 2; 267 double epsabs = 0.0, epsrel = 1e-8; 268 double result, abserr; 269 int neval, ier, last; 270 const int leniw = 1000, maxp1=21, lenw=leniw*2+maxp1*25; 271 272 int[leniw] iwork; 273 double[lenw] work; 274 275 qawo(&dlog, a, b, omega, integr, epsabs, epsrel, result, abserr, 276 neval, ier, leniw, maxp1, lenw, last, iwork.ptr, work.ptr); 277 278 double ans = -0.128136848399167; 279 assert (isAccurate!double(result, abserr, ans, epsrel, epsabs)); 280 } 281 282 283 unittest 284 { 285 // This is integral 14 in the QUADPACK book. 286 real alpha = 3.0; 287 real f(real x) { return x <= 0.0 ? 0.0 : exp(-(2.0L^^(-alpha))*x)/sqrt(x); } 288 289 real a = 0.0; 290 real b = 20*(2.0L^^alpha); 291 real omega = 1.0; 292 int integr = 1; // cos(x) 293 real epsabs = 0.0; 294 real epsrel = 1e-8; 295 296 real result, abserr; 297 int neval, ier, last; 298 299 enum 300 { 301 leniw = 1000, 302 maxp1 = 21, 303 lenw = leniw*2 + maxp1*25 304 } 305 306 int[leniw] iwork; 307 real[lenw] work; 308 309 qawo(&f, a, b, omega, integr, epsabs, epsrel, result, abserr, 310 neval, ier, leniw, maxp1, lenw, last, iwork.ptr, work.ptr); 311 312 real eps = 1e-20; 313 real ans = (1 + eps)*sqrt(PI) * ((1+(4.0L^^(-alpha)))^^(-0.25L)) 314 * cos(atan(2.0L^^alpha)/2); 315 assert (isAccurate(result, abserr, ans, epsrel, epsabs)); 316 } 317