Skip to content

Commit 051fb84

Browse files
committed
TriggerReader cleaned changes
1 parent 79d8b22 commit 051fb84

3 files changed

Lines changed: 883 additions & 76 deletions

File tree

dl1_data_handler/image_mapper.py

Lines changed: 98 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
from scipy import spatial
77
from scipy.sparse import csr_matrix
88
from collections import Counter, namedtuple
9+
import tables
10+
from importlib.resources import files
11+
import astropy.units as u
912

1013
from ctapipe.instrument.camera import PixelShape
11-
from ctapipe.core import Component
12-
from ctapipe.core.traits import Bool, Int, Float
14+
from ctapipe.core import TelescopeComponent
15+
from ctapipe.core.traits import Bool, Int
1316

1417
__all__ = [
1518
"ImageMapper",
@@ -21,9 +24,10 @@
2124
"RebinMapper",
2225
"ShiftingMapper",
2326
"SquareMapper",
27+
"HexagonalPatchMapper",
2428
]
2529

26-
class ImageMapper(Component):
30+
class ImageMapper(TelescopeComponent):
2731
"""
2832
Base component for mapping raw 1D vectors into 2D mapped images.
2933
@@ -56,6 +60,8 @@ class ImageMapper(Component):
5660
Multiplication factor used for rebinning.
5761
index_matrix : numpy.ndarray or None
5862
Matrix used for indexing, initialized to None.
63+
cam_neighbor_array : numpy.ndarray or None
64+
Matrix used for indexing, initialized to None.
5965
6066
Methods
6167
-------
@@ -84,29 +90,15 @@ def __init__(
8490
parent : ctapipe.core.Component or ctapipe.core.Tool
8591
Parent of this component in the configuration hierarchy,
8692
this is mutually exclusive with passing ``config``
87-
**kwargs
88-
Additional keyword arguments for traitlets. Non-traitlet kwargs
89-
(like 'subarray') are filtered out for compatibility.
9093
"""
9194

92-
# Filter out non-traitlet kwargs before passing to Component
93-
# This allows compatibility with ctapipe's reader which may pass extra kwargs
94-
component_kwargs = {
95-
key: value for key, value in kwargs.items()
96-
if self.class_own_traits().get(key) is not None
97-
}
98-
99-
super().__init__(
100-
config=config,
101-
parent=parent,
102-
**component_kwargs,
103-
)
104-
10595
# Camera types
10696
self.geometry = geometry
10797
self.camera_type = self.geometry.name
10898
self.n_pixels = self.geometry.n_pixels
10999
# Rotate the pixel positions by the pixel to align
100+
if self.camera_type == "AdvCamSiPM":
101+
self.geometry.pix_rotation = 8.213 * u.deg
110102
self.geometry.rotate(self.geometry.pix_rotation)
111103

