Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dl3 reader for LST data #7

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open

Dl3 reader for LST data #7

wants to merge 19 commits into from

Conversation

mstrzys
Copy link
Collaborator

@mstrzys mstrzys commented Jun 25, 2023

DL3 reader; first for LST data only; to add a DL3 reader also for MAGIC data the filetype check needs a rework, which will go into a separate PR.

Marcel Strzys added 5 commits June 13, 2023 19:58
- added an event class for LST dl3 files
- tested that class can read LST dl3 files
- entire chain not tested
- DL3 format for other instruments (e.g. MAGIC) can be slightly
  different and mya require a different reader

ToDo:
- MAGIC DL3 reader
- test entire chain with LST DL3 files
Found and corrected the following issues, some concern also previous
commits to the main branch:

- issue of the unti of the event time variable
- wrong unit of the ra event variable
- dl2 file reader had an error if the ra variable of the pointing
  position was not precomputed
- wrong overwriting of the setter function for the bkgmap maker in the
  bkg classes
to set attribute bkg_maps instead of the correct _bkg_maps
@mstrzys mstrzys requested a review from IevgenVovk June 25, 2023 06:22
@mstrzys mstrzys self-assigned this Jun 25, 2023
@IevgenVovk
Copy link
Collaborator

In general PR looks okey save the inserted docstring placeholders (useless unless replaced with actual docs). I would encourage in the future not to mix topical PRs like this with bug and style fixes in the unrelated classes and modules.

src/pybkgmodel/data.py Outdated Show resolved Hide resolved
Marcel Strzys added 4 commits March 19, 2024 18:42
 - Modified the reader so it can read DL3 file regardless the instrument.
 - Data units are read from hdu header as long as provided.
 - Tested with LST and MAGIC DL3 files.

TODO:
 - Observatory location not part of the mandatory header keywords. May
   need to be provided in input file by the user. Will be part of future
pull request
 - Drift method implemented but not tested due to lack of corresponding data.
performance
 - added some functionalities suggested by Lukas Nickel, but adapted
   them to the current version
 - changed the function for retrieving the Obs ID from filename to
   reading it from the hdu header
 - changed the is_compatible, so it also identifies compressed files
 - added the erfaAstromInterpolator for the coordinate transform for
   speed up
 - added docstrings to all functions related to the DL3 reader
modes are not to well defined e.g. the H.E.S.S. public data use WOBBLE
as the obs mode. Hence, added it to support such files until the allowed
modes are better defined.
@mstrzys
Copy link
Collaborator Author

mstrzys commented Mar 21, 2024

The latest changes allow the DL3 reader to not only read the LST files, but DL3 files in general. I tested it with LST, H.E.S.S. (public data release), and MAGIC DL3 files.

Furthermore added the suggestions from Lukas regarding the interpolator for the coordinate transform and the changes regarding the Obs ID retrieval; changed the compatibility check to not reject compressed files as well.

Current version is ready for review.

