Garfield++ 5.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
Garfield::Numerics::QUADPACK Namespace Reference

Functions

void qagi (std::function< double(double)> f, double bound, const int inf, const double epsabs, const double epsrel, double &result, double &abserr, unsigned int &status)
 
void qk15i (std::function< double(double)> f, double bound, const int inf, const double a, const double b, double &result, double &abserr, double &resabs, double &resasc)
 
void qk15 (std::function< double(double)> f, const double a, const double b, double &result, double &abserr, double &resabs, double &resasc)
 15-point Gauss-Kronrod integration with finite integration range.
 

Detailed Description

Functions for performing numerical integration (quadrature). Reimplemented from QUADPACK.

R. Piessens, E. de Doncker-Kapenger, C. Ueberhuber, D. Kahaner, QUADPACK, a Subroutine Package for Automatic Integration, Springer, 1983

Function Documentation

◆ qagi()

void Garfield::Numerics::QUADPACK::qagi ( std::function< double(double)> f,
double bound,
const int inf,
const double epsabs,
const double epsrel,
double & result,
double & abserr,
unsigned int & status )

Estimates an integral over a semi-infinite or infinite interval. Calculates an approximation to an integral

\[ I = \int_{a}^{\infty} f\left(x\right) dx, \]

or

\[ I = \int_{-\infty}^{a} f\left(x\right) dx, \]

or

\[ I = \int_{-\infty}^{\infty} f\left(x\right) dx \]

hopefully satisfying

\[    \left| I - \mathrm{RESULT} \right| \leq
    \max(\varepsilon_{\mathrm{abs}}, \varepsilon_{\mathrm{rel}} \left|I\right|).
\]

Parameters
ffunction to be integrated.
boundvalue of the finite endpoint of the integration range (if any).
infindicates the type of integration range.
  • 1: ( bound, +Infinity)
  • -1: (-Infinity, bound)
  • 2: (-Infinity, +Infinity)
epsabsrequested absolute accuracy
epsrelrequested relative accuracy
resultthe estimated value of the integral
abserrestimated error
statuserror flag
  • 0: normal and reliable termination, requested accuracy has been achieved.
  • > 0: abnormal termination, estimates for result and error are less reliable.
    • 1: maximum number of subdivisions reached.
    • 2: occurance of roundoff error prevents the requested tolerance from being achieved. Error may be underestimated.
    • 3: extremely bad integrand behaviour at some points of the integration interval.
    • 4: algorithm does not converge, roundoff error is detected in the extrapolation table. It is assumed that the requested tolerance cannot be achieved and that the returned result is the best that can be obtained.
    • 5: integral is probably divergent, or slowly convergent.
    • 6: invalid input.

< Left end point.

< Right end point.

< Approximation to the integral over this interval.

< Error estimate.

Definition at line 145 of file Numerics.cc.

