Skip to content
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f3d441d
Include template paths as telescope parameters
gschwefer Feb 25, 2025
cd6e775
Move template set up to reconstructor init
gschwefer Feb 25, 2025
5daf0cd
Make spe and pedestal width telescope parameters
gschwefer Feb 28, 2025
37c6bd4
Add tel_type key to dummy templates
gschwefer Feb 28, 2025
5beb81d
Make tests pass gain
gschwefer Feb 28, 2025
8fbb6fc
Remove dummy reconstructor option
gschwefer Feb 28, 2025
c4bdb2b
Update shower processor config to work with ImPACT
gschwefer Mar 3, 2025
f87bf31
Add template paths to reconstruction tests
gschwefer Mar 3, 2025
17bb07f
Add changelog
gschwefer Mar 3, 2025
f37c75b
Make template bounds a global variable
gschwefer Mar 21, 2025
a2542cc
Commit minimal version of FreePACT code
ParsonsRD Nov 6, 2025
438e307
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Nov 6, 2025
c03acdc
Minor commit to change to new container structure.
ParsonsRD Nov 7, 2025
cf85a0d
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Nov 11, 2025
a81c146
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Jan 19, 2026
4514e78
Add tensorflow as optional dependency
ParsonsRD Jan 21, 2026
4b34c72
Fix dependency versions in pyproject.toml
ParsonsRD Jan 21, 2026
c2226a3
Fixing CI build
ParsonsRD Jan 22, 2026
0250307
Reformat of file
ParsonsRD Jan 22, 2026
d039bf3
Fixing CI build... again
ParsonsRD Jan 22, 2026
ee0e95b
Making import of tensorflow optional
ParsonsRD Jan 23, 2026
64b92f2
Fix messed up merge
ParsonsRD Jan 23, 2026
ffd8d55
Fixing optional dependency
ParsonsRD Jan 23, 2026
35b22ad
Fixed failing unit tests
ParsonsRD Jan 23, 2026
2f468ec
Removed pedestal and spe default table
ParsonsRD Jan 23, 2026
22e1300
Reverted change in iminuit requirement
ParsonsRD Jan 23, 2026
fde474c
Fixin TF exceptions
ParsonsRD Jan 23, 2026
250d0ac
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Jan 26, 2026
46c97aa
Added FreePACT tests and minor fixes to impact
ParsonsRD Jan 27, 2026
53007eb
Fixing import error as minor doc changes
ParsonsRD Jan 27, 2026
e8e162f
Again fixing optional dependencies for tensorflow.
ParsonsRD Jan 27, 2026
aa90a60
Remove masked array copy function no longer supported
ParsonsRD Jan 27, 2026
da3af8f
Remove masked array copy function no longer supported
ParsonsRD Jan 27, 2026
61e9c60
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Jan 27, 2026
c702573
Add filter for tensorflow warnings
ParsonsRD Jan 27, 2026
a396795
Filter for pyparsing warnings.
ParsonsRD Jan 28, 2026
31ceb96
fix pyparsing warning
ParsonsRD Jan 28, 2026
1fb8b0e
Again parsin warning fix
ParsonsRD Jan 28, 2026
25c0b89
Fix filterwarnings
maxnoe Jan 28, 2026
d47afd1
Remove impact extra from minimal-dependencies CI job
maxnoe Jan 28, 2026
6683ef3
sonarqube error fixes
ParsonsRD Jan 28, 2026
64d93ca
Fix sonarqube complaints
ParsonsRD Jan 28, 2026
7e57590
First FreePACT trainer commit
ParsonsRD Jan 30, 2026
85a2b96
Reordered parameters in the interpolator
ParsonsRD Feb 5, 2026
5b30303
General refactor of training script to work with DL1 exported data
ParsonsRD Feb 5, 2026
5753282
Merge branch 'main' into freepact_repeat
ParsonsRD Feb 5, 2026
5f766d6
Add optional dependency check and fixed loading of subarray from hdf …
ParsonsRD Feb 5, 2026
f1b81e7
Made tf import optional
ParsonsRD Feb 6, 2026
b477ae5
Some options added to FreePACT trainer, include trainer as command li…
ParsonsRD Feb 24, 2026
a050b63
Merge branch 'cta-observatory:main' into freepact_repeat
ParsonsRD Apr 8, 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/2705.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make template paths and likelihood parameters TelecopeParameters in ImPACT
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ all = [
"ctapipe[eventio]",
"iminuit >=2",
Comment thread
maxnoe marked this conversation as resolved.
"matplotlib ~=3.0",
"pyirf ~=0.13.0"
"pyirf ~=0.13.0",
"tensorflow >=2.19.0"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd add a new extra here called impact or freepact that depnds on tensorflow and iminuit (you need both, right?). So that people can do pip install ctapipe[impact] and that gives them a working environment for impact.

In all you can then depend on ctapipe[impact].

]

