Skip to content

Commit 98c47a4

Browse files
ArmelleJBjlenain
andauthored
Flat field (#183)
* updated example with several charge extraction method * correct the dimension of bad_pixel array * update the name of the component * change the name of the component * add the possibility of using LocalPeakWindowSum and GlobalPeakWindowSum as charge estimators * update the flat field component name * Fix linter * add an example script to read and plot the output of the flat field tool --------- Co-authored-by: jlenain <[email protected]>
1 parent e39de9a commit 98c47a4

File tree

6 files changed

+191
-58
lines changed

6 files changed

+191
-58
lines changed

src/nectarchain/data/container/flatfield_container.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,9 @@ class FlatFieldContainer(NectarCAMContainer):
7575
description="the flat field coefficients, per event",
7676
)
7777

78-
# masked_wfs = Field(
79-
# type=np.ndarray,
80-
# dtype=np.uint64,
81-
# ndim=4,
82-
# description="Masked array for amplitude integration",
83-
# )
84-
8578
bad_pixels = Field(
8679
type=np.ndarray,
8780
dtype=np.uint16,
88-
ndim=2,
81+
ndim=1,
8982
description="pixels considered as bad in at least one gain channels",
9083
)

src/nectarchain/makers/calibration/flatfield_makers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class FlatfieldNectarCAMCalibrationTool(EventsLoopNectarCAMCalibrationTool):
1919

2020
componentsList = ComponentNameList(
2121
NectarCAMComponent,
22-
default_value=["PreFlatFieldComponent"],
22+
default_value=["FlatFieldComponent"],
2323
help="List of Component names to be apply, the order will be respected",
2424
).tag(config=True)
2525

src/nectarchain/makers/component/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from .charges_component import ChargesComponent
22
from .core import ArrayDataComponent, NectarCAMComponent, get_valid_component
3+
from .flatfield_component import FlatFieldComponent
34
from .flatfield_spe_component import (
45
FlatFieldCombinedSPEStdNectarCAMComponent,
56
FlatFieldSingleHHVSPENectarCAMComponent,
@@ -11,7 +12,6 @@
1112
from .pedestal_component import PedestalEstimationComponent
1213
from .photostatistic_algorithm import PhotoStatisticAlgorithm
1314
from .photostatistic_component import PhotoStatisticNectarCAMComponent
14-
from .preflatfield_component import PreFlatFieldComponent
1515
from .spe import (
1616
SPECombinedalgorithm,
1717
SPEHHValgorithm,
@@ -41,5 +41,5 @@
4141
"PhotoStatisticNectarCAMComponent",
4242
"PhotoStatisticAlgorithm",
4343
"GainNectarCAMComponent",
44-
"PreFlatFieldComponent",
44+
"FlatFieldComponent",
4545
]

src/nectarchain/makers/component/preflatfield_component.py renamed to src/nectarchain/makers/component/flatfield_component.py

Lines changed: 96 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import numpy as np
44
from 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
68
from ctapipe_io_nectarcam import constants
79
from ctapipe_io_nectarcam.containers import NectarCAMDataContainer
10+
from traitlets.config.loader import Config
811

912
from nectarchain.data.container import FlatFieldContainer
1013
from nectarchain.makers.component import NectarCAMComponent
@@ -13,10 +16,10 @@
1316
log = logging.getLogger(__name__)
1417
log.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

Comments
 (0)