ctao-calibpipe 0.1.0rc9__py3-none-any.whl → 0.2.0__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.

Files changed (24) hide show
  1. calibpipe/_version.py +2 -2
  2. calibpipe/core/common_metadata_containers.py +3 -0
  3. calibpipe/database/adapter/adapter.py +1 -1
  4. calibpipe/database/adapter/database_containers/__init__.py +2 -0
  5. calibpipe/database/adapter/database_containers/common_metadata.py +2 -0
  6. calibpipe/database/adapter/database_containers/throughput.py +30 -0
  7. calibpipe/database/interfaces/table_handler.py +79 -97
  8. calibpipe/telescope/throughput/containers.py +59 -0
  9. calibpipe/tests/unittests/array/test_cross_calibration.py +417 -0
  10. calibpipe/tests/unittests/database/test_table_handler.py +95 -0
  11. calibpipe/tests/unittests/telescope/camera/test_calculate_camcalib_coefficients.py +347 -0
  12. calibpipe/tests/unittests/telescope/camera/test_produce_camcalib_test_data.py +42 -0
  13. calibpipe/tests/unittests/telescope/throughput/test_muon_throughput_calibrator.py +189 -0
  14. calibpipe/tools/camcalib_test_data.py +361 -0
  15. calibpipe/tools/camera_calibrator.py +558 -0
  16. calibpipe/tools/muon_throughput_calculator.py +239 -0
  17. calibpipe/tools/telescope_cross_calibration_calculator.py +721 -0
  18. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/METADATA +3 -2
  19. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/RECORD +24 -14
  20. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/WHEEL +1 -1
  21. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/entry_points.txt +4 -0
  22. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/licenses/AUTHORS.md +0 -0
  23. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.dist-info}/licenses/LICENSE +0 -0
  24. {ctao_calibpipe-0.1.0rc9.dist-info → ctao_calibpipe-0.2.0.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()