gammasimtools 0.18.0__py3-none-any.whl → 0.20.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 (367) hide show
  1. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/METADATA +24 -69
  2. gammasimtools-0.20.0.dist-info/RECORD +395 -0
  3. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/entry_points.txt +11 -4
  4. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +16 -3
  6. simtools/applications/calculate_incident_angles.py +182 -0
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +4 -3
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
  9. simtools/applications/db_add_simulation_model_from_repository_to_db.py +17 -14
  10. simtools/applications/db_add_value_from_json_to_db.py +8 -10
  11. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
  12. simtools/applications/db_generate_compound_indexes.py +65 -0
  13. simtools/applications/db_get_file_from_db.py +12 -24
  14. simtools/applications/db_get_parameter_from_db.py +4 -4
  15. simtools/applications/db_inspect_databases.py +20 -10
  16. simtools/applications/derive_mirror_rnda.py +17 -11
  17. simtools/applications/derive_psf_parameters.py +59 -309
  18. simtools/applications/derive_trigger_rates.py +91 -0
  19. simtools/applications/docs_produce_array_element_report.py +1 -1
  20. simtools/applications/docs_produce_calibration_reports.py +1 -1
  21. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  22. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  23. simtools/applications/generate_corsika_histograms.py +1 -1
  24. simtools/applications/generate_default_metadata.py +8 -24
  25. simtools/applications/generate_simtel_event_data.py +11 -11
  26. simtools/applications/maintain_simulation_model_add_production_table.py +71 -0
  27. simtools/applications/maintain_simulation_model_compare_productions.py +98 -0
  28. simtools/applications/{verify_simulation_model_production_tables.py → maintain_simulation_model_verify_production_tables.py} +9 -1
  29. simtools/applications/merge_tables.py +2 -2
  30. simtools/applications/plot_array_layout.py +3 -3
  31. simtools/applications/plot_simtel_events.py +421 -0
  32. simtools/applications/plot_tabular_data.py +9 -2
  33. simtools/applications/plot_tabular_data_for_model_parameter.py +2 -1
  34. simtools/applications/print_version.py +8 -9
  35. simtools/applications/production_derive_corsika_limits.py +6 -7
  36. simtools/applications/production_derive_statistics.py +1 -1
  37. simtools/applications/production_generate_grid.py +2 -2
  38. simtools/applications/production_merge_corsika_limits.py +214 -0
  39. simtools/applications/run_application.py +47 -113
  40. simtools/applications/simulate_calibration_events.py +166 -0
  41. simtools/applications/simulate_flasher.py +141 -0
  42. simtools/applications/{simulate_light_emission.py → simulate_illuminator.py} +35 -99
  43. simtools/applications/simulate_prod.py +6 -24
  44. simtools/applications/simulate_prod_htcondor_generator.py +7 -0
  45. simtools/applications/submit_array_layouts.py +2 -1
  46. simtools/applications/submit_model_parameter_from_external.py +1 -1
  47. simtools/applications/validate_camera_efficiency.py +30 -12
  48. simtools/applications/validate_camera_fov.py +1 -1
  49. simtools/applications/validate_cumulative_psf.py +1 -1
  50. simtools/applications/validate_file_using_schema.py +9 -5
  51. simtools/applications/validate_optics.py +1 -1
  52. simtools/camera/camera_efficiency.py +61 -45
  53. simtools/camera/single_photon_electron_spectrum.py +1 -1
  54. simtools/configuration/commandline_parser.py +46 -11
  55. simtools/configuration/configurator.py +4 -4
  56. simtools/corsika/corsika_config.py +45 -25
  57. simtools/corsika/corsika_histograms.py +6 -5
  58. simtools/data_model/data_reader.py +2 -3
  59. simtools/data_model/metadata_collector.py +32 -36
  60. simtools/data_model/metadata_model.py +15 -12
  61. simtools/data_model/model_data_writer.py +13 -32
  62. simtools/data_model/schema.py +74 -24
  63. simtools/data_model/validate_data.py +42 -12
  64. simtools/db/db_handler.py +125 -62
  65. simtools/db/db_model_upload.py +14 -19
  66. simtools/dependencies.py +98 -30
  67. simtools/io/ascii_handler.py +279 -0
  68. simtools/{io_operations → io}/io_handler.py +25 -3
  69. simtools/job_execution/htcondor_script_generator.py +15 -4
  70. simtools/layout/array_layout.py +1 -1
  71. simtools/layout/array_layout_utils.py +51 -12
  72. simtools/model/array_model.py +41 -5
  73. simtools/model/flasher_model.py +106 -0
  74. simtools/model/model_parameter.py +4 -4
  75. simtools/model/model_repository.py +197 -2
  76. simtools/model/site_model.py +25 -0
  77. simtools/model/telescope_model.py +3 -1
  78. simtools/production_configuration/derive_corsika_limits.py +336 -427
  79. simtools/production_configuration/derive_production_statistics_handler.py +7 -6
  80. simtools/production_configuration/generate_production_grid.py +9 -11
  81. simtools/production_configuration/merge_corsika_limits.py +528 -0
  82. simtools/ray_tracing/incident_angles.py +706 -0
  83. simtools/ray_tracing/mirror_panel_psf.py +1 -0
  84. simtools/ray_tracing/psf_parameter_optimisation.py +792 -0
  85. simtools/ray_tracing/ray_tracing.py +6 -2
  86. simtools/reporting/docs_read_parameters.py +150 -62
  87. simtools/runners/corsika_runner.py +1 -1
  88. simtools/runners/corsika_simtel_runner.py +14 -5
  89. simtools/runners/runner_services.py +10 -5
  90. simtools/runners/simtools_runner.py +267 -0
  91. simtools/schemas/application_workflow.metaschema.yml +101 -68
  92. simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +1 -1
  93. simtools/schemas/input/single_pe_spectrum.schema.yml +1 -1
  94. simtools/schemas/metadata.metaschema.yml +577 -3
  95. simtools/schemas/model_parameter.metaschema.yml +6 -6
  96. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +4 -4
  97. simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
  98. simtools/schemas/model_parameters/altitude.schema.yml +1 -1
  99. simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
  100. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +1 -1
  101. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +1 -1
  102. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
  103. simtools/schemas/model_parameters/array_layouts.schema.yml +1 -1
  104. simtools/schemas/model_parameters/array_triggers.schema.yml +1 -1
  105. simtools/schemas/model_parameters/array_window.schema.yml +1 -1
  106. simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
  107. simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
  108. simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
  109. simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
  110. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
  111. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +1 -1
  112. simtools/schemas/model_parameters/axes_offsets.schema.yml +1 -1
  113. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
  114. simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
  115. simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
  116. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
  117. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
  118. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
  119. simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
  120. simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
  121. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +1 -1
  122. simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
  123. simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
  124. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  125. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  126. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +1 -1
  127. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +1 -1
  128. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +1 -1
  129. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
  130. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
  131. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
  132. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +1 -1
  133. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
  134. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +1 -1
  135. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +3 -3
  136. simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
  137. simtools/schemas/model_parameters/default_trigger.schema.yml +1 -1
  138. simtools/schemas/model_parameters/design_model.schema.yml +1 -1
  139. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +1 -1
  140. simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
  141. simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
  142. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
  143. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
  144. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
  145. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
  146. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
  147. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
  148. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +1 -1
  149. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
  150. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
  151. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
  152. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
  153. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
  154. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
  155. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
  156. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
  157. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
  158. simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
  159. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
  160. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
  161. simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
  162. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +1 -1
  163. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
  164. simtools/schemas/model_parameters/dsum_prescale.schema.yml +1 -1
  165. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
  166. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
  167. simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
  168. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +1 -1
  169. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -2
  170. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
  171. simtools/schemas/model_parameters/effective_focal_length.schema.yml +1 -1
  172. simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
  173. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +1 -1
  174. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
  175. simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
  176. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  177. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  178. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
  179. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
  180. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
  181. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  182. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  183. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
  184. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
  185. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
  186. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
  187. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
  188. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
  189. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
  190. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
  191. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
  192. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
  193. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +35 -0
  194. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +41 -0
  195. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +38 -0
  196. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
  197. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
  198. simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
  199. simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
  200. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
  201. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +1 -1
  202. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
  203. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
  204. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
  205. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
  206. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
  207. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
  208. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  209. simtools/schemas/model_parameters/flatfielding.schema.yml +1 -1
  210. simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
  211. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +1 -1
  212. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
  213. simtools/schemas/model_parameters/focus_offset.schema.yml +1 -1
  214. simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
  215. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
  216. simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
  217. simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
  218. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
  219. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  220. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  221. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  222. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
  223. simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
  224. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
  225. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
  226. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
  227. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
  228. simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
  229. simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
  230. simtools/schemas/model_parameters/led_events.schema.yml +1 -1
  231. simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
  232. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
  233. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
  234. simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
  235. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  236. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +50 -1
  237. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  238. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  239. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
  240. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +1 -1
  241. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +1 -1
  242. simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
  243. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
  244. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
  245. simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
  246. simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
  247. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +1 -1
  248. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +1 -1
  249. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +1 -1
  250. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
  251. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +1 -1
  252. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +1 -1
  253. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
  254. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +1 -1
  255. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
  256. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +2 -2
  257. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
  258. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
  259. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +1 -1
  260. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +23 -30
  261. simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
  262. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +1 -1
  263. simtools/schemas/model_parameters/optics_properties.schema.yml +1 -1
  264. simtools/schemas/model_parameters/parabolic_dish.schema.yml +1 -1
  265. simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
  266. simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
  267. simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
  268. simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
  269. simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
  270. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
  271. simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
  272. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
  273. simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
  274. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +1 -1
  275. simtools/schemas/model_parameters/pm_transit_time.schema.yml +1 -1
  276. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
  277. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
  278. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
  279. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
  280. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +11 -1
  281. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +1 -1
  282. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
  283. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
  284. simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
  285. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +1 -1
  286. simtools/schemas/model_parameters/random_focal_length.schema.yml +1 -1
  287. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  288. simtools/schemas/model_parameters/random_mono_probability.schema.yml +1 -1
  289. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
  290. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +1 -1
  291. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +1 -1
  292. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
  293. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
  294. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  295. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  296. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +1 -1
  297. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
  298. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  299. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
  300. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
  301. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +11 -1
  302. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +1 -1
  303. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
  304. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +11 -1
  305. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
  306. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
  307. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
  308. simtools/schemas/model_parameters/stars.schema.yml +2 -2
  309. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  310. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  311. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
  312. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
  313. simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
  314. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
  315. simtools/schemas/model_parameters/telescope_transmission.schema.yml +1 -1
  316. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
  317. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
  318. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
  319. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
  320. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
  321. simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
  322. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
  323. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
  324. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +1 -1
  325. simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
  326. simtools/schemas/plot_configuration.metaschema.yml +5 -2
  327. simtools/schemas/production_configuration_metrics.schema.yml +12 -2
  328. simtools/schemas/production_tables.schema.yml +7 -2
  329. simtools/simtel/simtel_config_reader.py +2 -2
  330. simtools/simtel/simtel_config_writer.py +33 -23
  331. simtools/simtel/simtel_io_event_histograms.py +483 -0
  332. simtools/simtel/simtel_io_event_reader.py +65 -43
  333. simtools/simtel/simtel_io_event_writer.py +40 -20
  334. simtools/simtel/simtel_io_metadata.py +1 -1
  335. simtools/simtel/simtel_table_reader.py +95 -13
  336. simtools/simtel/simulator_array.py +138 -10
  337. simtools/simtel/simulator_camera_efficiency.py +32 -23
  338. simtools/simtel/simulator_light_emission.py +437 -271
  339. simtools/simtel/simulator_ray_tracing.py +1 -1
  340. simtools/simulator.py +105 -147
  341. simtools/telescope_trigger_rates.py +119 -0
  342. simtools/testing/configuration.py +24 -26
  343. simtools/testing/helpers.py +2 -2
  344. simtools/testing/log_inspector.py +52 -0
  345. simtools/testing/validate_output.py +87 -37
  346. simtools/utils/general.py +125 -255
  347. simtools/utils/geometry.py +56 -0
  348. simtools/utils/names.py +1 -1
  349. simtools/visualization/legend_handlers.py +180 -264
  350. simtools/visualization/plot_array_layout.py +20 -8
  351. simtools/visualization/plot_incident_angles.py +431 -0
  352. simtools/visualization/plot_pixels.py +1 -1
  353. simtools/visualization/plot_simtel_event_histograms.py +376 -0
  354. simtools/visualization/plot_simtel_events.py +816 -0
  355. simtools/visualization/plot_tables.py +133 -37
  356. simtools/visualization/visualize.py +1 -100
  357. gammasimtools-0.18.0.dist-info/RECORD +0 -376
  358. simtools/applications/calculate_trigger_rate.py +0 -187
  359. simtools/applications/generate_sim_telarray_histograms.py +0 -196
  360. simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
  361. simtools/simtel/simtel_io_histogram.py +0 -621
  362. simtools/simtel/simtel_io_histograms.py +0 -552
  363. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/WHEEL +0 -0
  364. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/top_level.txt +0 -0
  365. /simtools/{io_operations → io}/hdf5_handler.py +0 -0
  366. /simtools/{io_operations → io}/legacy_data_handler.py +0 -0
  367. /simtools/{io_operations/io_table_handler.py → io/table_handler.py} +0 -0
