Garfield++ 4.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.

< Left end point.

< Right end point.

< Approximation to the integral over this interval.

< Error estimate.

Definition at line 144 of file Numerics.cc.

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

◆ 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 499 of file Numerics.cc.

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

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

Referenced by qagi().