147 {
148
149 status = 0;
150 result = 0.;
151 abserr = 0.;
152
153 // Test on validity of parameters.
154 constexpr double eps = std::numeric_limits<double>::epsilon();
155 if (epsabs <= 0. && epsrel < std::max(50. * eps, 0.5e-28)) {
156 status = 6;
157 return;
158 }
159 // First approximation to the integral
160 if (inf == 2) bound = 0.;
161 double resabs0 = 0., resasc0 = 0.;
162 qk15i(f, bound, inf, 0., 1., result, abserr, resabs0, resasc0);
163
164 // Calculate the requested accuracy.
165 double tol = std::max(epsabs, epsrel * std::abs(result));
166 // Test on accuracy.
167 if (abserr <= 100. * eps * resabs0 && abserr > tol) {
168 // Roundoff error at the first attempt.
169 status = 2;
170 return;
171 }
172 // Test if the first approximation was good enough.
173 if ((abserr <= tol && abserr != resasc0) || abserr == 0.) return;
174
175 struct Interval {
176 double a; ///< Left end point.
177 double b; ///< Right end point.
178 double r; ///< Approximation to the integral over this interval.
179 double e; ///< Error estimate.
180 };
181 std::vector<Interval> intervals(1);
182 intervals[0].a = 0.;
183 intervals[0].b = 1.;
184 intervals[0].r = result;
185 intervals[0].e = abserr;
186 constexpr unsigned int nMaxIntervals = 500;
187 unsigned int nIntervals = 1;
188 // Interval to be bisected.
189 auto it = intervals.begin();
190 size_t nrmax = 0;
191
192 // Initialize the epsilon table.
193 std::array<double, 52> epstab;
194 epstab[0] = result;
195 // Count the number of elements currently in the epsilon table.
196 unsigned int nEps = 2;
197 // Keep track of the last three results.
198 std::array<double, 3> lastRes = {0., 0., 0.};
199 // Count the number of calls to the epsilon extrapolation function.
200 unsigned int nRes = 0;
201 // Flag denoting that we are attempting to perform extrapolation.
202 bool extrap = false;
203 // Flag indicating that extrapolation is no longer allowed.
204 bool noext = false;
205 unsigned int ktmin = 0;
206
207 // Initialize the sum of the integrals over the subintervals.
208 double area = result;
209 // Initialize the sum of the errors over the subintervals.
210 double errSum = abserr;
211 // Length of the smallest interval considered up now, multiplied by 1.5.
212 double small = 0.375;
213 // Sum of the errors over the intervals larger than the smallest interval
214 // considered up to now.
215 double errLarge = 0.;
216 double errTest = 0.;
217 // Error estimate of the interval with the largest error estimate.
218 double errMax = abserr;
219 abserr = std::numeric_limits<double>::max();
220
221 // Count roundoff errors.
222 std::array<unsigned int, 3> nRoundOff = {0, 0, 0};
223 bool roundOffErrors = false;
224 double correc = 0.;
225
226 // Set flag whether the integrand is positive.
227 const bool pos = (std::abs(result) >= (1. - 50. * eps) * resabs0);
228
229 // Main loop.
230 bool dosum = false;
231 for (nIntervals = 2; nIntervals <= nMaxIntervals; ++nIntervals) {
232 // Bisect the subinterval.
233 const double a1 = (*it).a;
234 const double b2 = (*it).b;
235 const double b1 = 0.5 * (a1 + b2);
236 const double a2 = b1;
237 // Save the error on the interval before doing the subdivision.
238 const double errLast = (*it).e;
239 double area1 = 0., err1 = 0., resabs1 = 0., resasc1 = 0.;
240 qk15i(f, bound, inf, a1, b1, area1, err1, resabs1, resasc1);
241 double area2 = 0., err2 = 0., resabs2 = 0., resasc2 = 0.;
242 qk15i(f, bound, inf, a2, b2, area2, err2, resabs2, resasc2);
243 // Improve previous approximations to integral and error
244 // and test for accuracy.
245 const double area12 = area1 + area2;
246 const double err12 = err1 + err2;
247 errSum += err12 - errMax;
248 area += area12 - (*it).r;
249 if (resasc1 != err1 && resasc2 != err2) {
250 if (std::abs((*it).r - area12) <= 1.e-5 * std::abs(area12) &&
251 err12 >= 0.99 * errMax) {
252 if (extrap) {
253 ++nRoundOff[1];
254 } else {
255 ++nRoundOff[0];
256 }
257 }
258 if (nIntervals > 10 && err12 > errMax) ++nRoundOff[2];
259 }
260 tol = std::max(epsabs, epsrel * std::abs(area));
261 // Test for roundoff error and eventually set error flag.
262 if (nRoundOff[0] + nRoundOff[1] >= 10 || nRoundOff[2] >= 20) status = 2;
263 if (nRoundOff[1] >= 5) roundOffErrors = true;
264 // Set error flag in the case that the number of subintervals equals limit.
265 if (nIntervals == nMaxIntervals) status = 1;
266 // Set error flag in the case of bad integrand behaviour
267 // at some points of the integration range.
268 constexpr double tol1 = 1. + 100. * eps;
269 constexpr double tol2 = 1000. * std::numeric_limits<double>::min();
270 if (std::max(std::abs(a1), std::abs(b2)) <= tol1 * (std::abs(a2) + tol2)) {
271 status = 4;
272 }
273 // Append the newly-created intervals to the list.
274 if (err2 > err1) {
275 (*it).a = a2;
276 (*it).r = area2;
277 (*it).e = err2;
278 Interval interval;
279 interval.a = a1;
280 interval.b = b1;
281 interval.r = area1;
282 interval.e = err1;
283 intervals.push_back(std::move(interval));
284 } else {
285 (*it).b = b1;
286 (*it).r = area1;
287 (*it).e = err1;
288 Interval interval;
289 interval.a = a2;
290 interval.b = b2;
291 interval.r = area2;
292 interval.e = err2;
293 intervals.push_back(std::move(interval));
294 }
295 // Sort the intervals in descending order by error estimate.
296 std::sort(intervals.begin(), intervals.end(),
297 [](const Interval& lhs, const Interval& rhs) {
298 return (lhs.e > rhs.e);
299 });
300 // Select the subinterval to be bisected next.
301 it = intervals.begin() + nrmax;
302 errMax = (*it).e;
303 if (errSum <= tol) {
304 dosum = true;
305 break;
306 }
307 if (status != 0) break;
308 if (nIntervals == 2) {
309 errLarge = errSum;
310 errTest = tol;
311 epstab[1] = area;
312 continue;
313 }
314 if (noext) continue;
315 errLarge -= errLast;
316 if (std::abs(b1 - a1) > small) errLarge += err12;
317 if (!extrap) {
318 // Test whether the interval to be bisected next is the smallest one.
319 if (std::abs((*it).b - (*it).a) > small) continue;
320 extrap = true;
321 nrmax = 1;
322 }
323 // The smallest interval has the largest error.
324 // Before bisecting decrease the sum of the errors over the
325 // larger intervals (errLarge) and perform extrapolation.
326 if (!roundOffErrors && errLarge > errTest) {
327 const size_t k0 = nrmax;
328 size_t k1 = nIntervals;
329 if (nIntervals > (2 + nMaxIntervals / 2)) {
330 k1 = nMaxIntervals + 3 - nIntervals;
331 }
332 bool found = false;
333 for (unsigned int k = k0; k < k1; ++k) {
334 it = intervals.begin() + nrmax;
335 errMax = (*it).e;
336 if (std::abs((*it).b - (*it).a) > small) {
337 found = true;
338 break;
339 }
340 ++nrmax;
341 }
342 if (found) continue;
343 }
344 // Perform extrapolation.
345 epstab[nEps] = area;
346 ++nEps;
347 double resExtr = 0., errExtr = 0.;
348 qelg(nEps, epstab, resExtr, errExtr, lastRes, nRes);
349 ++ktmin;
350 if (ktmin > 5 && abserr < 1.e-3 * errSum) status = 5;
351 if (errExtr < abserr) {
352 ktmin = 0;
353 abserr = errExtr;
354 result = resExtr;
355 correc = errLarge;
356 errTest = std::max(epsabs, epsrel * std::abs(resExtr));
357 if (abserr <= errTest) break;
358 }
359 // Prepare bisection of the smallest interval.
360 if (nEps == 1) noext = true;
361 if (status == 5) break;
362 it = intervals.begin();
363 errMax = (*it).e;
364 nrmax = 0;
365 extrap = false;
366 small *= 0.5;
367 errLarge = errSum;
368 }
369 // Set final result and error estimate.
370 if (abserr == std::numeric_limits<double>::max()) dosum = true;
371 if (!dosum) {
372 if ((status != 0 || roundOffErrors)) {
373 if (roundOffErrors) {
374 abserr += correc;
375 if (status == 0) status = 3;
376 }
377 if (result != 0. && area != 0.) {
378 if (abserr / std::abs(result) > errSum / std::abs(area)) dosum = true;
379 } else {
380 if (abserr > errSum) {
381 dosum = true;
382 } else if (area == 0.) {
383 if (status > 2) --status;
384 return;
385 }
386 }
387 }
388 // Test on divergence
389 if (!dosum) {
390 if (!pos && std::max(std::abs(result), std::abs(area)) <= resabs0 * 0.01) {
391 if (status > 2) --status;
392 return;
393 }
394 const double r = result / area;
395 if (r < 0.01 || r > 100. || errSum > std::abs(area)) {
396 status = 5;
397 return;
398 }
399 }
400 } else {
401 // Compute global integral sum.
402 result = 0.;
403 for (const auto& interval : intervals) result += interval.r;
404 abserr = errSum;
405 if (status > 2) --status;
406 }
407}
void qk15i(std::function< double(double)> f, double bound, const int inf, const double a, const double b, double &result, double &abserr, double &resabs, double &resasc)
Definition Numerics.cc:409

