cta-observatory / ctapipe

Low-level data processing pipeline software for CTAO or similar arrays of Imaging Atmospheric Cherenkov Telescopes
https://ctapipe.readthedocs.org
BSD 3-Clause "New" or "Revised" License
64 stars 268 forks source link

h_max is inconsistent between HillasIntersection and HillasReconstructor #2395

Closed gschwefer closed 1 year ago

gschwefer commented 1 year ago

Both HillasReconstructor and HillasIntersection calculate a quantity h_max and return it in the ReconstructedGeometryContainer, but they are inconistent in the physical meaning of h_max: For HillasIntersection, it is the vertical height of the shower maximum above the array, for HillasReconstructor it is the distance of the shower maximum location from the array center, i.e. kind of a "slant height"

Whatever h_max really should be, it should of course be the same everywhere.

gschwefer commented 1 year ago

See the related discussion in #2305 about what quantity returned from ImPACT

gschwefer commented 1 year ago

Also, should the Hillas reconstructors in addition to h_max return the atmospheric slant depth xmax?

kosack commented 1 year ago

Yes, I believe we should return both h_max (which should really be the height of shower max, the z coordinate), and the distance to shower max (along the line of sight). Slant depth (what we call in the atmosphere module "column density") is then the distance transformed to a grammage using an atmosphere model, whereas x_max is the h_max transformed to grammage units.

And to be more explicit, we might want to rename these to e.g.:

or even just shower_distance, shower_height, since we usually define the shower's centroid point as the "Shower maximium", i.e. where most of the cherenkov light is emitted

gschwefer commented 1 year ago

Ok, I have one question: Do I understand you correctly that you want to give the name x_max to the vertical grammage equivalent to shower_max_height ?

maxnoe commented 1 year ago

I think that would be bad, since e.g. Auger uses x max for the depth of the shower maximum and I'm pretty sure that's generally the more common usage.

kosack commented 1 year ago

We might want to avoid x_max altogther if that is the case. Corsika/SimTelArray define it as a height (mass overburden), not corrected for the zenith angle. If Auger uses a different definition, it's clear that it is not well defined and we should pick names that are obvious.

Also "depth" as a word usually does not contain the cos factor (think water depth in the ocean, which is not a line-of-site measurement), so it's not even clear to me that Auger's definition is different. Normally, "depth", "height" and "altitude" (when not an angle) all mean really just the z-coordinate of a point in a fluid/gas defined from different 0 points.

The path-length from the observer to a point in a medium is different: it is the slant-depth, column-density, etc. It implies an integration over a path, which in our case is a line.

kosack commented 1 year ago

What we are describing is a point - the shower maximum. Here is a summary:

image
maxnoe commented 1 year ago

Corsika/SimTelArray define it as a height (mass overburden), not corrected for the zenith angle.

Actually it depends... CORSIKA has a compile time option for it (SLANT), if set, longittudinal development is sampled in slant depth, if not in vertical depth, which what determines the value of the true xmax, since that comes from a fit to that distribution.

I guess AUGER is of course always using SLANT. We are probably not, but maybe we should? At least for high zenith angles?

kosack commented 1 year ago

Yes, I mentioned that in the other issue: in CTA, we do not use the SLANT option. Probably it's too late to enable it.

In any case, we should just come up with clearer names than x_max... or at least be consistent. Is the SLANT option written to the simtel output in a way we can read it?

kosack commented 1 year ago

In the atmosphere module, the density profiles are all defined in terms of height from ground, and each defines an integral along the height direction (so everything is 1D), returning depth. The profile.line_of_sight_integral() does the 2D integration from infinity to a point, along the line-of-sight. It currently assumes a cartesian atmosphere for simplicity, but a proper numeric integral could be done for a spherical atmosphere or even a more realistic oblate spheroid if we really wanted, though the difference is small below 60° zenith angle. That's just an implementation detail, and probably not worth worrying about yet.

kosack commented 1 year ago

Note that neither of these are really what you want for doing particle physics: you want the integral along the line of the shower direction from infinity to the max point, not the line-of-sight from the observer. So even if we switch the definition, we still have the wrong answer. I mean the atmosphere module is still correct, you just have to give it the zenith angle of the shower not the observation

Though it's not really a problem: HillasReconstructor reconstructs the distance from the array to the shower max. But it also computes the full shower axis since it gets the impact point and the point-of-origin, so you can compute the proper slant depth from that. It's why we don't write out x_max currently... But h_max is probably the wrong name for that attribute, and we could simply just properly compute it as a height and be consistent. Then all the complexity of computing the correct x_max still has to happen externally

maxnoe commented 1 year ago

Also related: https://github.com/cta-observatory/ctapipe/issues/1030

kosack commented 1 year ago

Yes, I thought of tat as well. Here's a better diagram of what we have now...: image

But really we should add the observatory height, since the atmosphere model is from sea level.

So we want this: image

kosack commented 1 year ago

Anyhow, I suggest the following:

For the definition of height, there are two options:

  1. define heights as relative to the observatory's center point (0,0,0), e.g. consistent with the current definition of the telescope heights, which are also relative to the array center point (in x,y,z), where a telescope at height 10m is 10m above the array plane. Then in the conversion to x_max, the suer has to be careful to add the observatory height.
  2. add source.subarray.reference_location.geodetic.height to h_max, so heights are real heights above sea level. however, we could leave telescope z coordinates relative, which still makes sense and won't break the reconstruction.

Probably option 2 is safest.

Then fix the following bugs / add features:

If that sounds ok, I'll make a preliminary PR to fix at least this in the HillasReconstructor and HillasIntersection and update naming in the atmosphere module. So far nothing other than ImPACT actually uses x_max or h_max, so that can be fixed separately.

maxnoe commented 1 year ago

Sounds good to me

kosack commented 1 year ago

i did run into the issue that not all of our test files provide a subarray reference_location... So adding the height fails for those. Either we should update the example files, or have a fallback.

In particular:

lst_prod3_calibration_and_mcphotons.simtel.zst
maxnoe commented 1 year ago

i did run into the issue that not all of our test files provide a subarray reference_location... So adding the height fails for those. Either we should update the example files, or have a fallback.

The obs level height should be available for all and could be used as fallback. Specifying the reference location via simtel metadata is fairly new (Prod6 prototype files).

We could maybe fill the reference location with dummy lat/lon of 0 but the correct altitude from obslevel in that case: https://en.wikipedia.org/wiki/Null_Island

kosack commented 1 year ago

We could maybe fill the reference location with dummy lat/lon of 0 but the correct altitude from obslevel in that case: https://en.wikipedia.org/wiki/Null_Island

That sounds reasonable. I'll open a separate issue or PR for that. For now I fixed the problem by just switching what file is used for generating the dummy subarray in the tests.

kosack commented 1 year ago

@maxnoe Where can I access obslev? Is it only in corsika_inputcards, or is it parsed out somewhere?