tests = [
Expand Down
3 changes: 3 additions & 0 deletions src/ctapipe/reco/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

# reconstructors must be imported before ShowerProcessor, so
# they are available there
from .freepact import FreePACTProtonReconstructor, FreePACTReconstructor
from .hillas_intersection import HillasIntersection
from .hillas_reconstructor import HillasReconstructor
from .impact import ImPACTReconstructor
Expand All @@ -26,6 +27,8 @@
"ShowerProcessor",
"HillasReconstructor",
"ImPACTReconstructor",
"FreePACTReconstructor",
"FreePACTProtonReconstructor",
"HillasIntersection",
"EnergyRegressor",
"ParticleClassifier",
Expand Down
158 changes: 158 additions & 0 deletions src/ctapipe/reco/freepact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""FreePACT Reconstructor for ctapipe
This module implements the FreePACT reconstructor, which is a subclass of ImPACTReconstructor.
It uses a neural network to predict th likelihood camera images based on the shower parameters."""

import numpy as np
import numpy.ma as ma

from ctapipe.core import traits
from ctapipe.core.telescope_component import TelescopeParameter
from ctapipe.reco.impact import ImPACTReconstructor
from ctapipe.utils.template_network_interpolator import FreePACTInterpolator

from ..compat import COPY_IF_NEEDED
from .impact_utilities import rotate_translate

__all__ = ["FreePACTReconstructor"]


class FreePACTReconstructor(ImPACTReconstructor):
"""
FreePACT Reconstructor
This class implements the FreePACT reconstructor, which uses a neural network
to predict the likelihood of camera images based on shower parameters.
"""

image_template_path = TelescopeParameter(
trait=traits.Path(exists=True, directory_ok=True, allow_none=False),
allow_none=False,
help=("Path to the image templates to be used in the reconstruction"),
).tag(config=True)

def __init__(self, *args, **kwargs):
Comment thread
maxnoe marked this conversation as resolved.
Outdated
super().__init__(*args, **kwargs)

def set_up_templates(self):
"""Set up the templates for the FreePACT reconstructor."""
template_sort_dict = {}

for tel_id in self.subarray.tel_ids:
if self.image_template_path.tel[tel_id] not in template_sort_dict.keys():
template_sort_dict[self.image_template_path.tel[tel_id]] = [tel_id]
else:
template_sort_dict[self.image_template_path.tel[tel_id]].append(tel_id)

for template_path, tel_ids in template_sort_dict.items():
net_interpolator = FreePACTInterpolator(template_path)

self.prediction[tuple(tel_ids)] = net_interpolator

def get_likelihood(
self,
source_x,
source_y,
core_x,
core_y,
energy,
x_max_scale,
goodness_of_fit=False,
):
"""Get the likelihood that the image predicted at the given test
position matches the camera image.

Parameters
----------
source_x: float
Source position of shower in the nominal system (in deg)
source_y: float
Source position of shower in the nominal system (in deg)
core_x: float
Core position of shower in tilted telescope system (in m)
core_y: float
Core position of shower in tilted telescope system (in m)
energy: float
Shower energy (in TeV)
x_max_scale: float
Scaling factor applied to geometrically calculated Xmax

Returns
-------
float: Likelihood the model represents the camera image at this position

"""
if np.isnan(source_x) or np.isnan(source_y):
return 1e8

# First we add units back onto everything. Currently not
# handled very well, maybe in future we could just put
# everything in the correct units when loading in the class
# and ignore them from then on

zenith = self.zenith
azimuth = self.azimuth

# Geometrically calculate the depth of maximum given this test position
x_max_guess = self.get_shower_max(source_x, source_y, core_x, core_y, zenith)
x_max_guess *= x_max_scale

# Convert to binning of Xmax
x_max_diff = x_max_guess

# Calculate impact distance for all telescopes
impact = np.sqrt(
(self.tel_pos_x - core_x) ** 2 + (self.tel_pos_y - core_y) ** 2
)
# And the expected rotation angle
phi = np.arctan2(self.tel_pos_y - core_y, self.tel_pos_x - core_x)

# Rotate and translate all pixels such that they match the
# template orientation
# numba does not support masked arrays, work on underlying array and add mask back
pix_x_rot, pix_y_rot = rotate_translate(
self.pixel_y.data, self.pixel_x.data, source_y, source_x, -phi
)
pix_x_rot = np.ma.array(pix_x_rot, mask=self.pixel_x.mask, copy=COPY_IF_NEEDED)
pix_y_rot = np.ma.array(pix_y_rot, mask=self.pixel_y.mask, copy=COPY_IF_NEEDED)

# In the interpolator class we can gain speed advantages by using masked arrays
# so we need to make sure here everything is masked
likelihood = ma.zeros(self.image.shape)
likelihood.mask = ma.getmask(self.image)

for tel_ids, template in self.prediction.items():
template_mask = self.template_masks[tel_ids]
if np.any(template_mask):
likelihood[template_mask] = template(
np.rad2deg(zenith),
azimuth,
energy * np.ones_like(impact[template_mask]),
impact[template_mask],
x_max_diff * np.ones_like(impact[template_mask]),
np.rad2deg(pix_x_rot[template_mask]),
np.rad2deg(pix_y_rot[template_mask]),
self.image[template_mask],
)
if goodness_of_fit:
# return -2 * np.sum(likelihood[self.image>5]) / np.sum(self.image>5)
# print(ma.getmask(self.image))
return -2 * ma.sum(likelihood) / np.sum(~ma.getmask(self.image))

likelihood = ma.sum(likelihood) * -2

return likelihood


class FreePACTProtonReconstructor(FreePACTReconstructor):
"""FreePACT Proton Reconstructor
Comment thread
maxnoe marked this conversation as resolved.
Outdated
This class implements the FreePACT reconstructor for proton showers.
It uses a neural network to predict the likelihood of camera images based on shower parameters.
"""

image_template_path = TelescopeParameter(
trait=traits.Path(exists=True, directory_ok=True, allow_none=False),
allow_none=False,
help=("Path to the image templates to be used in the reconstruction"),
).tag(config=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Loading
Loading