gammasimtools 0.15.0__py3-none-any.whl → 0.17.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.
Files changed (248) hide show
  1. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/METADATA +5 -33
  2. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/RECORD +243 -229
  3. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/entry_points.txt +8 -3
  5. simtools/_version.py +2 -2
  6. simtools/applications/calculate_trigger_rate.py +10 -10
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +16 -16
  8. simtools/applications/convert_model_parameter_from_simtel.py +1 -1
  9. simtools/applications/derive_ctao_array_layouts.py +5 -5
  10. simtools/applications/derive_psf_parameters.py +12 -9
  11. simtools/applications/docs_produce_array_element_report.py +3 -3
  12. simtools/applications/docs_produce_calibration_reports.py +49 -0
  13. simtools/applications/docs_produce_simulation_configuration_report.py +50 -0
  14. simtools/applications/{generate_simtel_array_histograms.py → generate_sim_telarray_histograms.py} +2 -2
  15. simtools/applications/generate_simtel_event_data.py +36 -46
  16. simtools/applications/merge_tables.py +104 -0
  17. simtools/applications/plot_array_layout.py +145 -258
  18. simtools/applications/production_derive_corsika_limits.py +35 -167
  19. simtools/applications/production_derive_statistics.py +159 -0
  20. simtools/applications/production_generate_grid.py +197 -0
  21. simtools/applications/simulate_light_emission.py +6 -13
  22. simtools/applications/simulate_prod.py +45 -21
  23. simtools/applications/simulate_prod_htcondor_generator.py +0 -1
  24. simtools/applications/submit_array_layouts.py +93 -0
  25. simtools/applications/validate_cumulative_psf.py +6 -4
  26. simtools/applications/validate_file_using_schema.py +7 -3
  27. simtools/applications/validate_optics.py +5 -4
  28. simtools/applications/verify_simulation_model_production_tables.py +52 -0
  29. simtools/camera/camera_efficiency.py +17 -42
  30. simtools/configuration/commandline_parser.py +32 -37
  31. simtools/configuration/configurator.py +10 -4
  32. simtools/corsika/corsika_config.py +120 -17
  33. simtools/corsika/primary_particle.py +46 -13
  34. simtools/data_model/format_checkers.py +9 -0
  35. simtools/data_model/metadata_collector.py +7 -3
  36. simtools/data_model/model_data_writer.py +3 -0
  37. simtools/data_model/schema.py +27 -16
  38. simtools/data_model/validate_data.py +27 -7
  39. simtools/db/db_handler.py +21 -15
  40. simtools/db/db_model_upload.py +2 -2
  41. simtools/io_operations/io_handler.py +2 -2
  42. simtools/io_operations/io_table_handler.py +345 -0
  43. simtools/job_execution/htcondor_script_generator.py +2 -2
  44. simtools/job_execution/job_manager.py +7 -121
  45. simtools/layout/array_layout.py +1 -0
  46. simtools/layout/array_layout_utils.py +385 -0
  47. simtools/model/array_model.py +68 -29
  48. simtools/model/model_parameter.py +76 -51
  49. simtools/model/model_repository.py +134 -0
  50. simtools/model/model_utils.py +43 -1
  51. simtools/model/site_model.py +3 -2
  52. simtools/model/telescope_model.py +4 -4
  53. simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -116
  54. simtools/production_configuration/derive_corsika_limits.py +239 -111
  55. simtools/production_configuration/derive_corsika_limits_grid.py +189 -0
  56. simtools/production_configuration/derive_production_statistics.py +155 -0
  57. simtools/production_configuration/derive_production_statistics_handler.py +152 -0
  58. simtools/production_configuration/generate_production_grid.py +364 -0
  59. simtools/production_configuration/interpolation_handler.py +303 -96
  60. simtools/ray_tracing/mirror_panel_psf.py +16 -20
  61. simtools/ray_tracing/psf_analysis.py +2 -2
  62. simtools/ray_tracing/ray_tracing.py +12 -7
  63. simtools/reporting/docs_read_parameters.py +426 -81
  64. simtools/runners/corsika_runner.py +11 -1
  65. simtools/runners/corsika_simtel_runner.py +84 -90
  66. simtools/runners/runner_services.py +22 -8
  67. simtools/runners/simtel_runner.py +27 -10
  68. simtools/schemas/model_parameter.metaschema.yml +4 -0
  69. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +1 -0
  70. simtools/schemas/model_parameters/adjust_gain.schema.yml +2 -2
  71. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +2 -2
  72. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +2 -2
  73. simtools/schemas/model_parameters/array_window.schema.yml +2 -2
  74. simtools/schemas/model_parameters/asum_offset.schema.yml +2 -2
  75. simtools/schemas/model_parameters/asum_shaping.schema.yml +2 -2
  76. simtools/schemas/model_parameters/asum_threshold.schema.yml +2 -2
  77. simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
  78. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +2 -2
  79. simtools/schemas/model_parameters/camera_body_shape.schema.yml +2 -2
  80. simtools/schemas/model_parameters/camera_config_file.schema.yml +2 -2
  81. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +2 -2
  82. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +2 -2
  83. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +2 -2
  84. simtools/schemas/model_parameters/camera_depth.schema.yml +2 -2
  85. simtools/schemas/model_parameters/camera_filter.schema.yml +2 -2
  86. simtools/schemas/model_parameters/camera_pixels.schema.yml +2 -2
  87. simtools/schemas/model_parameters/camera_transmission.schema.yml +2 -2
  88. simtools/schemas/model_parameters/channels_per_chip.schema.yml +2 -2
  89. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +2 -2
  90. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +90 -1
  91. simtools/schemas/model_parameters/default_trigger.schema.yml +2 -2
  92. simtools/schemas/model_parameters/design_model.schema.yml +2 -2
  93. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +2 -2
  94. simtools/schemas/model_parameters/disc_bins.schema.yml +2 -2
  95. simtools/schemas/model_parameters/disc_start.schema.yml +2 -2
  96. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +2 -2
  97. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +2 -2
  98. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +2 -2
  99. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +2 -2
  100. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +2 -2
  101. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +2 -2
  102. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +2 -2
  103. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +2 -2
  104. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +2 -2
  105. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +2 -2
  106. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +2 -2
  107. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +2 -2
  108. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +2 -2
  109. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +2 -2
  110. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +2 -2
  111. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +2 -2
  112. simtools/schemas/model_parameters/dish_shape_length.schema.yml +2 -2
  113. simtools/schemas/model_parameters/dsum_clipping.schema.yml +2 -2
  114. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +2 -2
  115. simtools/schemas/model_parameters/dsum_offset.schema.yml +2 -2
  116. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +2 -2
  117. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +2 -2
  118. simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
  119. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +2 -2
  120. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +2 -2
  121. simtools/schemas/model_parameters/dsum_shaping.schema.yml +2 -2
  122. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +2 -2
  123. simtools/schemas/model_parameters/dsum_threshold.schema.yml +44 -3
  124. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +2 -2
  125. simtools/schemas/model_parameters/effective_focal_length.schema.yml +2 -2
  126. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +2 -2
  127. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -2
  128. simtools/schemas/model_parameters/fadc_bins.schema.yml +2 -2
  129. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +2 -2
  130. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +2 -2
  131. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +2 -2
  132. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -2
  133. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +2 -2
  134. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +2 -2
  135. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +2 -2
  136. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +2 -2
  137. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +2 -2
  138. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +2 -2
  139. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +2 -2
  140. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +2 -2
  141. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +2 -2
  142. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +2 -2
  143. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +2 -2
  144. simtools/schemas/model_parameters/fadc_mhz.schema.yml +2 -2
  145. simtools/schemas/model_parameters/fadc_noise.schema.yml +2 -2
  146. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +2 -2
  147. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +2 -2
  148. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +2 -2
  149. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +2 -2
  150. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +2 -2
  151. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +2 -2
  152. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +2 -2
  153. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +2 -2
  154. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  155. simtools/schemas/model_parameters/flatfielding.schema.yml +2 -2
  156. simtools/schemas/model_parameters/focal_length.schema.yml +2 -2
  157. simtools/schemas/model_parameters/focus_offset.schema.yml +2 -2
  158. simtools/schemas/model_parameters/gain_variation.schema.yml +2 -2
  159. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +2 -2
  160. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +2 -2
  161. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +2 -2
  162. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  163. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +2 -2
  164. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +2 -2
  165. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +2 -2
  166. simtools/schemas/model_parameters/min_photons.schema.yml +2 -2
  167. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +2 -2
  168. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +2 -2
  169. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +2 -2
  170. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -2
  171. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +2 -2
  172. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +2 -2
  173. simtools/schemas/model_parameters/mirror_list.schema.yml +2 -2
  174. simtools/schemas/model_parameters/mirror_offset.schema.yml +2 -2
  175. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +2 -2
  176. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +2 -2
  177. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +2 -2
  178. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +2 -2
  179. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
  180. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +2 -2
  181. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +2 -2
  182. simtools/schemas/model_parameters/num_gains.schema.yml +2 -2
  183. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +2 -2
  184. simtools/schemas/model_parameters/optics_properties.schema.yml +2 -2
  185. simtools/schemas/model_parameters/pedestal_events.schema.yml +7 -3
  186. simtools/schemas/model_parameters/photon_delay.schema.yml +2 -2
  187. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +2 -2
  188. simtools/schemas/model_parameters/pm_average_gain.schema.yml +2 -2
  189. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +2 -2
  190. simtools/schemas/model_parameters/pm_gain_index.schema.yml +2 -2
  191. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +2 -2
  192. simtools/schemas/model_parameters/pm_transit_time.schema.yml +2 -2
  193. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +2 -2
  194. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +2 -2
  195. simtools/schemas/model_parameters/qe_variation.schema.yml +2 -2
  196. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +2 -2
  197. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
  198. simtools/schemas/model_parameters/random_generator.schema.yml +2 -2
  199. simtools/schemas/model_parameters/random_mono_probability.schema.yml +2 -2
  200. simtools/schemas/model_parameters/sampled_output.schema.yml +2 -2
  201. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +2 -2
  202. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +2 -2
  203. simtools/schemas/model_parameters/tailcut_scale.schema.yml +2 -2
  204. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +2 -2
  205. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +2 -2
  206. simtools/schemas/model_parameters/telescope_random_error.schema.yml +2 -2
  207. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +2 -2
  208. simtools/schemas/model_parameters/telescope_transmission.schema.yml +2 -2
  209. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +2 -2
  210. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +2 -2
  211. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +2 -2
  212. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +2 -2
  213. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +2 -2
  214. simtools/schemas/model_parameters/transit_time_error.schema.yml +2 -2
  215. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +2 -2
  216. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +2 -2
  217. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +2 -2
  218. simtools/schemas/model_parameters/trigger_pixels.schema.yml +2 -2
  219. simtools/schemas/production_configuration_metrics.schema.yml +2 -2
  220. simtools/simtel/simtel_config_reader.py +21 -17
  221. simtools/simtel/simtel_config_writer.py +258 -66
  222. simtools/simtel/simtel_io_event_reader.py +301 -194
  223. simtools/simtel/simtel_io_event_writer.py +207 -227
  224. simtools/simtel/simtel_io_file_info.py +62 -0
  225. simtools/simtel/simtel_io_histogram.py +10 -14
  226. simtools/simtel/simtel_io_histograms.py +2 -2
  227. simtools/simtel/simtel_io_metadata.py +106 -0
  228. simtools/simtel/simulator_array.py +28 -14
  229. simtools/simtel/simulator_camera_efficiency.py +12 -6
  230. simtools/simtel/simulator_light_emission.py +85 -45
  231. simtools/simtel/simulator_ray_tracing.py +16 -6
  232. simtools/simulator.py +286 -89
  233. simtools/testing/configuration.py +5 -0
  234. simtools/testing/helpers.py +18 -0
  235. simtools/testing/sim_telarray_metadata.py +212 -0
  236. simtools/testing/validate_output.py +16 -6
  237. simtools/utils/general.py +18 -27
  238. simtools/utils/names.py +32 -10
  239. simtools/visualization/plot_array_layout.py +242 -0
  240. simtools/visualization/plot_pixels.py +681 -0
  241. simtools/visualization/visualize.py +5 -221
  242. simtools/applications/production_generate_simulation_config.py +0 -162
  243. simtools/applications/production_scale_events.py +0 -185
  244. simtools/layout/ctao_array_layouts.py +0 -172
  245. simtools/production_configuration/event_scaler.py +0 -120
  246. simtools/production_configuration/generate_simulation_config.py +0 -158
  247. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/licenses/LICENSE +0 -0
  248. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,11 @@
