gammasimtools 0.18.0__py3-none-any.whl → 0.19.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 (358) hide show
  1. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/METADATA +26 -69
  2. gammasimtools-0.19.0.dist-info/RECORD +393 -0
  3. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/entry_points.txt +9 -2
  4. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +16 -3
  6. simtools/applications/calculate_trigger_rate.py +1 -1
  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_value_from_json_to_db.py +2 -1
  10. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
  11. simtools/applications/db_generate_compound_indexes.py +61 -0
  12. simtools/applications/db_get_file_from_db.py +1 -1
  13. simtools/applications/db_get_parameter_from_db.py +4 -4
  14. simtools/applications/db_inspect_databases.py +20 -10
  15. simtools/applications/derive_mirror_rnda.py +17 -11
  16. simtools/applications/derive_psf_parameters.py +59 -309
  17. simtools/applications/docs_produce_array_element_report.py +1 -1
  18. simtools/applications/docs_produce_calibration_reports.py +1 -1
  19. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  20. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  21. simtools/applications/generate_corsika_histograms.py +1 -1
  22. simtools/applications/generate_default_metadata.py +8 -24
  23. simtools/applications/generate_sim_telarray_histograms.py +1 -1
  24. simtools/applications/generate_simtel_event_data.py +11 -11
  25. simtools/applications/maintain_simulation_model_add_production_table.py +71 -0
  26. simtools/applications/maintain_simulation_model_compare_productions.py +98 -0
  27. simtools/applications/{verify_simulation_model_production_tables.py → maintain_simulation_model_verify_production_tables.py} +9 -1
  28. simtools/applications/merge_tables.py +2 -2
  29. simtools/applications/plot_array_layout.py +3 -3
  30. simtools/applications/plot_simtel_events.py +379 -0
  31. simtools/applications/plot_tabular_data.py +9 -2
  32. simtools/applications/plot_tabular_data_for_model_parameter.py +2 -1
  33. simtools/applications/print_version.py +8 -9
  34. simtools/applications/production_derive_corsika_limits.py +6 -7
  35. simtools/applications/production_derive_statistics.py +1 -1
  36. simtools/applications/production_generate_grid.py +2 -2
  37. simtools/applications/production_merge_corsika_limits.py +214 -0
  38. simtools/applications/run_application.py +47 -113
  39. simtools/applications/simulate_calibration_events.py +166 -0
  40. simtools/applications/simulate_flasher.py +141 -0
  41. simtools/applications/{simulate_light_emission.py → simulate_illuminator.py} +35 -99
  42. simtools/applications/simulate_prod.py +6 -24
  43. simtools/applications/simulate_prod_htcondor_generator.py +7 -0
  44. simtools/applications/submit_array_layouts.py +2 -1
  45. simtools/applications/submit_model_parameter_from_external.py +1 -1
  46. simtools/applications/validate_camera_efficiency.py +30 -12
  47. simtools/applications/validate_camera_fov.py +1 -1
  48. simtools/applications/validate_cumulative_psf.py +1 -1
  49. simtools/applications/validate_file_using_schema.py +2 -1
  50. simtools/applications/validate_optics.py +1 -1
  51. simtools/camera/camera_efficiency.py +61 -45
  52. simtools/camera/single_photon_electron_spectrum.py +1 -1
  53. simtools/configuration/commandline_parser.py +29 -0
  54. simtools/configuration/configurator.py +4 -4
  55. simtools/corsika/corsika_config.py +45 -25
  56. simtools/corsika/corsika_histograms.py +6 -5
  57. simtools/data_model/data_reader.py +2 -3
  58. simtools/data_model/metadata_collector.py +32 -36
  59. simtools/data_model/metadata_model.py +15 -12
  60. simtools/data_model/model_data_writer.py +13 -32
  61. simtools/data_model/schema.py +74 -24
  62. simtools/data_model/validate_data.py +34 -9
  63. simtools/db/db_handler.py +43 -37
  64. simtools/db/db_model_upload.py +3 -3
  65. simtools/dependencies.py +88 -25
  66. simtools/io/ascii_handler.py +279 -0
  67. simtools/{io_operations → io}/io_handler.py +25 -3
  68. simtools/job_execution/htcondor_script_generator.py +15 -4
  69. simtools/layout/array_layout.py +1 -1
  70. simtools/layout/array_layout_utils.py +14 -7
  71. simtools/model/array_model.py +23 -4
  72. simtools/model/flasher_model.py +106 -0
  73. simtools/model/model_parameter.py +4 -4
  74. simtools/model/model_repository.py +197 -2
  75. simtools/model/telescope_model.py +3 -1
  76. simtools/production_configuration/derive_corsika_limits.py +361 -427
  77. simtools/production_configuration/derive_production_statistics_handler.py +7 -6
  78. simtools/production_configuration/generate_production_grid.py +9 -11
  79. simtools/production_configuration/merge_corsika_limits.py +528 -0
  80. simtools/ray_tracing/mirror_panel_psf.py +1 -0
  81. simtools/ray_tracing/psf_parameter_optimisation.py +792 -0
  82. simtools/ray_tracing/ray_tracing.py +6 -2
  83. simtools/reporting/docs_read_parameters.py +150 -62
  84. simtools/runners/corsika_runner.py +1 -1
  85. simtools/runners/corsika_simtel_runner.py +14 -5
  86. simtools/runners/runner_services.py +10 -5
  87. simtools/runners/simtools_runner.py +267 -0
  88. simtools/schemas/application_workflow.metaschema.yml +101 -68
  89. simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +1 -1
  90. simtools/schemas/input/single_pe_spectrum.schema.yml +1 -1
  91. simtools/schemas/metadata.metaschema.yml +577 -3
  92. simtools/schemas/model_parameter.metaschema.yml +6 -6
  93. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +2 -2
  94. simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
  95. simtools/schemas/model_parameters/altitude.schema.yml +1 -1
  96. simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
  97. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +1 -1
  98. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +1 -1
  99. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
  100. simtools/schemas/model_parameters/array_layouts.schema.yml +1 -1
  101. simtools/schemas/model_parameters/array_triggers.schema.yml +1 -1
  102. simtools/schemas/model_parameters/array_window.schema.yml +1 -1
  103. simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
  104. simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
  105. simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
  106. simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
  107. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
  108. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +1 -1
  109. simtools/schemas/model_parameters/axes_offsets.schema.yml +1 -1
  110. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
  111. simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
  112. simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
  113. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
  114. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
  115. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
  116. simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
  117. simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
  118. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +1 -1
  119. simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
  120. simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
  121. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  122. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  123. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +1 -1
  124. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +1 -1
  125. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +1 -1
  126. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
  127. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
  128. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
  129. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +1 -1
  130. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
  131. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +1 -1
  132. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +3 -3
  133. simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
  134. simtools/schemas/model_parameters/default_trigger.schema.yml +1 -1
  135. simtools/schemas/model_parameters/design_model.schema.yml +1 -1
  136. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +1 -1
  137. simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
  138. simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
  139. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
  140. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
  141. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
  142. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
  143. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
  144. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
  145. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +1 -1
  146. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
  147. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
  148. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
  149. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
  150. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
  151. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
  152. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
  153. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
  154. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
  155. simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
  156. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
  157. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
  158. simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
  159. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +1 -1
  160. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
  161. simtools/schemas/model_parameters/dsum_prescale.schema.yml +1 -1
  162. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
  163. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
  164. simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
  165. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +1 -1
  166. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -2
  167. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
  168. simtools/schemas/model_parameters/effective_focal_length.schema.yml +1 -1
  169. simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
  170. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +1 -1
  171. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
  172. simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
  173. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  174. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  175. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
  176. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
  177. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
  178. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  179. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  180. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
  181. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
  182. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
  183. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
  184. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
  185. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
  186. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
  187. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
  188. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
  189. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
  190. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +35 -0
  191. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +41 -0
  192. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +38 -0
  193. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
  194. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
  195. simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
  196. simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
  197. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
  198. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +1 -1
  199. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
  200. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
  201. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
  202. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
  203. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
  204. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
  205. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  206. simtools/schemas/model_parameters/flatfielding.schema.yml +1 -1
  207. simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
  208. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +1 -1
  209. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
  210. simtools/schemas/model_parameters/focus_offset.schema.yml +1 -1
  211. simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
  212. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
  213. simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
  214. simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
  215. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
  216. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  217. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  218. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  219. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
  220. simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
  221. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
  222. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
  223. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
  224. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
  225. simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
  226. simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
  227. simtools/schemas/model_parameters/led_events.schema.yml +1 -1
  228. simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
  229. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
  230. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
  231. simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
  232. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  233. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +50 -1
  234. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  235. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  236. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
  237. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +1 -1
  238. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +1 -1
  239. simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
  240. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
  241. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
  242. simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
  243. simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
  244. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +1 -1
  245. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +1 -1
  246. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +1 -1
  247. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
  248. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +1 -1
  249. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +1 -1
  250. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
  251. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +1 -1
  252. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
  253. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +1 -1
  254. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
  255. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
  256. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +1 -1
  257. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +1 -1
  258. simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
  259. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +1 -1
  260. simtools/schemas/model_parameters/optics_properties.schema.yml +1 -1
  261. simtools/schemas/model_parameters/parabolic_dish.schema.yml +1 -1
  262. simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
  263. simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
  264. simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
  265. simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
  266. simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
  267. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
  268. simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
  269. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
  270. simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
  271. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +1 -1
  272. simtools/schemas/model_parameters/pm_transit_time.schema.yml +1 -1
  273. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
  274. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
  275. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
  276. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
  277. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +11 -1
  278. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +1 -1
  279. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
  280. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
  281. simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
  282. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +1 -1
  283. simtools/schemas/model_parameters/random_focal_length.schema.yml +1 -1
  284. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  285. simtools/schemas/model_parameters/random_mono_probability.schema.yml +1 -1
  286. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
  287. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +1 -1
  288. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +1 -1
  289. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
  290. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
  291. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  292. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  293. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +1 -1
  294. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
  295. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  296. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
  297. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
  298. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +11 -1
  299. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +1 -1
  300. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
  301. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +11 -1
  302. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
  303. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
  304. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
  305. simtools/schemas/model_parameters/stars.schema.yml +1 -1
  306. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  307. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  308. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
  309. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
  310. simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
  311. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
  312. simtools/schemas/model_parameters/telescope_transmission.schema.yml +1 -1
  313. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
  314. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
  315. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
  316. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
  317. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
  318. simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
  319. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
  320. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
  321. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +1 -1
  322. simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
  323. simtools/schemas/plot_configuration.metaschema.yml +5 -2
  324. simtools/schemas/production_configuration_metrics.schema.yml +12 -2
  325. simtools/schemas/production_tables.schema.yml +2 -2
  326. simtools/simtel/simtel_config_reader.py +2 -2
  327. simtools/simtel/simtel_config_writer.py +16 -4
  328. simtools/simtel/simtel_io_event_histograms.py +746 -0
  329. simtools/simtel/simtel_io_event_reader.py +15 -42
  330. simtools/simtel/simtel_io_event_writer.py +9 -9
  331. simtools/simtel/simtel_io_histogram.py +3 -1
  332. simtools/simtel/simtel_io_histograms.py +7 -3
  333. simtools/simtel/simtel_table_reader.py +92 -10
  334. simtools/simtel/simulator_array.py +138 -10
  335. simtools/simtel/simulator_camera_efficiency.py +32 -23
  336. simtools/simtel/simulator_light_emission.py +437 -271
  337. simtools/simtel/simulator_ray_tracing.py +1 -1
  338. simtools/simulator.py +105 -147
  339. simtools/testing/configuration.py +24 -26
  340. simtools/testing/helpers.py +2 -2
  341. simtools/testing/log_inspector.py +50 -0
  342. simtools/testing/validate_output.py +87 -37
  343. simtools/utils/general.py +125 -255
  344. simtools/utils/geometry.py +36 -0
  345. simtools/utils/names.py +1 -1
  346. simtools/visualization/legend_handlers.py +180 -264
  347. simtools/visualization/plot_array_layout.py +20 -8
  348. simtools/visualization/plot_pixels.py +1 -1
  349. simtools/visualization/plot_tables.py +133 -37
  350. simtools/visualization/simtel_event_plots.py +816 -0
  351. simtools/visualization/visualize.py +4 -101
  352. gammasimtools-0.18.0.dist-info/RECORD +0 -376
  353. simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
  354. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/WHEEL +0 -0
  355. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/top_level.txt +0 -0
  356. /simtools/{io_operations → io}/hdf5_handler.py +0 -0
  357. /simtools/{io_operations → io}/legacy_data_handler.py +0 -0
  358. /simtools/{io_operations/io_table_handler.py → io/table_handler.py} +0 -0
