GammaLib  2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GIntegral.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GIntegral.cpp - Integration class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2020 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GIntegral.cpp
23  * @brief Integration class implementation
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #include <cmath> // std::abs()
29 #include <vector>
30 #include <algorithm> // std::sort
31 #include "GIntegral.hpp"
32 #include "GException.hpp"
33 #include "GTools.hpp"
34 #include "GFunction.hpp"
35 
36 /* __ Method name definitions ____________________________________________ */
37 #define G_ROMBERG "GIntegral::romberg(double&, double&, int&)"
38 #define G_TRAPZD "GIntegral::trapzd(double&, double&, int&, double)"
39 #define G_POLINT "GIntegral::polint(double*, double*, int, double, double*)"
40 
41 /* __ Macros _____________________________________________________________ */
42 
43 /* __ Coding definitions _________________________________________________ */
44 
45 /* __ Debug definitions __________________________________________________ */
46 
47 /* __ Constants __________________________________________________________ */
48 namespace gammalib {
49 
50  // Gauss-Kronrod abscissae, common to the 10-, 21-, 43- and 87-point rule
51  const double gkx1[5] = {
52  0.973906528517171720077964012084452,
53  0.865063366688984510732096688423493,
54  0.679409568299024406234327365114874,
55  0.433395394129247190799265943165784,
56  0.148874338981631210884826001129720
57  };
58 
59  // Gauss-Kronrod weights of the 10-point rule
60  const double gkw10[5] = {
61  0.066671344308688137593568809893332,
62  0.149451349150580593145776339657697,
63  0.219086362515982043995534934228163,
64  0.269266719309996355091226921569469,
65  0.295524224714752870173892994651338
66  };
67 
68  // Gauss-Kronrod abscissae, common to the 21-, 43- and 87-point rule
69  const double gkx2[5] = {
70  0.995657163025808080735527280689003,
71  0.930157491355708226001207180059508,
72  0.780817726586416897063717578345042,
73  0.562757134668604683339000099272694,
74  0.294392862701460198131126603103866
75  };
76 
77  // Gauss-Kronrod weights of the 21-point rule for abscissae gkx1
78  const double gkw21a[5] = {
79  0.032558162307964727478818972459390,
80  0.075039674810919952767043140916190,
81  0.109387158802297641899210590325805,
82  0.134709217311473325928054001771707,
83  0.147739104901338491374841515972068
84  };
85 
86  // Gauss-Kronrod weights of the 21-point rule for abscissae gkx2
87  const double gkw21b[6] = {
88  0.011694638867371874278064396062192,
89  0.054755896574351996031381300244580,
90  0.093125454583697605535065465083366,
91  0.123491976262065851077958109831074,
92  0.142775938577060080797094273138717,
93  0.149445554002916905664936468389821
94  };
95 
96  // Gauss-Kronrod abscissae, common to the 43- and 87-point rule
97  const double gkx3[11] = {
98  0.999333360901932081394099323919911,
99  0.987433402908088869795961478381209,
100  0.954807934814266299257919200290473,
101  0.900148695748328293625099494069092,
102  0.825198314983114150847066732588520,
103  0.732148388989304982612354848755461,
104  0.622847970537725238641159120344323,
105  0.499479574071056499952214885499755,
106  0.364901661346580768043989548502644,
107  0.222254919776601296498260928066212,
108  0.074650617461383322043914435796506
109  };
110 
111  // Gauss-Kronrod weights of the 43-point rule for abscissae gkx1, gkx3
112  const double gkw43a[10] = {
113  0.016296734289666564924281974617663,
114  0.037522876120869501461613795898115,
115  0.054694902058255442147212685465005,
116  0.067355414609478086075553166302174,
117  0.073870199632393953432140695251367,
118  0.005768556059769796184184327908655,
119  0.027371890593248842081276069289151,
120  0.046560826910428830743339154433824,
121  0.061744995201442564496240336030883,
122  0.071387267268693397768559114425516
123  };
124 
125  // Gauss-Kronrod weights of the 43-point formula for abscissae gkx3
126  const double gkw43b[12] = {
127  0.001844477640212414100389106552965,
128  0.010798689585891651740465406741293,
129  0.021895363867795428102523123075149,
130  0.032597463975345689443882222526137,
131  0.042163137935191811847627924327955,
132  0.050741939600184577780189020092084,
133  0.058379395542619248375475369330206,
134  0.064746404951445885544689259517511,
135  0.069566197912356484528633315038405,
136  0.072824441471833208150939535192842,
137  0.074507751014175118273571813842889,
138  0.074722147517403005594425168280423
139  };
140 
141  // Gauss-Kronrod abscissae, of the 87-point rule
142  const double gkx4[22] = {
143  0.999902977262729234490529830591582,
144  0.997989895986678745427496322365960,
145  0.992175497860687222808523352251425,
146  0.981358163572712773571916941623894,
147  0.965057623858384619128284110607926,
148  0.943167613133670596816416634507426,
149  0.915806414685507209591826430720050,
150  0.883221657771316501372117548744163,
151  0.845710748462415666605902011504855,
152  0.803557658035230982788739474980964,
153  0.757005730685495558328942793432020,
154  0.706273209787321819824094274740840,
155  0.651589466501177922534422205016736,
156  0.593223374057961088875273770349144,
157  0.531493605970831932285268948562671,
158  0.466763623042022844871966781659270,
159  0.399424847859218804732101665817923,
160  0.329874877106188288265053371824597,
161  0.258503559202161551802280975429025,
162  0.185695396568346652015917141167606,
163  0.111842213179907468172398359241362,
164  0.037352123394619870814998165437704
165  };
166 
167  // Gauss-Kronrod weights of the 87-point rule for abscissae gkx1, gkx2, gkx3
168  const double gkw87a[21] = {
169  0.008148377384149172900002878448190,
170  0.018761438201562822243935059003794,
171  0.027347451050052286161582829741283,
172  0.033677707311637930046581056957588,
173  0.036935099820427907614589586742499,
174  0.002884872430211530501334156248695,
175  0.013685946022712701888950035273128,
176  0.023280413502888311123409291030404,
177  0.030872497611713358675466394126442,
178  0.035693633639418770719351355457044,
179  0.000915283345202241360843392549948,
180  0.005399280219300471367738743391053,
181  0.010947679601118931134327826856808,
182  0.016298731696787335262665703223280,
183  0.021081568889203835112433060188190,
184  0.025370969769253827243467999831710,
185  0.029189697756475752501446154084920,
186  0.032373202467202789685788194889595,
187  0.034783098950365142750781997949596,
188  0.036412220731351787562801163687577,
189  0.037253875503047708539592001191226
190  };
191 
192  // Gauss-Kronrod weights of the 87-point formula for abscissae gkx4
193  const double gkw87b[23] = {
194  0.000274145563762072350016527092881,
195  0.001807124155057942948341311753254,
196  0.004096869282759164864458070683480,
197  0.006758290051847378699816577897424,
198  0.009549957672201646536053581325377,
199  0.012329447652244853694626639963780,
200  0.015010447346388952376697286041943,
201  0.017548967986243191099665352925900,
202  0.019938037786440888202278192730714,
203  0.022194935961012286796332102959499,
204  0.024339147126000805470360647041454,
205  0.026374505414839207241503786552615,
206  0.028286910788771200659968002987960,
207  0.030052581128092695322521110347341,
208  0.031646751371439929404586051078883,
209  0.033050413419978503290785944862689,
210  0.034255099704226061787082821046821,
211  0.035262412660156681033782717998428,
212  0.036076989622888701185500318003895,
213  0.036698604498456094498018047441094,
214  0.037120549269832576114119958413599,
215  0.037334228751935040321235449094698,
216  0.037361073762679023410321241766599
217  };
218 
219 } // end gammalib namespace
220 
221 
222 /*==========================================================================
223  = =
224  = Constructors/destructors =
225  = =
226  ==========================================================================*/
227 
228 /***********************************************************************//**
229  * @brief Void constructor
230  ***************************************************************************/
232 {
233  // Initialise members
234  init_members();
235 
236  // Return
237  return;
238 }
239 
240 
241 /***********************************************************************//**
242  * @brief Function kernel constructor
243  *
244  * @param[in] kernel Pointer to function kernel.
245  *
246  * The function kernel constructor assigns the function kernel pointer in
247  * constructing the object.
248  ***************************************************************************/
250 {
251  // Initialise members
252  init_members();
253 
254  // Set function kernel
255  m_kernel = kernel;
256 
257  // Return
258  return;
259 }
260 
261 
262 /***********************************************************************//**
263  * @brief Copy constructor
264  *
265  * @param[in] integral Integral.
266  ***************************************************************************/
268 {
269  // Initialise members
270  init_members();
271 
272  // Copy members
273  copy_members(integral);
274 
275  // Return
276  return;
277 }
278 
279 
280 /***********************************************************************//**
281  * @brief Destructor
282  ***************************************************************************/
284 {
285  // Free members
286  free_members();
287 
288  // Return
289  return;
290 }
291 
292 
293 /*==========================================================================
294  = =
295  = Operators =
296  = =
297  ==========================================================================*/
298 
299 /***********************************************************************//**
300  * @brief Assignment operator
301  *
302  * @param[in] integral Integral.
303  * @return Integral.
304  ***************************************************************************/
306 {
307  // Execute only if object is not identical
308  if (this != &integral) {
309 
310  // Free members
311  free_members();
312 
313  // Initialise integral
314  init_members();
315 
316  // Copy members
317  copy_members(integral);
318 
319  } // endif: object was not identical
320 
321  // Return
322  return *this;
323 }
324 
325 
326 /*==========================================================================
327  = =
328  = Public methods =
329  = =
330  ==========================================================================*/
331 
332 /***********************************************************************//**
333  * @brief Clear integral
334  ***************************************************************************/
336 {
337  // Free members
338  free_members();
339 
340  // Initialise private members
341  init_members();
342 
343  // Return
344  return;
345 }
346 
347 
348 /***********************************************************************//**
349  * @brief Clone integral
350  *
351  * @return Pointer to deep copy of integral.
352  ***************************************************************************/
354 {
355  return new GIntegral(*this);
356 }
357 
358 
359 /***********************************************************************//**
360  * @brief Perform Romberg integration
361  *
362  * @param[in] bounds Integration boundaries.
363  * @param[in] order Integration order (default: 5)
364  *
365  * @exception GException::invalid_argument
366  * Integration order incompatible with number of iterations.
367  *
368  * Returns the integral of the integrand, computed over a number of
369  * intervals [a0,a1], [a1,a2], ... that are given as an unordered vector
370  * by the @p bounds argument.
371  *
372  * Integration is performed by Romberg's method of order 2*order, where
373  *
374  * order=1 is equivalent to the trapezoidal rule,
375  * order=2 is equivalent to Simpson's rule, and
376  * order=3 is equivalent to Boole's rule.
377  *
378  * The number of iterations is limited by m_max_iter. m_eps specifies the
379  * requested fractional accuracy. By default it is set to 1e-6.
380  ***************************************************************************/
381 double GIntegral::romberg(std::vector<double> bounds, const int& order)
382 {
383  // Sort integration boundaries in ascending order
384  std::sort(bounds.begin(), bounds.end());
385 
386  // Initialise integral
387  double value = 0.0;
388 
389  // Initialise integration status information
390  int calls = 0;
391 
392  // Add integral of all intervals
393  for (int i = 0; i < bounds.size()-1; ++i) {
394  value += romberg(bounds[i], bounds[i+1], order);
395  calls += m_calls;
396  }
397 
398  // Set integration status information
399  m_calls = calls;
400 
401  // Return value
402  return value;
403 }
404 
405 
406 /***********************************************************************//**
407  * @brief Perform Romberg integration
408  *
409  * @param[in] a Left integration boundary.
410  * @param[in] b Right integration boundary.
411  * @param[in] order Integration order (default: 5)
412  *
413  * @exception GException::invalid_argument
414  * Integration order incompatible with number of iterations.
415  *
416  * Returns the integral of the integrand from a to b. Integration is
417  * performed by Romberg's method of order 2*order, where
418  *
419  * order=1 is equivalent to the trapezoidal rule,
420  * order=2 is equivalent to Simpson's rule, and
421  * order=3 is equivalent to Boole's rule.
422  *
423  * The number of iterations is limited by m_max_iter. m_eps specifies the
424  * requested fractional accuracy. By default it is set to 1e-6.
425  ***************************************************************************/
426 double GIntegral::romberg(const double& a, const double& b, const int& order)
427 {
428  // Initialise result and status
429  double result = 0.0;
430 
431  // Initialise integration status information
432  m_isvalid = true;
433  m_calls = 0;
434  m_has_abserr = false;
435  m_has_relerr = false;
436 
437  // Continue only if integration range is valid
438  if (b > a) {
439 
440  // Initialise variables
441  bool converged = false;
442  double dss = 0.0;
443 
444  // Determine (maximum) number of iterations
445  int max_iter = (m_fix_iter > 0) ? m_fix_iter : m_max_iter;
446 
447  // Check whether maximum number of iterations is compliant with
448  // order
449  if (order > max_iter) {
450  std::string msg = "Requested integration order "+
451  gammalib::str(order)+" is larger than the "
452  "maximum number of iterations "+
453  gammalib::str(max_iter)+". Either reduce the "
454  "integration order or increase the (maximum) "
455  "number of iterations.";
457  }
458 
459  // Allocate temporal storage
460  double* s = new double[max_iter+2];
461  double* h = new double[max_iter+2];
462 
463  // Initialise step size
464  h[1] = 1.0;
465  s[0] = 0.0;
466 
467  // Iterative loop
468  for (m_iter = 1; m_iter <= max_iter; ++m_iter) {
469 
470  // Integration using Trapezoid rule
471  s[m_iter] = trapzd(a, b, m_iter, s[m_iter-1]);
472 
473  // Compile option: Check for NaN/Inf
474  #if defined(G_NAN_CHECK)
475  if (is_notanumber(s[m_iter]) || is_infinite(s[m_iter])) {
476  m_message = "*** ERROR: GIntegral::romberg"
477  "(a="+gammalib::str(a)+", b="+gammalib::str(b)+""
478  ", k="+gammalib::str(k)+"): NaN/Inf encountered"
479  " (s["+gammalib::str(m_iter)+"]="
480  ""+gammalib::str(s[m_iter])+")";
481  std::cout << m_message << std::endl;
482  m_isvalid = false;
483  }
484  #endif
485 
486  // Starting from iteration order on, use polynomial interpolation
487  if (m_iter >= order) {
488 
489  // Compute result using polynom interpolation
490  result = polint(&h[m_iter-order], &s[m_iter-order],
491  order, 0.0, &dss);
492 
493  // If a fixed number of iterations has been requested then
494  // check whether we reached the final one; otherwise check
495  // whether we reached the requested precision.
496  if (m_fix_iter > 0) {
497  if (m_iter == max_iter) {
498  converged = true;
499  }
500  }
501  else if (std::abs(dss) <= m_eps * std::abs(result)) {
502  converged = true;
503  }
504  if (converged) {
505  m_has_abserr = true;
506  m_abserr = std::abs(dss);
507  if (std::abs(result) > 0) {
508  m_has_relerr = true;
509  m_relerr = m_abserr / std::abs(result);
510  }
511  break;
512  }
513 
514  } // endif: polynomial interpolation performed
515 
516  // Reduce step size
517  h[m_iter+1]= 0.25 * h[m_iter];
518 
519  } // endfor: iterative loop
520 
521  // Free temporal storage
522  delete [] s;
523  delete [] h;
524 
525  // Set status and optionally dump warning
526  if (!converged) {
527  m_isvalid = false;
528  m_message = "Integration uncertainty "+
529  gammalib::str(std::abs(dss))+
530  " exceeds absolute tolerance of "+
531  gammalib::str(m_eps * std::abs(result))+
532  " after "+gammalib::str(m_iter)+
533  " iterations. Result "+
534  gammalib::str(result)+
535  " is inaccurate.";
536  if (!m_silent) {
537  std::string origin = "GIntegral::romberg("+
538  gammalib::str(a)+", "+
539  gammalib::str(b)+", "+
540  gammalib::str(order)+")";
541  gammalib::warning(origin, m_message);
542  }
543  }
544 
545  } // endif: integration range was valid
546 
547  // Return result
548  return result;
549 }
550 
551 
552 /***********************************************************************//**
553  * @brief Perform Trapezoidal integration
554  *
555  * @param[in] a Left integration boundary.
556  * @param[in] b Right integration boundary.
557  * @param[in] n Number of steps.
558  * @param[in] result Result from a previous trapezoidal integration step.
559  * @return Integration results.
560  *
561  * @exception GException::invalid_value
562  * Function kernel not set.
563  *
564  * The method performs a trapezoidal integration of the function kernel for
565  * the integration interval [a,b].
566  *
567  * If @p n = 1 the integral is computed using
568  *
569  * \f[
570  * \int_a^b f(x) \, dx = \frac{1}{2} (b-a) (f(a) + f(b))
571  * \f]
572  *
573  * For @p n > 1 the integral is computed using
574  *
575  * \f[
576  * \int_a^b f(x) \, dx = \frac{1}{2} \left[{\tt result} +
577  * \frac{b-a}{2^{n-1}}
578  * \sum_{i=0}^{2^{n-1}-1} f \left( a + (0.5+i) \frac{b-a}{2^{n-1}} \right) \right]
579  *
580  * \f]
581  *
582  * where \f${\tt result}\f$ is the integration result from a previous call
583  * to the method with @p n = @p n - 1.
584  ***************************************************************************/
585 double GIntegral::trapzd(const double& a, const double& b, const int& n,
586  double result)
587 {
588  // Throw an exception if the instance has no kernel
589  if (m_kernel == NULL) {
590  std::string msg = "Function kernel not set. Please set a function "
591  "kernel before calling the method.";
593  }
594 
595  // Handle case of identical boundaries
596  if (a == b) {
597  result = 0.0;
598  }
599 
600  // ... otherwise use trapeziodal rule
601  else {
602 
603  // Case A: Only a single step is requested
604  if (n == 1) {
605 
606  // Evaluate integrand at boundaries
607  double y_a = m_kernel->eval(a);
608  double y_b = m_kernel->eval(b);
609  m_calls += 2;
610 
611  // Compute result
612  result = 0.5*(b-a)*(y_a + y_b);
613 
614  } // endif: only a single step was requested
615 
616  // Case B: More than a single step is requested
617  else {
618 
619  // Compute step level 2^(n-1)
620  int it = 1;
621  for (int j = 1; j < n-1; ++j) {
622  it <<= 1;
623  }
624 
625  // Verify that step level is valid
626  if (it == 0) {
627  m_isvalid = false;
628  m_message = "Invalid step level "+gammalib::str(it)+
629  " encountered for"
630  " a="+gammalib::str(a)+
631  ", b="+gammalib::str(b)+
632  ", n="+gammalib::str(n)+
633  ", result="+gammalib::str(result)+
634  ". Looks like n is too large.";
635  if (!m_silent) {
637  }
638  }
639 
640  // Set step size
641  double tnm = double(it);
642  double del = (b-a)/tnm;
643 
644  // Verify that step is >0
645  if (del == 0) {
646  m_isvalid = false;
647  m_message = "Invalid step size "+gammalib::str(del)+
648  " encountered for"
649  " a="+gammalib::str(a)+
650  ", b="+gammalib::str(b)+
651  ", n="+gammalib::str(n)+
652  ", result="+gammalib::str(result)+
653  ". Step is too small to make sense.";
654  if (!m_silent) {
656  }
657  }
658 
659  // Sum up values
660  double x = a + 0.5*del;
661  double sum = 0.0;
662  for (int j = 0; j < it; ++j, x+=del) {
663 
664  // Evaluate integrand
665  double y = m_kernel->eval(x);
666  m_calls++;
667 
668  // Add integrand
669  sum += y;
670 
671  } // endfor: looped over steps
672 
673  // Set result
674  result = 0.5*(result + (b-a)*sum/tnm);
675  }
676 
677  } // endelse: trapeziodal rule was applied
678 
679  // Return result
680  return result;
681 }
682 
683 
684 /***********************************************************************//**
685  * @brief Adaptive Simpson's integration
686  *
687  * @param[in] a Left integration boundary.
688  * @param[in] b Right integration boundary.
689  *
690  * Integrates the function using an adaptive Simpson's rule. The initial
691  * interval [a,b] is split into two sub-intervals [a,c] and [c,b] for which
692  * the integral is computed using
693  *
694  * \f[
695  * \frac{b-a}{6} f(a) + 4f(c) + f(b)
696  * \f]
697  *
698  * where \f$c=(a+b)/2\f$ is the mid-point of interval [a,b]. Each
699  * sub-interval is then recursively divided into sub-interval and the process
700  * is repeated. Dividing of sub-intervals is stopped when the difference
701  * between subsequent intervals falls below the relative tolerance specified
702  * by eps(). The maximum recursion depth is set by the max_iter() method.
703  *
704  * I almost do not dare to confess, but the code has been taken from
705  * http://en.wikipedia.org/wiki/Adaptive_Simpson%27s_method
706  * It's really pretty simple ...
707  ***************************************************************************/
708 double GIntegral::adaptive_simpson(const double& a, const double& b) const
709 {
710  // Initialise integration status information
711  m_isvalid = true;
712  m_calls = 0;
713  m_iter = m_max_iter;
714  m_has_abserr = false;
715  m_has_relerr = false;
716 
717  // Compute mid-point c
718  double c = 0.5*(a + b); //!< Mid-point of interval [a,b]
719  double h = b - a; //!< Length of interval [a,b]
720 
721  // Evaluate function at boundaries and mid-point c
722  double fa = m_kernel->eval(a);
723  double fb = m_kernel->eval(b);
724  double fc = m_kernel->eval(c);
725  m_calls += 3;
726 
727  // Compute integral using Simpson's rule
728  double S = (h/6.0) * (fa + 4.0*fc + fb);
729 
730  // Initialise absolute precision
731  double epsilon = (std::abs(S) > 0) ? m_eps * S : m_eps;
732 
733  // Call recursive auxiliary function
734  double value = adaptive_simpson_aux(a, b, epsilon, S, fa, fb, fc, m_max_iter);
735 
736  // Deduce the number of iterations from the iteration counter
738 
739  // If result is not valid, set and output status message
740  if (!m_isvalid) {
741  m_message = "Integration uncertainty exceeds relative tolerance "
742  "of "+gammalib::str(m_eps)+" and absolute tolerance of "
743  ""+gammalib::str(epsilon)+" after "+gammalib::str(m_iter)+
744  " iterations and "+gammalib::str(m_calls)+" function "
745  "calls. Result "+gammalib::str(value)+" inaccurate.";
746  if (!m_silent) {
747  std::string origin = "GIntegral::adaptive_simpson("+
748  gammalib::str(a)+", "+
749  gammalib::str(b)+")";
750  gammalib::warning(origin, m_message);
751  }
752  }
753 
754  // Return result
755  return value;
756 }
757 
758 
759 /***********************************************************************//**
760  * @brief Gauss-Kronrod integration
761  *
762  * @param[in] a Left integration boundary.
763  * @param[in] b Right integration boundary.
764  *
765  ***************************************************************************/
766 double GIntegral::gauss_kronrod(const double& a, const double& b) const
767 {
768  // Initialise integration status information
769  m_isvalid = true;
770  m_iter = 0;
771  m_calls = 0;
772  m_has_abserr = false;
773  m_has_relerr = false;
774 
775  // Initialise integration result
776  double result = 0.0;
777 
778  // Allocate some arrays
779  double fv1[5];
780  double fv2[5];
781  double fv3[5];
782  double fv4[5];
783  double savfun[21];
784 
785  // Main code loop (so that we can exit using break)
786  do {
787 
788  // Tolerance check
789  if (m_eps < 1.12e-14) {
790  m_isvalid = false;
791  m_message = "Requested relative tolerance of "+gammalib::str(m_eps)+
792  " cannot be acheived. Please relax the integration "
793  "precision.";
794  if (!m_silent) {
795  std::string origin = "GIntegral::gauss_kronrod("+
796  gammalib::str(a)+", "+
797  gammalib::str(b)+")";
798  gammalib::warning(origin, m_message);
799  }
800  }
801 
802  // Compute function at mid-point
803  double h = 0.5 * (b - a);
804  double abs_h = std::abs(h);
805  double c = 0.5 * (b + a);
806  double f_c = m_kernel->eval(c);
807  m_calls++;
808 
809  // Compute the integral using the 10- and 21-point formulae
810  m_iter++;
811  double res10 = 0;
812  double res21 = gammalib::gkw21b[5] * f_c;
813  double resabs = gammalib::gkw21b[5] * std::abs(f_c);
814  for (int k = 0; k < 5; ++k) {
815  double x = h * gammalib::gkx1[k];
816  double fval1 = m_kernel->eval(c+x);
817  double fval2 = m_kernel->eval(c-x);
818  double fval = fval1 + fval2;
819  m_calls += 2;
820  res10 += gammalib::gkw10[k] * fval;
821  res21 += gammalib::gkw21a[k] * fval;
822  resabs += gammalib::gkw21a[k] * (std::abs(fval1) + std::abs(fval2));
823  savfun[k] = fval;
824  fv1[k] = fval1;
825  fv2[k] = fval2;
826  }
827  for (int k = 0; k < 5; ++k) {
828  double x = h * gammalib::gkx2[k];
829  double fval1 = m_kernel->eval(c+x);
830  double fval2 = m_kernel->eval(c-x);
831  double fval = fval1 + fval2;
832  m_calls += 2;
833  res21 += gammalib::gkw21b[k] * fval;
834  resabs += gammalib::gkw21b[k] * (std::abs(fval1) + std::abs(fval2));
835  savfun[k+5] = fval;
836  fv3[k] = fval1;
837  fv4[k] = fval2;
838  }
839  resabs *= abs_h;
840  double mean = 0.5 * res21;
841  double resasc = gammalib::gkw21b[5] * std::abs(f_c - mean);
842  for (int k = 0; k < 5; ++k) {
843  resasc += (gammalib::gkw21a[k] *
844  (std::abs(fv1[k] - mean) + std::abs(fv2[k] - mean)) +
845  gammalib::gkw21b[k] *
846  (std::abs(fv3[k] - mean) + std::abs(fv4[k] - mean)));
847  }
848  resasc *= abs_h ;
849  result = res21 * h;
850  double error = rescale_error((res21 - res10) * h, resabs, resasc);
851 
852  // Test for convergence */
853  if (error < m_eps * std::abs(result)) {
854  m_has_abserr = true;
855  m_abserr = error;
856  if (std::abs(result) > 0) {
857  m_has_relerr = true;
858  m_relerr = error / std::abs(result);
859  }
860  break;
861  }
862 
863  // Compute the integral using the 43-point formula
864  m_iter++;
865  double res43 = gammalib::gkw43b[11] * f_c;
866  for (int k = 0; k < 10; ++k) {
867  res43 += savfun[k] * gammalib::gkw43a[k];
868  }
869  for (int k = 0; k < 11; ++k) {
870  double x = h * gammalib::gkx3[k];
871  double fval = (m_kernel->eval(c+x) +
872  m_kernel->eval(c-x));
873  m_calls += 2;
874  res43 += fval * gammalib::gkw43b[k];
875  savfun[k+10] = fval;
876  }
877  result = res43 * h;
878 
879  // Test for convergence */
880  error = rescale_error((res43 - res21) * h, resabs, resasc);
881  if (error < m_eps * std::abs(result)) {
882  m_has_abserr = true;
883  m_abserr = error;
884  if (std::abs(result) > 0) {
885  m_has_relerr = true;
886  m_relerr = error / std::abs(result);
887  }
888  break;
889  }
890 
891  // Compute the integral using the 87-point formula
892  m_iter++;
893  double res87 = gammalib::gkw87b[22] * f_c;
894  for (int k = 0; k < 21; ++k) {
895  res87 += savfun[k] * gammalib::gkw87a[k];
896  }
897  for (int k = 0; k < 22; ++k) {
898  double x = h * gammalib::gkx4[k];
899  res87 += gammalib::gkw87b[k] *
900  (m_kernel->eval(c+x) +
901  m_kernel->eval(c-x));
902  m_calls += 2;
903  }
904  result = res87 * h ;
905 
906  // Test for convergence */
907  error = rescale_error ((res87 - res43) * h, resabs, resasc);
908  if (error < m_eps * std::abs(result)) {
909  m_has_abserr = true;
910  m_abserr = error;
911  if (std::abs(result) > 0) {
912  m_has_relerr = true;
913  m_relerr = error / std::abs(result);
914  }
915  break;
916  }
917 
918  // Failed to converge
919  m_isvalid = false;
920  m_message = "Integration uncertainty "+gammalib::str(error)+" exceeds "
921  "absolute tolerance of "+
922  gammalib::str(m_eps * std::abs(result))+" after "+
923  gammalib::str(m_iter)+" iterations and "+
924  gammalib::str(m_calls)+" function calls. Result "+
925  gammalib::str(result)+" inaccurate.";
926  if (!m_silent) {
927  std::string origin = "GIntegral::gauss_kronrod("+
928  gammalib::str(a)+", "+
929  gammalib::str(b)+")";
930  gammalib::warning(origin, m_message);
931  }
932 
933  } while (false); // end of main loop
934 
935  // Return result
936  return result;
937 }
938 
939 
940 /***********************************************************************//**
941  * @brief Print integral information
942  *
943  * @param[in] chatter Chattiness.
944  * @return String containing integral information.
945  ***************************************************************************/
946 std::string GIntegral::print(const GChatter& chatter) const
947 {
948  // Initialise result string
949  std::string result;
950 
951  // Continue only if chatter is not silent
952  if (chatter != SILENT) {
953 
954  // Append header
955  result.append("=== GIntegral ===");
956 
957  // Append information
958  result.append("\n"+gammalib::parformat("Relative precision"));
959  result.append(gammalib::str(eps()));
960  if (m_has_abserr) {
961  result.append("\n"+gammalib::parformat("Absolute error"));
962  result.append(gammalib::str(m_abserr));
963  }
964  if (m_has_relerr) {
965  result.append("\n"+gammalib::parformat("Relative error"));
966  result.append(gammalib::str(m_relerr));
967  }
968  result.append("\n"+gammalib::parformat("Function calls"));
969  result.append(gammalib::str(calls()));
970  result.append("\n"+gammalib::parformat("Iterations"));
971  result.append(gammalib::str(iter()));
972  if (m_fix_iter > 0) {
973  result.append(" (fixed: ");
974  result.append(gammalib::str(fixed_iter()));
975  result.append(")");
976  }
977  else {
978  result.append(" (maximum: ");
979  result.append(gammalib::str(max_iter()));
980  result.append(")");
981  }
982 
983  // Append status information
984  result.append("\n"+gammalib::parformat("Status"));
985  if (is_valid()) {
986  result.append("Result accurate.");
987  }
988  else {
989  result.append(message());
990  }
991  if (silent()) {
992  result.append("\n"+gammalib::parformat("Warnings")+"suppressed");
993  }
994  else {
995  result.append("\n"+gammalib::parformat("Warnings"));
996  result.append("in standard output");
997  }
998 
999  } // endif: chatter was not silent
1000 
1001  // Return result
1002  return result;
1003 }
1004 
1005 
1006 /*==========================================================================
1007  = =
1008  = Protected methods =
1009  = =
1010  ==========================================================================*/
1011 
1012 /***********************************************************************//**
1013  * @brief Initialise class members
1014  ***************************************************************************/
1016 {
1017  // Initialise members
1018  m_kernel = NULL;
1019  m_eps = 1.0e-6;
1020  m_max_iter = 20;
1021  m_fix_iter = 0;
1022  m_message.clear();
1023  m_silent = false;
1024 
1025  // Initialise results
1026  m_iter = 0;
1027  m_calls = 0;
1028  m_isvalid = true;
1029  m_has_abserr = false;
1030  m_has_relerr = false;
1031  m_abserr = 0.0;
1032  m_relerr = 0.0;
1033 
1034  // Return
1035  return;
1036 }
1037 
1038 
1039 /***********************************************************************//**
1040  * @brief Copy class members
1041  *
1042  * @param[in] integral Integral.
1043  ***************************************************************************/
1044 void GIntegral::copy_members(const GIntegral& integral)
1045 {
1046  // Copy attributes
1047  m_kernel = integral.m_kernel;
1048  m_eps = integral.m_eps;
1049  m_max_iter = integral.m_max_iter;
1050  m_fix_iter = integral.m_fix_iter;
1051  m_message = integral.m_message;
1052  m_silent = integral.m_silent;
1053 
1054  // Copy results
1055  m_iter = integral.m_iter;
1056  m_calls = integral.m_calls;
1057  m_isvalid = integral.m_isvalid;
1058  m_has_abserr = integral.m_has_abserr;
1059  m_has_relerr = integral.m_has_relerr;
1060  m_abserr = integral.m_abserr;
1061  m_relerr = integral.m_relerr;
1062 
1063  // Return
1064  return;
1065 }
1066 
1067 
1068 /***********************************************************************//**
1069  * @brief Delete class members
1070  ***************************************************************************/
1072 {
1073  // Return
1074  return;
1075 }
1076 
1077 
1078 /***********************************************************************//**
1079  * @brief Perform Polynomial interpolation
1080  *
1081  * @param[in] xa Pointer to array of X values.
1082  * @param[in] ya Pointer to array of Y values.
1083  * @param[in] n Number of elements in arrays.
1084  * @param[in] x X value at which interpolations should be performed.
1085  * @param[out] dy Error estimate for interpolated values.
1086  *
1087  * Given arrays xa[1,..,n] and ya[1,..,n], and given a value x, this
1088  * method returns a value y, and an error estimate dy. If P(x) is the
1089  * polynomial of degree n-1, then the returned value y=P(x).
1090  *
1091  * @todo Implement exceptions instead of screen dump.
1092  * @todo Use std::vector for xa and ya and start at 0
1093  ***************************************************************************/
1094 double GIntegral::polint(double* xa, double* ya, int n, double x, double* dy)
1095 {
1096  // Initialise result
1097  double y = 0.0;
1098 
1099  // Allocate temporary memory
1100  std::vector<double> c(n, 0.0);
1101  std::vector<double> d(n, 0.0);
1102 
1103  // Compute initial distance to first node
1104  double dif = std::abs(x-xa[1]);
1105 
1106  // Find index ns of the closest table entry
1107  int ns = 0;
1108  for (int i = 0; i < n; ++i) {
1109  double dift = std::abs(x-xa[i+1]);
1110  if (dift < dif) {
1111  ns = i;
1112  dif = dift;
1113  }
1114  c[i] = ya[i+1];
1115  d[i] = ya[i+1];
1116  }
1117 
1118  // Get initial approximation to y
1119  y = ya[ns+1];
1120  ns--;
1121 
1122  // Loop over each column of the tableau
1123  for (int m = 1; m < n; ++m) {
1124 
1125  // Update current c's and d's
1126  for (int i = 0; i < n-m; ++i) {
1127  double ho = xa[i+1] - x;
1128  double hp = xa[i+m+1] - x;
1129  double w = c[i+1] - d[i];
1130  double den = ho - hp;
1131  if (den == 0.0) {
1132  m_isvalid = false;
1133  m_message = "Invalid step size "+gammalib::str(den)+
1134  " encountered. Two values in xa array are"
1135  " identical.";
1136  if (!m_silent) {
1138  }
1139  }
1140  den = w/den;
1141  d[i] = hp*den;
1142  c[i] = ho*den;
1143  }
1144 
1145  // Compute y correction
1146  *dy = (2*(ns+1) < (n-m)) ? c[ns+1] : d[ns--];
1147 
1148  // Update y
1149  y += *dy;
1150 
1151  } // endfor: looped over columns of tableau
1152 
1153  // Return
1154  return y;
1155 }
1156 
1157 
1158 /***********************************************************************//**
1159  * @brief Auxiliary function for adaptive Simpson's method
1160  *
1161  * @param[in] a Left integration boundary.
1162  * @param[in] b Right integration boundary.
1163  * @param[in] eps Precision.
1164  * @param[in] S Integral of last computation.
1165  * @param[in] fa Function value at left integration boundary.
1166  * @param[in] fb Function value at right integration boundary.
1167  * @param[in] fc Function value at mid-point of interval [a,b]
1168  * @param[in] bottom Iteration counter (stop when 0)
1169  *
1170  * Implements a recursive auxiliary method for the adative_simpson()
1171  * integrator.
1172  ***************************************************************************/
1173 double GIntegral::adaptive_simpson_aux(const double& a, const double& b,
1174  const double& eps, const double& S,
1175  const double& fa, const double& fb,
1176  const double& fc,
1177  const int& bottom) const
1178 {
1179  // Store the iteration counter
1180  if (bottom < m_iter) {
1181  m_iter = bottom;
1182  }
1183 
1184  // Compute mid-point c bet
1185  double c = 0.5*(a + b); //!< Mid-point of interval [a,b]
1186  double h = b - a; //!< Length of interval [a,b]
1187  double d = 0.5*(a + c); //!< Mid-point of interval [a,c]
1188  double e = 0.5*(c + b); //!< Mid-point of interval [c,b]
1189 
1190  // Evaluate function at mid-points d and e
1191  double fd = m_kernel->eval(d);
1192  double fe = m_kernel->eval(e);
1193  m_calls += 2;
1194 
1195  // Compute integral using Simpson's rule for the left and right interval
1196  double h12 = h / 12.0;
1197  double Sleft = h12 * (fa + 4.0*fd + fc);
1198  double Sright = h12 * (fc + 4.0*fe + fb);
1199  double S2 = Sleft + Sright;
1200 
1201  // Allocate result
1202  double value;
1203 
1204  // If converged then compute the result ...
1205  if (std::abs(S2 - S) <= 15.0 * eps) {
1206 // if (std::abs(S2 - S) <= 15.0 * m_eps * std::abs(S2)) {
1207  value = S2 + (S2 - S)/15.0;
1208  }
1209 
1210  // ... else if the maximum recursion depth was reached then compute the
1211  // result and signal result invalidity
1212  else if (bottom <= 0) {
1213  value = S2 + (S2 - S)/15.0;
1214  m_isvalid = false;
1215  }
1216 
1217  // ... otherwise call this method recursively
1218  else {
1219  value = adaptive_simpson_aux(a, c, 0.5*eps, Sleft, fa, fc, fd, bottom-1) +
1220  adaptive_simpson_aux(c, b, 0.5*eps, Sright, fc, fb, fe, bottom-1);
1221  }
1222 
1223  // Return result
1224  return value;
1225 }
1226 
1227 
1228 /***********************************************************************//**
1229  * @brief Rescale errors for Gauss-Kronrod integration
1230  *
1231  * @param[in] err Error estimate.
1232  * @param[in] result_abs ???.
1233  * @param[in] result_asc ???.
1234  * @return Rescaled error estimate.
1235  ***************************************************************************/
1236 double GIntegral::rescale_error(double err,
1237  const double& result_abs,
1238  const double& result_asc) const
1239 {
1240  // Take absolute value of error
1241  err = std::abs(err);
1242 
1243  // ...
1244  if (result_asc != 0.0 && err != 0.0) {
1245  double scale = std::pow((200.0 * err / result_asc), 1.5);
1246  if (scale < 1.0) {
1247  err = result_asc * scale ;
1248  }
1249  else {
1250  err = result_asc ;
1251  }
1252  }
1253  if (result_abs > 2.2250738585072014e-308 / (50.0 * 2.2204460492503131e-16)) {
1254  double min_err = 50.0 * 2.2204460492503131e-16 * result_abs;
1255  if (min_err > err) {
1256  err = min_err ;
1257  }
1258  }
1259 
1260  // Return error
1261  return err;
1262 }
const double gkx2[5]
Definition: GIntegral.cpp:69
const double & eps(void) const
Get relative precision.
Definition: GIntegral.hpp:221
const double gkx1[5]
Definition: GIntegral.cpp:51
const int & fixed_iter(void) const
Return fixed number of iterations.
Definition: GIntegral.hpp:196
double romberg(std::vector< double > bounds, const int &order=5)
Perform Romberg integration.
Definition: GIntegral.cpp:381
int m_iter
Number of iterations used.
Definition: GIntegral.hpp:112
void warning(const std::string &origin, const std::string &message)
Emits warning.
Definition: GTools.cpp:1386
GVector abs(const GVector &vector)
Computes absolute of vector elements.
Definition: GVector.cpp:1253
const bool & silent(void) const
Get silence flag.
Definition: GIntegral.hpp:258
void init_members(void)
Initialise class members.
Definition: GIntegral.cpp:1015
const double gkw43a[10]
Definition: GIntegral.cpp:112
#define G_ROMBERG
Definition: GIntegral.cpp:37
double polint(double *xa, double *ya, int n, double x, double *dy)
Perform Polynomial interpolation.
Definition: GIntegral.cpp:1094
double sum(const GVector &vector)
Computes vector sum.
Definition: GVector.cpp:944
double rescale_error(double err, const double &result_abs, const double &result_asc) const
Rescale errors for Gauss-Kronrod integration.
Definition: GIntegral.cpp:1236
int m_max_iter
Maximum number of iterations.
Definition: GIntegral.hpp:107
const std::string & message(void) const
Return integration status message.
Definition: GIntegral.hpp:309
Gammalib tools definition.
GIntegral class interface definition.
Definition: GIntegral.hpp:46
double gauss_kronrod(const double &a, const double &b) const
Gauss-Kronrod integration.
Definition: GIntegral.cpp:766
int m_fix_iter
Fixed number of iterations.
Definition: GIntegral.hpp:108
GIntegral & operator=(const GIntegral &integral)
Assignment operator.
Definition: GIntegral.cpp:305
void clear(void)
Clear integral.
Definition: GIntegral.cpp:335
bool is_notanumber(const double &x)
Signal if argument is not a number.
Definition: GTools.hpp:201
#define G_TRAPZD
Definition: GIntegral.cpp:38
bool is_infinite(const double &x)
Signal if argument is infinite.
Definition: GTools.hpp:184
const int & iter(void) const
Return number of iterations.
Definition: GIntegral.hpp:141
bool m_isvalid
Integration result valid (true=yes)
Definition: GIntegral.hpp:114
GIntegral(void)
Void constructor.
Definition: GIntegral.cpp:231
Single parameter function abstract base class definition.
const double gkw87a[21]
Definition: GIntegral.cpp:168
const double gkw43b[12]
Definition: GIntegral.cpp:126
double trapzd(const double &a, const double &b, const int &n=1, double result=0.0)
Perform Trapezoidal integration.
Definition: GIntegral.cpp:585
bool m_has_abserr
Has absolute integration error.
Definition: GIntegral.hpp:115
std::string m_message
Status message (if result is invalid)
Definition: GIntegral.hpp:119
const double gkw87b[23]
Definition: GIntegral.cpp:193
GChatter
Definition: GTypemaps.hpp:33
bool m_has_relerr
Has relative integration error.
Definition: GIntegral.hpp:116
const int & calls(void) const
Get number of function calls.
Definition: GIntegral.hpp:233
virtual double eval(const double &x)=0
double adaptive_simpson_aux(const double &a, const double &b, const double &eps, const double &S, const double &fa, const double &fb, const double &fc, const int &bottom) const
Auxiliary function for adaptive Simpson&#39;s method.
Definition: GIntegral.cpp:1173
const GFunction * kernel(void) const
Get function kernel.
Definition: GIntegral.hpp:285
double m_relerr
Absolute integration error.
Definition: GIntegral.hpp:118
const double gkw21a[5]
Definition: GIntegral.cpp:78
void copy_members(const GIntegral &integral)
Copy class members.
Definition: GIntegral.cpp:1044
double m_eps
Requested relative integration precision.
Definition: GIntegral.hpp:106
bool m_silent
Suppress integration warnings in console.
Definition: GIntegral.hpp:109
std::string print(const GChatter &chatter=NORMAL) const
Print integral information.
Definition: GIntegral.cpp:946
virtual ~GIntegral(void)
Destructor.
Definition: GIntegral.cpp:283
const double gkw10[5]
Definition: GIntegral.cpp:60
Single parameter function abstract base class.
Definition: GFunction.hpp:44
const int & max_iter(void) const
Return maximum number of iterations.
Definition: GIntegral.hpp:166
#define G_POLINT
Definition: GIntegral.cpp:39
GVector pow(const GVector &vector, const double &power)
Computes tanh of vector elements.
Definition: GVector.cpp:1422
Exception handler interface definition.
GFunction * m_kernel
Pointer to function kernel.
Definition: GIntegral.hpp:105
int m_calls
Number of function calls used.
Definition: GIntegral.hpp:113
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
const double gkw21b[6]
Definition: GIntegral.cpp:87
Integration class interface definition.
const double gkx4[22]
Definition: GIntegral.cpp:142
const double gkx3[11]
Definition: GIntegral.cpp:97
const bool & is_valid(void) const
Signal if integration result is valid.
Definition: GIntegral.hpp:297
double m_abserr
Absolute integration error.
Definition: GIntegral.hpp:117
void free_members(void)
Delete class members.
Definition: GIntegral.cpp:1071
GIntegral * clone(void) const
Clone integral.
Definition: GIntegral.cpp:353
double adaptive_simpson(const double &a, const double &b) const
Adaptive Simpson&#39;s integration.
Definition: GIntegral.cpp:708
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489