1
- """Generate a reduced dataset from given simulation event list and save the output to file."""
1
+ """Generate a reduced dataset from sim_telarray output files using astropy tables."""
2
2
 
3
3
  import logging
4
- from dataclasses import dataclass, field
4
+ from dataclasses import dataclass
5
5
 
6
+ import astropy.units as u
6
7
  import numpy as np
7
- import tables
8
+ from astropy.table import Table
8
9
  from eventio import EventIOFile
9
10
  from eventio.simtel import (
10
11
  ArrayEvent,
@@ -15,107 +16,132 @@ from eventio.simtel import (
15
16
  TriggerInformation,
16
17
  )
17
18
 
19
+ from simtools.corsika.primary_particle import PrimaryParticle
20
+ from simtools.simtel.simtel_io_file_info import get_corsika_run_header
18
21
  from simtools.utils.geometry import calculate_circular_mean
19
22
 
20
- DEFAULT_FILTERS = tables.Filters(complevel=5, complib="zlib", shuffle=True, bitshuffle=False)
21
-
22
23
 
23
24
  @dataclass
24
- class ShowerEventData:
25
- """Shower event data."""
26
-
27
- simulated_energy: list = field(default_factory=list)
28
- x_core: list = field(default_factory=list)
29
- y_core: list = field(default_factory=list)
30
- shower_azimuth: list = field(default_factory=list)
31
- shower_altitude: list = field(default_factory=list)
32
- shower_id: list = field(default_factory=list)
33
- area_weight: list = field(default_factory=list)
34
-
35
- x_core_shower: list = field(default_factory=list)
36
- y_core_shower: list = field(default_factory=list)
37
- core_distance_shower: list = field(default_factory=list)
38
-
39
-
40
- @dataclass
41
- class TriggeredEventData:
42
- """Triggered event data."""
43
-
44
- triggered_id: list = field(default_factory=list)
45
- array_altitudes: list = field(default_factory=list)
46
- array_azimuths: list = field(default_factory=list)
47
- trigger_telescope_list_list: list = field(default_factory=list)
48
- angular_distance: list = field(default_factory=list)
25
+ class TableSchemas:
26
+ """Define schemas for output tables with units."""
27
+
28
+ shower_schema = {
29
+ "shower_id": (np.uint32, None),
30
+ "event_id": (np.uint32, None),
31
+ "file_id": (np.uint32, None),
32
+ "simulated_energy": (np.float64, u.TeV),
33
+ "x_core": (np.float64, u.m),
34
+ "y_core": (np.float64, u.m),
35
+ "shower_azimuth": (np.float64, u.rad),
36
+ "shower_altitude": (np.float64, u.rad),
37
+ "area_weight": (np.float64, None),
38
+ }
39
+
40
+ trigger_schema = {
41
+ "shower_id": (np.uint32, None),
42
+ "event_id": (np.uint32, None),
43
+ "file_id": (np.uint32, None),
44
+ "array_altitude": (np.float64, u.rad),
45
+ "array_azimuth": (np.float64, u.rad),
46
+ "telescope_list": (str, None), # Store as comma-separated string
47
+ }
48
+
49
+ file_info_schema = {
50
+ "file_name": (str, None),
51
+ "file_id": (np.uint32, None),
52
+ "particle_id": (np.uint32, None),
53
+ "energy_min": (np.float64, u.TeV),
54
+ "energy_max": (np.float64, u.TeV),
55
+ "viewcone_min": (np.float64, u.deg),
56
+ "viewcone_max": (np.float64, u.deg),
57
+ "core_scatter_min": (np.float64, u.m),
58
+ "core_scatter_max": (np.float64, u.m),
59
+ "zenith": (np.float64, u.deg),
60
+ "azimuth": (np.float64, u.deg),
61
+ "nsb_level": (np.float64, None),
62
+ }
49
63
 
50
64
 
51
65
  class SimtelIOEventDataWriter:
52
66
  """
53
- Generate a reduced dataset from given simulation event list and save the output to file.
67
+ Process sim_telarray events and write tables to file.
68
+
69
+ Extracts essential information from sim_telarray output files:
70
+
71
+ - Shower parameters (energy, core location, direction)
72
+ - Trigger patterns
73
+ - Telescope pointing
54
74
 
55
75
  Attributes
56
76
  ----------
57
77
  input_files : list
58
78
  List of input file paths to process.
59
- output_file : str
60
- Path to the output file.
61
79
  max_files : int, optional
62
80
  Maximum number of files to process.
63
81
  """
64
82
 
65
- def __init__(self, input_files, output_file, max_files=100):
83
+ def __init__(self, input_files, max_files=100):
66
84
  """Initialize class."""
67
85
  self._logger = logging.getLogger(__name__)
68
86
  self.input_files = input_files
69
- self.output_file = output_file
70
87
  try:
71
88
  self.max_files = max_files if max_files < len(input_files) else len(input_files)
72
89
  except TypeError as exc:
73
90
  raise TypeError("No input files provided.") from exc
74
- self.shower = None
91
+
75
92
  self.n_use = None
76
- self.shower_id_offset = 0
77
- self.event_data = ShowerEventData()
78
- self.triggered_data = TriggeredEventData()
79
- self.file_names = []
93
+ self.shower_data = []
94
+ self.trigger_data = []
95
+ self.file_info = []
80
96
 
81
97
  def process_files(self):
82
- """Process the input files and store them in an file."""
83
- self.shower_id_offset = 0
84
-
85
- for i, file in enumerate(self.input_files[: self.max_files], start=1):
86
- self._logger.info(f"Processing file {i}/{self.max_files}: {file}")
87
- self._process_file(file)
88
- if i == 1 or len(self.event_data.simulated_energy) >= 1e7:
89
- self._write_data(mode="w" if i == 1 else "a")
90
- self.shower_id_offset += len(self.event_data.simulated_energy)
91
- self._reset_data()
92
-
93
- self._write_data(mode="a")
94
-
95
- def get_event_data(self):
96
98
  """
97
- Return shower and triggered event data.
99
+ Process input files and return tables.
98
100
 
99
101
  Returns
100
102
  -------
101
- ShowerEventData, TriggeredEventData
102
- Shower and triggered event data.
103
+ list
104
+ List of astropy tables containing processed data.
103
105
  """
104
- return self.event_data, self.triggered_data
105
-
106
- def _process_file(self, file):
106
+ for i, file in enumerate(self.input_files[: self.max_files]):
107
+ self._logger.info(f"Processing file {i + 1}/{self.max_files}: {file}")
108
+ self._process_file(i, file)
109
+
110
+ return self.create_tables()
111
+
112
+ def create_tables(self):
113
+ """Create astropy tables from collected data."""
114
+ tables = []
115
+ for data, schema, name in [
116
+ (self.shower_data, TableSchemas.shower_schema, "SHOWERS"),
117
+ (self.trigger_data, TableSchemas.trigger_schema, "TRIGGERS"),
118
+ (self.file_info, TableSchemas.file_info_schema, "FILE_INFO"),
119
+ ]:
120
+ table = Table(rows=data, names=schema.keys())
121
+ table.meta["EXTNAME"] = name
122
+ self._add_units_to_table(table, schema)
123
+ tables.append(table)
124
+ return tables
125
+
126
+ def _add_units_to_table(self, table, schema):
127
+ """Add units to a single table's columns."""
128
+ for col, (_, unit) in schema.items():
129
+ if unit is not None:
130
+ table[col].unit = unit
131
+
132
+ def _process_file(self, file_id, file):
107
133
  """Process a single file and update data lists."""
134
+ self._process_file_info(file_id, file)
108
135
  with EventIOFile(file) as f:
109
136
  for eventio_object in f:
110
137
  if isinstance(eventio_object, MCRunHeader):
111
138
  self._process_mc_run_header(eventio_object)
112
139
  elif isinstance(eventio_object, MCShower):
113
- self._process_mc_shower(eventio_object)
140
+ self._process_mc_shower(eventio_object, file_id)
114
141
  elif isinstance(eventio_object, MCEvent):
115
142
  self._process_mc_event(eventio_object)
116
143
  elif isinstance(eventio_object, ArrayEvent):
117
- self._process_array_event(eventio_object)
118
- self.file_names.append(str(file))
144
+ self._process_array_event(eventio_object, file_id)
119
145
 
120
146
  def _process_mc_run_header(self, eventio_object):
121
147
  """Process MC run header and update data lists."""
@@ -123,36 +149,93 @@ class SimtelIOEventDataWriter:
123
149
  self.n_use = mc_head["n_use"] # reuse factor n_use needed to extend the values below
124
150
  self._logger.info(f"Shower reuse factor: {self.n_use} (viewcone: {mc_head['viewcone']})")
125
151
 
126
- def _process_mc_shower(self, eventio_object):
152
+ def _process_file_info(self, file_id, file):
153
+ """Process file information and append to file info list."""
154
+ run_info = get_corsika_run_header(file)
155
+ particle = PrimaryParticle(
156
+ particle_id_type="eventio_id", particle_id=run_info.get("primary_id", 1)
157
+ )
158
+ self.file_info.append(
159
+ {
160
+ "file_name": str(file),
161
+ "file_id": file_id,
162
+ "particle_id": particle.corsika7_id,
163
+ "energy_min": run_info["E_range"][0],
164
+ "energy_max": run_info["E_range"][1],
165
+ "viewcone_min": run_info["viewcone"][0],
166
+ "viewcone_max": run_info["viewcone"][1],
167
+ "core_scatter_min": run_info["core_range"][0],
168
+ "core_scatter_max": run_info["core_range"][1],
169
+ "zenith": 90.0 - np.degrees(run_info["direction"][1]),
170
+ "azimuth": np.degrees(run_info["direction"][0]),
171
+ "nsb_level": self._get_preliminary_nsb_level(str(file)),
172
+ }
173
+ )
174
+
175
+ def _process_mc_shower(self, eventio_object, file_id):
127
176
  """
128
177
  Process MC shower and update shower event list.
129
178
 
130
179
  Duplicated entries 'self.n_use' times to match the number simulated events with
131
180
  different core positions.
132
181
  """
133
- self.shower = eventio_object.parse()
134
-
135
- self.event_data.simulated_energy.extend([self.shower["energy"]] * self.n_use)
136
- self.event_data.shower_azimuth.extend([self.shower["azimuth"]] * self.n_use)
137
- self.event_data.shower_altitude.extend([self.shower["altitude"]] * self.n_use)
182
+ shower = eventio_object.parse()
183
+
184
+ self.shower_data.extend(
185
+ {
186
+ "shower_id": shower["shower"],
187
+ "event_id": None, # filled in _process_mc_event
188
+ "file_id": file_id,
189
+ "simulated_energy": shower["energy"],
190
+ "x_core": None, # filled in _process_mc_event
191
+ "y_core": None, # filled in _process_mc_event
192
+ "shower_azimuth": shower["azimuth"],
193
+ "shower_altitude": shower["altitude"],
194
+ "area_weight": None, # filled in _process_mc_event
195
+ }
196
+ for _ in range(self.n_use)
197
+ )
138
198
 
139
199
  def _process_mc_event(self, eventio_object):
140
- """Process MC event and update shower event list."""
200
+ """
201
+ Process MC event and update shower event list.
202
+
203
+ Expected to be called n_use times after _process_shower.
204
+ """
141
205
  event = eventio_object.parse()
142
206
 
143
- self.event_data.shower_id.append(event["shower_num"])
144
- self.event_data.x_core.append(event["xcore"])
145
- self.event_data.y_core.append(event["ycore"])
146
- self.event_data.area_weight.append(event["aweight"])
207
+ shower_data_index = len(self.shower_data) - self.n_use + event["event_id"] % 100
147
208
 
148
- def _process_array_event(self, eventio_object):
209
+ try:
210
+ if self.shower_data[shower_data_index]["shower_id"] != event["shower_num"]:
211
+ raise IndexError
212
+ except IndexError as exc:
213
+ raise IndexError(
214
+ f"Inconsistent shower and MC event data for shower id {event['shower_num']}"
215
+ ) from exc
216
+
217
+ self.shower_data[shower_data_index].update(
218
+ {
219
+ "event_id": event["event_id"],
220
+ "x_core": event["xcore"],
221
+ "y_core": event["ycore"],
222
+ "area_weight": event["aweight"],
223
+ }
224
+ )
225
+
226
+ def _process_array_event(self, eventio_object, file_id):
149
227
  """Process array event and update triggered event list."""
150
228
  tracking_positions = []
229
+ telescopes = []
151
230
 
152
- for _, obj in enumerate(eventio_object):
231
+ for obj in eventio_object:
153
232
  if isinstance(obj, TriggerInformation):
154
- self._process_trigger_information(obj)
155
-
233
+ trigger_info = obj.parse()
234
+ telescopes = (
235
+ trigger_info["triggered_telescopes"]
236
+ if len(trigger_info["triggered_telescopes"]) > 0
237
+ else []
238
+ )
156
239
  if isinstance(obj, TrackingPosition):
157
240
  tracking_position = obj.parse()
158
241
  tracking_positions.append(
@@ -162,156 +245,53 @@ class SimtelIOEventDataWriter:
162
245
  }
163
246
  )