@@ -0,0 +1,746 @@
1
+ """Histograms for shower and triggered events."""
2
+
3
+ import logging
4
+
5
+ import astropy.units as u
6
+ import matplotlib.pyplot as plt
7
+ import numpy as np
8
+ from matplotlib.colors import LogNorm
9
+
10
+ from simtools.simtel.simtel_io_event_reader import SimtelIOEventDataReader
11
+
12
+
13
+ class SimtelIOEventHistograms:
14
+ """
15
+ Generate and fill histograms for shower and triggered events.
16
+
17
+ Event data is read from the reduced MC event data file.
18
+
19
+ Parameters
20
+ ----------
21
+ event_data_file : str
22
+ Path to the event-data file.
23
+ array_name : str, optional
24
+ Name of the telescope array configuration (default is None).
25
+ telescope_list : list, optional
26
+ List of telescope IDs to filter the events (default is None).
27
+ """
28
+
29
+ def __init__(self, event_data_file, array_name=None, telescope_list=None):
30
+ """Initialize."""
31
+ self._logger = logging.getLogger(__name__)
32
+ self.event_data_file = event_data_file
33
+ self.array_name = array_name
34
+ self.telescope_list = telescope_list
35
+
36
+ self.histograms = {}
37
+ self.file_info = {}
38
+
39
+ self.reader = SimtelIOEventDataReader(event_data_file, telescope_list=telescope_list)
40
+
41
+ def _fill_histogram_and_bin_edges(self, name, data, bins, hist1d=True):
42
+ """
43
+ Fill histogram and bin edges and it both to histogram dictionary.
44
+
45
+ Adds histogram to existing histogram if it exists, otherwise initializes it.
46
+
47
+ """
48
+ if name in self.histograms:
49
+ if hist1d:
50
+ bins = self.histograms[f"{name}_bin_edges"]
51
+ hist, _ = np.histogram(data, bins=bins)
52
+ self.histograms[name] += hist
53
+ else:
54
+ x_bins = self.histograms[f"{name}_bin_x_edges"]
55
+ y_bins = self.histograms[f"{name}_bin_y_edges"]
56
+ hist, _, _ = np.histogram2d(data[0], data[1], bins=[x_bins, y_bins])
57
+ self.histograms[name] += hist
58
+ else:
59
+ if hist1d:
60
+ hist, bin_edges = np.histogram(data, bins=bins)
61
+ self.histograms[name] = hist
62
+ self.histograms[f"{name}_bin_edges"] = bin_edges
63
+ else:
64
+ hist, x_edges, y_edges = np.histogram2d(data[0], data[1], bins=bins)
65
+ self.histograms[name] = hist
66
+ self.histograms[f"{name}_bin_x_edges"] = x_edges
67
+ self.histograms[f"{name}_bin_y_edges"] = y_edges
68
+
69
+ def fill(self):
70
+ """
71
+ Fill histograms with event data.
72
+
73
+ Involves looping over all event data, and therefore is the slowest part of the
74
+ limit calculation. Adds the histograms to the histogram dictionary.
75
+
76
+ Assume that all event data files are generated with similar configurations
77
+ (self.file_info contains the latest file info).
78
+ """
79
+ for data_set in self.reader.data_sets:
80
+ self._logger.info(f"Reading event data from {self.event_data_file} for {data_set}")
81
+ _file_info_table, _, event_data, triggered_data = self.reader.read_event_data(
82
+ self.event_data_file, table_name_map=data_set
83
+ )
84
+ _file_info_table = self.reader.get_reduced_simulation_file_info(_file_info_table)
85
+ self.file_info = {
86
+ "energy_min": _file_info_table["energy_min"].to("TeV"),
87
+ "core_scatter_max": _file_info_table["core_scatter_max"].to("m"),
88
+ "viewcone_max": _file_info_table["viewcone_max"].to("deg"),
89
+ }
90
+
91
+ self._fill_histogram_and_bin_edges(
92
+ "energy", event_data.simulated_energy, self.energy_bins
93
+ )
94
+ self._fill_histogram_and_bin_edges(
95
+ "core_distance", event_data.core_distance_shower, self.core_distance_bins
96
+ )
97
+ self._fill_histogram_and_bin_edges(
98
+ "angular_distance", triggered_data.angular_distance, self.view_cone_bins
99
+ )
100
+
101
+ xy_bins = np.linspace(
102
+ -1.0 * self.core_distance_bins.max(),
103
+ self.core_distance_bins.max(),
104
+ len(self.core_distance_bins),
105
+ )
106
+ self._fill_histogram_and_bin_edges(
107
+ "shower_cores",
108
+ (event_data.x_core_shower, event_data.y_core_shower),
109
+ [xy_bins, xy_bins],
110
+ hist1d=False,
111
+ )
112
+ self._fill_histogram_and_bin_edges(
113
+ "core_vs_energy",
114
+ (event_data.core_distance_shower, event_data.simulated_energy),
115
+ [self.core_distance_bins, self.energy_bins],
116
+ hist1d=False,
117
+ )
118
+ self._fill_histogram_and_bin_edges(
119
+ "angular_distance_vs_energy",
120
+ (triggered_data.angular_distance, event_data.simulated_energy),
121
+ [self.view_cone_bins, self.energy_bins],
122
+ hist1d=False,
123
+ )
124
+
125
+ @property
126
+ def energy_bins(self):
127
+ """Return bins for the energy histogram."""
128
+ if "energy_bin_edges" in self.histograms:
129
+ return self.histograms["energy_bin_edges"]
130
+ return np.logspace(
131
+ np.log10(self.file_info.get("energy_min", 1.0e-3 * u.TeV).to("TeV").value),
132
+ np.log10(self.file_info.get("energy_max", 1.0e3 * u.TeV).to("TeV").value),
133
+ 100,
134
+ )
135
+
136
+ @property
137
+ def core_distance_bins(self):
138
+ """Return bins for the core distance histogram."""
139
+ if "core_distance_bin_edges" in self.histograms:
140
+ return self.histograms["core_distance_bin_edges"]
141
+ return np.linspace(
142
+ self.file_info.get("core_scatter_min", 0.0 * u.m).to("m").value,
143
+ self.file_info.get("core_scatter_max", 1.0e5 * u.m).to("m").value,
144
+ 100,
145
+ )
146
+
147
+ @property
148
+ def view_cone_bins(self):
149
+ """Return bins for the viewcone histogram."""
150
+ if "viewcone_bin_edges" in self.histograms:
151
+ return self.histograms["viewcone_bin_edges"]
152
+ return np.linspace(
153
+ self.file_info.get("viewcone_min", 0.0 * u.deg).to("deg").value,
154
+ self.file_info.get("viewcone_max", 20.0 * u.deg).to("deg").value,
155
+ 100,
156
+ )
157
+
158
+ def plot_data(self, output_path=None, limits=None, rebin_factor=2):
159
+ """
160
+ Histogram plotting.
161
+
162
+ Parameters
163
+ ----------
164
+ output_path: Path or str, optional
165
+ Directory to save plots. If None, plots will be displayed.
166
+ limits: dict, optional
167
+ Dictionary containing limits for plotting. Keys can include:
168
+ - "upper_radius_limit": Upper limit for core distance
169
+ - "lower_energy_limit": Lower limit for energy
170
+ - "viewcone_radius": Radius for the viewcone
171
+ rebin_factor: int, optional
172
+ Factor by which to reduce the number of bins in 2D histograms for rebinned plots.
173
+ Default is 2 (merge every 2 bins). Set to 0 or 1 to disable rebinning.
174
+ """
175
+ # Plot label constants
176
+ core_distance_label = "Core Distance [m]"
177
+ energy_label = "Energy [TeV]"
178
+ pointing_direction_label = "Distance to pointing direction [deg]"
179
+ cumulative_prefix = "Cumulative "
180
+ event_count_label = "Event Count"
181
+ core_x_label = "Core X [m]"
182
+ core_y_label = "Core Y [m]"
183
+
184
+ # Plot parameter constants
185
+ hist_1d_params = {"color": "tab:green", "edgecolor": "tab:green", "lw": 1}
186
+ hist_1d_cumulative_params = {"color": "tab:blue", "edgecolor": "tab:blue", "lw": 1}
187
+ hist_2d_params = {"norm": "log", "cmap": "viridis", "show_contour": False}
188
+ hist_2d_equal_params = {
189
+ "norm": "log",
190
+ "cmap": "viridis",
191
+ "aspect": "equal",
192
+ "show_contour": False,
193
+ }
194
+ hist_2d_normalized_params = {"norm": "linear", "cmap": "viridis", "show_contour": True}
195
+
196
+ self._logger.info(f"Plotting histograms written to {output_path}")
197
+
198
+ angular_dist_vs_energy = self.histograms.get("angular_distance_vs_energy")
199
+ normalized_cumulative_angular_vs_energy = self._calculate_cumulative_histogram(
200
+ angular_dist_vs_energy, axis=0, normalize=True
201
+ )
202
+
203
+ core_vs_energy = self.histograms.get("core_vs_energy")
204
+ normalized_cumulative_core_vs_energy = self._calculate_cumulative_histogram(
205
+ core_vs_energy, axis=0, normalize=True
206
+ )
207
+
208
+ energy_hist = self.histograms.get("energy")
209
+ cumulative_energy = self._calculate_cumulative_histogram(energy_hist, reverse=True)
210
+
211
+ core_distance_hist = self.histograms.get("core_distance")
212
+ cumulative_core_distance = self._calculate_cumulative_histogram(core_distance_hist)
213
+
214
+ angular_distance_hist = self.histograms.get("angular_distance")
215
+ cumulative_angular_distance = self._calculate_cumulative_histogram(angular_distance_hist)
216
+
217
+ upper_radius_limit, lower_energy_limit, viewcone_radius = self._get_limits(limits)
218
+
219
+ plots = {
220
+ "core_vs_energy": {
221
+ "data": self.histograms.get("core_vs_energy"),
222
+ "bins": [
223
+ self.histograms.get("core_vs_energy_bin_x_edges"),
224
+ self.histograms.get("core_vs_energy_bin_y_edges"),
225
+ ],
226
+ "plot_type": "histogram2d",
227
+ "plot_params": hist_2d_params,
228
+ "labels": {
229
+ "x": core_distance_label,
230
+ "y": energy_label,
231
+ "title": "Triggered events: core distance vs energy",
232
+ },
233
+ "lines": {"x": upper_radius_limit, "y": lower_energy_limit},
234
+ "scales": {"y": "log"},
235
+ "colorbar_label": event_count_label,
236
+ "filename": "core_vs_energy_distribution",
237
+ },
238
+ "energy_distribution": {
239
+ "data": self.histograms.get("energy"),
240
+ "bins": self.histograms.get("energy_bin_edges"),
241
+ "plot_type": "histogram",
242
+ "plot_params": hist_1d_params,
243
+ "labels": {
244
+ "x": energy_label,
245
+ "y": event_count_label,
246
+ "title": "Triggered events: energy distribution",
247
+ },
248
+ "scales": {"x": "log", "y": "log"},
249
+ "lines": {"x": lower_energy_limit},
250
+ "filename": "energy_distribution",
251
+ },
252
+ "energy_distribution_cumulative": {
253
+ "data": cumulative_energy,
254
+ "bins": self.histograms.get("energy_bin_edges"),
255
+ "plot_type": "histogram",
256
+ "plot_params": hist_1d_cumulative_params,
257
+ "labels": {
258
+ "x": energy_label,
259
+ "y": cumulative_prefix + event_count_label,
260
+ "title": "Triggered events: cumulative energy distribution",
261
+ },
262
+ "scales": {"x": "log", "y": "log"},
263
+ "lines": {"x": lower_energy_limit},
264
+ "filename": "energy_distribution_cumulative",
265
+ },
266
+ "core_distance": {
267
+ "data": self.histograms.get("core_distance"),
268
+ "bins": self.histograms.get("core_distance_bin_edges"),
269
+ "plot_type": "histogram",
270
+ "plot_params": hist_1d_params,
271
+ "labels": {
272
+ "x": core_distance_label,
273
+ "y": event_count_label,
274
+ "title": "Triggered events: core distance distribution",
275
+ },
276
+ "lines": {"x": upper_radius_limit},
277
+ "filename": "core_distance_distribution",
278
+ },
279
+ "core_distance_cumulative": {
280
+ "data": cumulative_core_distance,
281
+ "bins": self.histograms.get("core_distance_bin_edges"),
282
+ "plot_type": "histogram",
283
+ "plot_params": hist_1d_cumulative_params,
284
+ "labels": {
285
+ "x": core_distance_label,
286
+ "y": cumulative_prefix + event_count_label,
287
+ "title": "Triggered events: cumulative core distance distribution",
288
+ },
289
+ "lines": {"x": upper_radius_limit},
290
+ "filename": "core_distance_cumulative_distribution",
291
+ },
292
+ "core_xy": {
293
+ "data": self.histograms.get("shower_cores"),
294
+ "bins": [
295
+ self.histograms.get("shower_cores_bin_x_edges"),
296
+ self.histograms.get("shower_cores_bin_y_edges"),
297
+ ],
298
+ "plot_type": "histogram2d",
299
+ "plot_params": hist_2d_equal_params,
300
+ "labels": {
301
+ "x": core_x_label,
302
+ "y": core_y_label,
303
+ "title": "Triggered events: core x vs core y",
304
+ },
305
+ "colorbar_label": event_count_label,
306
+ "lines": {
307
+ "r": upper_radius_limit,
308
+ },
309
+ "filename": "core_xy_distribution",
310
+ },
311
+ "angular_distance": {
312
+ "data": self.histograms.get("angular_distance"),
313
+ "bins": self.histograms.get("angular_distance_bin_edges"),
314
+ "plot_type": "histogram",
315
+ "plot_params": hist_1d_params,
316
+ "labels": {
317
+ "x": pointing_direction_label,
318
+ "y": event_count_label,
319
+ "title": "Triggered events: angular distance distribution",
320
+ },
321
+ "lines": {"x": viewcone_radius},
322
+ "filename": "angular_distance_distribution",
323
+ },
324
+ "angular_distance_cumulative": {
325
+ "data": cumulative_angular_distance,
326
+ "bins": self.histograms.get("angular_distance_bin_edges"),
327
+ "plot_type": "histogram",
328
+ "plot_params": hist_1d_cumulative_params,
329
+ "labels": {
330
+ "x": pointing_direction_label,
331
+ "y": cumulative_prefix + event_count_label,
332
+ "title": "Triggered events: cumulative angular distance distribution",
333
+ },
334
+ "lines": {"x": viewcone_radius},
335
+ "filename": "angular_distance_cumulative_distribution",
336
+ },
337
+ "angular_distance_vs_energy": {
338
+ "data": self.histograms.get("angular_distance_vs_energy"),
339
+ "bins": [
340
+ self.histograms.get("angular_distance_vs_energy_bin_x_edges"),
341
+ self.histograms.get("angular_distance_vs_energy_bin_y_edges"),
342
+ ],
343
+ "plot_type": "histogram2d",
344
+ "plot_params": hist_2d_params,
345
+ "labels": {
346
+ "x": pointing_direction_label,
347
+ "y": energy_label,
348
+ "title": "Triggered events: angular distance distance vs energy",
349
+ },
350
+ "lines": {
351
+ "x": viewcone_radius,
352
+ "y": lower_energy_limit,
353
+ },
354
+ "scales": {"y": "log"},
355
+ "colorbar_label": event_count_label,
356
+ "filename": "angular_distance_vs_energy_distribution",
357
+ },
358
+ "angular_distance_vs_energy_cumulative": {
359
+ "data": normalized_cumulative_angular_vs_energy,
360
+ "bins": [
361
+ self.histograms.get("angular_distance_vs_energy_bin_x_edges"),
362
+ self.histograms.get("angular_distance_vs_energy_bin_y_edges"),
363
+ ],
364
+ "plot_type": "histogram2d",
365
+ "plot_params": hist_2d_normalized_params, # Includes contour line at value=1
366
+ "labels": {
367
+ "x": pointing_direction_label,
368
+ "y": energy_label,
369
+ "title": "Triggered events: fraction of events by angular distance vs energy",
370
+ },
371
+ "lines": {
372
+ "x": viewcone_radius,
373
+ "y": lower_energy_limit,
374
+ },
375
+ "scales": {"y": "log"},
376
+ "colorbar_label": "Fraction of events",
377
+ "filename": "angular_distance_vs_energy_cumulative_distribution",
378
+ },
379
+ "core_vs_energy_cumulative": {
380
+ "data": normalized_cumulative_core_vs_energy,
381
+ "bins": [
382
+ self.histograms.get("core_vs_energy_bin_x_edges"),
383
+ self.histograms.get("core_vs_energy_bin_y_edges"),
384
+ ],
385
+ "plot_type": "histogram2d",
386
+ "plot_params": hist_2d_normalized_params,
387
+ "labels": {
388
+ "x": core_distance_label,
389
+ "y": energy_label,
390
+ "title": "Triggered events: fraction of events by core distance vs energy",
391
+ },
392
+ "lines": {
393
+ "x": upper_radius_limit,
394
+ "y": lower_energy_limit,
395
+ },
396
+ "scales": {"y": "log"},
397
+ "colorbar_label": "Fraction of events",
398
+ "filename": "core_vs_energy_cumulative_distribution",
399
+ },
400
+ }
401
+
402
+ for plot_key, plot_args in plots.items():
403
+ plot_filename = plot_args.pop("filename")
404
+ if self.array_name and plot_args.get("labels", {}).get("title"):
405
+ plot_args["labels"]["title"] += f" ({self.array_name} array)"
406
+
407
+ filename = self._build_plot_filename(plot_filename, self.array_name)
408
+ output_file = output_path / filename if output_path else None
409
+ self._create_plot(**plot_args, output_file=output_file)
410
+
411
+ if self._should_create_rebinned_plot(rebin_factor, plot_args, plot_key):
412
+ self._create_rebinned_plot(plot_args, filename, output_path, rebin_factor)
413
+
414
+ def _get_limits(self, limits):
415
+ """Extract limits from the provided dictionary for plotting."""
416
+ upper_radius_limit = None
417
+ lower_energy_limit = None
418
+ viewcone_radius = None
419
+ if limits:
420
+ upper_radius_limit = (
421
+ limits["upper_radius_limit"].value if "upper_radius_limit" in limits else None
422
+ )
423
+ lower_energy_limit = (
424
+ limits["lower_energy_limit"].value if "lower_energy_limit" in limits else None
425
+ )
426
+ viewcone_radius = (
427
+ limits["viewcone_radius"].value if "viewcone_radius" in limits else None
428
+ )
429
+ return upper_radius_limit, lower_energy_limit, viewcone_radius
430
+
431
+ def _build_plot_filename(self, base_filename, array_name=None):
432
+ """
433
+ Build the full plot filename with appropriate extensions.
434
+
435
+ Parameters
436
+ ----------
437
+ base_filename : str
438
+ The base filename without extension
439
+ array_name : str, optional
440
+ Name of the array to append to filename
441
+
442
+ Returns
443
+ -------
444
+ str
445
+ Complete filename with extension
446
+ """
447
+ if array_name:
448
+ return f"{base_filename}_{array_name}.png"
449
+ return f"{base_filename}.png"
450
+
451
+ def _should_create_rebinned_plot(self, rebin_factor, plot_args, plot_key):
452
+ """
453
+ Check if a rebinned version of the plot should be created.
454
+
455
+ Parameters
456
+ ----------
457
+ rebin_factor : int
458
+ Factor by which to rebin the energy axis
459
+ plot_args : dict
460
+ Plot arguments
461
+ plot_key : str
462
+ Key identifying the plot type
463
+
464
+ Returns
465
+ -------
466
+ bool
467
+ True if a rebinned plot should be created, False otherwise
468
+ """
469
+ return (
470
+ rebin_factor > 1
471
+ and plot_args["plot_type"] == "histogram2d"
472
+ and plot_key.endswith("_cumulative")
473
+ and plot_args.get("plot_params", {}).get("norm") == "linear"
474
+ )
475
+
476
+ def _create_rebinned_plot(self, plot_args, filename, output_path, rebin_factor):
477
+ """
478
+ Create a rebinned version of a 2D histogram plot.
479
+
480
+ Parameters
481
+ ----------
482
+ plot_args : dict
483
+ Plot arguments for the original plot
484
+ filename : str
485
+ Filename of the original plot
486
+ output_path : Path or None
487
+ Path to save the plot to, or None
488
+ rebin_factor : int
489
+ Factor by which to rebin the energy axis
490
+ """
491
+ data = plot_args["data"]
492
+ bins = plot_args["bins"]
493
+
494
+ rebinned_data, rebinned_x_bins, rebinned_y_bins = self._rebin_2d_histogram(
495
+ data, bins[0], bins[1], rebin_factor
496
+ )
497
+
498
+ rebinned_plot_args = plot_args.copy()
499
+ rebinned_plot_args["data"] = rebinned_data
500
+ rebinned_plot_args["bins"] = [rebinned_x_bins, rebinned_y_bins]
501
+
502
+ if rebinned_plot_args.get("labels", {}).get("title"):
503
+ rebinned_plot_args["labels"]["title"] += f" (Energy rebinned {rebin_factor}x)"
504
+
505
+ rebinned_filename = f"{filename.replace('.png', '')}_rebinned.png"
506
+ rebinned_output_file = output_path / rebinned_filename if output_path else None
507
+ self._create_plot(**rebinned_plot_args, output_file=rebinned_output_file)
508
+
509
+ def _create_plot(
510
+ self,
511
+ data,
512
+ bins=None,
513
+ plot_type="histogram",
514
+ plot_params=None,
515
+ labels=None,
516
+ scales=None,
517
+ colorbar_label=None,
518
+ output_file=None,
519
+ lines=None,
520
+ ):
521
+ """
522
+ Create and save a plot with the given parameters.
523
+
524
+ For normalized 2D histograms, a contour line is drawn at the value of 1.0
525
+ to indicate the boundary where each energy bin reaches complete containment.
526
+ This can be controlled with the 'show_contour' parameter in plot_params.
527
+ """
528
+ plot_params = plot_params or {}
529
+ labels = labels or {}
530
+ scales = scales or {}
531
+ lines = lines or {}
532
+
533
+ fig, ax = plt.subplots(figsize=(8, 6))
534
+
535
+ if plot_type == "histogram":
536
+ plt.bar(bins[:-1], data, width=np.diff(bins), **plot_params)
537
+ elif plot_type == "histogram2d":
538
+ pcm = self._create_2d_histogram_plot(data, bins, plot_params)
539
+ plt.colorbar(pcm, label=colorbar_label)
540
+
541
+ if "x" in lines:
542
+ plt.axvline(lines["x"], color="r", linestyle="--", linewidth=0.5)
543
+ if "y" in lines:
544
+ plt.axhline(lines["y"], color="r", linestyle="--", linewidth=0.5)
545
+ if "r" in lines:
546
+ circle = plt.Circle(
547
+ (0, 0), lines["r"], color="r", fill=False, linestyle="--", linewidth=0.5
548
+ )
549
+ plt.gca().add_artist(circle)
550
+
551
+ ax.set(
552
+ xlabel=labels.get("x", ""),
553
+ ylabel=labels.get("y", ""),
554
+ title=labels.get("title", ""),
555
+ xscale=scales.get("x", "linear"),
556
+ yscale=scales.get("y", "linear"),
557
+ )
558
+
559
+ if output_file:
560
+ self._logger.info(f"Saving plot to {output_file}")
561
+ plt.savefig(output_file, dpi=300, bbox_inches="tight")
562
+ plt.close()
563
+ else:
564
+ plt.tight_layout()
565
+ plt.show()
566
+
567
+ return fig
568
+
569
+ def _create_2d_histogram_plot(self, data, bins, plot_params):
570
+ """
571
+ Create a 2D histogram plot with the given parameters.
572
+
573
+ Parameters
574
+ ----------
575
+ data : np.ndarray
576
+ 2D histogram data
577
+ bins : tuple of np.ndarray
578
+ Bin edges for x and y axes
579
+ plot_params : dict
580
+ Plot parameters including norm, cmap, and show_contour
581
+
582
+ Returns
583
+ -------
584
+ matplotlib.collections.QuadMesh
585
+ The created pcolormesh object for colorbar attachment
586
+ """
587
+ if plot_params.get("norm") == "linear":
588
+ pcm = plt.pcolormesh(
589
+ bins[0],
590
+ bins[1],
591
+ data.T,
592
+ vmin=0,
593
+ vmax=1,
594
+ cmap=plot_params.get("cmap", "viridis"),
595
+ )
596
+ # Add contour line at value=1.0 for normalized histograms
597
+ if plot_params.get("show_contour", True):
598
+ x_centers = (bins[0][1:] + bins[0][:-1]) / 2
599
+ y_centers = (bins[1][1:] + bins[1][:-1]) / 2
600
+ x_mesh, y_mesh = np.meshgrid(x_centers, y_centers)
601
+ plt.contour(
602
+ x_mesh,
603
+ y_mesh,
604
+ data.T,
605
+ levels=[0.999999], # very close to 1 for floating point precision
606
+ colors=["tab:red"],
607
+ linestyles=["--"],
608
+ linewidths=[0.5],
609
+ )
610
+ else:
611
+ pcm = plt.pcolormesh(
612
+ bins[0], bins[1], data.T, norm=LogNorm(vmin=1, vmax=data.max()), cmap="viridis"
613
+ )
614
+
615
+ return pcm
616
+
617
+ def _calculate_cumulative_histogram(self, hist, reverse=False, axis=None, normalize=False):
618
+ """
619
+ Calculate cumulative distribution of a histogram.
620
+
621
+ Works with both 1D and 2D histograms.
622
+
623
+ Parameters
624
+ ----------
625
+ hist : np.ndarray
626
+ Histogram (1D or 2D)
627
+ reverse : bool, optional
628
+ If True, sum from high to low values
629
+ axis : int, optional
630
+ For 2D histograms, axis along which to compute cumulative sum
631
+ None means default behavior: for 1D just cumsum, for 2D along rows
632
+ normalize : bool, optional
633
+ If True, normalize by the total sum for each slice along the specified axis
634
+ For 1D histograms, normalizes by the total sum
635
+
636
+ Returns
637
+ -------
638
+ np.ndarray
639
+ Histogram with cumulative counts, optionally normalized
640
+ """
641
+ if hist is None:
642
+ return None
643
+
644
+ if hist.ndim == 1:
645
+ result = self._calculate_cumulative_1d(hist, reverse)
646
+ if normalize and np.sum(hist) > 0:
647
+ result = result / np.sum(hist)
648
+ return result
649
+
650
+ if axis is None:
651
+ axis = 1
652
+
653
+ result = self._apply_cumsum_along_axis(hist.copy(), axis, reverse)
654
+
655
+ if normalize:
656
+ self._normalize_along_axis(result, hist, axis)
657
+
658
+ return result
659
+
660
+ def _normalize_along_axis(self, result, hist, axis):
661
+ """
662
+ Normalize cumulative histogram along the specified axis.
663
+
664
+ Parameters
665
+ ----------
666
+ result : np.ndarray
667
+ Cumulative histogram to normalize (modified in-place)
668
+ hist : np.ndarray
669
+ Original histogram (for calculating totals)
670
+ axis : int
671
+ Axis along which normalization should be applied
672
+ """
673
+ normalized = np.zeros_like(result, dtype=float)
674
+
675
+ if axis == 0:
676
+ for i in range(result.shape[1]):
677
+ col_total = np.sum(hist[:, i])
678
+ if col_total > 0:
679
+ normalized[:, i] = result[:, i] / col_total
680
+ else: # axis == 1
681
+ for i in range(result.shape[0]):
682
+ row_total = np.sum(hist[i, :])
683
+ if row_total > 0:
684
+ normalized[i, :] = result[i, :] / row_total
685
+
686
+ np.copyto(result, normalized)
687
+
688
+ def _calculate_cumulative_1d(self, hist, reverse):
689
+ """Calculate cumulative distribution for 1D histogram."""
690
+ if reverse:
691
+ return np.cumsum(hist[::-1])[::-1]
692
+ return np.cumsum(hist)
693
+
694
+ def _calculate_cumulative_2d(self, hist, reverse, axis=None):
695
+ """Calculate cumulative distribution for 2D histogram."""
696
+ if axis is None:
697
+ axis = 1
698
+
699
+ return self._apply_cumsum_along_axis(hist, axis, reverse)
700
+
701
+ def _apply_cumsum_along_axis(self, hist, axis, reverse):
702
+ """Apply cumulative sum along the specified axis of a 2D histogram."""
703
+
704
+ def cumsum_func(arr):
705
+ return np.cumsum(arr[::-1])[::-1] if reverse else np.cumsum(arr)
706
+
707
+ return np.apply_along_axis(cumsum_func, axis, hist)
708
+
709
+ def _rebin_2d_histogram(self, hist, x_bins, y_bins, rebin_factor=2):
710
+ """
711
+ Rebin a 2D histogram by merging neighboring bins along the energy dimension (y-axis) only.
712
+
713
+ Parameters
714
+ ----------
715
+ hist : np.ndarray
716
+ Original 2D histogram data
717
+ x_bins : np.ndarray
718
+ Original x-axis bin edges (preserved)
719
+ y_bins : np.ndarray
720
+ Original y-axis (energy) bin edges
721
+ rebin_factor : int, optional
722
+ Factor by which to reduce the number of bins in the energy dimension
723
+ Default is 2 (merge every 2 bins)
724
+
725
+ Returns
726
+ -------
727
+ tuple
728
+ (rebinned_hist, x_bins, rebinned_y_bins)
729
+ """
730
+ if rebin_factor <= 1:
731
+ return hist, x_bins, y_bins
732
+
733
+ x_size = hist.shape[0]
734
+ new_y_size = hist.shape[1] // rebin_factor
735
+
736
+ new_hist = np.zeros((x_size, new_y_size), dtype=float)
737
+
738
+ for i in range(x_size):
739
+ for j in range(new_y_size):
740
+ y_start = j * rebin_factor
741
+ y_end = (j + 1) * rebin_factor
742
+ new_hist[i, j] = np.sum(hist[i, y_start:y_end])
743
+
744
+ new_y_bins = y_bins[::rebin_factor]
745
+
746
+ return new_hist, x_bins, new_y_bins