Arbeletche2021CherenkovAngularModel.cc
Go to the documentation of this file.
1 #include <cmath>
2 #include <algorithm>
3 #include <string>
4 
5 #include <atm/Arbeletche2021CherenkovAngularModel.h>
6 
7 #include <utl/AugerException.h>
8 #include <utl/MathConstants.h>
9 #include <utl/Integrator.h>
10 
11 // make the math constants visible
12 using utl::kPi;
13 using utl::kPiOnTwo;
14 
15 // ctor
17  const double showerEnergyTeV,
18  const double maxTheta
19 ) :
20  fMaxTheta(maxTheta)
21 {
22  fOmegaEnergyDep = 4.513 * std::pow(showerEnergyTeV, -0.008843);
23  fEpsilon = 0.009528 + 0.022552 * std::pow(showerEnergyTeV, -0.4207);
24 }
25 
26 
27 
28 void
30  const double showerAge,
31  const double refractiveIndex
32 ) const
33 {
34  // Truncate the values of shower age and refractive index within the following bounds:
35  // 0.5 <= showerAge <= 1.5
36  // refractiveIndex >= 1.0 + 3e-5
37  const double nMinusOne = std::max(refractiveIndex, 1.0 + 3e-5) - 1.0;
38  const double truncatedAge = std::max(0.5, std::min(showerAge, 1.5));
39  const double logShowerAge = std::log(truncatedAge);
40 
41  // Compute the parametrized parameters
42  fNu = 0.21155 * std::pow(nMinusOne, -0.16639) + 1.21803 * logShowerAge;
43  fOmega1 = 1.0 / (fOmegaEnergyDep * std::pow(nMinusOne, 0.45092) - 0.058687 * logShowerAge);
44  fOmega2 = fOmega1 * (1.0 - 1.0 / (0.90725 + 0.41722 * truncatedAge));
45 
46  // Compute thetaEm (maximum Cherenkov emission angle) and its sines and cosines
47  const double v = 1. - 1./refractiveIndex;
48  fThetaEm = v < 0.001 ? std::sqrt(2.*v)*(1.+(v/12.)*(1.+9.*v/40.)) : std::acos(1./refractiveIndex);
49  fCosThetaEm = 1./refractiveIndex;
50  fSinThetaEm = std::sqrt((1.-fCosThetaEm)*(1.+fCosThetaEm));
51 
52  // Compute the normalization
53  fNormalization = UnnormalizedIntegral(0.0, fMaxTheta);
54 }
55 
56 
57 
58 double
60  const double theta
61 ) const
62 {
63  return std::pow(theta,fNu-1.0) *
64  (std::exp(-theta*fOmega1) + fEpsilon*std::exp(-theta*fOmega2));
65 }
66 
67 
68 
69 double
71  const double lowAngle,
72  const double highAngle
73 ) const
74 {
75  double integral = 0;
76 
77  // If lowAngle is to the left of the peak, integrate between lowAngle and min(thetaEm, highAngle)
78  if (lowAngle < fThetaEm) {
79  integral += UnnormalizedIntegralLeft(lowAngle, std::min(highAngle, fThetaEm));
80  }
81 
82  // If highAngle is to the right of the peak, integrate between max(thetaEm,lowAngle) and highAngle
83  if (highAngle > fThetaEm) {
84  integral += UnnormalizedIntegralRight(std::max(lowAngle, fThetaEm), highAngle);
85  }
86 
87  return integral;
88 }
89 
90 
91 
92 double
94  const double lowAngle,
95  const double highAngle
96 ) const
97 {
98  // In this region of theta < thetaEm, we provide the integral as a series in powers of
99  // (theta - thetaEm) plus some terms resulting from integration by parts. Since
100  // thetaEm < 0.025, no more than 7 terms are required for the computation of the series
101 
102  // Helper quantities
103  const double deltaLow = fThetaEm-lowAngle;
104  const double deltaHigh = fThetaEm-highAngle;
105  const double deltaCosLow = std::cos(lowAngle) - fCosThetaEm;
106  const double deltaCosHigh = std::cos(highAngle) - fCosThetaEm;
107  const double coeffs[4] = {fCosThetaEm, fSinThetaEm, -fCosThetaEm, -fSinThetaEm};
108 
109  // The power series
110  double integral = 0;
111  double powLow = 1;
112  double powHigh = 1;
113  double term;
114  bool hasConverged = false;
115  for (int k = 1; k < 1000; ++k) {
116  powLow *= deltaLow / k;
117  powHigh *= deltaHigh / k;
118  term = (powLow - powHigh) / k;
119  integral += coeffs[k%4] * term;
120  if (std::fabs(term) <= 1e-10*std::fabs(integral)) {
121  hasConverged = true;
122  break;
123  }
124  }
125 
126  // check if the series has actually converged
127  if (!hasConverged) {
128  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
129  ERROR(message);
130  throw utl::DoesNotComputeException(message);
131  }
132 
133  // Terms from integration by parts
134  integral += kPi * (deltaCosLow - deltaCosHigh);
135  integral += deltaHigh < 1e-10 ?
136  std::log(std::pow(deltaHigh/fThetaEm+1e-200,deltaCosHigh)) :
137  deltaCosHigh * std::log(deltaHigh/fThetaEm);
138  integral -= deltaLow < 1e-10 ?
139  std::log(std::pow(deltaLow/fThetaEm+1e-200,deltaCosLow)) :
140  deltaCosLow * std::log(deltaLow/fThetaEm);
141 
142  // A remaining factor
143  integral *= FunctionK(fThetaEm) / fSinThetaEm;
144 
145  return integral;
146 }
147 
148 
149 
150 double
152  const double lowAngle,
153  const double highAngle
154 ) const
155 {
156  //
157  // Helper functions (which are used below)
158  //
159 
160  // Integrand (after the change t = sqrt(1-thetaEm/theta)) used for numerical integration
161  auto integrand = [=](double t) {
162  const double theta = fThetaEm / ((1.0-t)*(1.0+t));
163  return std::pow(theta, fNu + 1) *
164  (t <= 1e-10 ? t*kPiOnTwo + 1.0 - std::pow(t, t) : t * (kPiOnTwo - std::log(t))) *
165  (std::exp(-fOmega1*theta) + fEpsilon*std::exp(-fOmega2*theta));
166  };
167 
168  // Indefinite integral given as an expansion in powers of (1 - thetaEm/theta)
169  auto uSeries = [=](const double theta, const double omega)->double{
170  const double u = 1.0 - fThetaEm/theta;
171  if (u < 1e-18) {
172  return 0;
173  }
174 
175  const double pimlog = u > 1e-5 ?
176  u*(kPi - std::log(u)) :
177  u*kPi - std::log( std::pow(u+1e-200, u) );
178 
179  double bm2 = 0;
180  double bm1 = std::exp(-omega*fThetaEm);
181  double sum = (pimlog+u)*bm1;
182  double a1 = fNu-1-omega*fThetaEm;
183  double a2 = -1;
184  double upn = u;
185  double b, term;
186  for (int n = 2; n < 1000; ++n) {
187  a1 += 2;
188  ++a2;
189  b = a1*bm1 - a2*(fNu+a2)*bm2*upn;
190  upn = u/n;
191  b *= upn;
192  term = b * (pimlog + upn);
193  sum += term;
194  if (std::fabs(term) <= 1e-10*std::fabs(sum)) {
195  return sum * std::pow(fThetaEm, fNu);
196  }
197  bm2 = bm1;
198  bm1 = b;
199  }
200 
201  // if we arrived here, the series has not converged
202  throw;
203  };
204 
205  // Indefinite integral given as an expansion in powers of thetaEm/theta
206  auto rSeries = [=](const double theta, const double omega)->double{
207  const double wt = omega*theta;
208  const double r = fThetaEm / theta;
209  const double wtem = fThetaEm * omega;
210 
211  double h = 0;
212 
213  // Compute IncGammaUpper(nu, x)/(t^nu e^(-x)) according
214  // to Press et al., Numerical Recipes, 3ed, 2007
215  // The quantity computed here corresponds to the first term of
216  // the power series computed in the following loop (uses recursion)
217  bool hasConverged = false;
218  if (wt > fNu + 1) {
219  // Use continued fraction expansion
220  const double fpMin = 1e-30;
221  double b = wt + 1 - fNu;
222  double c = 1.0 / fpMin;
223  double d = 1.0 / b;
224  h = -d;
225  for (int n = 1; n < 1000; ++n) {
226  double an = -n * (n - fNu);
227  b += 2.0;
228  d = an * d + b;
229  if (std::fabs(d) < fpMin) {
230  d = fpMin;
231  }
232  c = b + an/c;
233  if (std::fabs(c) < fpMin) {
234  c = fpMin;
235  }
236  d = 1.0/d;
237  double del = d*c;
238  h *= del;
239  if (std::fabs(del - 1) < 1e-10) {
240  hasConverged = true;
241  break;
242  }
243  }
244  } else {
245  // Use power series
246  double term = 1.0/fNu;
247  h = term;
248  for (int n = 1; n < 1000; ++n) {
249  term *= wt / (n + fNu);
250  h += term;
251  if (std::fabs(term) < 1e-10*std::fabs(h)) {
252  hasConverged = true;
253  break;
254  }
255  }
256  h -= std::exp(wt - fNu*std::log(wt) + std::lgamma(fNu));
257  }
258 
259  // only proceed if the calculation above has converged
260  if (hasConverged) {
261  double rPower = 1;
262  double sum = kPi * h;
263  for (int k = 1; k < 1000; ++k) {
264  rPower *= r;
265  h = (rPower + wtem*h) / (fNu - k);
266  sum += h / double(k);
267  if (std::fabs(h / double(k)) <= 1e-10*std::fabs(sum)) {
268  return sum * std::exp(-wt + fNu * std::log(theta));
269  }
270  }
271  }
272 
273  // if we arrived here, the series has not converged
274  throw;
275  };
276 
277  //
278  // Computation of the integral
279  //
280 
281  // Define a threshold used to select a power series expansion either in terms of
282  // 1-thetaEm/theta (below threshold) and thetaEm/theta (above threshold)
283  const double thresholdAngle = 2*fThetaEm;
284 
285  // Start the integral with a 0 value
286  double integral = 0;
287 
288  // Region below the threshold angle: always use the expansion in powers of
289  // 1-thetaEm/theta, which converges for every combination of parameters
290  if (lowAngle < thresholdAngle) {
291  const double actualHighAngle = std::min(highAngle, thresholdAngle);
292  try {
293  integral += uSeries(actualHighAngle, fOmega1) - uSeries(lowAngle, fOmega1) +
294  fEpsilon * (uSeries(actualHighAngle, fOmega2) - uSeries(lowAngle, fOmega2));
295  } catch (...) {
296  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
297  ERROR(message);
298  throw utl::DoesNotComputeException(message);
299  }
300  }
301 
302  // Region above the threshold angle: use the expansion in powers of thetaEm/theta
303  // only if parameter Nu is not too close to an integer value, in which the series
304  // may diverge due to a factor of 1/(nu-k). If nu is close to an integer value,
305  // use numerical integration instead.
306  if (highAngle > thresholdAngle) {
307  const double actualLowAngle = std::max(lowAngle, thresholdAngle);
308  if (std::fabs(fNu - std::round(fNu)) < 1e-4) {
309  // Nu is close to integer value, use numerical integration
310  const double tMin = std::sqrt(1.0 - fThetaEm/actualLowAngle);
311  const double tMax = std::sqrt(1.0 - fThetaEm/highAngle);
312  utl::Integrator<decltype(integrand)> integrator(integrand, 1e-6);
313  integral += (4.0 / fThetaEm) * integrator.GetRombergIntegral(tMin, tMax);
314  } else {
315  // Use power series
316  try {
317  integral += rSeries(highAngle, fOmega1) - rSeries(actualLowAngle, fOmega1) +
318  fEpsilon * (rSeries(highAngle, fOmega2) - rSeries(actualLowAngle, fOmega2));
319  } catch (...) {
320  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
321  ERROR(message);
322  throw utl::DoesNotComputeException(message);
323  }
324  }
325  }
326 
327  return integral;
328 }
329 
330 
331 
332 double
334  const double lowAngle,
335  const double highAngle
336 ) const
337 {
338  // In this region of theta < thetaEm, we provide the integral as a series in powers of
339  // (theta - thetaEm) plus some terms resulting from integration by parts. Since
340  // thetaEm < 0.025, no more than 7 terms are required for the computation of the series
341 
342  // Helper quantities
343  const double deltaLow = 2.0*(fThetaEm - lowAngle);
344  const double deltaHigh = 2.0*(fThetaEm - highAngle);
345  const double twoThetaEm = 2.*fThetaEm;
346  const double cosTwoThetaEm = 2.0*fCosThetaEm*fCosThetaEm - 1.0;
347  const double sinTwoThetaEm = 2.0*fSinThetaEm*fCosThetaEm;
348  const double factorLow = sinTwoThetaEm - std::sin(2.0*lowAngle) - deltaLow;
349  const double factorHigh = sinTwoThetaEm - std::sin(2.0*highAngle) - deltaHigh;
350  const double coeffs[4] = {sinTwoThetaEm, -cosTwoThetaEm, -sinTwoThetaEm, cosTwoThetaEm};
351 
352  // The integral value
353  double integral = 0;
354 
355  // Compute the series part
356  double powLow = 1.0;
357  double powHigh = 1.0;
358  double term;
359  bool hasConverged = false;
360  for (int k = 1; k < 1000; ++k) {
361  powLow *= deltaLow / k;
362  powHigh *= deltaHigh / k;
363  term = (powLow - powHigh) / k;
364  integral += coeffs[k%4] * term;
365  if (std::fabs(term) <= 1e-10*std::fabs(integral)) {
366  hasConverged = true;
367  break;
368  }
369  }
370 
371  // Check convergence of the power series part
372  if (!hasConverged) {
373  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
374  ERROR(message);
375  throw utl::DoesNotComputeException(message);
376  }
377 
378  // Terms obtained from integration by parts
379  integral += deltaLow - deltaHigh;
380  integral += kPi * (factorHigh-factorLow);
381  integral -= deltaHigh < 1e-10 ?
382  std::log(std::pow(deltaHigh/twoThetaEm+1e-200,factorHigh)) :
383  factorHigh * std::log(deltaHigh/twoThetaEm);
384  integral += deltaLow < 1e-10 ?
385  std::log(std::pow(deltaLow/twoThetaEm+1e-200,factorLow)) :
386  factorLow * std::log(deltaLow/twoThetaEm);
387 
388  // A last factor depending on thetaEm alone
389  integral *= 0.25 * FunctionK(fThetaEm) / fSinThetaEm;
390 
391  return integral;
392 }
393 
394 
395 
396 double
398  const double lowAngle,
399  const double highAngle
400 ) const
401 {
402  //
403  // Helper functions (which are used below)
404  //
405 
406  // Integrand (after the change t = sqrt(1-thetaEm/theta)) used for numerical integration
407  auto integrand = [=](double t) {
408  const double theta = fThetaEm / ((1.0-t)*(1.0+t));
409  return std::sin(theta) * std::pow(theta, fNu + 1) *
410  (t <= 1e-10 ? t*kPiOnTwo + 1.0 - std::pow(t, t) : t * (kPiOnTwo - std::log(t))) *
411  (std::exp(-fOmega1*theta) + fEpsilon*std::exp(-fOmega2*theta));
412  };
413 
414  // Indefinite integral given as an expansion in powers of (1 - thetaEm/theta)
415  auto uSeries = [=](const double theta, const double omega)->double{
416  const double u = 1.0 - fThetaEm/theta;
417  if (u < 1e-18) {
418  return 0;
419  }
420 
421  const double pimlog = u > 1e-5 ?
422  u*(kPi - std::log(u)) :
423  u*kPi - std::log(std::pow(u+1e-200,u));
424 
425  double bm2 = 0;
426  double am2 = 0;
427  double bm1 = std::exp(-omega*fThetaEm);
428  double am1 = bm1 * fCosThetaEm;
429  bm1 *= fSinThetaEm;
430  double sum = (pimlog + u) * bm1;
431  double c1 = fNu-1-omega*fThetaEm;
432  double c2 = -1;
433  double upn = u;
434  double a, b, term;
435  for (int n = 2; n < 1000; ++n) {
436  c1 += 2;
437  ++c2;
438  a = c1*am1 - c2*(fNu+c2)*am2*upn - fThetaEm*bm1;
439  b = c1*bm1 - c2*(fNu+c2)*bm2*upn + fThetaEm*am1;
440  upn = u/n;
441  a *= upn;
442  b *= upn;
443  term = (pimlog + upn) * b;
444  sum += term;
445  if (std::fabs(term) <= 1e-10*std::fabs(sum)) {
446  return sum * std::pow(fThetaEm, fNu);
447  }
448  bm2 = bm1;
449  bm1 = b;
450  am2 = am1;
451  am1 = a;
452  }
453 
454  // if the series has not converged
455  throw;
456  };
457 
458  // Indefinite integral given as an expansion in powers of thetaEm/theta
459  auto rSeries = [=](const double theta, const double omega) {
460  const double r = fThetaEm/theta;
461  const double sint = std::sin(theta);
462  const double cost = std::cos(theta);
463  const double invt = 1.0/theta;
464 
465  // here we compute the first coefficient of the following power series,
466  // which is also computed as a power series. the following terms are
467  // obtained through recursion
468  double kap = 1.0/fNu;
469  double sig = 0;
470  double old;
471  double c = kap;
472  double s = sig;
473  double den = fNu * invt;
474  bool hasConverged = false;
475  for (int n = 1; n < 1000; ++n) {
476  den += invt;
477  old = kap;
478  kap = (omega*kap - sig) / den;
479  sig = (omega*sig + old) / den;
480  c += kap;
481  s += sig;
482  if (std::fabs(kap) <= 1e-10*std::fabs(c) && std::fabs(sig) <= 1e-10*std::fabs(s)) {
483  hasConverged = true;
484  break;
485  }
486  }
487 
488  // ensure the computation above has converged
489  if (hasConverged) {
490  double b = sint*c - cost*s;
491  double rp = 1;
492  double series = kPi * b;
493  den = fNu;
494  for (int k = 1; k < 1000; ++k) {
495  den--;
496  rp *= r;
497  old = s;
498  s = (omega*s + c) * fThetaEm / den;
499  c = (rp + fThetaEm*(omega*c - old)) / den;
500  b = (sint*c - cost*s) / k;
501  series += b;
502  if (std::fabs(b) < 1e-10*std::fabs(series)) {
503  return series * std::exp(-omega*theta + fNu*std::log(theta));
504  }
505  }
506  }
507 
508  // if the series has not converged, throw
509  throw;
510  };
511 
512  //
513  // Computation of the integral
514  //
515 
516  // Define a threshold used to select a power series expansion either in terms of
517  // 1-thetaEm/theta (below threshold) and thetaEm/theta (above threshold)
518  const double thresholdAngle = 2*fThetaEm;
519 
520  // Start the integral with a 0 value
521  double integral = 0;
522 
523  // Region below the threshold angle: always use the expansion in powers of
524  // 1-thetaEm/theta, which converges for every combination of parameters
525  if (lowAngle < thresholdAngle) {
526  const double actualHighAngle = std::min(highAngle, thresholdAngle);
527  try {
528  integral += uSeries(actualHighAngle, fOmega1) - uSeries(lowAngle, fOmega1) +
529  fEpsilon * (uSeries(actualHighAngle, fOmega2) - uSeries(lowAngle, fOmega2));
530  } catch (...) {
531  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
532  ERROR(message);
533  throw utl::DoesNotComputeException(message);
534  }
535  }
536 
537  // Region above the threshold angle: use the expansion in powers of thetaEm/theta
538  // only if parameter Nu is not too close to an integer value, in which the series
539  // may diverge due to a factor of 1/(nu-k). If nu is close to an integer value,
540  // use numerical integration instead.
541  if (highAngle > thresholdAngle) {
542  const double actualLowAngle = std::max(lowAngle, thresholdAngle);
543  if (std::fabs(fNu - std::round(fNu)) < 1e-4) {
544  // Nu is close to integer value, use numerical integration
545  const double tMin = std::sqrt(1.0 - fThetaEm/actualLowAngle);
546  const double tMax = std::sqrt(1.0 - fThetaEm/highAngle);
547  utl::Integrator<decltype(integrand)> integrator(integrand, 1e-6);
548  integral += (4.0 / fThetaEm) * integrator.GetRombergIntegral(tMin, tMax);
549  } else {
550  // Use power series
551  try {
552  integral += rSeries(highAngle, fOmega1) - rSeries(actualLowAngle, fOmega1) +
553  fEpsilon * (rSeries(highAngle, fOmega2) - rSeries(actualLowAngle, fOmega2));
554  } catch (...) {
555  const std::string message = "Failed to integrate the angular distribution of Cherenkov photons";
556  ERROR(message);
557  throw utl::DoesNotComputeException(message);
558  }
559  }
560  }
561 
562  return integral;
563 }
564 
565 
566 
567 double
569  const double theta,
570  const double showerAge,
571  const double refractiveIndex
572 ) const
573 {
574  static const double peakWidth = 1e-5;
575 
576  // Check the value of theta
577  if (theta <= 0) {
578  return 0;
579  } else if (theta > fMaxTheta) {
580  return 0;
581  }
582 
583  // Update the parameters that depend on refractive index and shower age
584  UpdateParameters(showerAge, refractiveIndex);
585 
586  if (theta <= fThetaEm - peakWidth) {
587  // Region to the left of the peak: compute the distribution function as defined
588  return FunctionK(fThetaEm) * (kPi - std::log(1.0-theta/fThetaEm)) *
589  (std::sin(theta)/fSinThetaEm) / fNormalization;
590  }
591  else if (theta < fThetaEm + peakWidth) {
592  // Very close to the peak: compute the average in an interval containing the peak
593  return UnnormalizedIntegral(fThetaEm - peakWidth, fThetaEm + peakWidth) /
594  (2.0*peakWidth*fNormalization);
595  }
596  else {
597  // Right of the peak: compute the distribution function as defined
598  return FunctionK(theta) * (kPi - std::log(1.0 - fThetaEm/theta)) / fNormalization;
599  }
600 }
601 
602 
603 
604 double
606  const double theta,
607  const double showerAge,
608  const double refractiveIndex
609 ) const
610 {
611  // Check value of theta
612  if (theta <= 0) {
613  return 0;
614  } else if (theta >= fMaxTheta) {
615  return 1;
616  }
617 
618  // Update the parameters that depend on refractive index and shower age
619  UpdateParameters(showerAge, refractiveIndex);
620 
621  // Return the (normalized) integral of the distribution between 0 and theta (given)
622  return UnnormalizedIntegral(0, theta) / fNormalization;
623 }
624 
625 
626 
627 double
629  const double lowAngle,
630  const double highAngle,
631  const double showerAge,
632  const double refractiveIndex
633 ) const
634 {
635  // Reorder angles, if necessary
636  if (lowAngle > highAngle) {
637  return -Integral(highAngle, lowAngle, showerAge, refractiveIndex);
638  }
639 
640  // Update the parameters that depend on refractive index and shower age
641  UpdateParameters(showerAge, refractiveIndex);
642 
643  double integral = 0;
644 
645  if (lowAngle < fThetaEm) {
646  integral += UnnormalizedIntegralSineLeft(std::max(lowAngle,0.), std::min(highAngle,fThetaEm));
647  }
648 
649  if (highAngle > fThetaEm) {
650  integral += UnnormalizedIntegralSineRight(std::max(lowAngle,fThetaEm), std::min(highAngle,fMaxTheta));
651  }
652 
653  // return the normalized integral
654  return integral / fNormalization;
655 }
Class for integration of functions with one independent parameter.
Definition: Integrator.h:72
double UnnormalizedIntegral(const double lowAngle, const double highAngle) const
double UnnormalizedIntegralLeft(const double lowAngle, const double highAngle) const
double CDF(const double theta, const double showerAge, const double refractiveIndex) const
Computes the CDF of the angular distribution of Cherenkov photons.
double UnnormalizedIntegralSineRight(const double lowAngle, const double highAngle) const
double pow(const double x, const unsigned int i)
double PDF(const double theta, const double showerAge, const double refractiveIndex) const
Computes the PDF of the angular distribution of Cherenkov photons.
#define max(a, b)
constexpr double s
Definition: AugerUnits.h:163
Arbeletche2021CherenkovAngularModel(const double showerEnergyTeV, const double maxTheta)
constexpr double kPi
Definition: MathConstants.h:24
double Integral(const double lowAngle, const double highAngle, const double showerAge, const double refractiveIndex) const
Compute the integral of PDF(theta)*sin(theta) between specified angles.
constexpr double kPiOnTwo
Definition: MathConstants.h:25
double UnnormalizedIntegralRight(const double lowAngle, const double highAngle) const
Base class for inconsistency/illogicality exceptions.
void UpdateParameters(const double showerAge, const double refractiveIndex) const
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
double UnnormalizedIntegralSineLeft(const double lowAngle, const double highAngle) const
double GetRombergIntegral(const double a, const double b, const int order=5, const int maxIterations=20) const
Romberg integration Setting order to 2 is equivalent to the Simpson&#39;s method.
Definition: Integrator.h:89

, generated on Tue Sep 26 2023.