ShowerLightSimulator.cc
Go to the documentation of this file.
1 
12 #include <utl/Reader.h>
13 #include <utl/ErrorLogger.h>
14 #include <utl/AugerUnits.h>
15 #include <utl/MathConstants.h>
16 #include <utl/PhysicalConstants.h>
17 #include <utl/PhysicalFunctions.h>
18 #include <utl/Vector.h>
19 #include <utl/Point.h>
20 #include <utl/TabulatedFunction.h>
21 #include <utl/TabulatedFunctionErrors.h>
22 #include <utl/MultiTabulatedFunction.h>
23 #include <utl/UTMPoint.h>
24 #include <utl/Particle.h>
25 #include <utl/GeometryUtilities.h>
26 
27 #include <evt/Event.h>
28 #include <evt/ShowerSimData.h>
29 
30 #include <fwk/CentralConfig.h>
31 #include <fwk/CoordinateSystemRegistry.h>
32 
33 #include <det/Detector.h>
34 #include <fdet/FDetector.h>
35 #include <fdet/Eye.h>
36 
37 #include <atm/AttenuationResult.h>
38 #include <atm/ProfileResult.h>
39 #include <atm/Atmosphere.h>
40 #include <atm/InclinedAtmosphericProfile.h>
41 
42 #include <CLHEP/Random/Randomize.h>
43 
44 #include "ShowerLightSimulator.h"
45 
46 #include <boost/tuple/tuple.hpp>
47 #include <boost/tuple/tuple_io.hpp>
48 
49 using namespace ShowerLightSimulatorKG;
50 using namespace std;
51 using namespace utl;
52 using namespace evt;
53 using namespace fwk;
54 using namespace det;
55 using namespace fdet;
56 using namespace atm;
57 
58 
61 {
62  const Branch topB = CentralConfig::GetInstance()->GetTopBranch("ShowerLightSimulatorKG");
63 
64  fPrimaryCherenkov = false;
65  topB.GetChild("fluorescence").GetData(fFluorescence);
66  topB.GetChild("cherenkov").GetData(fCherenkov);
67  topB.GetChild("cherenkovFromCORSIKA").GetData(fUseCherenkovProfile);
68  topB.GetChild("flatEarth").GetData(fFlatEarth);
69  topB.GetChild("timeBin").GetData(fTimeBin);
70 
71  // info output
72  ostringstream info;
73  info << " Version: "
74  << GetVersionInfo(VModule::eRevisionNumber) << "\n"
75  " Parameters:\n"
76  " fluorescence: " << (fFluorescence ? "ON" : "OFF") << "\n"
77  " cherenkov: " << (fCherenkov ? "ON" : "OFF") << "\n"
78  " cherenkov mode: " << (fUseCherenkovProfile ? "CORSIKA" : "model") << "\n"
79  " atmosphere geometry: " << (fFlatEarth ? "FLAT" : "CURVED") << '\n';
80 
81  INFO(info);
82 
83  return eSuccess;
84 }
85 
86 
89 {
90  if (!event.HasSimShower()) {
91  ERROR("Event has no simulated shower.");
92  return eFailure;
93  }
94 
95  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84);
96  Detector& theDetector = Detector::GetInstance();
97 
98  const evt::ShowerSimData& simShower = event.GetSimShower();
99 
100  CoordinateSystemPtr showerCS = simShower.GetShowerCoordinateSystem();
101  Point showerCore(0, 0, 0, showerCS);
102  Vector showerDir(0, 0, -1, showerCS);
103 
104  // just needed for flat geometry
105  const CoordinateSystemPtr localCS = simShower.GetLocalCoordinateSystem();
106  const double zenith = (-simShower.GetDirection()).GetTheta(localCS);
107  const double cosTheta = cos (zenith); // JUST FOR LIMITS
108  const double absCosTheta = std::fabs(cosTheta);
109  const double coreZ = wgs84.PointToLatitudeLongitudeHeight (showerCore).get<2>(); // JUST FOR FLAT-EARTH
110 
111  //double cosTheta = std::fabs(cos (zenith)); // JUST FOR LIMITS
112  //double Xfirst = simShower.GetXFirst();
113 
114  const ProfileResult& heightVsDepth = theDetector.GetAtmosphere().EvaluateHeightVsDepth();
115 
116  const ProfileResult* distanceVsSlantDepth = 0;
117  double atmDepthMin = 0;
118  double atmDepthMax = 0;
119  double atmDistanceMin = 0;
120  double atmDistanceMax = 0;
121 
122  bool upward = false;
123  bool skimming = false;
124 
125  if (!fFlatEarth) {
126 
127  try {
128 
129  // theDir has to point to where the shower is comming from !!!!
130  // this is now done in FieldOfViewCalculator:
131  // theDetector.GetAtmosphere().InitSlantProfileModel(showerCore, -showerDir, 10.*g/cm2);
132 
133  distanceVsSlantDepth = &theDetector.GetAtmosphere().EvaluateDistanceVsSlantDepth();
134 
135 
136  // get the LIMITS of the slant atmosphere
137  atmDepthMin = distanceVsSlantDepth->MinX();
138  atmDepthMax = distanceVsSlantDepth->MaxX();
139  atmDistanceMin = distanceVsSlantDepth->Y(atmDepthMin);
140  atmDistanceMax = distanceVsSlantDepth->Y(atmDepthMax);
141 
142  const ProfileResult& slantDepthVsDistance = theDetector.GetAtmosphere().EvaluateSlantDepthVsDistance();
143  const ProfileResult& heightVsDistance = theDetector.GetAtmosphere().EvaluateHeightVsDistance();
144  upward = (heightVsDistance.Y(atmDistanceMin) < heightVsDistance.Y(atmDistanceMax) - 1.*m); // security margin
145  skimming = ((upward ? heightVsDistance.Y(atmDistanceMin) : heightVsDistance.Y(atmDistanceMax)) >
146  heightVsDistance.Y((atmDistanceMax+atmDistanceMin)/2));
147  double atmHeightMax = (upward ? heightVsDistance.Y(atmDistanceMax) : heightVsDistance.Y(atmDistanceMin));
148  double atmHeightMin = (skimming ? heightVsDistance.Y((atmDistanceMax+atmDistanceMin)/2) :
149  (upward ? heightVsDistance.Y(atmDistanceMin) : heightVsDistance.Y(atmDistanceMax)));
150 
151  // check against limits of vertical atmosphere
152  const ProfileResult& temperaturProfile = theDetector.GetAtmosphere().EvaluateTemperatureVsHeight();
153  const ProfileResult& densityProfile = theDetector.GetAtmosphere().EvaluateDensityVsHeight();
154 
155  if (atmHeightMax>temperaturProfile.MaxX() ||
156  atmHeightMax>densityProfile.MaxX()) {
157 
158  atmHeightMax = std::min(temperaturProfile.MaxX(), densityProfile.MaxX()) - 1.*mm; // security margin
159  const vector<Point> intersections =
160  Intersection(ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84), atmHeightMax,
161  Line(showerCore, -showerDir));
162  if (intersections.size() != 2) {
163  ostringstream warn;
164  warn << "\n"
165  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
166  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
167  "Number of intersections point of air shower axis with ellipsoid of height="
168  << atmHeightMax/km << " km is: " << intersections.size()
169  << " ---> skip event\n"
170  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
171  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
172  ERROR(warn);
173  return eContinueLoop;
174  }
175 
176  if (upward) { // -> redefine exit point
177 
178  atmDistanceMax = (intersections[0] - showerCore).GetMag();
179  atmDepthMax = slantDepthVsDistance.Y(atmDistanceMax);
180 
181  } else { // -> redefine entry point
182 
183  atmDistanceMin = (intersections[1] - showerCore).GetMag();
184  atmDepthMin = slantDepthVsDistance.Y(atmDistanceMin);
185 
186  if (skimming) {
187  atmDistanceMax = std::min(atmDistanceMax, (intersections[0] - showerCore).GetMag());
188  atmDepthMax = slantDepthVsDistance.Y(atmDistanceMax);
189  atmHeightMin = heightVsDistance.Y((atmDistanceMax + atmDistanceMin)/2);
190  }
191  }
192  }
193 
194  if (atmHeightMin<temperaturProfile.MinX() ||
195  atmHeightMin<densityProfile.MinX()) {
196 
197  atmHeightMin = std::max(temperaturProfile.MinX(), densityProfile.MinX()) + 1.*mm; // security margin
198  const vector<Point> intersections =
199  Intersection(ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84), atmHeightMin,
200  Line(showerCore, -showerDir));
201 
202  if (intersections.size() != 2) {
203  ostringstream warn;
204  warn << "\n"
205  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
206  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
207  "Number of intersections point of air shower axis with ellipsoid of height="
208  << atmHeightMin/km << " km is: " << intersections.size()
209  << " ---> skip event\n"
210  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
211  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
212  ERROR(warn);
213  return eContinueLoop;
214  }
215 
216  if (upward) { // -> redefine entry point
217 
218  atmDistanceMin = (intersections[0] - showerCore).GetMag();
219  atmDepthMin = slantDepthVsDistance.Y(atmDistanceMin);
220 
221  } else { // -> redefine exit point
222 
223  atmDistanceMax = (intersections[1] - showerCore).GetMag();
224  atmDepthMax = slantDepthVsDistance.Y(atmDistanceMax);
225 
226  if (skimming) {
227  atmDistanceMin = std::max(atmDistanceMin, (intersections[0] - showerCore).GetMag());
228  atmDepthMin = slantDepthVsDistance.Y(atmDistanceMin);
229  atmHeightMax = heightVsDistance.Y((atmDistanceMax + atmDistanceMin)/2);
230  }
231  }
232  }
234 
235  ERROR(string("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
236  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
237  "InclinedAtmosphericProfile EXCEPTION:\n") + e.GetMessage() +
238  // " ---> switch back to FLAT GEOMETRY\n"
239  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
240  "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
241  //fFlatEarth = true; // -> GO ON ANYWAY
242  return eContinueLoop; // -> SKIP EVENT
243  }
244  }
245 
246  if (fFlatEarth) {
247 
248  upward = (cosTheta < 0);
249  // skimming not suppoted for flat geometry !!
250 
251  // check against limits of flat atmosphere
252  const ProfileResult& temperaturProfile = theDetector.GetAtmosphere().EvaluateTemperatureVsHeight();
253  const ProfileResult& densityProfile = theDetector.GetAtmosphere().EvaluateDensityVsHeight();
254  const ProfileResult& depthVsHeight = theDetector.GetAtmosphere().EvaluateDepthVsHeight();
255 
256  // get the LIMITS of the atmosphere
257  atmDistanceMin = (coreZ - depthVsHeight.MinX()) / absCosTheta;
258  atmDistanceMin = std::max(atmDistanceMin, (coreZ - densityProfile.MinX()) / absCosTheta);
259  atmDistanceMin = std::max(atmDistanceMin, (coreZ - temperaturProfile.MinX()) / absCosTheta);
260 
261  atmDistanceMax = (coreZ - depthVsHeight.MaxX()) / absCosTheta;
262  atmDistanceMax = std::min(atmDistanceMax, (coreZ - densityProfile.MaxX()) / absCosTheta);
263  atmDistanceMax = std::min(atmDistanceMax, (coreZ - temperaturProfile.MaxX()) / absCosTheta);
264 
265  atmDepthMin = depthVsHeight.Y(coreZ - atmDistanceMax*absCosTheta);
266  atmDepthMax = depthVsHeight.Y(coreZ - atmDistanceMin*absCosTheta);
267 
268  atmDepthMin /= absCosTheta;
269  atmDepthMax /= absCosTheta;
270 
271  ostringstream info;
272  info << "Flat geometry with Theta=" << zenith/deg;
273  INFO(info);
274  }
275 
276  // print some information for the logfile
277  ostringstream info;
278  info << "Atmosphere starts at " << atmDepthMin/g*cm*cm << "g/cm2, "
279  "d=" << atmDistanceMin/m << "m, "
280  "and ends at " << atmDepthMax/g*cm*cm << "g/cm2, "
281  "d=" << atmDistanceMax/m << "m";
282  INFO(info);
283 
284  // RESTRICT SHOWER TO VALID ATMOSPHERE !!!!!!!!!!!!!!!!!!!!!!! ------------
285  bool truncated = false;
286 
287  if (!simShower.HasdEdX()) {
288  ERROR("The shower has no energy deposit profile.");
289  return eFailure;
290  }
291  const TabulatedFunction& energyDep = simShower.GetdEdX();
292  if (!energyDep.GetNPoints()) {
293  ERROR("The shower has an empty energy deposit profile.");
294  return eFailure;
295  }
296  int iFirstValidBin_dEdX = 0;
297  int iLastValidBin_dEdX = energyDep.GetNPoints() - 1;
298 
299  if (!simShower.HasLongitudinalProfile()) {
300  ERROR("The shower has no charge profile.");
301  return eFailure;
302  }
303  const TabulatedFunction& chargeProfile = simShower.GetLongitudinalProfile();
304  if (!chargeProfile.GetNPoints()) {
305  ERROR("The shower has an empty charge profile.");
306  return eFailure;
307  }
308  int iFirstValidBin_Ne = 0;
309  int iLastValidBin_Ne = chargeProfile.GetNPoints() - 1;
310 
311  const double profDepthMin =
312  std::max(energyDep[iFirstValidBin_dEdX].X(), chargeProfile[iFirstValidBin_Ne].X());
313  const double profDepthMax =
314  std::min(energyDep[iLastValidBin_dEdX].X(), chargeProfile[iLastValidBin_Ne].X());
315 
316  info.str("");
317  info << "Shower profile starts at " << profDepthMin/g*cm*cm << " g/cm2, "
318  "and ends at " << profDepthMax/g*cm*cm << " g/cm2";
319  INFO(info);
320 
321  // check dEdX profile
322  while (energyDep[iFirstValidBin_dEdX].X() < atmDepthMin &&
323  iFirstValidBin_dEdX<iLastValidBin_dEdX) {
324  ++iFirstValidBin_dEdX;
325  truncated = true;
326  }
327 
328  while (energyDep[iLastValidBin_dEdX].X() > atmDepthMax &&
329  iLastValidBin_dEdX>iFirstValidBin_dEdX) {
330  --iLastValidBin_dEdX;
331  truncated = true;
332  }
333 
334  // check Ne profile
335  while (chargeProfile[iFirstValidBin_Ne].X() < atmDepthMin &&
336  iFirstValidBin_Ne<iLastValidBin_Ne) {
337  ++iFirstValidBin_Ne;
338  truncated = true;
339  }
340 
341  while (chargeProfile[iLastValidBin_Ne].X() > atmDepthMax &&
342  iLastValidBin_Ne>iFirstValidBin_Ne) {
343  --iLastValidBin_Ne;
344  truncated = true;
345  }
346 
347  const double truncDepthMin =
348  std::max(energyDep[iFirstValidBin_dEdX].X(), chargeProfile[iFirstValidBin_Ne].X());
349 
350  const double truncDepthMax =
351  std::min(energyDep[iLastValidBin_dEdX].X(), chargeProfile[iLastValidBin_Ne].X());
352 
353  //
354  double truncHeightMax;
355  double truncHeightMin;
356  if (fFlatEarth) {
357  truncHeightMax = heightVsDepth.Y(truncDepthMin*absCosTheta);
358  truncHeightMin = heightVsDepth.Y(truncDepthMax*absCosTheta);
359  } else {
360  const ProfileResult& heightVsSlantDepth =
362  truncHeightMax = heightVsSlantDepth.Y(truncDepthMin);
363  truncHeightMin = heightVsSlantDepth.Y(truncDepthMax);
364  }
365  // -----------------------------------------------------------------------
366 
367  // ------- determine start and stop of observation along shower axis --------
368  double distInitCore;
369  double distFinalCore;
370  if (fFlatEarth) {
371  distInitCore = (coreZ-truncHeightMax) / cosTheta;
372  distFinalCore = (coreZ-truncHeightMin) / cosTheta;
373  } else {
374  distInitCore = distanceVsSlantDepth->Y(truncDepthMin);
375  distFinalCore = distanceVsSlantDepth->Y(truncDepthMax);
376  }
377  // -------------------------------------------------------------------------
378 
379  /*
380  for DOWNWARD shower:
381  distance from core to init is smaller than to final point of atmosphere
382 
383  for UPWARD shower:
384  distance from core to init is bigger than to final point of atmosphere
385 
386 
387  DOWNWARD GOING UPWARD GOING
388  / /
389  init --> \/ ^
390  / /
391  / /
392  / /
393  final --> / /
394  -------------X-----------------X----
395  CORE CORE
396 
397  */
398 
399  //const bool upwardGoing = (distInitCore>distFinalCore);
400  //if (upwardGoing) swap(distFinalCore, distInitCore);
401 
402  if (truncated) {
403  info.str("");
404  info << "Profile was truncated to fit the Atmosphere:\n"
405  " -> start at "
406  "X=" << truncDepthMin/g*cm2 << "g/cm2, "
407  "h=" << truncHeightMax/m << "m, "
408  "d=" << distInitCore/m << "m\n"
409  " -> end at "
410  "X=" << truncDepthMax/g*cm2 << "g/cm2, "
411  "h=" << truncHeightMin/m << "m, "
412  "d=" << distFinalCore/m << "m";
413  INFO(info);
414  } else {
415  info.str("");
416  info << "Profile fits into the Atmosphere definition:\n"
417  " -> start at "
418  "h=" << truncHeightMax/m << "m, "
419  "d=" << distInitCore/m << "m\n"
420  " -> end at "
421  "h=" << truncHeightMin/m << "m, "
422  "d=" << distFinalCore/m << "m";
423  INFO(info);
424  }
425 
426  if (upward) {
427  INFO ("shower is UPWARD going");
428  }
429 
430  if (fFluorescence) {
431  // Calculate Fluorescence light
432  FluorescenceLight(event, distInitCore, distFinalCore, upward);
433  }
434 
435  if (fCherenkov) {
436 
437  if (!simShower.HasGHParameters()) {
438  ERROR("Shower without GH-fit: Cannot compute Cherenkov light ! SKIPPING !");
439  // return eFailure;
440  return eContinueLoop;
441  }
442 
443  // Calculate Cherenkov light beam
444  CherenkovLight(event, distInitCore, distFinalCore, upward, fUseCherenkovProfile);
445  }
446 
447  if (fPrimaryCherenkov) {
448  // Calculate Cherenkov light of primary
449  PrimaryCherenkovLight(event);
450  }
451 
452  return eSuccess;
453 }
454 
455 
458 {
459  return eSuccess;
460 
461 }
462 
463 
471 void
473  double distInitCore,
474  double distFinalCore,
475  bool /*upwardGoing*/)
476 {
477  INFO ("Calculating fluorescence light");
478 
479  Detector& theDetector = Detector::GetInstance();
480  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84);
481 
482  evt::ShowerSimData& simShower = event.GetSimShower();
483  CoordinateSystemPtr showerCS = simShower.GetShowerCoordinateSystem();
484  Point core(0, 0, 0, showerCS);
485  Vector axis(0, 0, -1, showerCS);
486 
487  // just needed for flat geometry
488  const CoordinateSystemPtr localCS = simShower.GetLocalCoordinateSystem();
489  const double cosTheta = (-simShower.GetDirection()).GetCosTheta(localCS);
490  const double absCosTheta = std::fabs(cosTheta);
491  const double coreZ = wgs84.PointToLatitudeLongitudeHeight(core).get<2>();
492 
493  const ProfileResult& slantDepthVsDistance = // for curved geometry
495  const ProfileResult& depthVsHeight = // for flat geometry
496  theDetector.GetAtmosphere().EvaluateDepthVsHeight();
497 
498  const double distInitFinal = std::fabs(distFinalCore - distInitCore);
499  const double delta = kSpeedOfLight*fTimeBin;
500  const int nPoints = int(distInitFinal / delta);
501 
502  const TabulatedFunction& energyDep = simShower.GetdEdX();
503 
504  const double dEdX0 = theDetector.GetAtmosphere().GetdEdX0();
505 
506  // Vector of wavelengths
507  const vector<double>& wavelengths =
508  theDetector.GetAtmosphere().GetWavelengths(Atmosphere::eFluorescence);
509  unsigned int nWaves = wavelengths.size();
510 
511  for (unsigned int iwl = 0; iwl < nWaves; ++iwl) {
512  if (!simShower.HasFluorescencePhotons(iwl)) {
513  TabulatedFunction fEmpty;
514  simShower.AddFluorescencePhotons(fEmpty, iwl);
515  } else {
516  ERROR("Flourescence light produced twice !!!!!");
517  }
518  }
519 
520  //const double distanceStart = ( upwardGoing ? distFinalCore : distInitCore );
521  const double distanceStart = distInitCore;
522 
523  for (int i = 0; i < nPoints; ++i) {
524 
525  const double distance = distanceStart + delta * (0.5 + i); // take bin middle because of Cherenkov ...
526  //const double distance = distanceStart + delta*i;
527  //double distance = distFinalCore - delta * (.5+i);
528 
529  double heightBin;
530  double depthBin;
531 
532  if (fFlatEarth) {
533  heightBin = coreZ - distance * cosTheta;
534  depthBin = depthVsHeight.Y(heightBin) / absCosTheta;
535  } else {
536  Point p (core + axis * distance);
537  heightBin = wgs84.PointToLatitudeLongitudeHeight(p).get<2>();
538  depthBin = slantDepthVsDistance.Y(distance);
539  }
540 
541  const TabulatedFunction& spectrum =
542  theDetector.GetAtmosphere().EvaluateFluorescenceYield(heightBin);
543 
544  // double cPh = 0;
545 
546  for (unsigned int iwl = 0; iwl < nWaves; ++iwl) {
547 
548  if (!simShower.HasFluorescencePhotons(iwl)) {
549  ERROR ("Data structure SimShowerData not initialized properly!");
550  continue;
551  }
552 
553  const double photons = spectrum[iwl].Y()*energyDep.Y(depthBin)/dEdX0; // [1/m]
554 
555  TabulatedFunction& fluoPhotons = simShower.GetFluorescencePhotons(iwl);
556  fluoPhotons.Insert(distance, photons);
557 
558  //cPh += photons;
559 
560  } // end of loop over wavelengths
561 
562  } // end of loop over distance
563 }
564 
565 
572 void
574  double distInitCore,
575  double distFinalCore,
576  bool /*upwardGoing*/,
577  bool useCherenkovProfile)
578 {
579  INFO("Calculating Cherenkov light and beam");
580 
581  Detector& theDetector = Detector::GetInstance();
582  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84);
583 
584  evt::ShowerSimData& simShower = event.GetSimShower();
585  CoordinateSystemPtr showerCS = simShower.GetShowerCoordinateSystem();
586  Point core(0, 0, 0, showerCS);
587  Vector axis(0, 0, -1, showerCS);
588 
589  // just needed for flat geometry
590  const CoordinateSystemPtr localCS = simShower.GetLocalCoordinateSystem();
591  const double cosTheta = (-simShower.GetDirection()).GetCosTheta(localCS);
592  const double absCosTheta = std::fabs(cosTheta);
593  const double coreZ = wgs84.PointToLatitudeLongitudeHeight (core).get<2>();
594 
595  const ProfileResult& slantDepthVsDistance = // for curved geometry
597  const ProfileResult& depthVsHeight = // for flat geometry
598  theDetector.GetAtmosphere().EvaluateDepthVsHeight();
599 
600  const double distInitFinal = std::fabs(distFinalCore - distInitCore);
601  const double delta = kSpeedOfLight * fTimeBin;
602  const int nPoints = int(distInitFinal / delta);
603 
604  /* set the Cherenkov energy cutoff for electrons */
605 
606  const TabulatedFunction* chargeProfile = nullptr;
607  const TabulatedFunction* cherenkovProfile = nullptr;
608  double Xmax = 0;
609  double wl_spect_norm_prod = 0;
610 
611  if (!useCherenkovProfile) {
612  const double eCut = simShower.GetEnergyCutoff(evt::ShowerSimData::eElectron);
613  theDetector.GetAtmosphere().SetCherenkovEnergyCutoff(eCut);
614  chargeProfile = &simShower.GetLongitudinalProfile();
615  Xmax = simShower.GetGHParameters().GetXMax();
616  } else {
617  cherenkovProfile = &simShower.GetLongitudinalProfile(ShowerSimData::eCherenkov);
618  const double lambdaMin = simShower.GetMinCherenkovWavelength();
619  const double lambdaMax = simShower.GetMaxCherenkovWavelength();
620  wl_spect_norm_prod = 1./lambdaMin - 1./lambdaMax;
621  }
622 
623  // Vector of wavelengths
624  const vector<double>& wavelengths =
625  theDetector.GetAtmosphere().GetWavelengths(Atmosphere::eCerenkov);
626  const unsigned int nWaves = wavelengths.size();
627 
628  // initialize data structures
629  for (unsigned int iwl = 0; iwl < nWaves; ++iwl) {
630 
631  if (!simShower.HasCherenkovPhotons(iwl)) {
632  TabulatedFunction fTmp;
633  simShower.AddCherenkovPhotons(fTmp, iwl);
634  } else {
635  ERROR("Cherenkov light produced twice !!!!!");
636  }
637 
638  if (!simShower.HasCherenkovBeamPhotons(iwl)) {
639  TabulatedFunction fTmp;
640  simShower.AddCherenkovBeamPhotons(fTmp, iwl);
641  } else {
642  ERROR("Cherenkov beam produced twice !!!!!");
643  }
644 
645  if (!simShower.HasCherenkovBeamProductionPhotons(iwl)) {
646  TabulatedFunction fTmp;
647  simShower.AddCherenkovBeamProductionPhotons(fTmp, iwl);
648  } else {
649  ERROR("Cherenkov beam produced twice !!!!!");
650  }
651  }
652 
653  //const double distanceStart = ( upwardGoing ? distFinalCore : distInitCore );
654  const double distanceStart = distInitCore;
655 
656  vector<double> beam(nWaves, 0); // cherenkov beam photon number
657  for (int i = 0; i < nPoints; ++i) {
658 
659  const double distance = distanceStart + delta * (0.5 + i); // use middle of bin
660  const double distanceStart = distance - 0.5*delta;
661  const double distanceEnd = distance + 0.5*delta;
662  const Point xStepStart(core + axis * distanceStart);
663  const Point xStepEnd(core + axis * distanceEnd);
664 
665  double heightBin = 0;
666  double depthBin = 0;
667  if (fFlatEarth) {
668  heightBin = coreZ - distance * cosTheta;
669  depthBin = depthVsHeight.Y(heightBin) / absCosTheta;
670  } else {
671  const Point p(core + axis * distance);
672  heightBin = wgs84.PointToLatitudeLongitudeHeight(p).get<2>();
673  depthBin = slantDepthVsDistance.Y(distance);
674  }
675 
676  AttenuationResult mieAtt =
677  theDetector.GetAtmosphere().EvaluateMieAttenuation(xStepStart, xStepEnd, wavelengths);
678  AttenuationResult rayleightAtt =
679  theDetector.GetAtmosphere().EvaluateRayleighAttenuation(xStepStart, xStepEnd, wavelengths);
680 
681  double showerAge = 0;
682  const TabulatedFunction* cherPh = nullptr;
683  double cherenkovProduction = 0;
684  if (!useCherenkovProfile) {
685  showerAge = utl::ShowerAge(depthBin,Xmax);
686  cherPh = &theDetector.GetAtmosphere().EvaluateCherenkovPhotons (xStepStart, xStepEnd, showerAge);
687  } else {
688  cherenkovProduction = cherenkovProfile->Y(depthBin) / wl_spect_norm_prod;
689  // This is cherenkov production PER _tracklength_ !
690  }
691 
692  // We calculate three quantities here, all as a (binned) function
693  // along the shower axis and separately for each wavelength:
694  // the Cherenkov photons
695  // => Simply the number of Cherenkov photons that are generated
696  // in the given distance bin per distance.
697  // the CherenkovBeamPhotons
698  // => These are the accumulated photons in the beam, attenuated
699  // along the track:
700  // beam[i] = (beam[i-1] + generated[i]) * attenuation[i]
701  // the CherenkovBeamProductionPhotons
702  // => The tricky bit. For a total of n bins:
703  // beamP[i] = generated[i] * Product[j=i..n](attenuation[j])
704  // That means the last bin simply contains generated[n]*attenuation[n],
705  // the second-to-last contains
706  // generated[n-1]*attenuation[n-1]*attenuation[n]
707  // and so on.
708  // This makes sense because in the ShowerPhotonGenerator, we walk
709  // along the axis again. When generating photons there, we draw their
710  // source distance from a PDF that is taken to be the above beamP[i]
711  // for all bins up to and including the current one.
712  // When looking at this PDF for any bin k except the last, it will
713  // have a constant factor Product[i=k+1..n](attenuation[i]), but
714  // since the PDF is automatically normalized, this factor (that is the
715  // same for all bins) drops out.
716  // Using this strategy, we can calculate these PDFs for all bins in
717  // one go.
718 
719  double minWaveBin = 0;
720  double maxWaveBin = 0;
721  for (unsigned int iwl = 0; iwl < nWaves; ++iwl) {
722 
723  const double mieAttenuation = mieAtt.GetTransmissionFactor()[iwl].Y();
724  const double rayleightAttenuation = rayleightAtt.GetTransmissionFactor()[iwl].Y();
725  const double attenuation = mieAttenuation * rayleightAttenuation;
726 
727  // -------------------------
728  // WARNING:
729  // THIS is very bad code, see also AnalyticalCherenkovModel:
730  // the "binning" is not defined. The user must know how this works...
731  minWaveBin = (iwl == 0 ?
732  wavelengths[iwl] - 0.5*(wavelengths[iwl+1] - wavelengths[iwl]) :
733  maxWaveBin);
734  maxWaveBin = (iwl == nWaves-1 ?
735  wavelengths[iwl] + 0.5*(wavelengths[iwl] - wavelengths[iwl-1]) :
736  0.5*(wavelengths[iwl] + wavelengths[iwl+1]));
737  // -------------------------
738 
739  // Apply attenuation to entire beam, not just the
740  // most recent contribution.
741 
742  double generatedPhotons = 0;
743  if (!useCherenkovProfile) {
744  const double Nch = chargeProfile->Y(depthBin);
745  generatedPhotons = cherPh->Y(wavelengths[iwl]) * Nch;
746  beam[iwl] += generatedPhotons;
747 
748  } else {
749 
750  // wavelength spectrum, dN/dlambda = 1/lambda2
751  const double deltaDepth = fFlatEarth ?
752  (depthVsHeight.Y(coreZ - distanceEnd*cosTheta) - depthVsHeight.Y(coreZ - distanceStart*cosTheta)) / absCosTheta :
753  slantDepthVsDistance.Y(distanceEnd) - slantDepthVsDistance.Y(distanceStart);
754  generatedPhotons = cherenkovProduction * deltaDepth * (1./minWaveBin - 1./maxWaveBin);
755  beam[iwl] += generatedPhotons;
756  }
757 
758  beam[iwl] *= attenuation;
759 
760  TabulatedFunction& ckovPhotons = simShower.GetCherenkovPhotons(iwl);
761  TabulatedFunction& ckovBeam = simShower.GetCherenkovBeamPhotons(iwl);
762  TabulatedFunction& ckovBeamProduction = simShower.GetCherenkovBeamProductionPhotons(iwl);
763 
764  ckovPhotons.Insert(distance, generatedPhotons/delta);
765  ckovBeam.Insert(distance, beam[iwl]);
766  ckovBeamProduction.Insert(distance, generatedPhotons/delta);
767 
768  for (unsigned int jTrace = 0; jTrace < ckovBeamProduction.GetNPoints(); ++jTrace) {
769  ckovBeamProduction.GetY(jTrace) *= attenuation;
770  if (ckovBeamProduction.GetY(jTrace) < 0) {
771  WARNING("Cherenkov beam production negative. Forcing it to zero.");
772  ckovBeamProduction.GetY(jTrace) = 0.;
773  }
774  }
775 
776  } // loop over wavelengths
777 
778  } // loop along shower axis
779 }
780 
781 
782 void
784 {
785  INFO("Calculating primary Cherenkov light and beam");
786 
787  Detector& theDetector = Detector::GetInstance();
788  const ReferenceEllipsoid& wgs84 = ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84);
789 
790  evt::ShowerSimData& simShower = event.GetSimShower();
791  CoordinateSystemPtr showerCS = simShower.GetShowerCoordinateSystem();
792  Point core(0, 0, 0, showerCS);
793  Vector axis(0, 0, -1, showerCS);
794 
795  //const double cosTheta = cos (simShower.GetZenith()); // just needed for flat geometry
796 
797  const ProfileResult& depthVsHeight = // for flat geometry
798  theDetector.GetAtmosphere().EvaluateDepthVsHeight();
799  const ProfileResult& heightVsSlantDepth =
801  const ProfileResult& distanceVsSlantDepth =
803 
804  //const double distInitFinal = std::fabs(distFinalCore-distInitCore);
805  //const double delta = kSpeedOfLight*(fTimeBin);
806  //const int nPoints = int(distInitFinal / delta);
807 
808  // get the LIMITS of the atmosphere
809  double atmDepthMin = heightVsSlantDepth.MinX();
810  double atmDepthMax = heightVsSlantDepth.MaxX();
811  double atmHeightMin = heightVsSlantDepth.Y (atmDepthMax);
812 
813  double Z = 0;
814  if (fOverRidePrimaryCharge >= 0) {
815  Z = fOverRidePrimaryCharge;
816  cout << " Override primary charge ----------> Z=" << Z << endl;
817  } else {
818  const int particleID = simShower.GetPrimaryParticle();
819  switch (particleID) {
821  Z = 0;
822  break;
824  Z = 0;
825  break;
827  Z = 1;
828  break;
830  Z = 26;
831  break;
832  default:
833  ostringstream err;
834  err << "Unkown primary particle type " << particleID;
835  ERROR(err);
836  throw AugerException(err.str());
837  break;
838  }
839  }
840 
841  // just needed for flat geometry
842  //double cosTheta = cos (simShower.GetZenith());
843  //double coreZ = wgs84.PointToLatitudeLongitudeHeight (core).get<2>();
844 
845  const double Xfirst = simShower.GetXFirst();
846 
847  if (Xfirst < atmDepthMin) {
848  ostringstream info;
849  info << "Xfirst is out of the bounds of the atmosphere. X1=" << Xfirst/g*cm2
850  << ", Xmin=" << atmDepthMin/g*cm2 ;
851  INFO(info);
852  return;
853  }
854 
855  double height1 = heightVsSlantDepth.Y(atmDepthMin);
856  double height2 = heightVsSlantDepth.Y(Xfirst);
857  double dist1 = distanceVsSlantDepth.Y(atmDepthMin);
858  double dist2 = distanceVsSlantDepth.Y(Xfirst);
859  double distInitFinal = std::abs(dist2 - dist1);
860  double delta = kSpeedOfLight * fTimeBin;
861  int nPoints = std::min(10000, int(distInitFinal / delta));
862  dist1 = dist2 - nPoints*delta; // correction to right start point
863 
864  ostringstream info;
865  info << "track primary from depth="
866  << atmDepthMin/g*cm2 << " g/cm2, dist=" << dist1/m << " m, height=" << height1/m << " m; "
867  "point of first interaction at "
868  << Xfirst/(g/cm2) << " " << dist2/m << " " << height2/m;
869  INFO(info);
870 
871  // use cherenkov wl bins for now !!!! TODO check
872  vector<double> wavelength = theDetector.GetAtmosphere().GetWavelengths(Atmosphere::eCerenkov);
873  const unsigned int nWaves = wavelength.size();
874 
875  const ProfileResult& refractiveIndex = theDetector.GetAtmosphere().EvaluateRefractionIndexVsHeight();
876  const double minHeight = refractiveIndex.MinX();
877  const double maxHeight = refractiveIndex.MaxX();
878  const double minRI = refractiveIndex.Y(maxHeight);
879  //const double maxRI = refractiveIndex.Y(minHeight);
880 
881  const double minRIdepth = depthVsHeight.Y(maxHeight);
882 
883  double maxWaveBin = wavelength[0] - (wavelength[1] - wavelength[0])/2;
884  for (unsigned int wl = 0; wl < nWaves; ++wl) {
885 
886  // wavelength bins (centered around mean value)
887  double minWaveBin = maxWaveBin;
888 
889  if (wl == nWaves-1)
890  maxWaveBin = wavelength[wl] + (wavelength[wl] - wavelength[wl-1])/2.;
891  else
892  maxWaveBin = (wavelength[wl] + wavelength[wl+1])/2.;
893 
894  double lambda1 = minWaveBin;
895  double lambda2 = maxWaveBin;
896 
897  vector<double> vPhotons;
898  vector<double> vPhotonsBeam;
899  vector<double> vDist;
900 
901  double beam = 0; // beam photons
902  for (int i = 0; i < nPoints*1.5; ++i) {
903 
904  double distBin1 = dist1 + delta * i;
905  double distBin2 = dist1 + delta * (i + 1);
906  double dist = (distBin2 + distBin1)/2.;
907 
908  Point p1 = core + distBin1*axis;
909  Point p2 = core + distBin2*axis;
910  Point p = core + dist*axis;
911  double height = wgs84.PointToLatitudeLongitudeHeight (p).get<2>();
912 
913  if (height < atmHeightMin)
914  break; // left atmosphere
915 
916  vDist.push_back(dist);
917 
918  if (i <= 0) {
919  vPhotons.push_back(0);
920  vPhotonsBeam.push_back(0);
921  continue;
922  }
923 
924  AttenuationResult mieAtt =
925  theDetector.GetAtmosphere().EvaluateMieAttenuation(p1, p2, wavelength);
926  AttenuationResult rayleightAtt =
927  theDetector.GetAtmosphere().EvaluateRayleighAttenuation(p1, p2, wavelength);
928 
929  double mie = mieAtt.GetTransmissionFactor()[wl].Y();
930  double rayleight = rayleightAtt.GetTransmissionFactor()[wl].Y();
931  double attenuation = mie * rayleight;
932 
933  // refractive index scales with air density (approximation taken from F. Nerlings thesis)
934  double vertDepth = depthVsHeight.Y(height);
935  double n_height = (minRI - 0.000283) + 0.000283 * (vertDepth/minRIdepth);
936  if (height > minHeight && height < maxHeight) {
937  n_height = refractiveIndex.Y(height);
938  }
939  // wavelength dependence (approximation taken from F. Nerlings thesis)
940  double lambda = (lambda1 + lambda2)/2 / (1e-10*m); // wavelength in [angstrom]
941  double n = n_height + 1e-7 * (2726.42 + 12.288/(lambda*lambda*1e-8) + 0.3555/(lambda*lambda*lambda*lambda+1.e-16) );
942 
943  double beta = 1.; // CHECK, if this is good enough
944 
945  double dNdl = Z*Z * 2.*kPi * 1./137. * (1.-1./(pow(n*beta, 2))) * (-1) * (1./lambda2 - 1./lambda1);
946 
947  if (i >= nPoints-1) { // after first interaction
948  dNdl = 0;
949  }
950 
951  // dN/dl = Z^2 2.*kPi*alpha*(1-1/(n*n * beta*beta)) * dlambda 1/lambda2;
952  // cos (delta) = 1/(beta n)
953  // beta_thresh = 1/(n)
954 
955  //double dN = dNdl * delta;
956  //double dN_BinBefore = vPhotons.back() * delta;
957  beam += dNdl * delta;
958  beam *= attenuation;
959 
960  vPhotons.push_back(dNdl);
961  vPhotonsBeam.push_back(beam);
962 
963  } // loop along axis
964 
965  TabulatedFunction cher(vDist, vPhotons);
966  TabulatedFunction cherBeam(vDist, vPhotonsBeam);
967 
968 #ifdef PRIM_CHER
969  simShower.AddPrimaryCherenkovPhotons(cher, wl);
970  simShower.AddPrimaryCherenkovBeamPhotons(cherBeam, wl);
971 #else
972  ERROR("You cannot us primary particle cherenkov light, without compiling with -DPRIM_CHER !!! ");
973  throw AugerException();
974 #endif
975 
976  } // loop wavelengths
977 
978 }
Branch GetTopBranch() const
Definition: Branch.cc:63
double GetEnergyCutoff(const ProfileType type=eElectron) const
Get the energy cutoff for which the profile of charged particles was calculated.
utl::CoordinateSystemPtr GetLocalCoordinateSystem() const
Get the Auger coordinate system associated to the shower core position.
int GetPrimaryParticle() const
Get the type of the shower primary particle.
Definition: ShowerSimData.h:84
unsigned int GetNPoints() const
const utl::TabulatedFunction & EvaluateFluorescenceYield(const double heightAboveSeaLevel) const
Evaluated Fluorescence Yield for a specific model.
constexpr double mm
Definition: AugerUnits.h:113
const utl::TabulatedFunction & GetdEdX() const
Get the energy deposit of the shower.
Point object.
Definition: Point.h:32
atm::AttenuationResult EvaluateMieAttenuation(const utl::Point &xInit, const utl::Point &xFinal, const std::vector< double > &wLength) const
double GetMinCherenkovWavelength() const
Get the minimal Cherenkov wavelength for photons in longitudinal profile.
Base class for all exceptions used in the auger offline code.
const std::vector< double > & GetWavelengths(const EmissionMode mode=eFluorescence) const
const utl::TabulatedFunction & GetCherenkovBeamProductionPhotons(const int wavelength) const
Get the cherenkov beam production along the shower axis.
const utl::TabulatedFunctionErrors & GetTransmissionFactor() const
Transmission factor.
const atm::ProfileResult & EvaluateHeightVsSlantDepth() const
Table of height as a function of slant depth.
bool HasCherenkovBeamProductionPhotons(const int wavelength) const
const evt::VGaisserHillasParameter & GetGHParameters() const
Get the Gaisser-Hillas parameters of the shower.
void AddCherenkovBeamPhotons(const utl::TabulatedFunction &cp, const int wavelength)
Class to hold collection (x,y) points and provide interpolation between them.
bool HasCherenkovPhotons(const int wavelength) const
void CherenkovLight(evt::Event &event, double distInit, double distFinal, bool upwardGoing, bool useCherenkovProfile)
Simulate the Cherenkov light beam along the shower axis.
const atm::ProfileResult & EvaluateDensityVsHeight() const
Tabulated function giving Y=density as a function of X=height.
fwk::VModule::ResultFlag Init()
Initialize: invoked at beginning of run (NOT beginning of event)
bool HasSimShower() const
const utl::TabulatedFunction & GetFluorescencePhotons(const int wavelength) const
Get the fluorescence photons generated along the shower axis.
bool HasFluorescencePhotons(const int wavelength) const
const atm::ProfileResult & EvaluateDistanceVsSlantDepth() const
Table of distance as a function of slant depth.
const utl::TabulatedFunction & GetCherenkovBeamPhotons(const int wavelength) const
Get the beam of Cherenkov beam photons along the shower axis.
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
bool HasCherenkovBeamPhotons(const int wavelength) const
double GetMaxCherenkovWavelength() const
Get the maximal Cherenkov wavelength for photons in longitudinal profile.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
double pow(const double x, const unsigned int i)
fwk::VModule::ResultFlag Run(evt::Event &event)
Run: invoked once per event.
double Y(const double x) const
Get the Y value (coordinate) for given X (ordinate)
Line Intersection(const Plane &p1, const Plane &p2)
constexpr double deg
Definition: AugerUnits.h:140
Interface class to access Shower Simulated parameters.
Definition: ShowerSimData.h:49
const atm::Atmosphere & GetAtmosphere() const
Definition: Detector.h:113
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
const utl::TabulatedFunction & GetCherenkovPhotons(const int wavelength) const
Get the Cherenkov photon production along the shower axis.
double GetXFirst() const
Get depth of first interaction.
#define max(a, b)
Class representing a document branch.
Definition: Branch.h:107
Reference ellipsoids for UTM transformations.
fwk::VModule::ResultFlag Finish()
Finish: invoked at end of the run (NOT end of the event)
const utl::Vector & GetDirection() const
Get the direction of the shower axis. This is the true direction of shower movement.
Class describing the Atmospheric profile.
Definition: ProfileResult.h:25
constexpr double kPi
Definition: MathConstants.h:24
double abs(const SVector< n, T > &v)
atm::AttenuationResult EvaluateRayleighAttenuation(const utl::Point &xInit, const utl::Point &xFinal, const std::vector< double > &wLength) const
Compute Rayleigh attenuation between points.
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.
const atm::ProfileResult & EvaluateDepthVsHeight() const
Tabulated function giving Y=depth as a function of X=height.
Iterator Insert(const double x, const double y)
const double km
constexpr double g
Definition: AugerUnits.h:200
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
void AddCherenkovBeamProductionPhotons(const utl::TabulatedFunction &fp, const int wavelength)
const double & GetY(const unsigned int idx) const
bool HasdEdX() const
Check initialization of the energy deposit.
void PrimaryCherenkovLight(evt::Event &event)
Generate Cherenkov emission from primary particle.
void AddCherenkovPhotons(const utl::TabulatedFunction &cp, const int wavelength)
constexpr double kSpeedOfLight
double MinX() const
Return the minimum value for X (ordinate) stored in the profile.
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
double ShowerAge(const double slantDepth, const double showerMax)
General definition of shower age.
electron/positron profile
Definition: ShowerSimData.h:62
bool HasGHParameters() const
Check initialization of the Gaisser-Hillas parameters.
constexpr double cm
Definition: AugerUnits.h:117
const utl::TabulatedFunction & EvaluateCherenkovPhotons(const utl::Point &xA, const utl::Point &xB, const double showerAge) const
execption handling for calculation/access for inclined atmosphere model
void AddFluorescencePhotons(const utl::TabulatedFunction &fp, const int wavelength)
Vector object.
Definition: Vector.h:30
double GetdEdX0() const
get reference energy deposit for fluorescence yield model
void FluorescenceLight(evt::Event &event, double distInit, double distFinal, bool upwardGoing)
Simulate the Fluorescence photons generated isotropically along the shower axis.
void SetCherenkovEnergyCutoff(const double eCut) const
utl::CoordinateSystemPtr Get(const std::string &id)
Get a well-known Coordinate System.
const atm::ProfileResult & EvaluateHeightVsDepth() const
Tabulated function giving Y=height as a function of X=depth.
const atm::ProfileResult & EvaluateRefractionIndexVsHeight() const
Tabulated function giving Y=refraction index as a function of X=height.
double Y(const double x) const
Get or interpolate the Y value that corresponds to parameter x.
const atm::ProfileResult & EvaluateSlantDepthVsDistance() const
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
constexpr double m
Definition: AugerUnits.h:121
const std::string & GetMessage() const
Retrieve the message from the exception.
bool HasLongitudinalProfile(const ProfileType type=eCharged) const
Check initialization of the longitudinal profile.
utl::CoordinateSystemPtr GetShowerCoordinateSystem() const
Class describing the Atmospheric attenuation.
const atm::ProfileResult & EvaluateTemperatureVsHeight() const
Tabulated function giving Y=temperature as a function of X=height.
Definition: Line.h:17
double MaxX() const
Return the maximum value for X (ordinate) stored in the profile.
const utl::TabulatedFunction & GetLongitudinalProfile(const ProfileType type=eCharged) const
Get the longitudinal charge profile of the shower.
const atm::ProfileResult & EvaluateHeightVsDistance() const
Table of height as a function of distance.
constexpr double cm2
Definition: AugerUnits.h:118

, generated on Tue Sep 26 2023.