33import warnings
44from contextlib import nullcontext
55from enum import Enum , IntFlag , auto , unique
6+ from functools import lru_cache
67from gzip import GzipFile
78from io import BufferedReader
89
@@ -119,6 +120,11 @@ class SimTelTriggerMask(IntFlag):
119120 RANDOM_MONO = auto ()
120121
121122
123+ @lru_cache ()
124+ def _get_pixel_index (n_pixels ):
125+ return np .arange (n_pixels )
126+
127+
122128def _trigger_mask_to_event_type (trigger_mask ):
123129 trigger_mask = SimTelTriggerMask (int (trigger_mask ))
124130
@@ -333,11 +339,10 @@ def _telescope_from_meta(telescope_meta, mirror_area):
333339
334340
335341def apply_simtel_r1_calibration (
336- r0_waveforms , pedestal , factor , gain_selector , calib_scale = 1.0 , calib_shift = 0.0
342+ r0_waveforms , pedestal , factor , calib_scale = 1.0 , calib_shift = 0.0
337343):
338344 """
339345 Perform the R1 calibration for R0 simtel waveforms. This includes:
340- - Gain selection
341346 - Pedestal subtraction
342347 - Conversion of samples into units proportional to photoelectrons
343348 (If the full signal in the waveform was integrated, then the resulting
@@ -356,7 +361,6 @@ def apply_simtel_r1_calibration(
356361 Conversion factor between R0 waveform samples and ~p.e., stored in the
357362 simtel file for each gain channel
358363 Shape: (n_channels, n_pixels)
359- gain_selector : ctapipe.calib.camera.gainselection.GainSelector
360364 calib_scale : float
361365 Extra global scale factor for calibration.
362366 Conversion factor to transform the integrated charges
@@ -369,27 +373,47 @@ def apply_simtel_r1_calibration(
369373 Returns
370374 -------
371375 r1_waveforms : ndarray
372- Calibrated waveforms
376+ Calibrated waveforms not gain-selected, in units of photoelectrons (p.e.).
373377 Shape: (n_channels, n_pixels, n_samples)
374- selected_gain_channel : ndarray
375- The gain channel selected for each pixel
376- Shape: (n_pixels)
377378 """
378- n_pixels = r0_waveforms .shape [- 2 ]
379379 ped = pedestal [..., np .newaxis ]
380380 factor = factor [..., np .newaxis ]
381381 gain = factor * calib_scale
382382
383383 r1_waveforms = (r0_waveforms - ped ) * gain + calib_shift
384384
385- if gain_selector is not None :
386- selected_gain_channel = gain_selector (r0_waveforms )
387- r1_waveforms = r1_waveforms [
388- np .newaxis , selected_gain_channel , np .arange (n_pixels )
389- ]
390- else :
391- selected_gain_channel = None
385+ return r1_waveforms
392386
387+
388+ def apply_gain_selection (r0_waveforms , r1_waveforms , gain_selector ):
389+ """
390+ Apply gain selection to the R1 waveform.
391+
392+ Parameters
393+ ----------
394+ r0_waveforms : ndarray
395+ Raw ADC waveforms from a simtel file. All gain channels available.
396+ Shape: (n_channels, n_pixels, n_samples)
397+ r1_waveforms : ndarray
398+ Calibrated waveforms not gain-selected, in units of photoelectrons (p.e.).
399+ Shape: (n_channels, n_pixels, n_samples)
400+ gain_selector : ctapipe.calib.camera.gainselection.GainSelector
401+ The GainSelector to use for selecting the gain channel.
402+
403+ Returns
404+ -------
405+ r1_waveforms : ndarray
406+ Calibrated waveforms after gain selection, in units of photoelectrons (p.e.).
407+ Shape: (1, n_pixels, n_samples)
408+ selected_gain_channel : ndarray
409+ The gain channel selected for each pixel. Shape: (n_pixels,)
410+ """
411+ selected_gain_channel = gain_selector (r0_waveforms )
412+ r1_waveforms = r1_waveforms [
413+ np .newaxis ,
414+ selected_gain_channel ,
415+ _get_pixel_index (r0_waveforms .shape [- 2 ]),
416+ ]
393417 return r1_waveforms , selected_gain_channel
394418
395419
@@ -1060,29 +1084,30 @@ def _generate_events(self):
10601084 factor = array_event ["laser_calibrations" ][tel_id ]["calib" ]
10611085 disabled_pixel_mask = self ._get_disabled_pixel_mask (tel_id )
10621086
1063- select_gain = self .select_gain is True or (
1064- self .select_gain is None
1065- and trigger .event_type is EventType .SUBARRAY
1066- )
1067- if select_gain :
1068- gain_selector = self .gain_selector
1069- else :
1070- gain_selector = None
1071-
10721087 if self .skip_r1_calibration :
10731088 # Skip the simtel R1 calibration
10741089 r1_waveform = adc_samples .astype (np .float32 )
1075- selected_gain_channel = None
10761090 else :
1077- # Apply the simtel R1 calibration and the gain selector if selected
1078- r1_waveform , selected_gain_channel = apply_simtel_r1_calibration (
1091+ # Apply the simtel R1 calibration to get waveforms in units of photoelectrons (p.e.)
1092+ r1_waveform = apply_simtel_r1_calibration (
10791093 adc_samples ,
10801094 pedestal ,
10811095 factor ,
1082- gain_selector ,
10831096 self .calib_scale ,
10841097 self .calib_shift ,
10851098 )
1099+ # Perform the gain selection if requested.
1100+ # By default, the cosmic events will be gain-selected, not for calibration events.
1101+ select_gain = self .select_gain is True or (
1102+ self .select_gain is None
1103+ and trigger .event_type is EventType .SUBARRAY
1104+ )
1105+ if select_gain :
1106+ r1_waveform , selected_gain_channel = apply_gain_selection (
1107+ adc_samples , r1_waveform , self .gain_selector
1108+ )
1109+ else :
1110+ selected_gain_channel = None
10861111
10871112 pixel_status = self ._get_r1_pixel_status (
10881113 tel_id = tel_id ,
0 commit comments