22
33import numpy as np
44from ctapipe .containers import EventType
5- from ctapipe .core .traits import Integer , List
5+ from ctapipe .core .traits import Bool , Integer , List , Unicode
6+ from ctapipe .image .extractor import GlobalPeakWindowSum # noqa: F401
7+ from ctapipe .image .extractor import LocalPeakWindowSum # noqa: F401
68from ctapipe_io_nectarcam import constants
79from ctapipe_io_nectarcam .containers import NectarCAMDataContainer
10+ from traitlets .config .loader import Config
811
912from nectarchain .data .container import FlatFieldContainer
1013from nectarchain .makers .component import NectarCAMComponent
1316log = logging .getLogger (__name__ )
1417log .handlers = logging .getLogger ("__main__" ).handlers
1518
16- __all__ = ["PreFlatFieldComponent " ]
19+ __all__ = ["FlatFieldComponent " ]
1720
1821
19- class PreFlatFieldComponent (NectarCAMComponent ):
22+ class FlatFieldComponent (NectarCAMComponent ):
2023 """
2124 Component that computes flat field coefficients from raw data.
2225
@@ -29,11 +32,18 @@ class PreFlatFieldComponent(NectarCAMComponent):
2932 duration of the extraction window in ns (default value = 12)
3033
3134 gain: list
32- array of gain value
35+ array of gain value (default value = array of 58 and 58/13)
3336
3437 bad_pix: list
3538 list of bad pixels (default value = [])
3639
40+ charge_extraction_method: srt
41+ name of the charge extraction method ("LocalPeakWindowSum"
42+ or "GlobalPeakWindowSum" ; default value = None)
43+
44+ charge_integration_correction: bool
45+ application of a correction from the charge extractor (defaut value = False)
46+
3747 """
3848
3949 window_shift = Integer (
@@ -61,6 +71,17 @@ class PreFlatFieldComponent(NectarCAMComponent):
6171 help = "list of bad pixels" ,
6272 ).tag (config = True )
6373
74+ charge_extraction_method = Unicode (
75+ defaut_value = None ,
76+ help = "name of the charge extraction method" ,
77+ allow_none = True ,
78+ ).tag (config = True )
79+
80+ charge_integration_correction = Bool (
81+ default_value = False ,
82+ help = "correction applied by the charge extractor" ,
83+ ).tag (config = True )
84+
6485 def __init__ (self , subarray , config = None , parent = None , * args , ** kwargs ):
6586 super ().__init__ (
6687 subarray = subarray , config = config , parent = parent , * args , ** kwargs
@@ -73,13 +94,12 @@ def __init__(self, subarray, config=None, parent=None, *args, **kwargs):
7394 self .__FF_coef = []
7495 self .__bad_pixels = []
7596
76- print ("gain" )
77- print (type (self .gain ))
78- print (self .gain )
79-
80- print ("bad_pixels" )
81- print (type (self .bad_pix ))
82- print (self .bad_pix )
97+ log .info (f"Charge extraction method : { self .charge_extraction_method } " )
98+ log .info (
99+ f"Charge integration correciton : { self .charge_integration_correction } "
100+ )
101+ log .info (f"Gain : { self .gain } " )
102+ log .info (f"List of bad pixels : { self .bad_pix } " )
83103
84104 def __call__ (self , event : NectarCAMDataContainer , * args , ** kwargs ):
85105 if event .trigger .event_type .value == EventType .FLATFIELD .value :
@@ -93,31 +113,57 @@ def __call__(self, event: NectarCAMDataContainer, *args, **kwargs):
93113 # subtract pedestal using the mean of the 20 first samples
94114 wfs_pedsub = self .subtract_pedestal (wfs , 20 )
95115
96- # get the masked array for integration window
97- t_peak = np .argmax (wfs_pedsub , axis = - 1 )
98- masked_wfs = self .make_masked_array (
99- t_peak , self .window_shift , self .window_width
100- )
101-
102116 # mask bad pixels
103- self .__bad_pixels .append (self .bad_pix )
104- masked_wfs [:, self .bad_pix , :] = False
105-
106- # get integrated amplitude and mean amplitude over all pixels per event
107- amp_int_per_pix_per_event = np .sum (
108- wfs_pedsub [0 ], axis = - 1 , where = masked_wfs .astype ("bool" )
109- )
110- self .__amp_int_per_pix_per_event .append (amp_int_per_pix_per_event )
111- # --< We could use ctapipe.image.extractor.LocalPeakWindowSum >--
117+ self .__bad_pixels = np .array (self .bad_pix )
118+ bad_pixels_mask = self .make_badpix_mask (self .bad_pix )
119+
120+ if self .charge_extraction_method is None :
121+ # get the masked array for integration window
122+ t_peak = np .argmax (wfs_pedsub , axis = - 1 )
123+ masked_wfs = self .make_masked_array (
124+ t_peak , self .window_shift , self .window_width
125+ )
126+ masked_wfs [:, self .bad_pix , :] = False
127+ # get integrated amplitude and mean amplitude over all pixels per event
128+ amp_int_per_pix_per_event = np .sum (
129+ wfs_pedsub , axis = - 1 , where = masked_wfs
130+ )
131+ self .__amp_int_per_pix_per_event .append (amp_int_per_pix_per_event )
132+ amp_int_per_pix_per_event_pe = (
133+ amp_int_per_pix_per_event [:] / self .gain [:]
134+ )
135+
136+ else :
137+ config = Config (
138+ {
139+ self .charge_extraction_method : {
140+ "window_shift" : self .window_shift ,
141+ "window_width" : self .window_width ,
142+ }
143+ }
144+ )
145+ integrator = eval (self .charge_extraction_method )(
146+ self .subarray ,
147+ config = config ,
148+ apply_integration_correction = self .charge_integration_correction ,
149+ )
150+ amp_int_per_pix_per_event = integrator (
151+ wfs_pedsub , 0 , 0 , bad_pixels_mask
152+ )
153+ self .__amp_int_per_pix_per_event .append (amp_int_per_pix_per_event .image )
154+ amp_int_per_pix_per_event_pe = (
155+ amp_int_per_pix_per_event .image [:] / self .gain [:]
156+ )
112157
113- amp_int_per_pix_per_event_pe = amp_int_per_pix_per_event [:] / self .gain [:]
114158 mean_amp_cam_per_event_pe = np .mean (amp_int_per_pix_per_event_pe , axis = - 1 )
115159
160+ # efficiency coefficients
116161 eff = np .divide (
117162 amp_int_per_pix_per_event_pe ,
118163 np .expand_dims (mean_amp_cam_per_event_pe , axis = - 1 ),
119164 )
120165
166+ # flat-field coefficients
121167 FF_coef = np .ma .array (1.0 / eff , mask = eff == 0 )
122168 self .__FF_coef .append (FF_coef )
123169
@@ -169,6 +215,29 @@ def make_masked_array(t_peak, window_shift, window_width):
169215
170216 return masked_wfs
171217
218+ @staticmethod
219+ def make_badpix_mask (bad_pixel_list ):
220+ """
221+ Make a boulean mask with the list of bad pixels (used by GlobalPeakWindowSum)
222+
223+ Args:
224+ bad_pixel_list: list of bad pixels
225+
226+ Returns:
227+ badpix_mask: boulean mask
228+ """
229+
230+ badpix_mask = np .zeros (
231+ shape = (constants .N_GAINS , constants .N_PIXELS ), dtype = bool
232+ )
233+ pixels = np .arange (constants .N_PIXELS )
234+
235+ for i in pixels :
236+ if i in bad_pixel_list :
237+ badpix_mask [:, i ] = 1
238+
239+ return badpix_mask
240+
172241 def finish (self ):
173242 output = FlatFieldContainer (
174243 run_number = FlatFieldContainer .fields ["run_number" ].type (self ._run_number ),
0 commit comments