AnalyticalCherenkovModel.cc
Go to the documentation of this file.
1 
9 #include <sstream>
10 #include <iostream>
11 
12 #include <utl/TabulatedFunction.h>
13 #include <utl/Reader.h>
14 #include <utl/ErrorLogger.h>
15 #include <utl/MathConstants.h>
16 #include <utl/Vector.h>
17 #include <utl/AxialVector.h>
18 #include <utl/AugerUnits.h>
19 #include <utl/PhysicalConstants.h>
20 #include <utl/PhysicalFunctions.h>
21 #include <utl/Transformation.h>
22 #include <utl/TabulatedFunctionErrors.h>
23 #include <utl/ReferenceEllipsoid.h>
24 #include <utl/Point.h>
25 #include <utl/StringCompare.h>
26 #include <utl/TimeStamp.h>
27 
28 #include <atm/ProfileResult.h>
29 #include <atm/AnalyticalCherenkovModel.h>
30 
31 #include <fwk/CentralConfig.h>
32 #include <fwk/CoordinateSystemRegistry.h>
33 #include <fwk/MagneticFieldModel.h>
34 #include <fwk/RunController.h>
35 
36 #include <det/Detector.h>
37 
38 #include <evt/Event.h>
39 #include <evt/Header.h>
40 
41 using namespace det;
42 using namespace utl;
43 using namespace atm;
44 using namespace fwk;
45 using namespace std;
46 
47 
48 const double AnalyticalCherenkovModel::fdE = 10*MeV;
49 const double AnalyticalCherenkovModel::fjMax = 800*MeV;
50 
51 const double AnalyticalCherenkovModel::fNerlingMinValidShowerAge = 0.1;
52 const double AnalyticalCherenkovModel::fNerlingMaxValidShowerAge = 2.;
53 
54 // model parameters used in AngularCDF(), AngularPDF() and CherenkovIntegral()
55 
56 
57 
58 // --------------- Hillas parameters -----------------
59 inline double HillasAngle(const double energyThreshold) {
60 
61  const double kAngHillas_aPar = 0.83;
62  const double kAngHillas_bPar = -0.67;
63 
64  // Parametrization with et in MeV
65  return kAngHillas_aPar * pow(energyThreshold/MeV, kAngHillas_bPar);
66 
67 }
68 
69 // --------------- Nerling parameters --------------
70 inline double NerlingNormA(const double s) {
71  const double kAngNerling_a0 = 4.2489e-1;
72  const double kAngNerling_a1 = 5.8371e-1;
73  const double kAngNerling_a2 = -8.2373e-2;
74  return kAngNerling_a0 + kAngNerling_a1 * s + kAngNerling_a2 * s*s;
75 }
76 
77 inline double NerlingNormB(const double s) {
78  const double kAngNerling_b0 = 5.5108e-2;
79  const double kAngNerling_b1 = -9.5587e-2;
80  const double kAngNerling_b2 = 5.6952e-2;
81  return kAngNerling_b0 + kAngNerling_b1 * s + kAngNerling_b2 * s*s;
82 }
83 
84 inline double NerlingAngle(const double energyThreshold) {
85  const double kAngNerling_thc0 = 6.2694e-1;
86  const double kAngNerling_thc1 = -6.0590e-1;
87  return kAngNerling_thc0 * pow(energyThreshold/MeV, kAngNerling_thc1);
88 }
89 
90 inline double NerlingAngleFactor(const double s) {
91  const double kAngNerling_thcc0 = 1.0509e+1;
92  const double kAngNerling_thcc1 = -4.9644;
93  return kAngNerling_thcc0 + kAngNerling_thcc1 * s;
94 }
95 
96 // ---------- Giller parameters --------------
98  eTheta0 = 0,
99  eA1,
103  eNGillerAngular, // number of independent parameters
104  eA2 // derived parameter
105 };
106 
107 double
109  const double showerAge,
110  const double height)
111 {
112  const unsigned int nValues = 4;
113  const double parArray[eNGillerAngular][nValues] =
114  { {6.058, -1.103e-3, -2.886e-3, -5.447},
115  {2.905, -3.851e-2, 1.072e-1, 1.066e+1},
116  {7.320, -3.778e-1, 7.202e-2, 7.143},
117  {-2.754, -4.242e-1, 1.084e-1, 3.344},
118  {2.620, 6.837e-2, -2.247e-2, 1.110} };
119  const double additionalC2Par = -4.294;
120 
121  const double* p = parArray[par];
122  const double s = showerAge;
123  const double h = height / km;
124 
125  if (par != eC2)
126  return p[0] * exp(p[1] * s + p[2] * h) + p[3];
127  else {
128  const double s2 = s*s;
129  return p[0] * exp(p[1] * s2 + p[2] * h) +
130  (p[3] * s2 + additionalC2Par * s);
131  }
132 }
133 
134 
135 // utility function for CherenkovIntegral - int(sin(x)*exp(-x/t)/t
136 inline double IntegralExpSin(const double x, const double t) {
137 
138  return -exp(-x/t)*(t*cos(x)+sin(x))/(t*t+1);
139 
140 }
141 
142 // utility function for CherenkovIntegral - int(sin(x)*exp(-x/t)*exp(-ax)/t
143 inline double IntegralExpSinExp(const double x, const double t, const double a) {
144 
145  return -exp(-x*(1./t+a))*(a*t*sin(x)+t*cos(x)+sin(x))/(t*t*(a*a+1)+2*t*a+1);
146 
147 }
148 
149 AnalyticalCherenkovModel::AnalyticalCherenkovModel() :
150  fArbeletcheAngularDistribution(1e6, kPiOnTwo),
151  fShowerAgePrevious(-10)
152 {
153  SetEnergyCutoff(1.*MeV);
154 }
155 
156 
160 void
162 {
163  Branch topB =
164  CentralConfig::GetInstance()->GetTopBranch("AnalyticalCherenkovModel");
165 
166  topB.GetChild("wavelength").GetData(fWavelength);
167 
168  const string param = topB.GetChild("parametrization").Get<string>();
169  if (param == "eHillas")
170  fParam = eHillas;
171  else if (param == "eGiller")
172  fParam = eGiller;
173  else if (param == "eNerling")
174  fParam = eNerling;
175  else {
176  ERROR("Parametrization not implemented");
178  }
179 
180  const string paramAngular =
181  topB.GetChild("parametrization_angular").Get<string>();
182  if (paramAngular == "eAngHillas")
184  else if (paramAngular == "eAngNerling")
186  else if (paramAngular == "eAngGiller")
188  else if (paramAngular == "eAngArbeletche")
190  else {
191  ERROR("Parametrization Angular not implemented");
193  }
194 
195  const string cherenkovDistribution =
196  topB.GetChild("cherenkovDistribution").Get<string>();
197  if (cherenkovDistribution == "symmetric")
199  else if (cherenkovDistribution == "asymmetric")
201  else {
202  ERROR("Unknown \"cherenkovDistribution\"");
204  }
205 
206  Branch asymmLimits = topB.GetChild("asymmetryCorrectionLimits");
207  asymmLimits.GetChild("minAge").GetData(fCherenkovAsymmetryMinAge);
208  asymmLimits.GetChild("maxAge").GetData(fCherenkovAsymmetryMaxAge);
209  asymmLimits.GetChild("minAngle").GetData(fCherenkovAsymmetryMinAngle);
210  asymmLimits.GetChild("maxAngle").GetData(fCherenkovAsymmetryMaxAngle);
211  asymmLimits.GetChild("minA").GetData(fCherenkovAsymmetryMinA);
212  asymmLimits.GetChild("maxA").GetData(fCherenkovAsymmetryMaxA);
213 
214  topB.GetChild("wavelengthDepRefraction").GetData(fWlRefrac);
215  topB.GetChild("lowEnergyCorrectedNerling").GetData(fLowENerling);
216 
217 }
218 
219 
225  const utl::Point& xB,
226  const utl::Point& xEye,
227  const double showerAge)
228  const
229 {
230  const double prob = fWlRefrac ? 1. : EvaluateDirectCherenkovProbability (xA, xB, xEye, showerAge);
231 
232  const TabulatedFunction& photons = EvaluateCherenkovPhotons(xA, xB, showerAge);
233 
234  const int numWaves = fWavelength.size();
235  vector<double> directCher;
236  for (int i = 0; i < numWaves; ++i) {
237  const double probWl = fWlRefrac ? EvaluateDirectCherenkovProbability (xA, xB, xEye, showerAge, fWavelength[i]) : 1.;
238  directCher.push_back(photons[i].Y()*prob*probWl);
239  }
240 
243 }
244 
245 
249 double
251  const utl::Point& xB,
252  const utl::Point& xEye,
253  const double showerAge)
254  const
255 {
256  Detector& theDetector = Detector::GetInstance();
257  const CoordinateSystemPtr cs = theDetector.GetSiteCoordinateSystem();
258  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::GetWGS84();
259 
260  const Point xMean((xA.GetX(cs) + xB.GetX(cs))*0.5,
261  (xA.GetY(cs) + xB.GetY(cs))*0.5,
262  (xA.GetZ(cs) + xB.GetZ(cs))*0.5, cs);
263  const Vector xAxEyeDir = xEye - xA;
264  const Vector xBxEyeDir = xEye - xB;
265 
266  double aBin = Angle(xAxEyeDir, xBxEyeDir);
267 
268  // Calculate impact parameter rp
269 //#warning this is not generally true! Better change the interface to the Cherenkov Model.
270  const Vector shwDir = Normalized(xB - xA);
271  const double projection = shwDir*xAxEyeDir;
272  const double rp = (xA + projection*shwDir - xEye).GetMag();
273  const Vector xMeanVec = xMean - xEye;
274 
275  // Calculate emission angle.
276  const double emissAngle = Angle(-shwDir,xMeanVec);
277 
278  const double hA = (wgs84.PointToLatitudeLongitudeHeight(xA)).get<2>();
279  const double hB = (wgs84.PointToLatitudeLongitudeHeight(xB)).get<2>();
280 
281  double lowAngle = emissAngle - aBin*0.5;
282  if (lowAngle <= 0.)
283  lowAngle = emissAngle*0.5;
284  const double highAngle = emissAngle + aBin*0.5;
285  aBin = highAngle-lowAngle;
286 
287  const ProfileResult& dvh = theDetector.GetAtmosphere().EvaluateDepthVsHeight();
288  const double atmHeightMin = dvh.MinX();
289  const double atmHeightMax = dvh.MaxX();
290  double depthA = 0.;
291  double depthB = 0.;
292  if (hA < atmHeightMin)
293  depthA = dvh.Y(atmHeightMin);
294  else if (hA > atmHeightMax)
295  depthA = dvh.Y(atmHeightMax);
296  else
297  depthA = dvh.Y(hA);
298 
299  if (hB < atmHeightMin)
300  depthB = dvh.Y(atmHeightMin);
301  else if (hB > atmHeightMax)
302  depthB = dvh.Y(atmHeightMax);
303  else
304  depthB = dvh.Y(hB);
305 
306  const double meanDepth = (depthA + depthB)*0.5;
307 
308  // Refraction Index for Middle of the Bin
309  const double nIndex = RefractionIndex::LorentzLorentz(meanDepth);
310 
311  double eTimesSine = CherenkovIntegral(lowAngle, highAngle, showerAge, nIndex);
312 
313  if (CorrectCherenkovAsymmetry(showerAge, emissAngle))
314  eTimesSine *= AsymmCorrection(xA, xB, xEye);
315 
316  // Area of tilted annulus on ground that receives
317  // light = dA, is area along line of sight = dA = 2*pi*Rp*dx
318  // where dx = distance to the track * abin = Rp/sin(emissAngle)*abin
319  // The sin(emissAngle) term is contained inside eTimesSine.
320 
321  const double dAModified = kTwoPi*rp*rp*aBin;
322 
323  return eTimesSine/dAModified;
324 }
325 
326 double
328  const utl::Point& xB,
329  const utl::Point& xEye,
330  const double showerAge,
331  const double wavelength)
332  const
333 {
334  Detector& theDetector = Detector::GetInstance();
335  const CoordinateSystemPtr cs = theDetector.GetSiteCoordinateSystem();
336  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::GetWGS84();
337 
338  const Point xMean((xA.GetX(cs) + xB.GetX(cs))*0.5,
339  (xA.GetY(cs) + xB.GetY(cs))*0.5,
340  (xA.GetZ(cs) + xB.GetZ(cs))*0.5, cs);
341  const Vector xAxEyeDir = xEye - xA;
342  const Vector xBxEyeDir = xEye - xB;
343 
344  double aBin = Angle(xAxEyeDir, xBxEyeDir);
345 
346  // Calculate impact parameter rp
347 //#warning this is not generally true! Better change the interface to the Cherenkov Model.
348  const Vector shwDir = Normalized(xB - xA);
349  const double projection = shwDir*xAxEyeDir;
350  const double rp = (xA + projection*shwDir - xEye).GetMag();
351  const Vector xMeanVec = xMean - xEye;
352 
353  // Calculate emission angle.
354  const double emissAngle = Angle(-shwDir,xMeanVec);
355 
356  const double hA = (wgs84.PointToLatitudeLongitudeHeight(xA)).get<2>();
357  const double hB = (wgs84.PointToLatitudeLongitudeHeight(xB)).get<2>();
358 
359  double lowAngle = emissAngle - aBin*0.5;
360  if (lowAngle <= 0.)
361  lowAngle = emissAngle*0.5;
362  const double highAngle = emissAngle + aBin*0.5;
363  aBin = highAngle-lowAngle;
364 
365  const ProfileResult& dvh = theDetector.GetAtmosphere().EvaluateDepthVsHeight();
366  const double atmHeightMin = dvh.MinX();
367  const double atmHeightMax = dvh.MaxX();
368  /*
369  double depthA = 0.;
370  double depthB = 0.;
371  if (hA < atmHeightMin)
372  depthA = dvh.Y(atmHeightMin);
373  else if (hA > atmHeightMax)
374  depthA = dvh.Y(atmHeightMax);
375  else
376  depthA = dvh.Y(hA);
377 
378  if (hB < atmHeightMin)
379  depthB = dvh.Y(atmHeightMin);
380  else if (hB > atmHeightMax)
381  depthB = dvh.Y(atmHeightMax);
382  else
383  depthB = dvh.Y(hB);
384 
385  const double meanDepth = (depthA + depthB)*0.5;
386  */
387 
388  const double meanHeight = ((hA+hB)/2.) < atmHeightMin ? atmHeightMin : ( ((hA+hB)/2.) > atmHeightMax ? atmHeightMax : ((hA+hB)/2.) );
389  const double pres = theDetector.GetAtmosphere().EvaluatePressureVsHeight().Y(meanHeight);
390  const double vapPres = theDetector.GetAtmosphere().EvaluateVaporPressureVsHeight().Y(meanHeight);
391  const double temp = theDetector.GetAtmosphere().EvaluateTemperatureVsHeight().Y(meanHeight);
392  // Refraction Index for Middle of the Bin
393  const double nIndex = RefractionIndex::Ciddor95(wavelength, temp, pres, vapPres);
394 
395  double eTimesSine = CherenkovIntegral(lowAngle, highAngle, showerAge, nIndex);
396 
397  if (CorrectCherenkovAsymmetry(showerAge, emissAngle))
398  eTimesSine *= AsymmCorrection(xA, xB, xEye);
399 
400  // Area of tilted annulus on ground that receives
401  // light = dA, is area along line of sight = dA = 2*pi*Rp*dx
402  // where dx = distance to the track * abin = Rp/sin(emissAngle)*abin
403  // The sin(emissAngle) term is contained inside eTimesSine.
404 
405  const double dAModified = kTwoPi*rp*rp*aBin;
406 
407  return eTimesSine/dAModified;
408 }
409 
410 bool
412 {
414  && (age > fCherenkovAsymmetryMinAge)
415  && (age < fCherenkovAsymmetryMaxAge)
416  && (theta > fCherenkovAsymmetryMinAngle)
417  && (theta < fCherenkovAsymmetryMaxAngle);
418  // a accounted for separately in the correction routine, as it requires geometry calculation
419 }
420 
428  const utl::Point& xB,
429  const double showerAge)
430  const
431 {
432 
434  if (xA == fxAprevious && xB == fxBprevious && showerAge == fShowerAgePrevious)
435  return fCherenkovPhotons;
436 
437  fxAprevious = xA;
438  fxBprevious = xB;
439  fShowerAgePrevious = showerAge;
440 
441  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::GetWGS84();
442 
443  const double distAB = (xA - xB).GetMag();
444 
445  const double hA = (wgs84.PointToLatitudeLongitudeHeight(xA)).get<2>();
446  const double hB = (wgs84.PointToLatitudeLongitudeHeight(xB)).get<2>();
447 
448  Detector& theDetector = Detector::GetInstance();
449  const ProfileResult& dvh = theDetector.GetAtmosphere().EvaluateDepthVsHeight();
450  const double atmHeightMin = dvh.MinX();
451  const double atmHeightMax = dvh.MaxX();
452  double depthA = 0.;
453  double depthB = 0.;
454  if (hA < atmHeightMin)
455  depthA = dvh.Y(atmHeightMin);
456  else if (hA > atmHeightMax)
457  depthA = dvh.Y(atmHeightMax);
458  else
459  depthA = dvh.Y(hA);
460 
461  if (hB < atmHeightMin)
462  depthB = dvh.Y(atmHeightMin);
463  else if (hB > atmHeightMax)
464  depthB = dvh.Y(atmHeightMax);
465  else
466  depthB = dvh.Y(hB);
467 
468  const double meanDepth = (depthA + depthB)*0.5;
469 
470  const double meanHeight = ((hA+hB)/2.) < atmHeightMin ? atmHeightMin : ( ((hA+hB)/2.) > atmHeightMax ? atmHeightMax : ((hA+hB)/2.) );
471  const double pres = theDetector.GetAtmosphere().EvaluatePressureVsHeight().Y(meanHeight);
472  const double vapPres = theDetector.GetAtmosphere().EvaluateVaporPressureVsHeight().Y(meanHeight);
473  const double temp = theDetector.GetAtmosphere().EvaluateTemperatureVsHeight().Y(meanHeight);
474  // Refraction Index for Middle of the Bin
475  // Assumes that fWavelength[0] is the lowest wavelength (means the lowest thresholdEnergy)
476  const double nIndex = fWlRefrac ? RefractionIndex::Ciddor95(fWavelength[0], temp, pres, vapPres) : RefractionIndex::LorentzLorentz(meanDepth);
477 
478  // Emission Threshold Energy (assumed electron mass)
479  const double thresholdEnergy = fmin(utl::CherenkovThreshold(nIndex), 2.e3*MeV);
480 
481  // From Threshold to Ultra-relativistic
482  const int numWaves = fWavelength.size();
483 
484  vector<double> nPhotons(numWaves,0);
485 
486  for (double energy = thresholdEnergy; energy < fjMax; energy += fdE) {
487 
488  double deltaLength = 0.;
489  if (fParam == eHillas) {
490  const double tl1 = CherenkovTrackLength(energy, showerAge);
491  const double tl2 = CherenkovTrackLength(energy+fdE, showerAge);
492  deltaLength = tl1 - tl2;
493  } else if (fParam == eGiller || fParam == eNerling)
494  deltaLength = CherenkovTrackLength(energy+fdE*0.5, showerAge);
495  else {
496  ERROR("Non-existent parametrization");
498  }
499 
500  const double eelec = energy + fdE*0.5;
501 
502  // Note that wavelength bins do not have to be constant
503  // RU: THIS IS A FAILURE OF THE CHERNKOV MODEL DESIGN !!!!!
504  double minWaveBin;
505  double maxWaveBin = 0;
506  for (int i = 0; i < numWaves; ++i) {
507 
508  const double nIndexWl = RefractionIndex::Ciddor95(fWavelength[i], temp, pres, vapPres);
509  const double thresholdEnergyWl = fmin(utl::CherenkovThreshold(nIndexWl), 2.e3*MeV);
510  if (fWlRefrac && (energy < thresholdEnergyWl)) continue;
511 
512  if (!i)
513  minWaveBin = fWavelength[i] - 0.5*(fWavelength[i+1] - fWavelength[i]);
514  else
515  minWaveBin = maxWaveBin;
516 
517  if (i == numWaves-1)
518  maxWaveBin = fWavelength[i] + 0.5*(fWavelength[i]-fWavelength[i-1]);
519  else
520  maxWaveBin = 0.5*(fWavelength[i] + fWavelength[i+1]);
521 
522  nPhotons[i] += deltaLength * DeltaPhotons(fWlRefrac ? nIndexWl : nIndex, eelec, distAB,
523  minWaveBin, maxWaveBin);
524  }
525  } // energy loop
526 
527 
528  const double ehigh = 10.*GeV;
529 
530  // Adding ultra-relativistic energy contribution
531  double trackLength = 0.;
532  if (fParam == eHillas)
533  trackLength = CherenkovTrackLength(fjMax, showerAge);
534  //F.N. BUG 329
535  else if (fParam == eGiller || fParam == eNerling) {
536  trackLength = 0;
537  for (double jj = fjMax; jj < ehigh; jj += fdE) {
538  trackLength += CherenkovTrackLength(jj + fdE*0.5, showerAge);
539  }
540  } else {
541  ERROR("Non-existent parametrization");
543  }
544 
545  double minWaveBin;
546  double maxWaveBin = 0;
547  for (int i = 0; i < numWaves; ++i) {
548 
549  if (!i)
550  minWaveBin = fWavelength[i] - 0.5*(fWavelength[i+1] - fWavelength[i]);
551  else
552  minWaveBin = maxWaveBin;
553 
554  if (i == numWaves-1)
555  maxWaveBin = fWavelength[i] + 0.5*(fWavelength[i] - fWavelength[i-1]);
556  else
557  maxWaveBin = 0.5*(fWavelength[i] + fWavelength[i+1]);
558 
559  const double nIndexWl = RefractionIndex::Ciddor95(fWavelength[i], temp, pres, vapPres);
560 
561  nPhotons[i] += trackLength * DeltaPhotons(fWlRefrac ? nIndexWl : nIndex, ehigh, distAB,
562  minWaveBin, maxWaveBin);
563  }
564 
566 
567  return fCherenkovPhotons;
568 }
569 
577 double
579  const double showerAge)
580  const
581 {
582 
583  if (fParam == eHillas) {
584  /* A.M. Hillas parametrization.
585 
586  "Angular and energy distributions of charged
587  particles in electron-photon cascades in air"
588 
589  J.Phys.G: Nucl.Phys. 8 --> 1982 <-- (pag: 1461-1473)
590  */
591 
592  const double e0 = (showerAge < 0.4) ?
593  26.0*MeV :
594  (44.0 - 17.0*(showerAge - 1.46)*(showerAge - 1.46))*MeV;
595 
596  // The following formula is valid for energy in units of MeV
597  const double trackLength =
598  pow((0.89*e0/MeV - 1.2)/(e0/MeV + energy/MeV), showerAge) /
599  pow((1.0 + 1.0e-4*showerAge*energy/MeV), 2);
600 
601  return trackLength;
602 
603  } else if (fParam == eGiller) {
604  /* Maria Giller parametrization.
605 
606  "Energy spectra of electrons in the extensive
607  air showers of ultra-high energy"
608 
609  J.Phys.G: Nucl.Part.Phys 30 --> 2004 <-- (pag:97-105)
610  */
611 
612  const double a = 1.005;
613  const double b = 0.06;
614  const double c = 189.0;
615 
616  const double cs = 0.111*showerAge + 0.134;
617  const double ds = 7.06*showerAge + 12.48;
618 
619  const double energyCr = 80.0*MeV;
620 
621  const double auxExponent = -(showerAge + b*log(energy/(c*energyCr)));
622 
623  const double dist_giller = cs*(1.0 - a*exp(-ds*energy/energyCr)) *
624  pow(1.0 + energy/energyCr, auxExponent);
625 
626  const double trackLength = dist_giller *(fdE/energy);
627 
628  return trackLength;
629 
630  } else if (fParam == eNerling) {
631  const double truncatedAge = fmax(fmin(fNerlingMaxValidShowerAge, showerAge),
633  const double a1 = 6.42522 - 1.53183 * truncatedAge;
634  const double a2 = 168.168 - 42.1368 * truncatedAge;
635 
636 
637  const double a0 = fNerlingFactor[0] *
638  exp(fNerlingFactor[1]*truncatedAge + fNerlingFactor[2]*truncatedAge*truncatedAge);
639 
640  // The following formula is valid for energy in units of MeV !!!
641  double dist_nerling = a0 / (energy/MeV + a1) / pow(energy/MeV + a2, truncatedAge);
642 
643  const double trackLength = dist_nerling * (fdE/MeV);
644 
645  return trackLength;
646 
647  } else {
648  ERROR("Non-existent parametrization");
650  }
651 }
652 
653 
658 double
660  const double eelec, const double dSlant,
661  const double wl1, const double wl2)
662  const
663 {
664  const double xme = kElectronMass;
665  const double delta = nIndex - 1.;
666  const double alpha = 1./137.035989;
667  const double photons = (kTwoPi*alpha) * abs(1.0/wl1 - 1.0/wl2) *
668  (2.*delta - xme*xme/(eelec*eelec))*dSlant;
669  //F.N. *(1.0 - 1.0/((beta*nIndex)*(beta*nIndex)))*dSlant;
670  return photons;
671 }
672 
673 
677 double
679  const double highAngle,
680  const double showerAge,
681  const double refractiveIndex)
682  const
683 {
684  // Threshold energy for the emission of Cherenkov light
685  const double energyThreshold = fmin(utl::CherenkovThreshold(refractiveIndex), 2.e3*MeV);
686 
687  if (fParamAngular == eAngHillas) {
688 
689  const double t = HillasAngle(energyThreshold);
690  return IntegralExpSin(highAngle,t)-IntegralExpSin(lowAngle,t);
691 
692  }
693  else if (fParamAngular == eAngNerling) {
694 
695  const double truncatedAge = fmax(fmin(fNerlingMaxValidShowerAge, showerAge),
697  const double A = NerlingNormA(truncatedAge);
698  const double B = NerlingNormB(truncatedAge);
699  const double c = NerlingAngleFactor(truncatedAge);
700  const double t1 = NerlingAngle(energyThreshold);
701  const double t2 = c * t1;
702 
703  //standard Nerling parametrization
704  if (!fLowENerling || highAngle > 30*deg) {
705  return A*(IntegralExpSin(highAngle,t1)-IntegralExpSin(lowAngle,t1))+
706  B*(IntegralExpSin(highAngle,t2)-IntegralExpSin(lowAngle,t2));
707  }
708 
709  //correction to Nerling model based on 3D CORSIKA simulations at low energy (10^15-10^17 eV)
710  double par[5] = {6.98169e-01, -2.86420e-01, 1.76411e-01/deg, -2.36286e+00, -7.70755e-02/deg};
711  return par[0]*( A*(IntegralExpSin(highAngle,t1)-IntegralExpSin(lowAngle,t1))+
712  B*(IntegralExpSin(highAngle,t2)-IntegralExpSin(lowAngle,t2)) ) +
713  exp(par[1])*( A*(IntegralExpSinExp(highAngle,t1,par[2])-IntegralExpSinExp(lowAngle,t1,par[2]))+
714  B*(IntegralExpSinExp(highAngle,t2,par[2])-IntegralExpSinExp(lowAngle,t2,par[2])) ) +
715  exp(par[3])*( A*(IntegralExpSinExp(highAngle,t1,par[4])-IntegralExpSinExp(lowAngle,t1,par[4]))+
716  B*(IntegralExpSinExp(highAngle,t2,par[4])-IntegralExpSinExp(lowAngle,t2,par[4])) );
717 
718  }
719  else if (fParamAngular == eAngArbeletche) {
720  return fArbeletcheAngularDistribution.Integral(lowAngle, highAngle, showerAge, refractiveIndex);
721  }
722  else {
723  ERROR("Non-existent parametrization");
725  return 0.;
726  }
727 
728 }
729 
730 double
732  const double verticalDepth,
733  const double showerAge)
734  const
735 {
736 
737  if (theta < 0.)
738  return 0.;
739  else if (theta > kPiOnTwo)
740  return 1.;
741 
742  // emission energy threshold
743  const double refractiveIndex = RefractionIndex::LorentzLorentz(verticalDepth);
744  const double energyThreshold =
745  fmin(utl::CherenkovThreshold(refractiveIndex), 2.e3*MeV);
746 
747  if (fParamAngular == eAngHillas) {
748 
749  const double elecAngle = HillasAngle(energyThreshold);
750  const double hillasCDF = 1.- exp(-theta/elecAngle);
751 
752  return hillasCDF;
753 
754  }
755  else if (fParamAngular == eAngNerling) {
756 
757  /* Parametrisation of Cherenkov Light Production by Air Showers
758  F. Nerling et al., Astropart.Phys.24:421-437,2006
759  */
760 
761  // shower age
762  const double s = fmax(fmin(fNerlingMaxValidShowerAge, showerAge),
764  const double A = NerlingNormA(s);
765  const double B = NerlingNormB(s);
766  const double c = NerlingAngleFactor(s);
767  const double th_c = NerlingAngle(energyThreshold);
768  const double th_cc = c * th_c;
769 
770  const double nerlingCDF = A*(1 - exp(-theta/th_c)) +
771  B*(1 - exp(-theta/th_cc));
772 
773  // normalize to 0-90 deg.
774  const double nerlingNorm = A*(1 - exp(-kPiOnTwo/th_c)) +
775  B*(1 - exp(-kPiOnTwo/th_cc));
776 
777  return nerlingCDF/nerlingNorm;
778 
779  }
780  else if (fParamAngular == eAngArbeletche) {
781  return fArbeletcheAngularDistribution.CDF(theta, showerAge, refractiveIndex);
782  }
783  else {
784  ERROR("Non-existent CDF parametrization");
786  }
787 }
788 
789 /* Helper functions for asymmetric Cherenkov correction */
791 
792 inline double
793 b1param (double theta, int species)
794 {
795  static const double b11[]=
796  {/*[electrons]=*/ -8.2e-5/(degree*degree), /*[positrons]=*/ -6.2e-5/(degree*degree)};
797  static const double b12[]=
798  {/*[electrons]=*/ 2.8e-3/degree, /*[positrons]=*/ 2.2e-3/degree};
799  static const double b13[]=
800  {/*[electrons]=*/ 1.24e-2, /*[positrons]=*/ 1.36e-2};
801 
802  return b11[species] * theta * theta + b12[species] * theta + b13[species];
803 }
804 
805 inline double
806 bparam (double a, double theta, int species)
807 {
808  static const double b2 [] = {/*[electrons]=*/ -8.4e-3, /*[positrons]=*/ -9.3e-3};
809  return b1param(theta, species) * a + b2[species];
810 }
811 
812 double
813 AnalyticalCherenkovModel::AsymmCorrection(double a, double theta, double sinphi) const
814 {
815  const double bsum = bparam (a, theta, electrons) + bparam (a, theta, positrons);
816  const double bdiff = bparam (a, theta, electrons) - bparam (a, theta, positrons);
817  const double Fs = 0.5 * bsum;
818  const double Fa = bdiff / bsum;
819 
820  const double binning = 10./360.;
821  const double A = binning - 3./8. * Fs;
822 
823  const double sinphi4=(sinphi*sinphi) * (sinphi*sinphi);
824 
825  return (1./binning) * (A + Fs * (1 + Fa*sinphi) * sinphi4);
826 }
827 
828 inline utl::Vector
829 perpendicular (const utl::Vector& v, const utl::Vector& axis)
830 {
831  return v - axis*v*axis;
832 }
833 
834 inline double
835 vectorsine (const utl::Vector& u, const utl::Vector& v)
836 {
837  return Cross(u,v).GetMag() / (u.GetMag() * v.GetMag());
838 }
839 
840 inline double
841 vectorsine_oriented (const utl::Vector& u, const utl::Vector& v, const utl::Vector& up)
842 {
843  const utl::Vector uXv = Cross(u,v);
844  const double sin = uXv.GetMag() / (u.GetMag() * v.GetMag());
845  const int neg = signbit (uXv * up); //negative if uxv not "upward"
846  return neg?-sin:sin;
847 }
848 
849 //inline double
850 //vectorsine_inplane (const utl::Vector& u, const utl::Vector& v, const utl::Vector& up)
851 //{
852 // const Vector u_perp = perpendicular (u, up);
853 // const Vector v_perp = perpendicular (v, up);
854 // return vectorsine_oriented (u_perp, v_perp, up);
855 //}
856 
857 inline utl::Vector
859 {
860  Detector& det = Detector::GetInstance();
861  return det.GetGeoMagneticFieldAt (x);
862 }
863 
864 double
866  const utl::Vector& v_toeye,
867  const utl::Vector& axis,
868  const double density) const
869 {
870  Vector b = bFieldAt (emissionPoint);
871 
872  Vector v_perp = perpendicular (v_toeye, axis);
873  Vector b_perp = perpendicular (b, axis);
874 
875  double sinphi = vectorsine_oriented (b_perp, v_perp, -axis);
876  double theta = Angle(axis, v_toeye);
877 
878  double a = b_perp.GetMag()*(1./gauss) / density * (kg/m3);
879  if (a < fCherenkovAsymmetryMinA)
880  return 1.0;
881  if (a > fCherenkovAsymmetryMaxA)
883 
884  return AsymmCorrection (a, theta, sinphi);
885 }
886 
887 double
889  const utl::Point& xB,
890  const utl::Point& xEye) const
891 {
892  utl::Vector axis=Normalized(xB - xA);
893 
894  const CoordinateSystemPtr cs = det::Detector::GetInstance().GetSiteCoordinateSystem();
895  const Point xMean((xA.GetX(cs) + xB.GetX(cs))*0.5,
896  (xA.GetY(cs) + xB.GetY(cs))*0.5,
897  (xA.GetZ(cs) + xB.GetZ(cs))*0.5, cs);
898  Vector xMeanVec = xMean-xEye;
899  double altitude=xMean.GetZ(cs);
900 
901  const Atmosphere& atmo = det::Detector::GetInstance().GetAtmosphere();
902  double density = atmo.EvaluateDensityVsHeight().Y(altitude);
903 
904  return AsymmCorrection(xMean, -xMeanVec, axis, density);
905 }
906 
907 
908 // Old Cherenkov asymmetry parametrisation:
909 
910 double
911 fparam (double a, double theta, int species)
912 {
913  static const double b11[]={/*[electrons]=*/ 0.00206, /*[positrons]=*/0.00156};
914  static const double b12[]={/*[electrons]=*/ 0.0072, /*[positrons]=*/ 0.0065};
915 
916  const double b1=b11[species] * theta/degree + b12[species];
917  return b1 * a*a;
918 }
919 
920 double
922  const double bp,
923  const double sinphi) const
924 {
925  const double binning=10./360.;
926  const double sinphi4= (sinphi*sinphi)*(sinphi*sinphi);
927  return (1./binning) * ( (binning - bp*3./8.) + bp * (1+bm*sinphi) * sinphi4 );
928 }
929 
930 double
932  const utl::Vector& axis,
933  const double density) const
934 {
935  CoordinateSystemPtr siteCS = det::Detector::GetInstance().GetSiteCoordinateSystem();
936  Vector b(24.6*microtesla, 55*degree, 87*degree, siteCS, Vector::kSpherical);
937 
938  Vector v_perp = perpendicular (v_toeye, axis);
939  Vector b_perp = perpendicular (b, axis);
940 
941  double sinphi = vectorsine_oriented (b_perp, v_perp, -axis);
942  double theta = Angle(axis, v_toeye);
943 
944  double a = b_perp.GetMag()*(1./gauss) / density * (kg/m3);
945  double fm = fparam (a, theta, electrons);
946  double fp = fparam (a, theta, positrons);
947  double bm = (fm - fp) / (fm + fp);
948  double bp = 0.5* (fm + fp);
949 
950  return AsymmCorrectionOld (bm, bp, sinphi);
951 }
952 
953 
954 double
956  const double verticalDepth,
957  const double showerAge)
958  const
959 {
960 
961  if (theta < 0.)
962  return 0.;
963  else if (theta > kPiOnTwo)
964  return 0.;
965 
966  // emission energy threshold
967  const double refractiveIndex = RefractionIndex::LorentzLorentz(verticalDepth);
968  const double energyThreshold =
969  fmin(utl::CherenkovThreshold(refractiveIndex), 2.e3*MeV);
970 
971  if (fParamAngular == eAngHillas) {
972 
973  const double elecAngle = HillasAngle(energyThreshold);
974  // normalize to 0-90 deg.
975  const double hillasNorm = 1.- exp(-kPiOnTwo/elecAngle);
976  return exp(-theta/elecAngle)/hillasNorm;
977 
978  }
979  else if (fParamAngular == eAngNerling) {
980 
981  // shower age
982  const double s = fmax(fmin(fNerlingMaxValidShowerAge, showerAge),
984  const double A = NerlingNormA(s);
985  const double B = NerlingNormB(s);
986  const double c = NerlingAngleFactor(s);
987  const double th_c = NerlingAngle(energyThreshold);
988  const double th_cc = c * th_c;
989 
990  // normalize to 0-90 deg.
991  const double nerlingNorm = A*(1 - exp(-kPiOnTwo/th_c)) +
992  B*(1 - exp(-kPiOnTwo/th_cc));
993 
994  return (A*exp(-theta/th_c)/th_c+B*exp(-theta/th_cc)/th_cc)/nerlingNorm;
995 
996  }
997  else if (fParamAngular == eAngGiller) {
998 
999  const ProfileResult& heightVsDepth =
1000  Detector::GetInstance().GetAtmosphere().EvaluateHeightVsDepth();
1001  const double height =
1002  heightVsDepth.Y(fmin(fmax(verticalDepth, heightVsDepth.MinX()),
1003  heightVsDepth.MaxX()));
1004 
1005  const double theta0 = GetGillerAngularParameter(eTheta0, showerAge, height);
1006  if (theta < theta0) {
1007  const double a1 = GetGillerAngularParameter(eA1, showerAge, height);
1008  const double c1 = GetGillerAngularParameter(eC1, showerAge, height);
1009  const double c2 = GetGillerAngularParameter(eC2, showerAge, height);
1010  return a1 * exp(-(c1 * theta + c2 * theta*theta));
1011  }
1012  else {
1013  const double alpha = GetGillerAngularParameter(eAlpha, showerAge, height);
1014  const double a2 = GetGillerAngularParameter(eA2, showerAge, height);
1015  return a2 * pow(theta, -alpha);
1016  }
1017  }
1018  else if (fParamAngular == eAngArbeletche) {
1019  return fArbeletcheAngularDistribution.PDF(theta, showerAge, refractiveIndex);
1020  }
1021  else {
1022  ERROR("Non-existent PDF parametrization");
1024  }
1025 }
1026 
1027 
1028 void
1030  const
1031 {
1032  if (!fNerlingFactor.empty() && fEcut == ecut)
1033  return;
1034 
1035  fEcut = ecut;
1036 
1037  /* F. Nerling et al., Astropart.Phys.24:421-437,2006 */
1038 
1039  const int nEnergies = 6;
1040 
1041  const double Ecut[nEnergies] = { 2.*MeV, 1.*MeV, 0.5*MeV,
1042  0.25*MeV, 0.1*MeV, 0.05*MeV };
1043 
1044  const double p0[nEnergies] = { 1.48071e-01, 1.45098e-01,
1045  1.43458e-01, 1.42589e-01,
1046  1.42049e-01, 1.41866e-01 };
1047 
1048  const double p1[nEnergies] = { 6.22334, 6.20114,
1049  6.18979, 6.18413,
1050  6.18075, 6.17963 };
1051 
1052  const double p2[nEnergies] = { -5.89710e-01, -5.96851e-01,
1053  -6.01298e-01, -6.03838e-01,
1054  -6.05484e-01, -6.06055e-01 };
1055 
1056  int i1, i2;
1057  if (fEcut > Ecut[0]) {
1058  i1 = 0;
1059  i2 = 1;
1060  } else {
1061  i1 = nEnergies - 2;
1062  i2 = nEnergies - 1;
1063 
1064  for (int i = 1; i < nEnergies; ++i)
1065  if (fEcut > Ecut[i]) {
1066  i1 = i - 1;
1067  i2 = i;
1068  break;
1069  }
1070  }
1071 
1072  fNerlingFactor.resize(3);
1073  fNerlingFactor[0] =
1074  p0[i1] + (p0[i2] - p0[i1])/(Ecut[i2] - Ecut[i1]) * (fEcut - Ecut[i1]);
1075  fNerlingFactor[1] =
1076  p1[i1] + (p1[i2] - p1[i1])/(Ecut[i2] - Ecut[i1]) * (fEcut - Ecut[i1]);
1077  fNerlingFactor[2] =
1078  p2[i1] + (p2[i2] - p2[i1])/(Ecut[i2] - Ecut[i1]) * (fEcut - Ecut[i1]);
1079 }
1080 
1081 // Configure (x)emacs for this file ...
1082 // Local Variables:
1083 // mode: c++
1084 // compile-command: "make -C .. -k"
1085 // End:
Branch GetTopBranch() const
Definition: Branch.cc:63
double NerlingAngle(const double energyThreshold)
AxialVector Cross(const Vector &l, const Vector &r)
Definition: OperationsAV.h:25
double CherenkovIntegral(double lowAngle, double highAngle, double meanShowerAge, double refractiveIndex) const
Calculation of the Cherenkov angle integral int(df/dtheta*sin(theta))
Top of the interface to Atmosphere information.
utl::TabulatedFunction & EvaluateCherenkovPhotons(const utl::Point &xA, const utl::Point &xB, const double showerAge) const
Calculation of Cherenkov photons produced between two points.
const double degree
Point object.
Definition: Point.h:32
Parametrization fParam
Parametrization to be used in the Cherenkov light calculation.
double Angle(const double theta1, const double phi1, const double theta2, const double phi2)
double b1param(double theta, int species)
constexpr double kElectronMass
double AngularPDF(const double theta, const double verticalDepth, const double showerAge) const
double fEcut
electron energy-cutoff
double NerlingAngleFactor(const double s)
Class to hold collection (x,y) points and provide interpolation between them.
const atm::ProfileResult & EvaluatePressureVsHeight() const
Tabulated function giving Y=air pressure as a function of X=height.
const atm::ProfileResult & EvaluateDensityVsHeight() const
Tabulated function giving Y=density as a function of X=height.
double fparam(double a, double theta, int species)
double GetMag() const
Definition: AxialVector.h:56
utl::Vector GetGeoMagneticFieldAt(const utl::Point &location)
Definition: Detector.cc:238
void Init()
Init method of the model.
Arbeletche2021CherenkovAngularModel fArbeletcheAngularDistribution
double CDF(const double theta, const double showerAge, const double refractiveIndex) const
Computes the CDF of the angular distribution of Cherenkov photons.
Base class for exceptions trying to access non-existing components.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
utl::CoordinateSystemPtr GetSiteCoordinateSystem() const
Get the coordinate system for the site.
Definition: Detector.h:137
double pow(const double x, const unsigned int i)
utl::TabulatedFunction fCherenkovPhotons
Number of photons as function of the wavelength.
double GetMag() const
Definition: Vector.h:58
utl::Point fxAprevious
Keep reference to previous calculation, to prevent re-calculation.
double PDF(const double theta, const double showerAge, const double refractiveIndex) const
Computes the PDF of the angular distribution of Cherenkov photons.
double Y(const double x) const
Get the Y value (coordinate) for given X (ordinate)
constexpr double deg
Definition: AugerUnits.h:140
constexpr double MeV
Definition: AugerUnits.h:184
double AngularCDF(const double theta, const double verticalDepth, const double showerAge) const
bool CorrectCherenkovAsymmetry(double age, double theta) const
utl::Vector bFieldAt(utl::Point x)
const atm::Atmosphere & GetAtmosphere() const
Definition: Detector.h:113
T Get() const
Definition: Branch.h:271
double NerlingNormB(const double s)
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
Class representing a document branch.
Definition: Branch.h:107
const double gauss
Definition: GalacticUnits.h:41
constexpr double s
Definition: AugerUnits.h:163
Reference ellipsoids for UTM transformations.
double IntegralExpSinExp(const double x, const double t, const double a)
double CherenkovThreshold(const double nRef)
Calculate the electron Cherenkov threshold energy for refraction index.
double DeltaPhotons(double nIndex, double eelec, double dSlant, double wl1, double wl2) const
Calculation of Cherenkov Photons between two wavelengths.
double GetX(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:206
Class describing the Atmospheric profile.
Definition: ProfileResult.h:25
double EvaluateDirectCherenkovProbability(const utl::Point &xA, const utl::Point &xB, const utl::Point &xEye, const double showerAge) const
double abs(const SVector< n, T > &v)
constexpr double m3
Definition: AugerUnits.h:123
double HillasAngle(const double energyThreshold)
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.
Top of the hierarchy of the detector description interface.
Definition: Detector.h:81
Triple PointToLatitudeLongitudeHeight(const Point &thePoint) const
Convert Point to Lat/Long/Height.
constexpr double kTwoPi
Definition: MathConstants.h:27
const atm::ProfileResult & EvaluateDepthVsHeight() const
Tabulated function giving Y=depth as a function of X=height.
constexpr double kPiOnTwo
Definition: MathConstants.h:25
const double km
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
constexpr double microtesla
Definition: AugerUnits.h:250
a second level trigger
Definition: XbT2.h:8
AxialVector Normalized(const AxialVector &v)
Definition: AxialVector.h:81
double bparam(double a, double theta, int species)
double IntegralExpSin(const double x, const double t)
double LorentzLorentz(const double verticalDepth)
Calculate the refraction index for a given depth.
static const double fNerlingMinValidShowerAge
Limits for the shower age in the use of the Nerling parameterizations.
double GetGillerAngularParameter(const EGillerAngularParameter par, const double showerAge, const double height)
double vectorsine_oriented(const utl::Vector &u, const utl::Vector &v, const utl::Vector &up)
double GetY(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:209
double MinX() const
Return the minimum value for X (ordinate) stored in the profile.
constexpr double GeV
Definition: AugerUnits.h:187
double Ciddor95(const double wl, const double temperature, const double pressure, const double vaporPressure)
Wavelength-dependent index of refraction for humid air.
double AsymmCorrection(double a, double theta, double sinphi) const
Asymmetry correction as local parametrisation.
Vector object.
Definition: Vector.h:30
double NerlingNormA(const double s)
static const double fNerlingMaxValidShowerAge
double GetZ(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:212
utl::TabulatedFunction & EvaluateCherenkovDirect(const utl::Point &xA, const utl::Point &xB, const utl::Point &xEye, const double showerAge) const
Calculation of probability of Cherenkov emission.
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
std::vector< double > fWavelength
Wavelengths to calculate the light production.
void SetEnergyCutoff(double ecut) const
const atm::ProfileResult & EvaluateVaporPressureVsHeight() const
Tabulated function giving Y=H20 vapor pressure as a function of X=height.
double AsymmCorrectionOld(double bm, double bp, double sinphi) const
Asymmetry correction with old local parametrisation.
utl::Vector perpendicular(const utl::Vector &v, const utl::Vector &axis)
const atm::ProfileResult & EvaluateTemperatureVsHeight() const
Tabulated function giving Y=temperature as a function of X=height.
double CherenkovTrackLength(double energy, double showerAge) const
Calculation of a special track length to be used in the Cherenkov light evaluation.
double MaxX() const
Return the maximum value for X (ordinate) stored in the profile.
constexpr double kg
Definition: AugerUnits.h:199
double vectorsine(const utl::Vector &u, const utl::Vector &v)
utl::TabulatedFunction fDirectCherenkovPhotons

, generated on Tue Sep 26 2023.