Skip to content

Add possibility to produce DL3 with ctapipe #2727

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

Draft
wants to merge 62 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ac0d718
Add function to compute selection cuts
mdebony Feb 20, 2025
17f0ed0
Make a component for event selection
mdebony Feb 20, 2025
dbce02b
Generalise behaviour
mdebony Feb 20, 2025
7606d22
Update event loader in the view of using it for DL3 production
mdebony Feb 20, 2025
5111d1f
change the output format of get simulation
mdebony Feb 20, 2025
47fc14d
Fix cicular depedency
mdebony Feb 20, 2025
1b26017
Update documentation of cuts
mdebony Feb 20, 2025
1af4094
Update event processor to support gamaness event selection
mdebony Feb 20, 2025
62df524
Update event loader for gamma event selection
mdebony Feb 20, 2025
50c1af4
Update path criteria for input
mdebony Feb 20, 2025
aa64f93
Further update to prepare for DL3
mdebony Feb 21, 2025
207cb7c
Skeleton for creating DL3
mdebony Feb 21, 2025
2ea79cc
Basic DL3 structure
mdebony Feb 21, 2025
7811ab1
Add option for optional DL3 columns
mdebony Mar 3, 2025
861d2fa
Change option for flag
mdebony Mar 3, 2025
bf0cd86
Fix the option
mdebony Mar 3, 2025
4b3765c
Add DL3 and IRF mode for preprocessor
mdebony Mar 3, 2025
9ff2f21
Adapt preprocessor columns request
mdebony Mar 3, 2025
912d871
Add option to skip missing optional column
mdebony Mar 3, 2025
8336606
Allow for variation of the pointing for DL3
mdebony Mar 3, 2025
636fbae
Move the derived columns function
mdebony Mar 6, 2025
15e089f
Update make_derived_columns function for producing columns needed for…
mdebony Mar 6, 2025
df6a9c6
Update columns selection for DL3 production
mdebony Mar 6, 2025
8dcfea5
Update table creation
mdebony Mar 6, 2025
3fcfe22
Correct handling of optional columns for DL3
mdebony Mar 6, 2025
0f795b4
Fix on optional columns
mdebony Mar 6, 2025
f3ba314
Format column for DL3 format
mdebony Mar 6, 2025
aedf6d8
Change x max for h max
mdebony Mar 6, 2025
e2a119b
Set base object for DL3 formating
mdebony Mar 6, 2025
475a4bd
Update message
mdebony Mar 6, 2025
fa25592
Compute average uncertainty on core position
mdebony Mar 6, 2025
9b0c9f9
Switch to fully use the object for DL3 format
mdebony Mar 6, 2025
8e45123
Fix setter
mdebony Mar 6, 2025
5c06050
Fix getter
mdebony Mar 6, 2025
e06054f
Add edisp getter and setter
mdebony Mar 6, 2025
0a1d94b
Direct access for overwrite
mdebony Mar 10, 2025
60f14aa
Add a base abstract class for generic DL3 format
mdebony Mar 10, 2025
d2d87ff
Add loading of location and gti
mdebony Mar 10, 2025
663faf4
Fix
mdebony Mar 10, 2025
0f712c6
Update headers of the DL3 file
mdebony Mar 11, 2025
a9ca20c
Update way of reading metainformation of the runs
mdebony Mar 11, 2025
e8e3d0a
Merge branch 'table_loader_schedule_block' into create_dl3
mdebony Mar 11, 2025
09e9ce5
Merge branch 'table_loader_schedule_block' into create_dl3
mdebony Mar 11, 2025
74cca7f
Add reading of additional observation metadata from dl2 file
mdebony Mar 12, 2025
681ce64
Add new metadata to dl3 class
mdebony Mar 12, 2025
1ce26f5
Add dead time fraction as metadata
mdebony Mar 12, 2025
25f8f8b
Proper selection of mandatory only columns
mdebony Mar 12, 2025
8e03bdb
Add metadata header to the DL3 file
mdebony Mar 12, 2025
df027d1
Add GTI table in the DL3 file
mdebony Mar 12, 2025
3223155
Add pointing table and header to the DL3 file
mdebony Mar 12, 2025
e3c5245
Fix interpolation
mdebony Mar 12, 2025
ae70fd4
Add docstring to the dl3.py file
mdebony Mar 13, 2025
e3c5af9
Merge remote-tracking branch 'upstream/main' into create_dl3
mdebony Mar 13, 2025
917240f
Fix errors encountered in automatic tests
mdebony Mar 17, 2025
58ecd82
Multiple fix
mdebony Mar 21, 2025
0fde645
Add docstring
mdebony Mar 21, 2025
3de36aa
Multiple fix
mdebony Mar 24, 2025
5017133
Merge branch 'main' into create_dl3
mdebony Mar 25, 2025
7914648
Fix configurations
mdebony Mar 25, 2025
46b5a9b
Add X max calculation
mdebony Mar 26, 2025
e099c12
Switch time scale to TAI
mdebony Mar 26, 2025
bd20df6
Add configurable reference time
mdebony Mar 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespell-ignores
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ usera
nd
studi
referenc
livetime
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ ctapipe-process = "ctapipe.tools.process:main"
ctapipe-merge = "ctapipe.tools.merge:main"
ctapipe-optimize-event-selection = "ctapipe.tools.optimize_event_selection:main"
ctapipe-compute-irf = "ctapipe.tools.compute_irf:main"
ctapipe-create-dl3 = "ctapipe.tools.create_dl3:main"
ctapipe-fileinfo = "ctapipe.tools.fileinfo:main"
ctapipe-quickstart = "ctapipe.tools.quickstart:main"
ctapipe-calculate-pixel-statistics = "ctapipe.tools.calculate_pixel_stats:main"
Expand Down
16 changes: 12 additions & 4 deletions src/ctapipe/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ def irf_event_loader_test_config():
"energy_reconstructor": "ExtraTreesRegressor",
"geometry_reconstructor": "HillasReconstructor",
"gammaness_classifier": "ExtraTreesClassifier",
"EventQualityQuery": {
"EventQualitySelection": {
"quality_criteria": [
(
"multiplicity 4",
Expand Down Expand Up @@ -816,9 +816,17 @@ def irf_events_table():
N2 = 100
N = N1 + N2
epp = EventPreprocessor()
tab = epp.make_empty_table()

ids, bulk, unitless = tab.colnames[:2], tab.colnames[2:-2], tab.colnames[-2:]
keep_columns, _, _ = epp.get_columns_keep_rename_scheme(None, True)
tab = epp.make_empty_table(keep_columns)

ids, bulk, unitless = [], [], []
for c in tab.columns:
if "id" in c:
ids.append(c)
elif tab[c].unit == u.dimensionless_unscaled:
unitless.append(c)
else:
bulk.append(c)

id_tab = QTable(
data=np.zeros((N, len(ids)), dtype=np.uint64),
Expand Down
3 changes: 3 additions & 0 deletions src/ctapipe/irf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
check_bins_in_range,
make_bins_per_decade,
)
from .cuts import EventQualitySelection, EventSelection
from .irfs import (
BackgroundRate2dMaker,
EffectiveArea2dMaker,
Expand Down Expand Up @@ -56,4 +57,6 @@
"FLUX_UNIT",
"check_bins_in_range",
"make_bins_per_decade",
"EventSelection",
"EventQualitySelection",
]
4 changes: 4 additions & 0 deletions src/ctapipe/irf/cuts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .quality_cuts import EventQualitySelection
from .selection_cuts import EventSelection

__all__ = ["EventSelection", "EventQualitySelection"]
58 changes: 58 additions & 0 deletions src/ctapipe/irf/cuts/quality_cuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from astropy.table import Table

from ...core import QualityQuery
from ...core.traits import List, Tuple, Unicode


class EventQualitySelection(QualityQuery):
"""
Event pre-selection quality criteria for IRF and DL3 computation with different defaults.
"""

quality_criteria = List(
Copy link
Member

@kosack kosack Mar 25, 2025

Choose a reason for hiding this comment

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

We can't really allow the user to select events in a totally arbitrary way when going from DL2→DL3, as the cuts applied in DL2 also define the IRFs, and if you change them, the IRFs are wrong. So I think the correct thing here is not to allow a general QualityQuery (though maybe we need a fixed one to drop missing or non-reconstructed events), but rather to load the cuts that are output from ctapipe-optimize-event-selection and apply them to the observed DL2 data.

Tuple(Unicode(), Unicode()),
default_value=[
(
"multiplicity 4",
"np.count_nonzero(HillasReconstructor_telescopes,axis=1) >= 4",
),
("valid classifier", "RandomForestClassifier_is_valid"),
("valid geom reco", "HillasReconstructor_is_valid"),
("valid energy reco", "RandomForestRegressor_is_valid"),
],
help=QualityQuery.quality_criteria.help,
).tag(config=True)

def calculate_selection(self, events: Table):
"""
Add the selection columns to the events, will only compute quality selection

Parameters
----------
events: Table
The table containing the events on which selection need to be applied

Returns
-------
Table
events with selection columns added.
"""
return self.calculate_quality_selection(events)

def calculate_quality_selection(self, events: Table):
"""
Add the selection columns to the events, will only compute quality selection

Parameters
----------
events: Table
The table containing the events on which selection need to be applied

Returns
-------
Table
events with selection columns added.
"""
events["selected_quality"] = self.get_table_mask(events)
events["selected"] = events["selected_quality"]
return events
88 changes: 88 additions & 0 deletions src/ctapipe/irf/cuts/selection_cuts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import operator

from astropy.table import Table
from pyirf.cuts import evaluate_binned_cut

from ...core.traits import Path
from ..optimize.results import OptimizationResult
from .quality_cuts import EventQualitySelection

__all__ = ["EventSelection"]


class EventSelection(EventQualitySelection):
"""
Event selection quality and gammaness criteria for IRF and DL3
"""

cuts_file = Path(
allow_none=False,
directory_ok=False,
exists=True,
help="Path to the cuts file to apply to the observation.",
).tag(config=True)

def __init__(self, config=None, parent=None, **kwargs):
super().__init__(config=config, parent=parent, **kwargs)
self.cuts = OptimizationResult.read(self.cuts_file)

def calculate_selection(self, events: Table, apply_spatial_selection: bool = False):
"""
Add the selection columns to the events

Parameters
----------
events: Table
The table containing the events on which selection need to be applied
apply_spatial_selection: bool
True if the theta cuts should be applied

Returns
-------
Table
events with selection columns added.
"""
events = self.calculate_quality_selection(events)
events = self.calculate_gamma_selection(events, apply_spatial_selection)
events["selected"] = events["selected_quality"] & events["selected_gamma"]
return events

def calculate_gamma_selection(
self, events: Table, apply_spatial_selection: bool = False
) -> Table:
"""
Add the selection columns to the events, will compute only gamma criteria

Parameters
----------
events: Table
The table containing the events on which selection need to be applied
apply_spatial_selection: bool
True if the theta cuts should be applied

Returns
-------
Table
events with selection columns added.
"""

events["selected_gh"] = evaluate_binned_cut(
events["gh_score"],
events["reco_energy"],
self.cuts.gh_cuts,
operator.ge,
)

if apply_spatial_selection:
events["selected_theta"] = evaluate_binned_cut(
events["theta"],
events["reco_energy"],
self.cuts.spatial_selection_table,
operator.le,
)
events["selected_gamma"] = events["selected_theta"] & events["selected_gh"]
else:
events["selected_gamma"] = events["selected_gh"]

events["selected"] = events["selected_gamma"]
return events
Loading
Loading