◆ qk15()

void Garfield::Numerics::QUADPACK::qk15 ( std::function< double(double)> f,
const double a,
const double b,
double & result,
double & abserr,
double & resabs,
double & resasc )

15-point Gauss-Kronrod integration with finite integration range.

Definition at line 500 of file Numerics.cc.

501 {
502
503 // Gauss quadrature weights and Kronron quadrature abscissae and weights
504 // as evaluated with 80 decimal digit arithmetic by L. W. Fullerton,
505 // Bell labs, Nov. 1981.
506
507 // Weights of the 7-point Gauss rule.
508 constexpr double wg[4] = {
509 0.129484966168869693270611432679082,
510 0.279705391489276667901467771423780,
511 0.381830050505118944950369775488975,
512 0.417959183673469387755102040816327};
513 // Abscissae of the 15-point Kronrod rule.
514 constexpr double xgk[8] = {
515 0.991455371120812639206854697526329,
516 0.949107912342758524526189684047851,
517 0.864864423359769072789712788640926,
518 0.741531185599394439863864773280788,
519 0.586087235467691130294144838258730,
520 0.405845151377397166906606412076961,
521 0.207784955007898467600689403773245,
522 0.};
523 // Weights of the 15-point Kronrod rule.
524 constexpr double wgk[8] = {
525 0.022935322010529224963732008058970,
526 0.063092092629978553290700663189204,
527 0.104790010322250183839876322541518,
528 0.140653259715525918745189590510238,
529 0.169004726639267902826583426598550,
530 0.190350578064785409913256402421014,
531 0.204432940075298892414161999234649,
532 0.209482141084727828012999174891714};
533
534 // Mid point of the interval.
535 const double xc = 0.5 * (a + b);
536 // Half-length of the interval.
537 const double h = 0.5 * (b - a);
538 const double dh = std::abs(h);
539 // Compute the 15-point Kronrod approximation to the integral,
540 // and estimate the absolute error.
541 const double fc = f(xc);
542 // Result of the 7-point Gauss formula.
543 double resg = fc * wg[3];
544 // Result of the 15-point Kronrod formula.
545 double resk = fc * wgk[7];
546 resabs = std::abs(resk);
547 std::array<double, 7> fv1, fv2;
548 for (unsigned int j = 0; j < 3; ++j) {
549 const unsigned int k = j * 2 + 1;
550 const double x = h * xgk[k];
551 double y1 = f(xc - x);
552 double y2 = f(xc + x);
553 fv1[k] = y1;
554 fv2[k] = y2;
555 const double fsum = y1 + y2;
556 resg += wg[j] * fsum;
557 resk += wgk[k] * fsum;
558 resabs += wgk[k] * (std::abs(y1) + std::abs(y2));
559 }
560 for (unsigned int j = 0; j < 4; ++j) {
561 const unsigned int k = j * 2;
562 const double x = h * xgk[k];
563 const double y1 = f(xc - x);
564 const double y2 = f(xc + x);
565 fv1[k] = y1;
566 fv2[k] = y2;
567 const double fsum = y1 + y2;
568 resk += wgk[k] * fsum;
569 resabs += wgk[k] * (std::abs(y1) + std::abs(y2));
570 }
571 // Approximation to the mean value of f over (a,b), i.e. to i/(b-a).
572 const double reskh = resk * 0.5;
573 resasc = wgk[7] * std::abs(fc - reskh);
574 for (unsigned int j = 0; j < 7; ++j) {
575 resasc += wgk[j] * (std::abs(fv1[j] - reskh) + std::abs(fv2[j] - reskh));
576 }
577 result = resk * h;
578 resabs *= dh;
579 resasc *= dh;
580 abserr = std::abs((resk - resg) * h);
581 if (resasc != 0. && abserr != 0.) {
582 abserr = resasc * std::min(1., pow(200. * abserr / resasc, 1.5));
583 }
584 constexpr double eps = 50. * std::numeric_limits<double>::epsilon();
585 if (resabs > std::numeric_limits<double>::min() / eps) {
586 abserr = std::max(eps * resabs, abserr);
587 }
588}
DoubleAc pow(const DoubleAc &f, double p)
Definition DoubleAc.cpp:337

