Skip to content

Commit 1e95f07

Browse files
guillaumegrolleronGuillaume Grolleron
andauthored
Improvement of the gain calibration + Bugfix of charge extraction (#214)
* change defaut value of n and pp from HHV the free fit * user script to produce ICRC2025 results * Bug fix FF SPE combined * add gain err to photostat method -> propagation of SPE resolution error * follows photostat error * logging improbemeltb * fix name bug in charge makers * -change pp and n to be taken from config file instead of SPE HHV result in SPE combined algo -better management of SPE res used for PS method * bugfix : wrong position of the yeild within a loop -> generator returned was pointing the last slice * typo * bugfix : when extracting charge and peak time with ctapipe extractor, even if selected_gain_channel is none, the expected sample axis is wafevorms.shape[-1]. Then if we provide (n_events,n_pix,n_sample) the output is (n_events, n_pix) * imrove unit test to detect the issue related to charge extraction * imporove debugging of scripts * formatting * fix pedestal makers and component according to last update of the containers I/O methods + unit test fix * linter * fix unit test of charge and waveforms makers improve behavior if event_per_slice > n_events iin data * fix gain pstat strict arguments --------- Co-authored-by: Guillaume Grolleron <[email protected]>
1 parent 412b951 commit 1e95f07

29 files changed

+517
-351
lines changed

src/nectarchain/data/container/core.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,16 +269,16 @@ def _container_from_hdf5(
269269
if isinstance(path, str):
270270
path = Path(path)
271271
module = importlib.import_module(f"{container_class.__module__}") # noqa :F841
272-
container = eval(f"module.{container_class.__name__}")()
273272

274273
with HDF5TableReader(path) as reader:
275274
if len(reader._h5file.root.__members__) > 1 and slice_index is None:
276275
log.info(
277276
f"reading {container_class.__name__} containing"
278-
f"{len(reader._h5file.root.__members__)}"
279-
f"slices, will return a generator"
277+
f" {len(reader._h5file.root.__members__)}"
278+
f" slices, will return a generator"
280279
)
281280
for data in np.sort(reader._h5file.root.__members__):
281+
container = eval(f"module.{container_class.__name__}")()
282282
# container.containers[data] =
283283
# eval(f"module.{container_class.__name__}s")()
284284

@@ -319,8 +319,9 @@ def _container_from_hdf5(
319319
f"table save, unable to load"
320320
)
321321

322-
yield container
322+
yield container
323323
else:
324+
container = eval(f"module.{container_class.__name__}")()
324325
if slice_index is None:
325326
log.info(
326327
f"reading {container_class.__name__} containing"

src/nectarchain/makers/calibration/gain/flatfield_spe_makers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def _init_output_path(self):
221221
if "run" in word:
222222
HHVrun = int(word.split("run")[-1])
223223
str_extractor_kwargs = CtapipeExtractor.get_extractor_kwargs_str(
224-
self.extractor_kwargs
224+
method=self.method, extractor_kwargs=self.extractor_kwargs
225225
)
226226
if self.max_events is None:
227227
filename = (

src/nectarchain/makers/calibration/gain/photostat_makers.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,18 @@ def start(
122122
if len(FF_files) != 1 or len(Ped_files) != 1:
123123
self.log.info(
124124
f"{len(FF_files)} computed charges FF files found"
125-
f"with max_events >"
125+
f" with max_events >"
126126
f"{self.max_events} for run {self.run_number}"
127-
f"with extraction method"
127+
f" with extraction method"
128128
f"{self.method} and {str_extractor_kwargs},\n reload charges"
129-
f"from event loop"
129+
f" from event loop"
130130
)
131131
self.log.info(
132132
f"{len(Ped_files)} computed charges FF files found"
133-
f"with max_events >"
133+
f" with max_events >"
134134
f"{self.max_events} for run {self.Ped_run_number}"
135-
f"with extraction"
136-
f"method FullWaveformSum,\n reload charges from event loop"
135+
f" with extraction"
136+
f" method FullWaveformSum,\n reload charges from event loop"
137137
)
138138

139139
super().start(
@@ -162,7 +162,7 @@ def start(
162162
self.components[
163163
0
164164
]._FF_chargesContainers = merge_map_ArrayDataContainer(
165-
chargesContainers
165+
next(chargesContainers)
166166
)
167167
else:
168168
self.log.info("merging along slices")
@@ -196,7 +196,7 @@ def start(
196196
self.components[
197197
0
198198
]._Ped_chargesContainers = merge_map_ArrayDataContainer(
199-
chargesContainers
199+
next(chargesContainers)
200200
)
201201
else:
202202
self.log.info("merging along slices")

src/nectarchain/makers/calibration/pedestal_makers.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pathlib
44

55
import numpy as np
6+
import tables
67
from ctapipe.core.traits import ComponentNameList
78
from ctapipe_io_nectarcam.constants import HIGH_GAIN, LOW_GAIN, N_GAINS
89

@@ -55,18 +56,28 @@ def _combine_results(self):
5556
Method that combines sliced results to reduce memory load
5657
Can only be called after the file with the sliced results has been saved to disk
5758
"""
59+
already_combined = False
60+
with tables.open_file(self.output_path, mode="r") as f:
61+
keys = list(f.root._v_children)
62+
if "data_combined" in keys:
63+
log.error(
64+
"Trying to combine results that already contain combined data"
65+
)
66+
already_combined = True
5867

5968
# re-open results
60-
pedestalContainers = next(
61-
NectarCAMPedestalContainers.from_hdf5(self.output_path)
62-
)
69+
if already_combined:
70+
pedestalContainers = NectarCAMPedestalContainers.from_hdf5(
71+
self.output_path,
72+
slice_index="combined",
73+
)
74+
else:
75+
pedestalContainers = NectarCAMPedestalContainers.from_hdf5(self.output_path)
76+
6377
# Loop over sliced results to fill the combined results
64-
if "data_combined" in pedestalContainers.containers.keys():
65-
log.error("Trying to combine results that already contain combined data")
6678
self.log.info("Combine sliced results")
67-
for i, (_, pedestalContainer) in enumerate(
68-
pedestalContainers.containers.items()
69-
):
79+
for i, _pedestalContainer in enumerate(pedestalContainers):
80+
pedestalContainer = list(_pedestalContainer.containers.values())[0]
7081
if i == 0:
7182
# initialize fields for the combined results based on first slice
7283
nsamples = pedestalContainer.nsamples

src/nectarchain/makers/calibration/tests/test_pedestal_tool.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import os
12
import tempfile
23

34
import numpy as np
5+
import tables
46
from ctapipe.utils import get_dataset_path
57
from ctapipe_io_nectarcam.constants import N_SAMPLES
68

@@ -36,6 +38,12 @@ def test_base(self):
3638
n_pixels = runs["N pixels"][i]
3739
with tempfile.TemporaryDirectory() as tmpdirname:
3840
outfile = tmpdirname + "/pedestal.h5"
41+
try:
42+
os.remove(outfile)
43+
except FileNotFoundError:
44+
pass
45+
except Exception as e:
46+
raise e
3947

4048
# run tool
4149
tool = PedestalNectarCAMCalibrationTool(
@@ -76,18 +84,16 @@ def test_base(self):
7684
assert np.allclose(output.pedestal_std_lg, 2.5, atol=2.3)
7785

7886
# Check output on disk
79-
# FIXME: use tables for the moment, update when h5 reader in nectarchain
80-
# is working
81-
pedestalContainers = next(
82-
NectarCAMPedestalContainers.from_hdf5(outfile)
83-
)
87+
pedestalContainers = NectarCAMPedestalContainers.from_hdf5(outfile)
8488
j = 0
85-
for key, pedestalContainer in pedestalContainers.containers.items():
89+
list_slices = []
90+
for _pedestalContainer in pedestalContainers:
91+
key = list(_pedestalContainer.containers.keys())[0]
8692
if "combined" in key:
8793
continue
88-
# Check individual groups
89-
group_name = "data_{}".format(i + 1)
90-
assert group_name in pedestalContainers.containers.keys()
94+
pedestalContainer = _pedestalContainer.containers[key]
95+
list_slices.append(int(key.split("data_")[1]))
96+
9197
assert pedestalContainer.nsamples == N_SAMPLES
9298
assert np.allclose(
9399
pedestalContainer.nevents, events_per_slice, atol=7
@@ -110,11 +116,19 @@ def test_base(self):
110116
N_SAMPLES,
111117
)
112118
j += 1
119+
assert (np.sort(list_slices) == np.arange(1, n_slices[i] + 1)).all()
120+
113121
# Check combined results
122+
group_name = "data_combined"
123+
with tables.open_file(outfile, mode="r") as f:
124+
keys = list(f.root._v_children)
125+
assert group_name in keys
114126
pedestalContainers = next(
115-
NectarCAMPedestalContainers.from_hdf5(outfile)
127+
NectarCAMPedestalContainers.from_hdf5(
128+
outfile, slice_index="combined"
129+
)
116130
)
117-
group_name = "data_combined"
131+
118132
pedestalContainer = pedestalContainers.containers[group_name]
119133
assert pedestalContainer.nsamples == N_SAMPLES
120134
assert np.all(pedestalContainer.nevents == max_events[i])

src/nectarchain/makers/charges_makers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _init_output_path(self):
5656
if self.max_events is None:
5757
filename = (
5858
f"{self.name}_run{self.run_number}_{self.method}"
59-
f"{str_extractor_kwargs}.h5"
59+
f"_{str_extractor_kwargs}.h5"
6060
)
6161
else:
6262
filename = (
@@ -87,7 +87,7 @@ def start(
8787
files = []
8888
if len(files) != 1:
8989
self.log.info(
90-
f"{len(files)} computed wavforms files found with max_events >="
90+
f"{len(files)} computed waveforms files found with max_events >="
9191
f"{self.max_events} for run {self.run_number}, reload waveforms"
9292
f"from event loop"
9393
)
@@ -99,7 +99,7 @@ def start(
9999
)
100100
else:
101101
self.log.info(
102-
f"{files[0]} is the computed wavforms files found"
102+
f"{files[0]} is the computed waveforms files found"
103103
f"with max_events >="
104104
f"{self.max_events} for run {self.run_number}"
105105
)

src/nectarchain/makers/component/charges_component.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -588,23 +588,21 @@ def compute_charges(
588588
method=method, subarray=subarray, **kwargs
589589
)
590590
out = np.array(
591-
[
592-
CtapipeExtractor.get_image_peak_time(
593-
imageExtractor(
594-
waveforms=waveforms,
595-
tel_id=tel_id,
596-
selected_gain_channel=None,
597-
broken_pixels=broken_pixels,
598-
)
591+
CtapipeExtractor.get_image_peak_time(
592+
imageExtractor(
593+
waveforms=waveforms,
594+
tel_id=tel_id,
595+
selected_gain_channel=None,
596+
broken_pixels=broken_pixels,
599597
)
600-
]
598+
)
601599
)
602-
600+
# bc waveforms shape is (n_events, n_pix, n_samples),
601+
# out is (2, n_events, n_pix)
602+
# with the first dimension for charge and the second for peak time
603603
return ChargesContainer.fields[f"charges_{gain_label}"].dtype.type(
604-
out[:, 0, channel, :]
605-
), ChargesContainer.fields[f"peak_{gain_label}"].dtype.type(
606-
out[:, 1, channel, :]
607-
)
604+
out[0, ...]
605+
), ChargesContainer.fields[f"peak_{gain_label}"].dtype.type(out[1, ...])
608606

609607
@staticmethod
610608
def histo_hg(

src/nectarchain/makers/component/photostatistic_algorithm.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ def __get_charges_FF_Ped_reshaped(
9494
],
9595
dtype=bool,
9696
)
97-
out["SPE_resolution"] = SPE_result.resolution[mask_SPE].T[0]
98-
out["SPE_high_gain"] = SPE_result.high_gain[mask_SPE].T[0]
97+
out["SPE_resolution"] = SPE_result.resolution[mask_SPE].T
98+
out["SPE_high_gain"] = SPE_result.high_gain[mask_SPE].T
9999

100100
out["pixels_id"] = SPEFFPed_intersection
101101
out["FFcharge_hg"] = ChargesComponent.select_charges_hg(
@@ -120,7 +120,7 @@ def __check_shape(self) -> None:
120120
try:
121121
self.__FFcharge_hg[0] * self.__FFcharge_lg[0] * self.__Pedcharge_hg[
122122
0
123-
] * self.__Pedcharge_lg[0] * self.__SPE_resolution * self._pixels_id
123+
] * self.__Pedcharge_lg[0] * self.__SPE_resolution[0] * self._pixels_id
124124
except Exception as e:
125125
log.error(e, exc_info=True)
126126
raise e
@@ -132,19 +132,21 @@ def run(self, pixels_id: np.ndarray = None, **kwargs) -> None:
132132
mask = np.array(
133133
[pixel_id in pixels_id for pixel_id in self._pixels_id], dtype=bool
134134
)
135+
gainHG_err = self.gainHG_err * mask
136+
gainLG_err = self.gainLG_err * mask
135137
self._results.high_gain = np.array(
136-
(self.gainHG * mask, np.zeros(self.npixels), np.zeros(self.npixels))
138+
(self.gainHG * mask, gainHG_err, gainHG_err)
137139
).T
138140
self._results.low_gain = np.array(
139-
(self.gainLG * mask, np.zeros(self.npixels), np.zeros(self.npixels))
141+
(self.gainLG * mask, gainLG_err, gainLG_err)
140142
).T
141143
self._results.is_valid = mask
142144

143145
figpath = kwargs.get("figpath", False)
144146
if figpath:
145147
os.makedirs(figpath, exist_ok=True)
146148
fig = __class__.plot_correlation(
147-
self._results.high_gain.T[0], self.__SPE_high_gain
149+
self._results.high_gain.T[0], self.__SPE_high_gain[0]
148150
)
149151
fig.savefig(f"{figpath}/plot_correlation_Photo_Stat_SPE.pdf")
150152
fig.clf()
@@ -286,7 +288,39 @@ def gainHG(self) -> float:
286288
np.power(self.sigmaChargeHG, 2)
287289
- np.power(self.sigmaPedHG, 2)
288290
- np.power(self.BHG * self.meanChargeHG, 2)
289-
) / (self.meanChargeHG * (1 + np.power(self.SPE_resolution, 2)))
291+
) / (self.meanChargeHG * (1 + np.power(self.SPE_resolution[0], 2)))
292+
293+
@property
294+
def gainHG_err(self) -> float:
295+
"""Calculates and returns the gain for high gain charge data.
296+
297+
Returns:
298+
float: The gain for high gain charge data.
299+
"""
300+
return np.sqrt(
301+
np.power(
302+
self.gainHG
303+
* (-2 * self.SPE_resolution[0])
304+
* np.mean(self.SPE_resolution[1:], axis=0),
305+
2,
306+
)
307+
)
308+
309+
@property
310+
def gainLG_err(self) -> float:
311+
"""Calculates and returns the gain for high gain charge data.
312+
313+
Returns:
314+
float: The gain for high gain charge data.
315+
"""
316+
return np.sqrt(
317+
np.power(
318+
self.gainLG
319+
* (-2 * self.SPE_resolution[0])
320+
* np.mean(self.SPE_resolution[1:], axis=0),
321+
2,
322+
)
323+
)
290324

291325
@property
292326
def sigmaPedLG(self) -> float:
@@ -357,7 +391,7 @@ def gainLG(self) -> float:
357391
np.power(self.sigmaChargeLG, 2)
358392
- np.power(self.sigmaPedLG, 2)
359393
- np.power(self.BLG * self.meanChargeLG, 2)
360-
) / (self.meanChargeLG * (1 + np.power(self.SPE_resolution, 2)))
394+
) / (self.meanChargeLG * (1 + np.power(self.SPE_resolution[0], 2)))
361395

362396
@property
363397
def results(self):

src/nectarchain/makers/component/spe/parameters_SPECombined_fromHHVFit.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
min : 0.01,
1010
max : 5.0
1111
},
12-
pp : {
13-
value : 0.454,
12+
pp : { #useless bc use HHVStd result
13+
value : 0.42,
1414
min : .NAN,
1515
max : .NAN
1616
},
17-
resolution : {
18-
value : 0.5,
17+
resolution : { #useless bc use HHVStd result
18+
value : 0.54,
1919
min : .NAN,
2020
max : .NAN
2121
},
22-
n : {
23-
value : 0.713,
22+
n : { #useless bc use HHVStd result
23+
value : 0.813,
2424
min : .NAN,
2525
max : .NAN
2626
},

src/nectarchain/makers/component/spe/parameters_SPEHHVStd.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
max : 5.0
1111
},
1212
pp : {
13-
value : 0.454,
13+
value : 0.42,
1414
min : .NAN,
1515
max : .NAN
1616
},
@@ -20,7 +20,7 @@
2020
max : 0.7
2121
},
2222
n : {
23-
value : 0.713,
23+
value : 0.813,
2424
min : .NAN,
2525
max : .NAN
2626
},

0 commit comments

Comments
 (0)