112104
self.pix_x = np.around(
@@ -122,7 +114,7 @@ def __init__(
122114
# Additional smooth the ticks for 'DigiCam', 'RealLSTCam' and 'CHEC' cameras
123115
if self.camera_type in ["DigiCam", "RealLSTCam"]:
124116
self.pix_y, self.y_ticks = self._smooth_ticks(self.pix_y, self.y_ticks)
125-
if self.camera_type == "CHEC":
117+
if self.camera_type in ["CHEC", "AdvCamSiPM"]:
126118
self.pix_x, self.x_ticks = self._smooth_ticks(self.pix_x, self.x_ticks)
127119
self.pix_y, self.y_ticks = self._smooth_ticks(self.pix_y, self.y_ticks)
128120

@@ -140,6 +132,7 @@ def __init__(
140132

141133
# Set the indexed matrix to None
142134
self.index_matrix = None
135+
self.cam_neighbor_array = None
143136

144137
def map_image(self, raw_vector: np.array) -> np.array:
145138
"""
@@ -374,7 +367,7 @@ def _get_grids_for_oversampling(
374367
)
375368
# Adjust for odd tick_diff
376369
# TODO: Check why MAGICCam, VERITAS, and UNKNOWN-7987PX (AdvCam) do not need this adjustment
377-
if tick_diff % 2 != 0 and self.camera_type not in ["MAGICCam", "VERITAS", "UNKNOWN-7987PX"]:
370+
if tick_diff % 2 != 0 and self.camera_type not in ["MAGICCam", "VERITAS", "AdvCamSiPM"]:
378371
grid_second.insert(
379372
0,
380373
np.around(
@@ -664,6 +657,90 @@ def _get_grids(
664657
return input_grid, output_grid
665658

666659

660+
class HexagonalPatchMapper(ImageMapper):
661+
"""
662+
HexagonalPatchMapper retrieves the necessary information to perform indexed
663+
convolutions, also allows croping the images following the "sipm_patches.h5"
664+
patches geometry and reorders the pixels.
665+
666+
This class extends the functionality of ImageMapper by implementing
667+
methods to look up at the configuration file and perform the image cropping.
668+
It is particularly useful for applications where we are working with waveforms
669+
with high time dimension.
670+
"""
671+
672+
def __init__(
673+
self,
674+
geometry,
675+
config=None,
676+
parent=None,
677+
**kwargs,
678+
):
679+
super().__init__(
680+
geometry=geometry,
681+
config=config,
682+
parent=parent,
683+
**kwargs,
684+
)
685+
686+
if geometry.pix_type != PixelShape.HEXAGON:
687+
raise ValueError(
688+
f"HexagonalPatchMapper is only available for hexagonal pixel cameras. Pixel type of the selected camera is '{geometry.pix_type}'."
689+
)
690+
691+
if geometry.name == "AdvCamSiPM":
692+
path = files("dl1_data_handler.ressources").joinpath("triggergeometry_AdvCam_v1.h5")
693+
with tables.open_file(path, mode="r") as f:
694+
self.trigger_patches = f.root.patches.masks[:]
695+
self.index_map = f.root.mappings.index_map[:]
696+
self.neighbor_array = f.root.neighbors.patch0_neighbors[:]
697+
self.cam_neighbor_array = f.root.neighbors.camera_neighbors[:]
698+
self.fl_neighbor_array_tdscan = f.root.neighbors.flower_neighbors_tdscan[:]
699+
self.fl_neighbor_array_l1 = f.root.neighbors.flower_neighbors_l1[:]
700+
self.feb_indices = f.root.modules.indices[:]
701+
self.feb_neighbors = f.root.neighbors.feb_neighbors[:]
702+
self.supfl_neighbor_array_l1 = f.root.neighbors.superflower_neighbors_l1[:]
703+
self.sectors_bool = f.root.sectors.mask[:]
704+
self.sectors_indices = f.root.sectors.sectors_indices[:]
705+
self.sect0_neighbors = f.root.sectors.sect0_neighbors[:]
706+
self.sector_mappings = f.root.sectors.mapping[:]
707+
# Remove -1 padding from each row
708+
self.neighbor_tdscan_eps1_list = [row[row != -1].tolist() for row in self.fl_neighbor_array_tdscan]
709+
self.fl_neighbor_l1_list = [row[row != -1].tolist() for row in self.fl_neighbor_array_l1]
710+
self.supfl_neighbor_l1_list = [row[row != -1].tolist() for row in self.supfl_neighbor_array_l1]
711+
712+
713+
self.num_patches = len(self.trigger_patches)
714+
self.patch_size = self.neighbor_array.shape[0]
715+
self.sector_size = self.sect0_neighbors.shape[0]
716+
717+
self.supfl_neighbor_l1_mask = self.supfl_neighbor_array_l1 >= 0
718+
# Retrieve the camera neighbor array to perform convolutions with cameras different from AdvCamSiPM.
719+
else:
720+
self.log.debug(f"Computing neighbor array for {geometry.name} ...")
721+
neighbor_matrix = geometry.neighbor_matrix
722+
num_pixels = neighbor_matrix.shape[0]
723+
neighbor_lists = []
724+
for i in range(num_pixels):
725+
# Find indices where the row is True
726+
neighbors = np.where(neighbor_matrix[i])[0]
727+
neighbor_lists.append([i] + neighbors.tolist())
728+
729+
self.cam_neighbor_array = np.full((num_pixels, 7), -1, dtype=int)
730+
for i, neighbors in enumerate(neighbor_lists):
731+
self.cam_neighbor_array[i, :len(neighbors)] = neighbors
732+
733+
def get_reordered_patch(self, raw_vector, patch_index, out_size):
734+
# Retrieve the patch needed remapped to a standarized patch order.
735+
if out_size == "patch":
736+
mapper = self.index_map
737+
else: #sector
738+
mapper = self.sector_mappings
739+
mapper = mapper[patch_index]
740+
unmapped_waveform=raw_vector[mapper]
741+
return unmapped_waveform
742+
743+
667744
class ShiftingMapper(ImageMapper):
668745
"""
669746
ShiftingMapper applies a shifting transformation to map images
@@ -1182,16 +1259,6 @@ class RebinMapper(ImageMapper):
11821259
),
11831260
).tag(config=True)
11841261

1185-
max_memory_gb = Float(
1186-
default_value=10,
1187-
allow_none=True,
1188-
help=(
1189-
"Maximum memory in GB that RebinMapper is allowed to allocate. "
1190-
"Set to None to disable memory checks. Default is 10 GB. "
1191-
"Note: RebinMapper uses approximately (image_shape * 10)^2 * image_shape^2 * 4 bytes."
1192-
),
1193-
).tag(config=True)
1194-
11951262
def __init__(
11961263
self,
11971264
geometry,
@@ -1231,26 +1298,6 @@ def __init__(
12311298
self.image_shape = self.interpolation_image_shape
12321299
self.internal_shape = self.image_shape + self.internal_pad * 2
12331300
self.rebinning_mult_factor = 10
1234-
1235-
# Validate memory requirements before proceeding (if max_memory_gb is set)
1236-
if self.max_memory_gb is not None:
1237-
# RebinMapper uses a fine grid (internal_shape * rebinning_mult_factor)^2
1238-
# and creates a mapping matrix of shape (fine_grid_size, internal_shape, internal_shape)
1239-
fine_grid_size = (self.internal_shape * self.rebinning_mult_factor) ** 2
1240-
estimated_memory_gb = (
1241-
fine_grid_size * self.internal_shape * self.internal_shape * 4
1242-
) / (1024**3) # 4 bytes per float32
1243-
1244-
if estimated_memory_gb > self.max_memory_gb:
1245-
raise ValueError(
1246-
f"RebinMapper with image_shape={self.image_shape} would require "
1247-
f"approximately {estimated_memory_gb:.1f} GB of memory, which exceeds "
1248-
f"the limit of {self.max_memory_gb:.1f} GB. "
1249-
f"To allow this allocation, set max_memory_gb to a higher value or None. "
1250-
f"Alternatively, consider using a smaller interpolation_image_shape (recommended < 60) "
1251-
f"or use BilinearMapper or BicubicMapper instead, which are more memory-efficient."
1252-
)
1253-
12541301
# Creating the hexagonal and the output grid for the conversion methods.
12551302
input_grid, output_grid = super()._get_grids_for_interpolation()
12561303
# Calculate the mapping table

0 commit comments

Comments
 (0)