◆ qk15i()

void Garfield::Numerics::QUADPACK::qk15i ( std::function< double(double)> f,
double bound,
const int inf,
const double a,
const double b,
double & result,
double & abserr,
double & resabs,
double & resasc )

15-point Gauss-Kronrod integration with (semi-)infinite integration range.

Parameters
ffunction to be integrated.
boundfinite bound of original integration range (0 if inf = 2).
infindicates the type of integration range.
alower limit for integration over subrange of (0, 1).
bupper limit for integration over subrange of (0, 1).
resultapproximation to the integral.
abserrestimate of the modulus of the absolute error.
resabsapproximation to the integral over $\left|f\right|$.
resascapproximation to the integral of $\left|f - I / (b-a)\right|$ over $(a,b)$.

Definition at line 409 of file Numerics.cc.

411 {
412
413 // The abscissae and weights are supplied for the interval (-1, 1).
414 // Because of symmetry only the positive abscissae and
415 // their corresponding weights are given.
416
417 // Weights of the 7-point Gauss rule.
418 constexpr double wg[8] = {0., 0.129484966168869693270611432679082,
419 0., 0.279705391489276667901467771423780,
420 0., 0.381830050505118944950369775488975,
421 0., 0.417959183673469387755102040816327};
422 // Abscissae of the 15-point Kronrod rule.
423 constexpr double xgk[8] = {
424 0.991455371120812639206854697526329,
425 0.949107912342758524526189684047851,
426 0.864864423359769072789712788640926,
427 0.741531185599394439863864773280788,
428 0.586087235467691130294144838258730,
429 0.405845151377397166906606412076961,
430 0.207784955007898467600689403773245,
431 0.};
432 // Weights of the 15-point Kronrod rule.
433 constexpr double wgk[8] = {
434 0.022935322010529224963732008058970,
435 0.063092092629978553290700663189204,
436 0.104790010322250183839876322541518,
437 0.140653259715525918745189590510238,
438 0.169004726639267902826583426598550,
439 0.190350578064785409913256402421014,
440 0.204432940075298892414161999234649,
441 0.209482141084727828012999174891714};
442
443 const int dinf = std::min(1, inf);
444
445 // Mid point of the interval.
446 const double xc = 0.5 * (a + b);
447 // Half-length of the interval.
448 const double h = 0.5 * (b - a);
449 // Transformed abscissa.
450 const double tc = bound + dinf * (1. - xc) / xc;
451 double fc = f(tc);
452 if (inf == 2) fc += f(-tc);
453 fc = (fc / xc) / xc;
454 // Result of the 7-point Gauss formula.
455 double resg = wg[7] * fc;
456 // Result of the 15-point Kronrod formula.
457 double resk = wgk[7] * fc;
458 resabs = std::abs(resk);
459 std::array<double, 7> fv1, fv2;
460 for (unsigned int j = 0; j < 7; ++j) {
461 const double x = h * xgk[j];
462 const double x1 = xc - x;
463 const double x2 = xc + x;
464 const double t1 = bound + dinf * (1. - x1) / x1;
465 const double t2 = bound + dinf * (1. - x2) / x2;
466 double y1 = f(t1);
467 double y2 = f(t2);
468 if (inf == 2) {
469 y1 += f(-t1);
470 y2 += f(-t2);
471 }
472 y1 = (y1 / x1) / x1;
473 y2 = (y2 / x2) / x2;
474 fv1[j] = y1;
475 fv2[j] = y2;
476 const double fsum = y1 + y2;
477 resg += wg[j] * fsum;
478 resk += wgk[j] * fsum;
479 resabs += wgk[j] * (std::abs(y1) + std::abs(y2));
480 }
481 // Approximation to the mean value of the transformed integrand over (a,b).
482 const double reskh = resk * 0.5;
483 resasc = wgk[7] * std::abs(fc - reskh);
484 for (unsigned int j = 0; j < 7; ++j) {
485 resasc += wgk[j] * (std::abs(fv1[j] - reskh) + std::abs(fv2[j] - reskh));
486 }
487 result = resk * h;
488 resasc *= h;
489 resabs *= h;
490 abserr = std::abs((resk - resg) * h);
491 if (resasc != 0. && abserr != 0.) {
492 abserr = resasc * std::min(1., pow(200. * abserr / resasc, 1.5));
493 }
494 constexpr double eps = 50. * std::numeric_limits<double>::epsilon();
495 if (resabs > std::numeric_limits<double>::min() / eps) {
496 abserr = std::max(eps * resabs, abserr);
497 }
498}

Referenced by qagi().