@@ -0,0 +1,816 @@
1
+ #!/usr/bin/python3
2
+ """Plots for light emission (flasher/calibration) sim_telarray events."""
3
+
4
+ import logging
5
+
6
+ import astropy.units as u
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ from ctapipe.calib import CameraCalibrator
10
+ from ctapipe.io import EventSource
11
+ from ctapipe.visualization import CameraDisplay
12
+ from scipy import signal as _signal
13
+
14
+ __all__ = [
15
+ "plot_simtel_event_image",
16
+ "plot_simtel_integrated_pedestal_image",
17
+ "plot_simtel_integrated_signal_image",
18
+ "plot_simtel_peak_timing",
19
+ "plot_simtel_step_traces",
20
+ "plot_simtel_time_traces",
21
+ "plot_simtel_waveform_matrix",
22
+ ]
23
+
24
+ _logger = logging.getLogger(__name__)
25
+
26
+ # Reusable literal constants (duplicated from visualize to avoid circular deps)
27
+ AXES_FRACTION = "axes fraction"
28
+ NO_R1_WAVEFORMS_MSG = "No R1 waveforms available in event"
29
+ TIME_NS_LABEL = "time [ns]"
30
+ R1_SAMPLES_LABEL = "R1 samples [d.c.]"
31
+
32
+
33
+ def _compute_integration_window(
34
+ peak_idx: int, n_samp: int, half_width: int, mode: str, offset: int | None
35
+ ) -> tuple[int, int]:
36
+ """Return [a, b) window bounds for integration for signal/pedestal modes."""
37
+ hw = int(half_width)
38
+ win_len = 2 * hw + 1
39
+ if mode == "signal":
40
+ a = max(0, peak_idx - hw)
41
+ b = min(n_samp, peak_idx + hw + 1)
42
+ return a, b
43
+
44
+ g = int(offset) if offset is not None else 16
45
+ start = peak_idx + g
46
+ if start + win_len <= n_samp:
47
+ return start, start + win_len
48
+ start = max(0, peak_idx - g - win_len)
49
+ a = start
50
+ b = min(n_samp, start + win_len)
51
+ if a >= b:
52
+ return 0, min(n_samp, win_len)
53
+ return a, b
54
+
55
+
56
+ def _format_integrated_title(
57
+ tel_label: str, et_name: str, half_width: int, mode: str, offset: int | None
58
+ ) -> str:
59
+ win_len = 2 * int(half_width) + 1
60
+ if mode == "signal":
61
+ return f"{tel_label} integrated signal (win {win_len}) ({et_name})"
62
+ g = int(offset) if offset is not None else 16
63
+ return f"{tel_label} integrated pedestal (win {win_len}, offset {g}) ({et_name})"
64
+
65
+
66
+ def _select_event_by_type(source):
67
+ """
68
+ Build an event selector for a ctapipe EventSource.
69
+
70
+ Parameters
71
+ ----------
72
+ source : ctapipe.io.EventSource
73
+ Iterable event source.
74
+
75
+ Returns
76
+ -------
77
+ callable
78
+ A function ``select_event(event_index: int | None) -> Any`` that returns
79
+ the first event (when ``event_index`` is None) or the event at the given
80
+ index. Returns ``None`` if no event is available or the index is out of range.
81
+ """
82
+
83
+ def select_event(event_index=None):
84
+ if event_index is None:
85
+ for ev in source:
86
+ return ev
87
+ else:
88
+ for idx, ev in enumerate(source):
89
+ if idx == event_index:
90
+ return ev
91
+ _logger.warning("No events available from source or event_index out of range")
92
+ return None
93
+
94
+ return select_event
95
+
96
+
97
+ def _time_axis_from_readout(readout, n_samp):
98
+ """
99
+ Compute time axis in nanoseconds from a camera readout.
100
+
101
+ Parameters
102
+ ----------
103
+ readout : Any
104
+ Camera readout providing ``sampling_rate`` as an astropy Quantity.
105
+ n_samp : int
106
+ Number of samples per trace.
107
+
108
+ Returns
109
+ -------
110
+ numpy.ndarray
111
+ Array of shape ``(n_samp,)`` with time in nanoseconds.
112
+ """
113
+ try:
114
+ dt = (1 / readout.sampling_rate).to(u.ns).value
115
+ except (AttributeError, TypeError):
116
+ dt = 1.0
117
+ if not np.isfinite(dt) or dt <= 0:
118
+ dt = 1.0
119
+ return np.arange(int(n_samp)) * float(dt)
120
+
121
+
122
+ def plot_simtel_event_image(filename, distance=None, event_index=None):
123
+ """
124
+ Read a sim_telarray file and plot the DL1 image for one event via ctapipe.
125
+
126
+ Parameters
127
+ ----------
128
+ filename : str | pathlib.Path
129
+ Path to the ``.simtel`` file.
130
+ distance : astropy.units.Quantity | float | None, optional
131
+ Distance to annotate in the plot. If a Quantity, interpreted in meters.
132
+ If not provided, no unit conversion is attempted.
133
+ event_index : int | None, optional
134
+ Zero-based index of the event to plot. If None, the first event is used.
135
+
136
+ Returns
137
+ -------
138
+ matplotlib.figure.Figure | None
139
+ The created figure, or ``None`` if no suitable event/image is available.
140
+ """
141
+ source = EventSource(filename, max_events=None)
142
+ event = _select_event_by_type(source)(event_index=event_index)
143
+ if not event:
144
+ _logger.warning("No event found in the file.")
145
+ return None
146
+
147
+ tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
148
+ if not tel_ids:
149
+ _logger.warning("First event has no R1 telescope data")
150
+ return None
151
+ tel_id = tel_ids[0]
152
+
153
+ calib = CameraCalibrator(subarray=source.subarray)
154
+ calib(event)
155
+
156
+ geometry = source.subarray.tel[tel_id].camera.geometry
157
+ image = event.dl1.tel[tel_id].image
158
+
159
+ fig, ax = plt.subplots(dpi=300)
160
+ tel = source.subarray.tel[tel_id]
161
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
162
+ title = f"{tel_label}, run {event.index.obs_id} event {event.index.event_id}"
163
+ disp = CameraDisplay(geometry, image=image, norm="symlog", ax=ax)
164
+ disp.cmap = "RdBu_r"
165
+ disp.add_colorbar(fraction=0.02, pad=-0.1)
166
+ disp.set_limits_percent(100)
167
+ ax.set_title(title, pad=20)
168
+
169
+ try:
170
+ d_str = f"{distance.to(u.m)}"
171
+ except (AttributeError, TypeError, ValueError):
172
+ d_str = str(distance)
173
+
174
+ ax.annotate(
175
+ f"tel type: {source.subarray.tel[tel_id].type.name}\n"
176
+ f"optics: {source.subarray.tel[tel_id].optics.name}\n"
177
+ f"camera: {source.subarray.tel[tel_id].camera_name}\n"
178
+ f"distance: {d_str}",
179
+ xy=(0, 0),
180
+ xytext=(0.1, 1),
181
+ xycoords=AXES_FRACTION,
182
+ va="top",
183
+ size=7,
184
+ )
185
+ ax.annotate(
186
+ f"dl1 image,\ntotal ADC counts: {np.round(np.sum(image))}\n",
187
+ xy=(0, 0),
188
+ xytext=(0.75, 1),
189
+ xycoords=AXES_FRACTION,
190
+ va="top",
191
+ ha="left",
192
+ size=7,
193
+ )
194
+ ax.set_axis_off()
195
+ fig.tight_layout()
196
+ return fig
197
+
198
+
199
+ def plot_simtel_time_traces(
200
+ filename,
201
+ tel_id: int | None = None,
202
+ n_pixels: int = 3,
203
+ event_index: int | None = None,
204
+ ):
205
+ """
206
+ Plot R1 time traces for a few pixels of one event.
207
+
208
+ Parameters
209
+ ----------
210
+ filename : str | pathlib.Path
211
+ Path to the ``.simtel`` file.
212
+ tel_id : int | None, optional
213
+ Telescope ID to use. If None, the first telescope with R1 data is chosen.
214
+ n_pixels : int, optional
215
+ Number of pixels with highest signal to plot. Default is 3.
216
+ event_index : int | None, optional
217
+ Zero-based index of the event to plot. If None, the first event is used.
218
+
219
+ Returns
220
+ -------
221
+ matplotlib.figure.Figure | None
222
+ The created figure, or ``None`` if R1 waveforms are unavailable.
223
+ """
224
+ source = EventSource(filename, max_events=None)
225
+ event = _select_event_by_type(source)(event_index=event_index)
226
+
227
+ r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
228
+ if r1_tel_ids:
229
+ tel_id = tel_id or r1_tel_ids[0]
230
+ else:
231
+ dl1_tel_ids = sorted(getattr(event.dl1, "tel", {}).keys())
232
+ tel_id = tel_id or dl1_tel_ids[0]
233
+
234
+ calib = CameraCalibrator(subarray=source.subarray)
235
+ try:
236
+ calib(event)
237
+ image = event.dl1.tel[tel_id].image
238
+ except (RuntimeError, ValueError, KeyError, AttributeError):
239
+ image = None
240
+
241
+ waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
242
+ if waveforms is None:
243
+ _logger.warning(NO_R1_WAVEFORMS_MSG)
244
+ return None
245
+
246
+ w = np.asarray(waveforms)
247
+ if w.ndim == 3:
248
+ w = w[0]
249
+ _, n_samp = w.shape
250
+
251
+ if image is not None:
252
+ pix_ids = np.argsort(image)[-n_pixels:][::-1]
253
+ else:
254
+ integrals = w.sum(axis=1)
255
+ pix_ids = np.argsort(integrals)[-n_pixels:][::-1]
256
+
257
+ readout = source.subarray.tel[tel_id].camera.readout
258
+ t = _time_axis_from_readout(readout, n_samp)
259
+
260
+ fig, ax = plt.subplots(dpi=300)
261
+ for pid in pix_ids:
262
+ ax.plot(t, w[pid], label=f"pix {int(pid)}", drawstyle="steps-mid")
263
+ ax.set_xlabel(TIME_NS_LABEL)
264
+ ax.set_ylabel(R1_SAMPLES_LABEL)
265
+ et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
266
+ tel = source.subarray.tel[tel_id]
267
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
268
+ ax.set_title(f"{tel_label} waveforms ({et_name})")
269
+ ax.legend(loc="best", fontsize=7)
270
+ fig.tight_layout()
271
+ return fig
272
+
273
+
274
+ def plot_simtel_waveform_matrix(
275
+ filename,
276
+ tel_id: int | None = None,
277
+ vmax: float | None = None,
278
+ event_index: int | None = None,
279
+ pixel_step: int | None = None,
280
+ ):
281
+ """
282
+ Create a pseudocolor image of R1 waveforms (sample index vs. pixel id).
283
+
284
+ Parameters
285
+ ----------
286
+ filename : str | pathlib.Path
287
+ Path to the ``.simtel`` file.
288
+ tel_id : int | None, optional
289
+ Telescope ID to use. If None, the first telescope with R1 data is chosen.
290
+ vmax : float | None, optional
291
+ Upper limit for color normalization. If None, determined automatically.
292
+ event_index : int | None, optional
293
+ Zero-based index of the event to plot. If None, the first event is used.
294
+ pixel_step : int | None, optional
295
+ Step between plotted pixel ids (e.g., 1 plots all, 2 plots every second pixel).
296
+
297
+ Returns
298
+ -------
299
+ matplotlib.figure.Figure | None
300
+ The created figure, or ``None`` if R1 waveforms are unavailable.
301
+ """
302
+ source = EventSource(filename, max_events=None)
303
+ event = _select_event_by_type(source)(event_index=event_index)
304
+
305
+ r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
306
+ if r1_tel_ids:
307
+ tel_id = tel_id or r1_tel_ids[0]
308
+ else:
309
+ _logger.warning("Event has no R1 data for waveform plot")
310
+ return None
311
+
312
+ waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
313
+ if waveforms is None:
314
+ _logger.warning(NO_R1_WAVEFORMS_MSG)
315
+ return None
316
+
317
+ w = np.asarray(waveforms)
318
+ if w.ndim == 3:
319
+ w = w[0]
320
+ n_pix, n_samp = w.shape
321
+
322
+ step = max(1, int(pixel_step)) if pixel_step is not None else 1
323
+ pix_idx = np.arange(n_pix)[::step]
324
+ w_sel = w[pix_idx]
325
+
326
+ readout = source.subarray.tel[tel_id].camera.readout
327
+ t = _time_axis_from_readout(readout, n_samp)
328
+
329
+ fig, ax = plt.subplots(dpi=300)
330
+ mesh = ax.pcolormesh(t, pix_idx, w_sel, shading="auto", vmax=vmax)
331
+ cbar = fig.colorbar(mesh, ax=ax)
332
+ cbar.set_label(R1_SAMPLES_LABEL)
333
+ et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
334
+ tel = source.subarray.tel[tel_id]
335
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
336
+ ax.set_title(f"{tel_label} waveform matrix ({et_name})")
337
+ ax.set_xlabel(TIME_NS_LABEL)
338
+ ax.set_ylabel("pixel id")
339
+ fig.tight_layout()
340
+ return fig
341
+
342
+
343
+ def plot_simtel_step_traces(
344
+ filename,
345
+ tel_id: int | None = None,
346
+ pixel_step: int = 100,
347
+ max_pixels: int | None = None,
348
+ event_index: int | None = None,
349
+ ):
350
+ """
351
+ Plot step-style R1 traces for regularly sampled pixels (0, N, 2N, ...).
352
+
353
+ Parameters
354
+ ----------
355
+ filename : str | pathlib.Path
356
+ Path to the ``.simtel`` file.
357
+ tel_id : int | None, optional
358
+ Telescope ID to use. If None, the first telescope with R1 data is chosen.
359
+ pixel_step : int, optional
360
+ Interval between pixel indices to plot. Default is 100.
361
+ max_pixels : int | None, optional
362
+ Maximum number of pixels to plot. If None, plot all selected by ``pixel_step``.
363
+ event_index : int | None, optional
364
+ Zero-based index of the event to plot. If None, the first event is used.
365
+
366
+ Returns
367
+ -------
368
+ matplotlib.figure.Figure | None
369
+ The created figure, or ``None`` if R1 waveforms are unavailable.
370
+ """
371
+ source = EventSource(filename, max_events=None)
372
+ event = _select_event_by_type(source)(event_index=event_index)
373
+
374
+ r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
375
+ if r1_tel_ids:
376
+ tel_id = tel_id or r1_tel_ids[0]
377
+ else:
378
+ _logger.warning("Event has no R1 data for traces plot")
379
+ return None
380
+
381
+ waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
382
+ if waveforms is None:
383
+ _logger.warning(NO_R1_WAVEFORMS_MSG)
384
+ return None
385
+
386
+ w = np.asarray(waveforms)
387
+ if w.ndim == 3:
388
+ w = w[0]
389
+ n_pix, n_samp = w.shape
390
+
391
+ readout = source.subarray.tel[tel_id].camera.readout
392
+ t = _time_axis_from_readout(readout, n_samp)
393
+
394
+ pix_ids = np.arange(0, n_pix, max(1, pixel_step))
395
+ if max_pixels is not None:
396
+ pix_ids = pix_ids[:max_pixels]
397
+
398
+ fig, ax = plt.subplots(dpi=300)
399
+ for pid in pix_ids:
400
+ ax.plot(t, w[int(pid)], label=f"pix {int(pid)}", drawstyle="steps-mid")
401
+ ax.set_xlabel(TIME_NS_LABEL)
402
+ ax.set_ylabel(R1_SAMPLES_LABEL)
403
+ et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
404
+ tel = source.subarray.tel[tel_id]
405
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
406
+ ax.set_title(f"{tel_label} step traces ({et_name})")
407
+ ax.legend(loc="best", fontsize=7, ncol=2)
408
+ fig.tight_layout()
409
+ return fig
410
+
411
+
412
+ def _detect_peaks(trace, peak_width, signal_mod):
413
+ """
414
+ Detect peak indices using CWT if available, otherwise ``find_peaks``.
415
+
416
+ Parameters
417
+ ----------
418
+ trace : numpy.ndarray
419
+ One-dimensional waveform samples for a single pixel.
420
+ peak_width : int
421
+ Characteristic peak width (samples) for CWT.
422
+ signal_mod : module
423
+ SciPy ``signal``-like module providing ``find_peaks_cwt``/``find_peaks``.
424
+
425
+ Returns
426
+ -------
427
+ numpy.ndarray
428
+ Array of integer indices of detected peaks (possibly empty).
429
+ """
430
+ peaks = []
431
+ try:
432
+ if hasattr(signal_mod, "find_peaks_cwt"):
433
+ peaks = signal_mod.find_peaks_cwt(trace, widths=np.array([peak_width]))
434
+ if not np.any(peaks):
435
+ peaks, _ = signal_mod.find_peaks(trace, prominence=np.max(trace) * 0.1)
436
+ except (ValueError, TypeError):
437
+ peaks = []
438
+ return np.asarray(peaks, dtype=int) if np.size(peaks) else np.array([], dtype=int)
439
+
440
+
441
+ def _collect_peak_samples(w, sum_threshold, peak_width, signal_mod):
442
+ """
443
+ Compute peak-sample indices per pixel from waveform matrix.
444
+
445
+ Parameters
446
+ ----------
447
+ w : numpy.ndarray
448
+ Waveform array of shape ``(n_pix, n_samples)`` (or ``(1, n_pix, n_samples)``).
449
+ sum_threshold : float
450
+ Minimum sum over samples for a pixel to be considered.
451
+ peak_width : int
452
+ Characteristic peak width (samples) for CWT.
453
+ signal_mod : module
454
+ SciPy ``signal``-like module providing peak finding routines.
455
+
456
+ Returns
457
+ -------
458
+ tuple[numpy.ndarray | None, numpy.ndarray | None, int]
459
+ ``(peak_samples, pix_ids, found_count)`` where ``peak_samples`` are the
460
+ selected peak indices per considered pixel, ``pix_ids`` are the pixel
461
+ indices that passed ``sum_threshold``, and ``found_count`` is the number
462
+ of pixels with at least one detected peak. Returns ``(None, None, 0)`` if
463
+ no pixels passed the threshold.
464
+ """
465
+ n_pix, _ = w.shape
466
+ sums = w.sum(axis=1)
467
+ has_signal = sums > float(sum_threshold)
468
+ pix_ids = np.arange(n_pix)[has_signal]
469
+ if pix_ids.size == 0:
470
+ return None, None, 0
471
+
472
+ peak_samples = []
473
+ found_count = 0
474
+ for pid in pix_ids:
475
+ trace = w[int(pid)]
476
+ pks = _detect_peaks(trace, peak_width, signal_mod)
477
+ if pks.size:
478
+ found_count += 1
479
+ peak_idx = int(pks[np.argmax(trace[pks])])
480
+ else:
481
+ peak_idx = int(np.argmax(trace))
482
+ peak_samples.append(peak_idx)
483
+
484
+ return np.asarray(peak_samples), pix_ids, found_count
485
+
486
+
487
+ def _histogram_edges(n_samp, timing_bins):
488
+ """
489
+ Compute contiguous histogram bin edges for sample indices.
490
+
491
+ Parameters
492
+ ----------
493
+ n_samp : int
494
+ Number of samples per trace.
495
+ timing_bins : int | None
496
+ Number of histogram bins. If None, use unit-width bins.
497
+
498
+ Returns
499
+ -------
500
+ numpy.ndarray
501
+ Array of bin edges spanning the sample index range.
502
+ """
503
+ if timing_bins and timing_bins > 0:
504
+ return np.linspace(-0.5, n_samp - 0.5, int(timing_bins) + 1)
505
+ return np.arange(-0.5, n_samp + 0.5, 1.0)
506
+
507
+
508
+ def _draw_peak_hist(
509
+ ax,
510
+ peak_samples,
511
+ edges,
512
+ mean_sample,
513
+ std_sample,
514
+ tel_label,
515
+ et_name,
516
+ considered,
517
+ found_count,
518
+ ):
519
+ """
520
+ Draw a histogram of peak samples with overlays and annotations.
521
+
522
+ Parameters
523
+ ----------
524
+ ax : matplotlib.axes.Axes
525
+ Target axes to draw into.
526
+
527
+ Peak sample indices per pixel.
528
+ edges : numpy.ndarray
529
+ Histogram bin edges.
530
+ mean_sample : float
531
+ Mean of peak sample indices.
532
+ std_sample : float
533
+ Standard deviation of peak sample indices.
534
+ tel_label : str
535
+ Telescope label used in the title.
536
+ et_name : str
537
+ Event type name used in the title.
538
+ considered : int
539
+ Number of pixels considered (passed threshold).
540
+ found_count : int
541
+ Number of pixels with at least one detected peak.
542
+
543
+ Returns
544
+ -------
545
+ None
546
+ """
547
+ counts, edges = np.histogram(peak_samples, bins=edges)
548
+ ax.bar(edges[:-1], counts, width=np.diff(edges), color="#5B90DC", align="edge")
549
+ ax.set_xlim(edges[0], edges[-1])
550
+ ax.set_xlabel("peak sample")
551
+ ax.set_ylabel("N pixels")
552
+ ax.axvline(
553
+ mean_sample,
554
+ color="#D8153C",
555
+ linestyle="--",
556
+ label=f"mean={mean_sample:.2f}",
557
+ )
558
+ ax.axvspan(
559
+ mean_sample - std_sample,
560
+ mean_sample + std_sample,
561
+ color="#D8153C",
562
+ alpha=0.2,
563
+ label=f"std={std_sample:.2f}",
564
+ )
565
+ ax.set_title(f"{tel_label} peak timing ({et_name})")
566
+ ax.text(
567
+ 0.98,
568
+ 0.95,
569
+ f"considered: {considered}\npeaks found: {found_count}",
570
+ transform=ax.transAxes,
571
+ ha="right",
572
+ va="top",
573
+ fontsize=7,
574
+ bbox={
575
+ "boxstyle": "round,pad=0.2",
576
+ "facecolor": "white",
577
+ "alpha": 0.6,
578
+ "linewidth": 0.0,
579
+ },
580
+ )
581
+ ax.legend(fontsize=7)
582
+
583
+
584
+ def plot_simtel_peak_timing(
585
+ filename,
586
+ tel_id: int | None = None,
587
+ sum_threshold: float = 10.0,
588
+ peak_width: int = 8,
589
+ examples: int = 3,
590
+ timing_bins: int | None = None,
591
+ return_stats: bool = False,
592
+ event_index: int | None = None,
593
+ ):
594
+ """
595
+ Peak finding per pixel; report mean/std of peak sample and plot a histogram.
596
+
597
+ Parameters
598
+ ----------
599
+ filename : str | pathlib.Path
600
+ Path to the ``.simtel`` file.
601
+ tel_id : int | None, optional
602
+ Telescope ID to use. If None, the first telescope with R1 data is chosen.
603
+ sum_threshold : float, optional
604
+ Minimum sum over samples for a pixel to be considered. Default is 10.0.
605
+ peak_width : int, optional
606
+ Characteristic peak width (samples) for CWT. Default is 8.
607
+ examples : int, optional
608
+ Number of example pixel traces to overlay. Default is 3.
609
+ timing_bins : int | None, optional
610
+ Number of histogram bins. If None, use unit-width bins.
611
+ return_stats : bool, optional
612
+ If True, also return a statistics dictionary. Default is False.
613
+ event_index : int | None, optional
614
+ Zero-based index of the event to plot. If None, the first event is used.
615
+
616
+ Returns
617
+ -------
618
+ matplotlib.figure.Figure | tuple[matplotlib.figure.Figure, dict] | None
619
+ The created figure, or ``None`` if R1 waveforms are unavailable. If
620
+ ``return_stats`` is True, a tuple ``(fig, stats)`` is returned, where
621
+ ``stats`` has keys ``{"considered", "found", "mean", "std"}``.
622
+ """
623
+ source = EventSource(filename, max_events=None)
624
+ event = _select_event_by_type(source)(event_index=event_index)
625
+
626
+ r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
627
+ if r1_tel_ids:
628
+ tel_id = tel_id or r1_tel_ids[0]
629
+ else:
630
+ _logger.warning("Event has no R1 data for peak timing plot")
631
+ return None
632
+
633
+ waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
634
+ if waveforms is None:
635
+ _logger.warning(NO_R1_WAVEFORMS_MSG)
636
+ return None
637
+
638
+ w = np.asarray(waveforms)
639
+ if w.ndim == 3:
640
+ w = w[0]
641
+ _, n_samp = w.shape
642
+
643
+ peak_samples, pix_ids, found_count = _collect_peak_samples(
644
+ w, sum_threshold, peak_width, _signal
645
+ )
646
+ if peak_samples is None or pix_ids is None:
647
+ _logger.warning("No pixels exceeded sum_threshold for peak timing")
648
+ return None
649
+
650
+ mean_sample = float(np.mean(peak_samples))
651
+ std_sample = float(np.std(peak_samples))
652
+
653
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), dpi=300)
654
+
655
+ edges = _histogram_edges(n_samp, timing_bins)
656
+ et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
657
+ tel = source.subarray.tel[tel_id]
658
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
659
+ _draw_peak_hist(
660
+ ax1,
661
+ peak_samples,
662
+ edges,
663
+ mean_sample,
664
+ std_sample,
665
+ tel_label,
666
+ et_name,
667
+ pix_ids.size,
668
+ found_count,
669
+ )
670
+
671
+ readout = source.subarray.tel[tel_id].camera.readout
672
+ t = _time_axis_from_readout(readout, n_samp)
673
+
674
+ ex_ids = pix_ids[: max(1, int(examples))]
675
+ for pid in ex_ids:
676
+ trace = w[int(pid)]
677
+ pks = _detect_peaks(trace, peak_width, _signal)
678
+ ax2.plot(t, trace, drawstyle="steps-mid", label=f"pix {int(pid)}")
679
+ if pks.size:
680
+ ax2.scatter(t[pks], trace[pks], s=10)
681
+ ax2.set_xlabel(TIME_NS_LABEL)
682
+ ax2.set_ylabel(R1_SAMPLES_LABEL)
683
+ ax2.legend(fontsize=7)
684
+
685
+ fig.tight_layout()
686
+
687
+ if return_stats:
688
+ stats = {
689
+ "considered": int(pix_ids.size),
690
+ "found": int(found_count),
691
+ "mean": float(mean_sample),
692
+ "std": float(std_sample),
693
+ }
694
+ return fig, stats
695
+ return fig
696
+
697
+
698
+ def _prepare_waveforms_for_image(filename, tel_id, context_no_r1, event_index=None):
699
+ """
700
+ Fetch R1 waveforms for one event/telescope and return prepared arrays.
701
+
702
+ Parameters
703
+ ----------
704
+ filename : str | pathlib.Path
705
+ Path to the ``.simtel`` file.
706
+ tel_id : int | None
707
+ Telescope ID to use. If None, the first telescope with R1 data is chosen.
708
+ context_no_r1 : str
709
+ Short description used in warnings when no R1 data is available.
710
+ event_index : int | None, optional
711
+ Zero-based index of the event to use. If None, the first event is used.
712
+
713
+ Returns
714
+ -------
715
+ tuple | None
716
+ ``(w, n_pix, n_samp, source, event, tel_id)`` where ``w`` is a
717
+ ``numpy.ndarray`` of shape ``(n_pix, n_samples)``, ``n_pix`` and
718
+ ``n_samp`` are integers, and ``source``, ``event`` and ``tel_id`` are
719
+ the ctapipe objects used. Returns ``None`` on failure.
720
+ """
721
+ source = EventSource(filename, max_events=None)
722
+ event = _select_event_by_type(source)(event_index=event_index)
723
+
724
+ r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
725
+ if r1_tel_ids:
726
+ tel_id = tel_id or r1_tel_ids[0]
727
+ else:
728
+ _logger.warning(f"Event has no R1 data for {context_no_r1}")
729
+ return None
730
+
731
+ waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
732
+ if waveforms is None:
733
+ _logger.warning(NO_R1_WAVEFORMS_MSG)
734
+ return None
735
+
736
+ w = np.asarray(waveforms)
737
+ if w.ndim == 3:
738
+ w = w[0]
739
+ n_pix, n_samp = w.shape
740
+ return w, n_pix, n_samp, source, event, tel_id
741
+
742
+
743
+ def plot_simtel_integrated_signal_image(
744
+ filename,
745
+ tel_id: int | None = None,
746
+ half_width: int = 8,
747
+ event_index: int | None = None,
748
+ ):
749
+ """Plot camera image of integrated signal per pixel around the flasher peak."""
750
+ return _plot_simtel_integrated_image(
751
+ filename=filename,
752
+ tel_id=tel_id,
753
+ half_width=half_width,
754
+ event_index=event_index,
755
+ mode="signal",
756
+ )
757
+
758
+
759
+ def plot_simtel_integrated_pedestal_image(
760
+ filename,
761
+ tel_id: int | None = None,
762
+ half_width: int = 8,
763
+ offset: int = 16,
764
+ event_index: int | None = None,
765
+ ):
766
+ """Plot camera image of integrated pedestal per pixel away from the flasher peak."""
767
+ return _plot_simtel_integrated_image(
768
+ filename=filename,
769
+ tel_id=tel_id,
770
+ half_width=half_width,
771
+ event_index=event_index,
772
+ mode="pedestal",
773
+ offset=offset,
774
+ )
775
+
776
+
777
+ def _plot_simtel_integrated_image(
778
+ filename,
779
+ tel_id: int | None,
780
+ half_width: int,
781
+ event_index: int | None,
782
+ mode: str,
783
+ offset: int | None = None,
784
+ ):
785
+ """Shared implementation for integrated signal/pedestal images.
786
+
787
+ mode: "signal" or "pedestal". For "pedestal", ``offset`` is used.
788
+ """
789
+ context = "integrated-signal image" if mode == "signal" else "integrated-pedestal image"
790
+ prepared = _prepare_waveforms_for_image(filename, tel_id, context, event_index=event_index)
791
+ if prepared is None:
792
+ return None
793
+
794
+ w, n_pix, n_samp, source, event, tel_id = prepared
795
+ img = np.zeros(n_pix, dtype=float)
796
+
797
+ for pid in range(n_pix):
798
+ trace = w[pid]
799
+ peak_idx = int(np.argmax(trace))
800
+ a, b = _compute_integration_window(peak_idx, n_samp, half_width, mode, offset)
801
+ img[pid] = float(np.sum(trace[a:b]))
802
+
803
+ geometry = source.subarray.tel[tel_id].camera.geometry
804
+ fig, ax = plt.subplots(dpi=300)
805
+ disp = CameraDisplay(geometry, image=img, norm="lin", ax=ax)
806
+ disp.cmap = "viridis" if mode == "signal" else "cividis"
807
+ disp.add_colorbar(fraction=0.02, pad=-0.1)
808
+ disp.set_limits_percent(100)
809
+
810
+ et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
811
+ tel = source.subarray.tel[tel_id]
812
+ tel_label = getattr(tel, "name", f"CT{tel_id}")
813
+ ax.set_title(_format_integrated_title(tel_label, et_name, half_width, mode, offset), pad=20)
814
+ ax.set_axis_off()
815
+ fig.tight_layout()
816
+ return fig