src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
from the corresponding PR (#7).

- change name from Dl2 to DL2 to agree with the DL3 naming
- changed to way the event times are read from DL3 files to be
  consistent with the 'mjd' naming of the dictionary
- the functionality of the code was not changed
src/pybkgmodel/data.py Outdated Show resolved Hide resolved
@maxnoe
Copy link
Member

maxnoe commented Jul 26, 2024

Hi,

I got a notification here and had a cursory glance, but what I saw made me very nervous about timing information.

You add and subtract raw mjd and unix values at different points in the code here instead of using astropy Time and TimeDelta objects.

This will result in timestamps that are off by the number of leap seconds between those two times. Never do that.

You are also completely neglecting the time scale.

event_data[name] *= u.one

# Event times need to be converted from Instrument reference epoch
ref_epoch = astropy.time.Time(evt_head['MJDREFI']+evt_head['MJDREFF'], format='mjd')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the two argument form of astropy.time.Time, otherwise you will have precision issues:

ref_epoch = astropy.time.Time(evt_head['MJDREFI'], evt_head['MJDREFF'], format='mjd')

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, you need to read the time scale

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added, though I found that in the header of the DL3 files the TIMESYS is given in all-caps whereas astropy prefers lower cases, is there a reason for why it is all-caps in the LST files?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because that's what the FITS standard is prescribing

# Event times need to be converted from Instrument reference epoch
ref_epoch = astropy.time.Time(evt_head['MJDREFI']+evt_head['MJDREFF'], format='mjd')

event_data['mjd'] = astropy.time.Time((evt_data['TIME'].to_numpy()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not do this! this results in times that are off by the number of leap seconds between the reference epoch and the event time.

The correct solution is (ref_epoch + evt_data["TIME"].quantity).mjd

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

).mjd * u.d

# Adapt
evt_time = astropy.time.Time(event_data['mjd'], format='mjd')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now you create a time object a second time.

Just to evt_time = ref_epoch + evt_data["TIME"].quantity and then evt_data["mjd"] = evt_time.mjd

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of this is also already implemented in Gammapy, e.g. here: https://github.com/gammapy/gammapy/blob/main/gammapy/utils/time.py

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified according to your suggestion.

try:
evt_hdu = input_file["EVENTS"]
evt_head = evt_hdu.header
evt_data = pandas.DataFrame(evt_hdu.data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use an astropy table to have support for the quantities and metadata stored in the hdu

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that hint, simplified it a lot. Adapted.

 - changed the time handling following Max' recommendations in the discussion of the PR
 - changed the way the fits file compatibility is checked following Michele's recommendations in the discussion of the PR
 - changed the setter for the bkg_map_makers following Ievgen's recommendation

TODO:
 - Test with data
+ ref_epoch.unix),
scale='utc',
format='unix'
event_data['mjd'] = astropy.time.Time((evt_data['TIME'].quantity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer time here is unnecessary.

The result of ref_epoch + evt_data["TIME"].quantity is a time, you are just making a copy again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

# TODO: current observatory location only La Palma, no mandatory header keyword
obs_loc = EarthLocation(lat=28.761758*u.deg,
lon=-17.890659*u.deg,
height=2200*u.m)

if evt_head['OBS_MODE'] in ('POINTING', 'WOBBLE'):

alt_az_frame = AltAz(obstime=evt_time,
alt_az_frame = AltAz(obstime=event_data['mjd'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you can pass a raw mjd value to AltAz(obstime=), this needs to be a Time instance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, corrected.

  - simplified the reading of the data from the fits files with astropy.Tables follwing Max' suggestion
  - fixed the obstime object in the AltAz frame definition following Max' comment in the discussion of the PR
 - fixed the setter function of the bkg_map_maker
 - debugging the setter for the bkg_map_maker
 - current version not running
 - intermediate commit for checking the updated setter
 - ready for further review
 - setter for bkg_map_maker fixed
 - time scale added to ref_epoch
 - checked that the code runs with with LST-1 data
@mstrzys mstrzys requested a review from maxnoe August 1, 2024 08:31
@@ -375,7 +375,7 @@ def get_pixel_coords(self):

x = (self.xedges[1:] + self.xedges[:-1]) / 2
y = (self.yedges[1:] + self.yedges[:-1]) / 2
xx, yy = np.meshgrid(x, y, indexing='ij')
xx, yy = numpy.meshgrid(x, y, indexing='ij')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the wrong resolution of a merge conflict?

The import at the top is import numpy as np

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As entire merge went wrong. I reset the merge. New version should not have this mistake.

@@ -312,11 +314,11 @@ def load_events(cls, file_name, cuts):
event_data[name] = data[key]

# Post processing
event_data['true_zd'] = np.degrees(event_data['true_zd'])
event_data['true_az'] = np.degrees(event_data['true_az'])
event_data['true_zd'] = numpy.degrees(event_data['true_zd'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As entire merge went wrong. I reset the merge. New version should not have this mistake.

@maxnoe
Copy link
Member

maxnoe commented Aug 1, 2024

@mstrzys I think something went seriously wrong in your last merge commit, all the changes related to reading LST DL3 data were removed

Marcel Strzys added 3 commits August 5, 2024 13:06
  - changed the remaining numpy's to np, not covered by the merger
 - removed the np.bool for bool inherited from the main branch
 - tested with LST1 data and ensure that it runs properly
@mstrzys mstrzys requested a review from maxnoe August 5, 2024 06:01
)

evt_time = evt_data['TIME'].quantity + ref_epoch
event_data['mjd'] = evt_time.mjd * u.d
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure, but I think this mjd will now depend on the timescale of the input file, and I guess you use this value internally and assume it's in UTC or some other fixed timescale?

Why are you storing the mjd in any case and not the full time object?

* len(event_data['pointing_zd'])
) * u.deg

elif evt_head['OBS_MODE'] == 'DRIFT':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just raise an error to let users know this is not supported instead of providing preliminary or invalid data (same for the next else block, this should be an actual error, not a print statement)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants