Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6a990fc
Remove DL2EventLoader.output_table_schema config
kosack Jan 19, 2026
3940d93
add changelog
kosack Jan 19, 2026
d29f64c
make output_table_schema a constructor option
kosack Jan 19, 2026
94dd6df
add typehint
kosack Jan 19, 2026
bad000e
removed column schema from test_config and renamed
kosack Jan 19, 2026
eb52694
removed redundant code
kosack Jan 19, 2026
40b968e
added required columns for default renaming scheme
kosack Jan 19, 2026
e4cf0f9
added altaz_to_fov helper function
kosack Jan 21, 2026
6bef7a0
added test for altaz_to_fov
kosack Jan 21, 2026
2072b7b
started refactoring DL2EventPreprocessor
kosack Jan 21, 2026
f023614
updated docstring
kosack Jan 21, 2026
b93bab8
add test
kosack Jan 21, 2026
29d0a46
improved computation of fov offsets
kosack Jan 22, 2026
57d3a52
cleaned up feature_set and removed old code
kosack Jan 22, 2026
d6e8b29
improved some tests and sanity checks
kosack Jan 22, 2026
a16609c
minor cleanup of tests
kosack Jan 23, 2026
bcf428b
renamed parameter, update simulation feature_set
kosack Jan 23, 2026
238d619
added test for using different reconstructors
kosack Jan 23, 2026
949cd82
fix bug in _shallow_copy_table that lost metadata
kosack Jan 23, 2026
6af3bc3
Refactor event weighting into irf.EventWeighter
kosack Jan 23, 2026
4684eb6
added Phi binning (for future impl)
kosack Jan 26, 2026
9072a3c
renamed DL2EventPreprocessor to EventPreprocessor
kosack Jan 26, 2026
ac38fc2
rename test
kosack Jan 26, 2026
63a5d89
renamed to event_preprocessor.py
kosack Jan 26, 2026
441fe50
rename dl2_simulation feature set to dl2_irf
kosack Jan 26, 2026
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 docs/changes/2919.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removed the option ``output_table_schema`` from ``DL2EventLoader``, which was incorrectly implemented.
56 changes: 22 additions & 34 deletions src/ctapipe/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -985,37 +985,25 @@ def irf_events_table():


@pytest.fixture(scope="function")
def test_config():
return {
"DL2EventLoader": {"event_reader_function": "read_telescope_events_chunked"},
"DL2EventPreprocessor": {
"energy_reconstructor": "ExtraTreesRegressor",
"gammaness_classifier": "ExtraTreesClassifier",
"columns_to_rename": {},
"output_table_schema": [
Column(
name="obs_id", dtype=np.uint64, description="Observation Block ID"
),
Column(name="event_id", dtype=np.uint64, description="Array event ID"),
Column(name="tel_id", dtype=np.uint64, description="Telescope ID"),
Column(
name="ExtraTreesRegressor_tel_energy",
unit=u.TeV,
description="Reconstructed energy",
),
Column(
name="ExtraTreesRegressor_tel_energy_uncert",
unit=u.TeV,
description="Reconstructed energy uncertainty",
),
],
"apply_derived_columns": False,
# "disable_column_renaming": True,
"allow_unsupported_pointing_frames": True,
},
"DL2EventQualityQuery": {
"quality_criteria": [
("valid reco", "ExtraTreesRegressor_tel_is_valid"),
]
},
}
def dl2_event_loader_config():
"""A traitlets Config for the DL2EventLoader class."""
from traitlets.config import Config

return Config(
{
"DL2EventLoader": {
"event_reader_function": "read_telescope_events_chunked"
},
"DL2EventPreprocessor": {
"energy_reconstructor": "ExtraTreesRegressor",
"gammaness_classifier": "ExtraTreesClassifier",
"apply_derived_columns": False,
"allow_unsupported_pointing_frames": True,
},
"DL2EventQualityQuery": {
"quality_criteria": [
("valid reco", "ExtraTreesRegressor_tel_is_valid"),
]
},
}
)
7 changes: 6 additions & 1 deletion src/ctapipe/coordinates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
from .impact_distance import impact_distance, shower_impact_distance
from .nominal_frame import NominalFrame
from .telescope_frame import TelescopeFrame
from .utils import altaz_to_righthanded_cartesian, get_point_on_shower_axis
from .utils import (
altaz_to_fov,
altaz_to_righthanded_cartesian,
get_point_on_shower_axis,
)

__all__ = [
"TelescopeFrame",
Expand All @@ -37,6 +41,7 @@
"impact_distance",
"shower_impact_distance",
"get_point_on_shower_axis",
"altaz_to_fov",
]


Expand Down
15 changes: 15 additions & 0 deletions src/ctapipe/coordinates/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,18 @@ def test_single_telescope(subarray_prod5_paranal):
# 10 km is around the shower maximum, should be around 1 degree from the source
with pytest.warns(MissingFrameAttributeWarning):
assert u.isclose(source.separation(point), 1.0 * u.deg, atol=0.1 * u.deg)


def test_altaz_to_fov():
from ctapipe.coordinates import altaz_to_fov

column = altaz_to_fov(
az=[220.0, 220.2] * u.deg,
alt=[80.0, 79.2] * u.deg,
pointing_az=[220.0, 220.0] * u.deg,
pointing_alt=[80.0, 80.0] * u.deg,
)

assert column.unit == u.deg
assert np.allclose(column[0].value, 0)
assert np.allclose(column[1].value, [-0.03747984, -0.79993558])
26 changes: 25 additions & 1 deletion src/ctapipe/coordinates/utils.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import astropy.units as u
import numpy as np
from astropy.coordinates import AltAz
from astropy.coordinates import AltAz, SkyCoord
from erfa.ufunc import p2s as cartesian_to_spherical
from erfa.ufunc import s2p as spherical_to_cartesian

from .ground_frames import _get_xyz
from .nominal_frame import NominalFrame

__all__ = [
"altaz_to_righthanded_cartesian",
"get_point_on_shower_axis",
"altaz_to_fov",
]


Expand Down Expand Up @@ -80,3 +82,25 @@ def get_point_on_shower_axis(core_x, core_y, alt, az, telescope_position, distan
cartesian = point[np.newaxis, :] - _get_xyz(telescope_position).T
lon, lat, _ = cartesian_to_spherical(cartesian)
return AltAz(alt=lat, az=-lon, copy=False)


def altaz_to_fov(az, alt, pointing_az, pointing_alt) -> u.Quantity[2]:
"""
Compute FOV coordinates from alt/az coordinates.

This can be used in an FeatureGenerator or ExpressionEngine to get a single
column with fov_lon, fov_lat coordinates.

Returns
-------
u.Quantity[2]:
2D array of coordinates with 2 columns: fov_lon, fov_lat
"""
pointing_coord = SkyCoord(az=pointing_az, alt=pointing_alt, frame="altaz")
event_coord = SkyCoord(az=az, alt=alt, frame="altaz", origin=pointing_coord)
nominal_coord = event_coord.transform_to(NominalFrame)

# note the minus sign for the fov_lon, which is to match GADF conventions
return u.Quantity(
np.column_stack((-nominal_coord.fov_lon.deg, nominal_coord.fov_lat.deg)), u.deg
)
8 changes: 7 additions & 1 deletion src/ctapipe/core/feature_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ def _shallow_copy_table(table):
the original table.
"""
# automatically return Table or QTable depending on input
return table.__class__({col: table[col] for col in table.colnames}, copy=False)
table_copy = table.__class__(
{col: table[col] for col in table.colnames}, copy=False
)
table_copy.meta = (
table.meta.copy()
) # here we don't want a shallow copy, as we might add metadata
return table_copy


class FeatureGeneratorException(TypeError):
Expand Down
21 changes: 14 additions & 7 deletions src/ctapipe/core/tests/test_feature_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,26 @@ def test_feature_generator():
generator = FeatureGenerator(features=expressions)

table = Table({"intensity": [1, 10, 100], "length": [2, 4, 8], "width": [1, 2, 4]})
table.meta["VERSION"] = 1.0

log_intensity = [0, 1, 2]
area = [2, 8, 32]
eccentricity = np.sqrt(0.75)

table = generator(table)
processed_table = generator(table)
processed_table.meta["GEN"] = True

assert "log_intensity" in processed_table.colnames
assert "area" in processed_table.colnames
assert "eccentricity" in processed_table.colnames

assert "log_intensity" in table.colnames
assert "area" in table.colnames
assert "eccentricity" in table.colnames
assert np.all(processed_table["log_intensity"] == log_intensity)
assert np.all(processed_table["area"] == area)
assert np.all(processed_table["eccentricity"] == eccentricity)

assert np.all(table["log_intensity"] == log_intensity)
assert np.all(table["area"] == area)
assert np.all(table["eccentricity"] == eccentricity)
# also check that metadata was propagated and doesn't overwrite old table:
assert "VERSION" in processed_table.meta
assert "GEN" not in table.meta


def test_existing_feature():
Expand Down
6 changes: 3 additions & 3 deletions src/ctapipe/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from .astropy_helpers import read_table, write_table # noqa: I001
from .datalevels import DataLevel
from .dl2_tables_preprocessing import DL2EventPreprocessor, DL2EventLoader
from .event_preprocessor import EventPreprocessor, PreprocessorFeatureSet
from .eventsource import EventSource
from .eventseeker import EventSeeker
from .tableio import TableReader, TableWriter
Expand Down Expand Up @@ -43,7 +43,7 @@
"DataWriter",
"DATA_MODEL_VERSION",
"get_hdf5_datalevels",
"DL2EventPreprocessor",
"DL2EventLoader",
"EventPreprocessor",
"get_hdf5_monitoring_types",
"PreprocessorFeatureSet",
]
Loading
Loading