164
247
 
165
- if tracking_positions:
166
- self._process_tracking_positions(tracking_positions)
248
+ if len(telescopes) > 0 and tracking_positions:
249
+ self._fill_array_event(telescopes, tracking_positions, eventio_object.event_id, file_id)
167
250
 
168
- def _process_tracking_positions(self, tracking_positions):
169
- """
170
- Process collected tracking positions and update triggered event list.
171
-
172
- Use mean telescope tracking positions, averaged over all triggered telescopes.
173
- """
251
+ def _fill_array_event(self, telescopes, tracking_positions, event_id, file_id):
252
+ """Add array event triggered events with tracking positions."""
174
253
  altitudes = [pos["altitude"] for pos in tracking_positions]
175
254
  azimuths = [pos["azimuth"] for pos in tracking_positions]
176
255
 
177
- self.triggered_data.array_altitudes.append(np.mean(altitudes))
178
- self.triggered_data.array_azimuths.append(calculate_circular_mean(azimuths))
179
-
180
- def _process_trigger_information(self, trigger_info):
181
- """Process trigger information and update triggered event list."""
182
- trigger_info = trigger_info.parse()
183
- telescopes = trigger_info["triggered_telescopes"]
184
- if len(telescopes) > 0:
185
- # add offset to obtain unique shower IDs among all files
186
- self.triggered_data.triggered_id.append(self.shower["shower"] + self.shower_id_offset)
187
- self.triggered_data.trigger_telescope_list_list.append(
188
- np.array(telescopes, dtype=np.int16)
189
- )
190
-
191
- def _table_descriptions(self):
192
- """HDF5 table descriptions for shower data, triggered data, and file names."""
193
- shower_data_desc = {
194
- "shower_id": tables.Int32Col(),
195
- "simulated_energy": tables.Float32Col(),
196
- "x_core": tables.Float32Col(),
197
- "y_core": tables.Float32Col(),
198
- "area_weight": tables.Float32Col(),
199
- "shower_azimuth": tables.Float32Col(),
200
- "shower_altitude": tables.Float32Col(),
201
- }
202
- triggered_data_desc = {
203
- "triggered_id": tables.Int32Col(),
204
- "array_altitudes": tables.Float32Col(),
205
- "array_azimuths": tables.Float32Col(),
206
- "telescope_list_index": tables.Int32Col(), # Index into VLArray
207
- }
208
- file_names_desc = {
209
- "file_names": tables.StringCol(256),
210
- }
211
- return shower_data_desc, triggered_data_desc, file_names_desc
212
-
213
- def _tables(self, output_file, data_group, mode="a"):
214
- """Create or get HDF5 tables."""
215
- descriptions = self._table_descriptions()
216
- table_names = ["reduced_data", "triggered_data", "file_names"]
217
-
218
- table_dict = {}
219
- for name, desc in zip(table_names, descriptions):
220
- path = f"/data/{name}"
221
- table_dict[name] = (
222
- output_file.create_table(
223
- data_group, name, desc, name.replace("_", " ").title(), filters=DEFAULT_FILTERS
224
- )
225
- if mode == "w" or path not in output_file
226
- else output_file.get_node(path)
227
- )
228
-
229
- return table_dict["reduced_data"], table_dict["triggered_data"], table_dict["file_names"]
230
-
231
- def _write_event_data(self, reduced_table):
232
- """Fill event data tables."""
233
- if len(self.event_data.simulated_energy) == 0:
234
- return
235
- row = reduced_table.row
236
- for i, energy in enumerate(self.event_data.simulated_energy):
237
- row["shower_id"] = (
238
- self.event_data.shower_id[i] if i < len(self.event_data.shower_id) else 0
239
- )
240
- row["simulated_energy"] = energy
241
- row["x_core"] = self.event_data.x_core[i] if i < len(self.event_data.x_core) else 0
242
- row["y_core"] = self.event_data.y_core[i] if i < len(self.event_data.y_core) else 0
243
- row["area_weight"] = (
244
- self.event_data.area_weight[i] if i < len(self.event_data.area_weight) else 0
245
- )
246
- row["shower_azimuth"] = (
247
- self.event_data.shower_azimuth[i] if i < len(self.event_data.shower_azimuth) else 0
248
- )
249
- row["shower_altitude"] = (
250
- self.event_data.shower_altitude[i]
251
- if i < len(self.event_data.shower_altitude)
252
- else 0
253
- )
254
- row.append()
255
- reduced_table.flush()
256
-
257
- def _writer_triggered_data(self, triggered_table, vlarray):
258
- """Fill triggered event data tables."""
259
- # Get or create VLArray for telescope lists
260
- if len(self.triggered_data.triggered_id) == 0:
261
- return
262
- row = triggered_table.row
263
- start_idx = vlarray.nrows
264
- for i, triggered_id in enumerate(self.triggered_data.triggered_id):
265
- row["triggered_id"] = triggered_id
266
- row["array_altitudes"] = (
267
- self.triggered_data.array_altitudes[i]
268
- if i < len(self.triggered_data.array_altitudes)
269
- else 0
270
- )
271
- row["array_azimuths"] = (
272
- self.triggered_data.array_azimuths[i]
273
- if i < len(self.triggered_data.array_azimuths)
274
- else 0
275
- )
276
- row["telescope_list_index"] = start_idx + i # Index into the VLArray
277
- row.append()
278
- vlarray.append(
279
- self.triggered_data.trigger_telescope_list_list[i]
280
- if i < len(self.triggered_data.trigger_telescope_list_list)
281
- else []
282
- )
283
- triggered_table.flush()
284
-
285
- def _write_data(self, mode="a"):
286
- """Write data to HDF5 file."""
287
- with tables.open_file(self.output_file, mode=mode) as f:
288
- data_group = (
289
- f.create_group("/", "data", "Data group")
290
- if mode == "w" or "/data" not in f
291
- else f.get_node("/data")
292
- )
293
-
294
- reduced_table, triggered_table, file_names_table = self._tables(f, data_group, mode)
295
- self._write_event_data(reduced_table)
296
-
297
- vlarray = (
298
- f.create_vlarray(
299
- data_group,
300
- "trigger_telescope_list_list",
301
- tables.Int16Atom(),
302
- "List of triggered telescope IDs",
303
- )
304
- if mode == "w" or "/data/trigger_telescope_list_list" not in f
305
- else f.get_node("/data/trigger_telescope_list_list")
306
- )
307
- self._writer_triggered_data(triggered_table, vlarray)
308
-
309
- if self.file_names:
310
- file_names_table.append([[name] for name in self.file_names])
311
- file_names_table.flush()
312
-
313
- def _reset_data(self):
314
- """Reset data structures for batch processing."""
315
- self.event_data = ShowerEventData()
316
- self.triggered_data = TriggeredEventData()
317
- self.file_names = []
256
+ self.trigger_data.append(
257
+ {
258
+ "shower_id": self.shower_data[-1]["shower_id"],
259
+ "event_id": event_id,
260
+ "file_id": file_id,
261
+ "array_altitude": float(np.mean(altitudes)),
262
+ "array_azimuth": float(calculate_circular_mean(azimuths)),
263
+ "telescope_list": ",".join(map(str, telescopes)),
264
+ }
265
+ )
266
+
267
+ def _get_preliminary_nsb_level(self, file):
268
+ """
269
+ Return preliminary NSB level from file name.
270
+
271
+ Hardwired values are used for "dark", "half", and "full" NSB levels
272
+ (actual values are made up for this example). Will be replaced with
273
+ reading of sim_telarray metadata entry for NSB level (to be implemented,
274
+ see issue #1572).
275
+
276
+ Parameters
277
+ ----------
278
+ file : str
279
+ File name to extract NSB level from.
280
+
281
+ Returns
282
+ -------
283
+ float
284
+ NSB level extracted from file name.
285
+ """
286
+ nsb_levels = {"dark": 1.0, "half": 2.0, "full": 5.0}
287
+
288
+ for key, value in nsb_levels.items():
289
+ try:
290
+ if key in file.lower():
291
+ self._logger.warning(f"NSB level set to hardwired value of {value}")
292
+ return value
293
+ except AttributeError as exc:
294
+ raise AttributeError("Invalid file name.") from exc
295
+
296
+ self._logger.warning("No NSB level found in file name, defaulting to 1.0")
297
+ return 1.0
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/python3
2
+ """Read file info and run headers from sim_telarray files."""
3
+
4
+ from eventio import EventIOFile
5
+ from eventio.simtel import MCRunHeader, MCShower, RunHeader
6
+
7
+
8
+ def get_corsika_run_number(file):
9
+ """
10
+ Return the CORSIKA run number from a sim_telarray file.
11
+
12
+ Parameters
13
+ ----------
14
+ file: str
15
+ Path to the sim_telarray file.
16
+
17
+ Returns
18
+ -------
19
+ int, None
20
+ CORSIKA run number. Returns None if not found.
21
+ """
22
+ run_header = get_corsika_run_header(file)
23
+ return run_header.get("run") if run_header else None
24
+
25
+
26
+ def get_corsika_run_header(file):
27
+ """
28
+ Return the CORSIKA run header information from a sim_telarray file.
29
+
30
+ Reads both RunHeader and MCRunHeader object from file and
31
+ returns a merged dictionary. Adds primary id from the first event.
32
+
33
+ Parameters
34
+ ----------
35
+ file: str
36
+ Path to the sim_telarray file.
37
+
38
+ Returns
39
+ -------
40
+ dict, None
41
+ CORSIKA run header. Returns None if not found.
42
+ """
43
+ run_header = None
44
+ mc_run_header = None
45
+ primary_id = None
46
+
47
+ with EventIOFile(file) as f:
48
+ for o in f:
49
+ if isinstance(o, RunHeader) and run_header is None:
50
+ run_header = o.parse()
51
+ elif isinstance(o, MCRunHeader) and mc_run_header is None:
52
+ mc_run_header = o.parse()
53
+ elif isinstance(o, MCShower): # get primary_id from first MCShower
54
+ primary_id = o.parse().get("primary_id")
55
+ if run_header and mc_run_header and primary_id is not None:
56
+ break
57
+
58
+ run_header = run_header or {}
59
+ mc_run_header = mc_run_header or {}
60
+ if primary_id is not None:
61
+ mc_run_header["primary_id"] = primary_id
62
+ return run_header | mc_run_header or None
@@ -1,4 +1,4 @@
1
- """Reads the content of either a single histogram or simtel_array output file."""
1
+ """Reads the content of either a single histogram or sim_telarray output file."""
2
2
 
3
3
  import copy
4
4
  import logging
@@ -11,7 +11,8 @@ from ctao_cr_spectra.definitions import IRFDOC_PROTON_SPECTRUM
11
11
  from ctao_cr_spectra.spectral import cone_solid_angle
12
12
  from eventio import EventIOFile, Histograms
13
13
  from eventio.search_utils import yield_toplevel_of_type
14
- from eventio.simtel import MCRunHeader
14
+
15
+ from simtools.simtel.simtel_io_file_info import get_corsika_run_header
15
16
 
16
17
  __all__ = [
17
18
  "HistogramIdNotFoundError",
@@ -32,13 +33,13 @@ class SimtelIOHistogram:
32
33
  """
33
34
  Reads and generates histograms from sim_telarray output.
34
35
 
35
- Read the content of either a single histogram (.hdata, or .hdata.zst) or a single simtel_array
36
+ Read the content of either a single histogram (.hdata, or .hdata.zst) or a single sim_telarray
36
37
  output file (.simtel or .simtel.zst).
37
38
 
38
39
  Parameters
39
40
  ----------
40
41
  histogram_file: str
41
- The histogram (.hdata.zst) or simtel_array (.simtel.zst) file.
42
+ The histogram (.hdata.zst) or sim_telarray (.simtel.zst) file.
42
43
  area_from_distribution: bool
43
44
  If true, the area thrown (the area in which the simulated events are distributed)
44
45
  in the trigger rate calculation is estimated based on the event distribution.
@@ -135,14 +136,9 @@ class SimtelIOHistogram:
135
136
  Returns
136
137
  -------
137
138
  dict:
138
- dictionary with information about the simulation (pyeventio MCRunHeader object).
139
+ dictionary with information about the simulation
139
140
  """
140
- if self._config is None:
141
- with EventIOFile(self.histogram_file) as f:
142
- self._config = next(
143
- (obj.parse() for obj in f if isinstance(obj, MCRunHeader)), None
144
- )
145
- return self._config
141
+ return self._config if self._config else get_corsika_run_header(self.histogram_file)
146
142
 
147
143
  @property
148
144
  def total_number_of_events(self):
@@ -348,7 +344,7 @@ class SimtelIOHistogram:
348
344
  for the triggered events.
349
345
  """
350
346
  if self.trigger_rate is None:
351
- # Get the simulated and triggered 2D histograms from the simtel_array output file
347
+ # Get the simulated and triggered 2D histograms from the sim_telarray output file
352
348
  if events_histogram is None and triggered_events_histogram is None:
353
349
  events_histogram, triggered_events_histogram = self.fill_event_histogram_dicts()
354
350
  # Calculate triggered/simulated event 1D histogram (energy dependent)
@@ -408,7 +404,7 @@ class SimtelIOHistogram:
408
404
  """
409
405
  Produce the meta data to include in the tabulated form of the trigger rate per energy bin.
410
406
 
411
- It shows some information from the input file (simtel_array file) and the final estimate
407
+ It shows some information from the input file (sim_telarray file) and the final estimate
412
408
  system trigger rate.
413
409
 
414
410
  Returns
@@ -417,7 +413,7 @@ class SimtelIOHistogram:
417
413
  dictionary with the metadata.
418
414
  """
419
415
  return {
420
- "simtel_array_file": self.histogram_file,
416
+ "sim_telarray_file": self.histogram_file,
421
417
  "simulation_input": self.print_info(mode="silent"),
422
418
  "system_trigger_rate (Hz)": self.trigger_rate.value,
423
419
  }