InclinedAtmosphericProfile.cc
Go to the documentation of this file.
1 #include <atm/InclinedAtmosphericProfile.h>
2 #include <atm/ProfileResult.h>
3 #include <atm/Atmosphere.h>
4 
5 #include <fwk/LocalCoordinateSystem.h>
6 #include <fwk/CoordinateSystemRegistry.h>
7 #include <utl/CoordinateSystemPtr.h>
8 
9 #include <utl/ErrorLogger.h>
10 #include <utl/AugerUnits.h>
11 #include <utl/AugerException.h>
12 #include <utl/PhysicalConstants.h>
13 #include <utl/TabulatedFunction.h>
14 #include <utl/TabulatedFunctionErrors.h>
15 #include <utl/UTMPoint.h>
16 #include <utl/Point.h>
17 #include <utl/Vector.h>
18 #include <utl/ReferenceEllipsoid.h>
19 #include <utl/GeometryUtilities.h>
20 
21 #include <det/Detector.h>
22 
23 #include <vector>
24 #include <sstream>
25 
26 using namespace utl;
27 using namespace fwk;
28 using namespace atm;
29 using namespace std;
30 
31 
32 //#define DEBUG
33 
34 
35 InclinedAtmosphericProfile::InclinedAtmosphericProfile(const Point& core,
36  const Vector& axis,
37  const double deltaX)
38 {
39  // axis points downward for upward going showers
40  bool upward = (axis.GetZ(LocalCoordinateSystem::Create(core)) < 0);
41  const Vector dir = -axis;
42  const Vector dirInverse = axis;
43 
44 #ifdef DEBUG
45  INFO(string("Running ") + (upward ? "upward" : "downward") + " mode!");
46 #endif
47 
48  // If event is "skimming" the earth, we need to revert through atmosphere at vertex
49  bool skimming = false;
50 
52 
53  auto& detector = det::Detector::GetInstance();
54  auto& atmosphere = detector.GetAtmosphere();
55 
56  const auto& atmDepthVsHeight = atmosphere.EvaluateDepthVsHeight();
57  const auto& atmHeightVsDepth = atmosphere.EvaluateHeightVsDepth();
58  const auto& atmDensityVsHeight = atmosphere.EvaluateDensityVsHeight();
59 
60  // Safety margin to the cosmos .... and to earth
61  const double atmSafetyMargin = 1*mm;
62  const double earthSafetyMargin = 1*m;
63 
64  /* Get upper and lower limit for height of the atmosphere
65 
66  ---X-------------------- Top of Atmosphere => First
67  \
68  \
69  ------X----------------- Actual altitude of core
70  -------X---------------- Surface of WGS84 => Last */
71 
72  const double atmHeightMax = atmDepthVsHeight.MaxX() - atmSafetyMargin;
73  const double atmHeightMin = atmDepthVsHeight.MinX() + earthSafetyMargin;
74  const double atmHeightMin_density = atmDensityVsHeight.MinX() + earthSafetyMargin;
75  const double minAtm = max(atmHeightMin, atmHeightMin_density);
76  const double maxVerticalDepth = atmDepthVsHeight.Y(atmHeightMin);
77  const double minVerticalDepth = atmDepthVsHeight.Y(atmHeightMax);
78 
79  // ---------------------- enter/leave atmosphere -------------------------
80 
81  const vector<Point> pAtm =
83 
84  // Catch unphysical shower geometry - less than two intersections with
85  // atmosphere at this point means no shower at all
86  if (pAtm.size() < 2) {
87  ostringstream err;
88  err << "This shower geometry does not intersect the atmosphere!!!" << endl;
89  ERROR(err);
90  throw InclinedAtmosphereModelException(err.str());
91  }
92 
93  // ---------------------- enter/leave earth ------------------------------
94 
95  const vector<Point> pEarth =
97  // pEarth[0,1] points, where particle [leaves, enters] the earth's ellipsoid (order defind by direction of line)
98 
99  Point firstPoint;
100  Point lastPoint;
101  switch (pEarth.size()) {
102  case 2:
103  if (upward) {
104  firstPoint = pEarth[0]; // Point, where particle enters the atmosphere (leaves earth)
105  lastPoint = pAtm[0]; // Point, where particle leaves the atmosphere
106  } else {
107  firstPoint = pAtm[1]; // Point, where particle enters the atmosphere
108  lastPoint = pEarth[1]; // Point, where particle enters earth
109  }
110  break;
111  default:
112  lastPoint = pAtm[0]; // Point, where particle leaves the atmosphere
113  firstPoint = pAtm[1]; // Point, where particle enters the atmosphere
114 
115  // here we need to go through the atmosphere in the standard way because shower does not intersect ground
116  upward = false;
117 
118 #ifdef DEBUG
119  cout << "Scraper!\n";
120 #endif
121  break;
122  }
123 
124 #ifdef DEBUG
125  const auto& csECEF = fwk::CoordinateSystemRegistry::Get("ECEF");
126  cout << "Core\t" << core.GetCoordinates(csECEF) << "\n"
127  "Destination\t" << lastPoint.GetCoordinates(csECEF) << "\n"
128  "First\t" << firstPoint.GetCoordinates(csECEF) << endl;
129 #endif
130 
131  /* Now we'll loop along the shower in steps of deltaX_incl =: deltaX
132  whilst deltaX_vert = deltaX_incl*cos(theta)
133  and cos(theta) is the angle between z-axis in the P_i-frame.
134  From these we'll get a vertical height which gives P_i+1 as
135  intersection from shower axis with the atmospheric ellipsoid
136  Giving us the distance between P_core and P_i+1.*/
137 
138  Point iPoint = firstPoint;
139  Point nextPoint;
140  double cosTheta = 0;
141  try {
142  cosTheta = dirInverse.GetCosTheta(LocalCoordinateSystem::Create(UTMPoint(firstPoint, wgs84)));
143  } catch (UTMPoint::UTMException& e) {
144  ostringstream err;
145  err << "InclinedAtmosphericProfile: un-physical local point ";
146  ERROR(err);
147  throw InclinedAtmosphereModelException(err.str());
148  }
149 
150  // Temporary variables for calculation
151  double verticalDeltaX = 0;
152  double tmpHeight = UTMPoint(firstPoint, wgs84).GetHeight();
153  double tmpDepth = atmDepthVsHeight.Y(tmpHeight);
154  vector<Point> tmpPoints;
155 
156  // Set vertical start points
157  double tmpSlantDepth = upward ? cosTheta*(tmpDepth - atmDepthVsHeight.Y(0)) : tmpDepth;
158 
159  // Result vectors
160  vector<double> verticalHeight;
161  vector<double> slantDepth;
162  vector<double> distanceToImpactPoint;
163 
164  // Store initial values as first entry
165  verticalHeight.push_back(atmHeightVsDepth.Y(tmpDepth));
166  slantDepth.push_back(log(tmpSlantDepth)); // log(slantDepth)!
167  distanceToImpactPoint.push_back(0 - (firstPoint - core).GetMag());
168  // Pi0 at first
169 
170  // Start loop
171  while (true) {
172 
173  // Double-check whether everything made sense up to this point
174  try {
175  cosTheta = dirInverse.GetCosTheta(LocalCoordinateSystem::Create(UTMPoint(iPoint, wgs84)));
176  } catch (UTMPoint::UTMException& e) {
177  ostringstream err;
178  err << "InclinedAtmosphericProfile: un-physical local point ";
179  ERROR(err.str());
180  throw InclinedAtmosphereModelException(err.str());
181  }
182 
183  verticalDeltaX = deltaX * cosTheta;
184  tmpDepth += verticalDeltaX;
185 
186  // Break loop as soon as we reach the last point
187  if (maxVerticalDepth <= tmpDepth || tmpDepth <= minVerticalDepth) {
188  tmpHeight = UTMPoint(lastPoint, wgs84).GetHeight();
189  verticalDeltaX = atmDepthVsHeight.Y(tmpHeight) - (tmpDepth - verticalDeltaX);
190  tmpSlantDepth += verticalDeltaX / cosTheta;
191 
192  // Save vertical height of new point
193  verticalHeight.push_back(tmpHeight);
194  // Save slant depth at new point
195  slantDepth.push_back(log(tmpSlantDepth)); // log(slantDepth)!
196  // Save distance to impact point
197  distanceToImpactPoint.push_back((lastPoint - core).GetMag());
198 
199  break;
200  } else {
201  tmpSlantDepth += deltaX;
202  tmpHeight = atmHeightVsDepth.Y(tmpDepth);
203 
204  // Calculate new point
205  tmpPoints = Intersection(ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84), tmpHeight, Line(core, dir));
206 
207  if (tmpPoints.size() < 2) {
208  if (!skimming) {
209  // For showers with no earth intersection
210  tmpDepth -= verticalDeltaX;
211  tmpSlantDepth -= deltaX;
212  tmpHeight = atmHeightVsDepth.Y(tmpDepth);
213  tmpPoints = Intersection(ReferenceEllipsoid::Get(ReferenceEllipsoid::eWGS84), tmpHeight, Line(core, dir));
214  tmpSlantDepth += atmDensityVsHeight.Y(tmpHeight) * (tmpPoints[0] - tmpPoints[1]).GetMag();
215  skimming = true;
216  } else {
217  // Beak, if we have reached space in this step! Should not happen...
218  INFO("Reached space!");
219  break;
220  }
221  }
222 
223  // Get correct next point
224  nextPoint = skimming ? (upward ? tmpPoints[1] : tmpPoints[0]) : (upward ? tmpPoints[0] : tmpPoints[1]);
225 
226  // Save vertical height of new point
227  verticalHeight.push_back(tmpHeight);
228  // Save slant depth at new point
229  slantDepth.push_back(log(tmpSlantDepth)); // log(slantDepth)!
230  // Save distance to impact point
231  distanceToImpactPoint.push_back((nextPoint - firstPoint).GetMag() - (core - firstPoint).GetMag());
232 
233  // Next step
234  iPoint = nextPoint;
235  }
236  } // End of loop
237 
238  // Check for empty vectors
239  if (slantDepth.size() <= 1) {
240  ostringstream err;
241  err << "No points calculated. Table is empty. " << endl;
242  ERROR(err);
243  throw InclinedAtmosphereModelException(err.str());
244  }
245 
246 #ifdef DEBUG
247  const double testCosTheta = dirirection.GetCosTheta(LocalCoordinateSystem::Create(UTMPoint(core, wgs84)));
248  cout << "CosTheta\tSlantDepth\tverticalHeight\tDistance\n";
249  for (unsigned int i = 0; i < verticalHeight.size(); ++i) {
250  cout << -testCosTheta << '\t'
251  << exp(slantDepth[i])/g*cm2 << '\t'
252  << verticalHeight[i]/m << '\t'
253  << distanceToImpactPoint[i]/m << endl;
254  }
255 #endif
256 
257  // Currently no uncertainties ...
258  vector<double> verticalHeightErrors(slantDepth.size());
259  vector<double> slantDepthErrors(slantDepth.size());
260  vector<double> distanceErrors(slantDepth.size());
261 
262  // reverse -----------------------------
263  vector<double> slantDepthReverse(slantDepth.size());
264  vector<double> verticalHeightReverse(slantDepth.size());
265 
266  reverse_copy(slantDepth.begin(), slantDepth.end(), slantDepthReverse.begin());
267  reverse_copy(verticalHeight.begin(), verticalHeight.end(), verticalHeightReverse.begin());
268 
269  const TabulatedFunctionErrors tabSlantDist(distanceToImpactPoint, distanceErrors,
270  slantDepth, slantDepthErrors);
271 
272  fSlantDepthVsDistance =
273  new ProfileResult(tabSlantDist, ProfileResult::eLinear, ProfileResult::eLog);
274 
275  const TabulatedFunctionErrors tabDistSlant(slantDepth, slantDepthErrors,
276  distanceToImpactPoint, distanceErrors);
277 
278  fDistanceVsSlantDepth =
279  new ProfileResult(tabDistSlant, ProfileResult::eLog, ProfileResult::eLinear);
280 
281  const TabulatedFunctionErrors tabHeightSlant(slantDepthReverse, slantDepthErrors,
282  verticalHeightReverse, verticalHeightErrors);
283 
284  fHeightVsSlantDepth =
285  new ProfileResult(tabHeightSlant, ProfileResult::eLog, ProfileResult::eLinear);
286 
287  const TabulatedFunctionErrors tabHeightDistance(distanceToImpactPoint, distanceErrors,
288  verticalHeight, verticalHeightErrors);
289 
290  fHeightVsDistance =
291  new ProfileResult(tabHeightDistance, ProfileResult::eLinear, ProfileResult::eLinear);
292 }
293 
294 
295 InclinedAtmosphericProfile::~InclinedAtmosphericProfile()
296 {
297  delete fSlantDepthVsDistance;
298  delete fDistanceVsSlantDepth;
299  delete fHeightVsSlantDepth;
300  delete fHeightVsDistance;
301 }
302 
303 
305 const atm::ProfileResult&
306 InclinedAtmosphericProfile::EvaluateSlantDepthVsDistance()
307  const
308 {
309  if (fSlantDepthVsDistance)
310  return *fSlantDepthVsDistance;
311  else
312  throw InclinedAtmosphereModelException("First Initialize the InclinedAtmosphericProfile Model !!");
313 }
314 
315 
317 const atm::ProfileResult&
318 InclinedAtmosphericProfile::EvaluateDistanceVsSlantDepth()
319  const
320 {
321  if (fDistanceVsSlantDepth)
322  return *fDistanceVsSlantDepth;
323  else
324  throw InclinedAtmosphereModelException("First Initialize the InclinedAtmosphericProfile Model !!");
325 }
326 
327 
329 const atm::ProfileResult&
330 InclinedAtmosphericProfile::EvaluateHeightVsSlantDepth()
331  const
332 {
333  if (fHeightVsSlantDepth)
334  return *fHeightVsSlantDepth;
335  else
336  throw InclinedAtmosphereModelException("First Initialize the InclinedAtmosphericProfile Model !!");
337 }
338 
339 
341 const atm::ProfileResult&
342 InclinedAtmosphericProfile::EvaluateHeightVsDistance()
343  const
344 {
345  if (fHeightVsDistance)
346  return *fHeightVsDistance;
347  else
348  throw InclinedAtmosphereModelException("First Initialize the InclinedAtmosphericProfile Model !!");
349 }
350 
351 
352 //############# Member functions ######################
353 
354 double
355 InclinedAtmosphericProfile::IntegratedGrammage(const utl::Point& pStart,
356  const utl::Point& pStop,
357  const double delta)
358 {
360 
361  auto& detector = det::Detector::GetInstance();
362  auto& atmosphere = detector.GetAtmosphere();
363  const ProfileResult& atmDepthVsHeight = atmosphere.EvaluateDepthVsHeight();
364 
365  const double atmHeightMin = atmDepthVsHeight.MinX();
366  const double atmHeightMax = atmDepthVsHeight.MaxX();
367 
368  Vector dir(pStop - pStart);
369  const double length = dir.GetMag();
370  dir.Normalize();
371 
372  double depth = 0;
373  double distance = 0;
374 
375  while (distance < length) {
376 
377  const double delta_now = (length - distance < delta ? length - distance : delta);
378  const Point p1 = pStart + distance * dir;
379  distance += delta_now;
380  const Point p2 = pStart + distance * dir;
381 
382  double cosLocalTheta = 0;
383  try {
384  const UTMPoint utm(p1, wgs84);
385  cosLocalTheta = dir.GetCosTheta(LocalCoordinateSystem::Create(utm));
386  } catch (UTMPoint::UTMException& e) {
387  continue;
388  }
389 
390  const double heightP1 = wgs84.PointToLatitudeLongitudeHeight(p1).get<2>();
391  const double heightP2 = wgs84.PointToLatitudeLongitudeHeight(p2).get<2>();
392 
393  if (heightP1 <= atmHeightMin || heightP1 >= atmHeightMax ||
394  heightP2 <= atmHeightMin || heightP2 >= atmHeightMax) {
395  // add nothing
396  continue;
397  }
398 
399  const double depthP1 = atmDepthVsHeight.Y(heightP1);
400  const double depthP2 = atmDepthVsHeight.Y(heightP2);
401 
402  depth += (depthP1 - depthP2) / cosLocalTheta;
403 
404  }
405 
406  return depth;
407 }
constexpr double mm
Definition: AugerUnits.h:113
void Normalize()
Definition: Vector.h:64
Point object.
Definition: Point.h:32
constexpr double atmosphere
Definition: AugerUnits.h:215
Class to hold and convert a point in geodetic coordinates.
Definition: UTMPoint.h:40
double GetCosTheta(const CoordinateSystemPtr &coordinateSystem) const
cos of zenith (theta) angle
Definition: BasicVector.h:251
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
static const ReferenceEllipsoid & Get(const EllipsoidID theID)
Get known ellipsoid by registered ID.
double GetMag() const
Definition: Vector.h:58
double Y(const double x) const
Get the Y value (coordinate) for given X (ordinate)
Line Intersection(const Plane &p1, const Plane &p2)
#define max(a, b)
double GetHeight() const
Get the height.
Definition: UTMPoint.h:212
Class describing the Atmospheric profile.
Definition: ProfileResult.h:25
constexpr double g
Definition: AugerUnits.h:200
double MinX() const
Return the minimum value for X (ordinate) stored in the profile.
Report problems in UTM handling.
Definition: UTMPoint.h:288
execption handling for calculation/access for inclined atmosphere model
Vector object.
Definition: Vector.h:30
utl::CoordinateSystemPtr Get(const std::string &id)
Get a well-known Coordinate System.
double GetZ(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:212
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
constexpr double m
Definition: AugerUnits.h:121
Definition: Line.h:17
double MaxX() const
Return the maximum value for X (ordinate) stored in the profile.
constexpr double cm2
Definition: AugerUnits.h:118

, generated on Tue Sep 26 2023.