ctao-calibpipe 0.1.0rc8__py3-none-any.whl → 0.2.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ctao-calibpipe might be problematic. Click here for more details.
- calibpipe/_version.py +2 -2
- calibpipe/core/common_metadata_containers.py +3 -0
- calibpipe/database/adapter/adapter.py +1 -1
- calibpipe/database/adapter/database_containers/__init__.py +2 -0
- calibpipe/database/adapter/database_containers/common_metadata.py +2 -0
- calibpipe/database/adapter/database_containers/throughput.py +30 -0
- calibpipe/database/interfaces/table_handler.py +79 -97
- calibpipe/telescope/throughput/containers.py +59 -0
- calibpipe/tests/unittests/array/test_cross_calibration.py +417 -0
- calibpipe/tests/unittests/database/test_table_handler.py +95 -0
- calibpipe/tests/unittests/telescope/camera/test_calculate_camcalib_coefficients.py +347 -0
- calibpipe/tests/unittests/telescope/camera/test_produce_camcalib_test_data.py +42 -0
- calibpipe/tests/unittests/telescope/throughput/test_muon_throughput_calibrator.py +189 -0
- calibpipe/tools/camcalib_test_data.py +361 -0
- calibpipe/tools/camera_calibrator.py +558 -0
- calibpipe/tools/muon_throughput_calculator.py +239 -0
- calibpipe/tools/telescope_cross_calibration_calculator.py +721 -0
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/METADATA +3 -2
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/RECORD +24 -14
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/WHEEL +1 -1
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/entry_points.txt +4 -0
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/licenses/AUTHORS.md +0 -0
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/licenses/LICENSE +0 -0
- {ctao_calibpipe-0.1.0rc8.dist-info → ctao_calibpipe-0.2.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"""Utility tool to produce test data for the camera calibration tools in the calibpipe package."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import astropy.units as u
|
|
5
|
+
import yaml
|
|
6
|
+
from astropy.time import Time
|
|
7
|
+
from ctapipe.core import Tool, run_tool
|
|
8
|
+
from ctapipe.core.traits import (
|
|
9
|
+
Bool,
|
|
10
|
+
CaselessStrEnum,
|
|
11
|
+
CInt,
|
|
12
|
+
List,
|
|
13
|
+
Path,
|
|
14
|
+
Set,
|
|
15
|
+
Unicode,
|
|
16
|
+
)
|
|
17
|
+
from ctapipe.instrument import SubarrayDescription
|
|
18
|
+
from ctapipe.io import read_table, write_table
|
|
19
|
+
from ctapipe.tools.calculate_pixel_stats import PixelStatisticsCalculatorTool
|
|
20
|
+
from ctapipe.tools.process import ProcessorTool
|
|
21
|
+
from traitlets.config.loader import Config
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"CamCalibTestDataTool",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CamCalibTestDataTool(Tool):
|
|
29
|
+
"""Utility tool to produce test data for the camera calibration."""
|
|
30
|
+
|
|
31
|
+
name = "calibpipe-produce-camcalib-test-data"
|
|
32
|
+
|
|
33
|
+
description = "Produce test data for the camera calibration tools."
|
|
34
|
+
|
|
35
|
+
examples = """
|
|
36
|
+
To produce the test data for the camera calibration tools, you need to provide the input files
|
|
37
|
+
for the pedestal and flatfield events, as well as the configuration files (see examples
|
|
38
|
+
in the calibpipe documentation) for processing these events and aggregating the statistics.
|
|
39
|
+
The timestamp of the events will be set to a realistic value based on the
|
|
40
|
+
reference time and trigger rate defined in the tool. The output files will be created in the
|
|
41
|
+
specified output directory, with the prefix defined in the configuration.
|
|
42
|
+
|
|
43
|
+
Run with the following command to produce the test data:
|
|
44
|
+
|
|
45
|
+
> calibpipe-produce-camcalib-test-data \\
|
|
46
|
+
--pedestal pedestal_events.simtel.gz \\
|
|
47
|
+
--flatfield flatfield_events.simtel.gz \\
|
|
48
|
+
--output-dir ./output \\
|
|
49
|
+
--CamCalibTestDataTool.process_pedestal_config ctapipe_process_pedestal.yaml \\
|
|
50
|
+
--CamCalibTestDataTool.process_flatfield_config ctapipe_process_flatfield.yaml \\
|
|
51
|
+
--CamCalibTestDataTool.agg_stats_pedestal_image_config ctapipe_calculate_pixel_stats_pedestal_image.yaml \\
|
|
52
|
+
--CamCalibTestDataTool.agg_stats_flatfield_image_config ctapipe_calculate_pixel_stats_flatfield_image.yaml \\
|
|
53
|
+
--CamCalibTestDataTool.agg_stats_flatfield_peak_time_config ctapipe_calculate_pixel_stats_flatfield_peak_time.yaml \\
|
|
54
|
+
--CamCalibTestDataTool.prefix calibpipe_vX.Y.Z_statsagg \\
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
pedestal_input_url = Path(
|
|
58
|
+
help="Simtel input file for pedestal events",
|
|
59
|
+
allow_none=False,
|
|
60
|
+
exists=True,
|
|
61
|
+
directory_ok=False,
|
|
62
|
+
file_ok=True,
|
|
63
|
+
).tag(config=True)
|
|
64
|
+
|
|
65
|
+
flatfield_input_url = Path(
|
|
66
|
+
help="Simtel input file for flatfield events",
|
|
67
|
+
allow_none=False,
|
|
68
|
+
exists=True,
|
|
69
|
+
directory_ok=False,
|
|
70
|
+
file_ok=True,
|
|
71
|
+
).tag(config=True)
|
|
72
|
+
|
|
73
|
+
process_pedestal_config = Path(
|
|
74
|
+
help="Path to the configuration file for processing pedestal events",
|
|
75
|
+
allow_none=False,
|
|
76
|
+
exists=True,
|
|
77
|
+
directory_ok=False,
|
|
78
|
+
file_ok=True,
|
|
79
|
+
).tag(config=True)
|
|
80
|
+
|
|
81
|
+
process_flatfield_config = Path(
|
|
82
|
+
help="Path to the configuration file for processing flatfield events",
|
|
83
|
+
allow_none=False,
|
|
84
|
+
exists=True,
|
|
85
|
+
directory_ok=False,
|
|
86
|
+
file_ok=True,
|
|
87
|
+
).tag(config=True)
|
|
88
|
+
|
|
89
|
+
agg_stats_pedestal_image_config = Path(
|
|
90
|
+
help="Path to the configuration file for aggregating pedestal image statistics",
|
|
91
|
+
allow_none=False,
|
|
92
|
+
exists=True,
|
|
93
|
+
directory_ok=False,
|
|
94
|
+
file_ok=True,
|
|
95
|
+
).tag(config=True)
|
|
96
|
+
|
|
97
|
+
agg_stats_flatfield_image_config = Path(
|
|
98
|
+
help="Path to the configuration file for aggregating flatfield image statistics",
|
|
99
|
+
allow_none=False,
|
|
100
|
+
exists=True,
|
|
101
|
+
directory_ok=False,
|
|
102
|
+
file_ok=True,
|
|
103
|
+
).tag(config=True)
|
|
104
|
+
|
|
105
|
+
agg_stats_flatfield_peak_time_config = Path(
|
|
106
|
+
help="Path to the configuration file for aggregating flatfield peak time statistics",
|
|
107
|
+
allow_none=False,
|
|
108
|
+
exists=True,
|
|
109
|
+
directory_ok=False,
|
|
110
|
+
file_ok=True,
|
|
111
|
+
).tag(config=True)
|
|
112
|
+
|
|
113
|
+
allowed_tels = Set(
|
|
114
|
+
trait=CInt(),
|
|
115
|
+
default_value=None,
|
|
116
|
+
allow_none=True,
|
|
117
|
+
help=(
|
|
118
|
+
"List of allowed telescope IDs, others will be ignored. If None, all "
|
|
119
|
+
"telescopes in the input stream will be included. Requires the "
|
|
120
|
+
"telescope IDs to match between the groups of the monitoring file."
|
|
121
|
+
),
|
|
122
|
+
).tag(config=True)
|
|
123
|
+
|
|
124
|
+
prefix = Unicode(
|
|
125
|
+
default_value="statsagg",
|
|
126
|
+
allow_none=False,
|
|
127
|
+
help="Prefix to be used for the output files of the statistics aggregation",
|
|
128
|
+
).tag(config=True)
|
|
129
|
+
|
|
130
|
+
aggregation_modes = List(
|
|
131
|
+
trait=CaselessStrEnum(["single_chunk", "same_chunks", "different_chunks"]),
|
|
132
|
+
default_value=["single_chunk", "same_chunks", "different_chunks"],
|
|
133
|
+
allow_none=False,
|
|
134
|
+
help=(
|
|
135
|
+
"List of aggregation modes for the pixel statistics. "
|
|
136
|
+
"Options are: 'single_chunk', 'same_chunks', 'different_chunks'. "
|
|
137
|
+
"If 'single_chunk' is selected, all monitoring groups are aggregated in a single chunk. "
|
|
138
|
+
"If 'same_chunks' is selected, all monitoring groups are aggregated in the same chunks. "
|
|
139
|
+
"If 'different_chunks' is selected, each monitoring groups are aggregated in different chunks."
|
|
140
|
+
),
|
|
141
|
+
).tag(config=True)
|
|
142
|
+
|
|
143
|
+
skip_r1_calibration = Bool(
|
|
144
|
+
default_value=True,
|
|
145
|
+
help=(
|
|
146
|
+
"If True (default), skip the R1 calibration step in the simtel event source. "
|
|
147
|
+
"This is useful for testing and validation purposes of the camera calibration routines. "
|
|
148
|
+
),
|
|
149
|
+
).tag(config=True)
|
|
150
|
+
|
|
151
|
+
output_dir = Path(
|
|
152
|
+
help="Directory to store the output files",
|
|
153
|
+
allow_none=False,
|
|
154
|
+
exists=True,
|
|
155
|
+
directory_ok=True,
|
|
156
|
+
file_ok=False,
|
|
157
|
+
).tag(config=True)
|
|
158
|
+
|
|
159
|
+
aliases = {
|
|
160
|
+
("p", "pedestal"): "CamCalibTestDataTool.pedestal_input_url",
|
|
161
|
+
("f", "flatfield"): "CamCalibTestDataTool.flatfield_input_url",
|
|
162
|
+
("o", "output-dir"): "CamCalibTestDataTool.output_dir",
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# Define the group in the DL1 file for the pedestal and flatfield images
|
|
166
|
+
IMAGE_TEL_GROUP = "/dl1/event/telescope/images/"
|
|
167
|
+
# Define the group in the monitoring file
|
|
168
|
+
MONITORING_TEL_GROUP = "/dl1/monitoring/telescope/"
|
|
169
|
+
# Define reference time and trigger rate for the tests. These values
|
|
170
|
+
# are used to create realistic timestamps for the aggregated chunks.
|
|
171
|
+
REFERENCE_TIME = Time.now()
|
|
172
|
+
REFERENCE_TRIGGER_RATE = 1000.0 * u.Hz
|
|
173
|
+
|
|
174
|
+
def setup(self):
|
|
175
|
+
"""Set up the tool."""
|
|
176
|
+
# Load the subarray description from the input files
|
|
177
|
+
subarray_pedestal = SubarrayDescription.read(self.pedestal_input_url)
|
|
178
|
+
subarray_flatfield = SubarrayDescription.read(self.flatfield_input_url)
|
|
179
|
+
# Check if the subarray descriptions match
|
|
180
|
+
if not subarray_pedestal.__eq__(subarray_flatfield):
|
|
181
|
+
raise ValueError(
|
|
182
|
+
"The subarray descriptions of the pedestal and flatfield input files do not match."
|
|
183
|
+
)
|
|
184
|
+
# Select a new subarray if the allowed_tels configuration is used
|
|
185
|
+
self.subarray = (
|
|
186
|
+
subarray_pedestal
|
|
187
|
+
if self.allowed_tels is None
|
|
188
|
+
else subarray_pedestal.select_subarray(self.allowed_tels)
|
|
189
|
+
)
|
|
190
|
+
# The monitoring groups and their configurations to be used in the tests
|
|
191
|
+
self.monitoring_groups = {
|
|
192
|
+
"pedestal_image": self.agg_stats_pedestal_image_config,
|
|
193
|
+
"flatfield_image": self.agg_stats_flatfield_image_config,
|
|
194
|
+
"flatfield_peak_time": self.agg_stats_flatfield_peak_time_config,
|
|
195
|
+
}
|
|
196
|
+
# Create a configuration suitable for the tests
|
|
197
|
+
self.sim_argv = [
|
|
198
|
+
"--SimTelEventSource.skip_calibration_events=False",
|
|
199
|
+
f"--SimTelEventSource.skip_r1_calibration={self.skip_r1_calibration}",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
def start(self):
|
|
203
|
+
"""Iterate over the telescope IDs and calculate the camera calibration coefficients."""
|
|
204
|
+
# Set the path to the simtel calibration events
|
|
205
|
+
# Set the output file path for pedestal images
|
|
206
|
+
pedestal_dl1_image_file = self.output_dir / "pedestal_events.dl1.h5"
|
|
207
|
+
with open(self.process_pedestal_config) as yaml_file:
|
|
208
|
+
pedestal_config = yaml.safe_load(yaml_file)
|
|
209
|
+
pedestal_dl1_image_file = self._run_ctapipe_process_tool(
|
|
210
|
+
self.pedestal_input_url, pedestal_dl1_image_file, pedestal_config
|
|
211
|
+
)
|
|
212
|
+
# Set the output file path for flatfield images
|
|
213
|
+
flatfield_dl1_image_file = self.output_dir / "flatfield_events.dl1.h5"
|
|
214
|
+
with open(self.process_flatfield_config) as yaml_file:
|
|
215
|
+
flatfield_config = yaml.safe_load(yaml_file)
|
|
216
|
+
flatfield_dl1_image_file = self._run_ctapipe_process_tool(
|
|
217
|
+
self.flatfield_input_url, flatfield_dl1_image_file, flatfield_config
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Iterate over the telescope IDs and calculate the camera calibration coefficients
|
|
221
|
+
for tel_id in self.subarray.tel_ids:
|
|
222
|
+
for aggregation_mode in self.aggregation_modes:
|
|
223
|
+
# Create the statistics aggregation file for the given aggregation mode
|
|
224
|
+
# Default chunk duration for the statistics aggregation
|
|
225
|
+
chunk_duration = 25.0 * u.s
|
|
226
|
+
# Set the output file path for the statistics aggregation
|
|
227
|
+
output_file = (
|
|
228
|
+
self.output_dir / f"{self.prefix}_{aggregation_mode}.dl1.h5"
|
|
229
|
+
)
|
|
230
|
+
# Loop over the monitoring groups and calculate pixel statistics
|
|
231
|
+
for mon_group, mon_config in self.monitoring_groups.items():
|
|
232
|
+
# Set the input file path for the PixelStatisticsCalculator
|
|
233
|
+
dl1_image_file = (
|
|
234
|
+
pedestal_dl1_image_file
|
|
235
|
+
if mon_group == "pedestal_image"
|
|
236
|
+
else flatfield_dl1_image_file
|
|
237
|
+
)
|
|
238
|
+
# Get the standard configuration for the PixelStatisticsCalculator
|
|
239
|
+
with open(mon_config) as yaml_file:
|
|
240
|
+
pix_stats_config = yaml.safe_load(yaml_file)
|
|
241
|
+
# Set some additional parameters using cli arguments
|
|
242
|
+
cli_argv = [
|
|
243
|
+
f"--input_url={dl1_image_file}",
|
|
244
|
+
f"--output_path={output_file}",
|
|
245
|
+
"--overwrite",
|
|
246
|
+
]
|
|
247
|
+
n_events = len(
|
|
248
|
+
read_table(
|
|
249
|
+
dl1_image_file,
|
|
250
|
+
path=f"{self.IMAGE_TEL_GROUP}tel_{tel_id:03d}",
|
|
251
|
+
)
|
|
252
|
+
)
|
|
253
|
+
# Modify the configuration for the specific chunk mode
|
|
254
|
+
if aggregation_mode == "single_chunk":
|
|
255
|
+
chunk_duration = 1000.0 * u.s
|
|
256
|
+
# Overwrite the chunk size for the specific aggregator
|
|
257
|
+
if mon_group == "flatfield_peak_time":
|
|
258
|
+
cli_argv.append(
|
|
259
|
+
f"--PlainAggregator.chunk_size={n_events}"
|
|
260
|
+
)
|
|
261
|
+
else:
|
|
262
|
+
cli_argv.append(
|
|
263
|
+
f"--SigmaClippingAggregator.chunk_size={n_events}"
|
|
264
|
+
)
|
|
265
|
+
elif aggregation_mode == "same_chunks":
|
|
266
|
+
chunk_duration = 100.0 * u.s
|
|
267
|
+
# Overwrite the chunk size for the specific aggregator
|
|
268
|
+
if mon_group == "flatfield_peak_time":
|
|
269
|
+
cli_argv.append(
|
|
270
|
+
f"--PlainAggregator.chunk_size={n_events//10}"
|
|
271
|
+
)
|
|
272
|
+
else:
|
|
273
|
+
cli_argv.append(
|
|
274
|
+
f"--SigmaClippingAggregator.chunk_size={n_events//10}"
|
|
275
|
+
)
|
|
276
|
+
elif aggregation_mode == "different_chunks":
|
|
277
|
+
# Use different chunk sizes for each monitoring group
|
|
278
|
+
if mon_group == "pedestal_image":
|
|
279
|
+
chunk_duration = 200.0 * u.s
|
|
280
|
+
cli_argv.append(
|
|
281
|
+
f"--SigmaClippingAggregator.chunk_size={2 * (n_events//10)}"
|
|
282
|
+
)
|
|
283
|
+
elif mon_group == "flatfield_image":
|
|
284
|
+
chunk_duration = 100.0 * u.s
|
|
285
|
+
cli_argv.append(
|
|
286
|
+
f"--SigmaClippingAggregator.chunk_size={n_events//10}"
|
|
287
|
+
)
|
|
288
|
+
elif mon_group == "flatfield_peak_time":
|
|
289
|
+
chunk_duration = 500.0 * u.s
|
|
290
|
+
cli_argv.append(
|
|
291
|
+
f"--PlainAggregator.chunk_size={5 * (n_events//10)}"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# Run the PixelStatisticsCalculatorTool to calculate pixel statistics
|
|
295
|
+
run_tool(
|
|
296
|
+
PixelStatisticsCalculatorTool(
|
|
297
|
+
config=Config(pix_stats_config)
|
|
298
|
+
),
|
|
299
|
+
argv=cli_argv,
|
|
300
|
+
cwd=self.output_dir,
|
|
301
|
+
raises=True,
|
|
302
|
+
)
|
|
303
|
+
# Overwrite timestamps in the output file to make them realistic
|
|
304
|
+
# Read the created statsagg table for the specific monitoring group
|
|
305
|
+
stats_aggregation_tab = read_table(
|
|
306
|
+
output_file,
|
|
307
|
+
path=f"{self.MONITORING_TEL_GROUP}{mon_group}/tel_{tel_id:03d}",
|
|
308
|
+
)
|
|
309
|
+
# Loop over the chunks and set the new timestamps
|
|
310
|
+
for chunk_nr in range(len(stats_aggregation_tab)):
|
|
311
|
+
stats_aggregation_tab["time_start"][chunk_nr] = (
|
|
312
|
+
self.REFERENCE_TIME
|
|
313
|
+
+ (1 / self.REFERENCE_TRIGGER_RATE).to(u.s)
|
|
314
|
+
+ chunk_nr * chunk_duration
|
|
315
|
+
)
|
|
316
|
+
stats_aggregation_tab["time_end"][chunk_nr] = (
|
|
317
|
+
self.REFERENCE_TIME + (chunk_nr + 1) * chunk_duration
|
|
318
|
+
)
|
|
319
|
+
# Set a different starting time (outside the default 1 second tolerance)
|
|
320
|
+
# for the pedestal group if the mode is 'diffent_chunks'. This is to ensure
|
|
321
|
+
# that the we can later test when the chunk interpolator is returning NaN values
|
|
322
|
+
# for the first and last unique timestamps.
|
|
323
|
+
if aggregation_mode == "different_chunks":
|
|
324
|
+
if mon_group == "pedestal_image":
|
|
325
|
+
stats_aggregation_tab["time_start"][0] -= 2 * u.s
|
|
326
|
+
stats_aggregation_tab["time_end"][-1] += 2 * u.s
|
|
327
|
+
# Overwrite the table in the output file
|
|
328
|
+
write_table(
|
|
329
|
+
stats_aggregation_tab,
|
|
330
|
+
output_file,
|
|
331
|
+
f"{self.MONITORING_TEL_GROUP}{mon_group}/tel_{tel_id:03d}",
|
|
332
|
+
overwrite=True,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def finish(self):
|
|
336
|
+
"""Shut down the tool."""
|
|
337
|
+
self.log.info("Tool is shutting down")
|
|
338
|
+
|
|
339
|
+
def _run_ctapipe_process_tool(self, input_data, output_file, config):
|
|
340
|
+
"""Produce the DL1A file containing the images."""
|
|
341
|
+
# Run the ProcessorTool to create images
|
|
342
|
+
run_tool(
|
|
343
|
+
ProcessorTool(config=Config(config)),
|
|
344
|
+
argv=[
|
|
345
|
+
f"--input={input_data}",
|
|
346
|
+
f"--output={output_file}",
|
|
347
|
+
"--overwrite",
|
|
348
|
+
]
|
|
349
|
+
+ self.sim_argv,
|
|
350
|
+
)
|
|
351
|
+
return output_file
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def main():
|
|
355
|
+
# Run the tool
|
|
356
|
+
tool = CamCalibTestDataTool()
|
|
357
|
+
tool.run()
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
if __name__ == "main":
|
|
361
|
+
main()
|