gammasimtools 0.20.0__py3-none-any.whl → 0.22.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 (315) hide show
  1. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/METADATA +2 -3
  2. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/RECORD +313 -296
  3. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/entry_points.txt +3 -2
  4. simtools/_version.py +2 -2
  5. simtools/applications/calculate_incident_angles.py +1 -4
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +1 -2
  7. simtools/applications/convert_model_parameter_from_simtel.py +0 -1
  8. simtools/applications/db_generate_compound_indexes.py +4 -17
  9. simtools/applications/db_upload_model_repository.py +122 -0
  10. simtools/applications/derive_psf_parameters.py +71 -42
  11. simtools/applications/docs_produce_array_element_report.py +1 -1
  12. simtools/applications/docs_produce_calibration_reports.py +1 -1
  13. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  14. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  15. simtools/applications/generate_corsika_histograms.py +8 -185
  16. simtools/applications/maintain_simulation_model_add_production.py +81 -0
  17. simtools/applications/merge_tables.py +1 -1
  18. simtools/applications/plot_array_layout.py +1 -2
  19. simtools/applications/plot_simtel_events.py +2 -228
  20. simtools/applications/print_version.py +8 -7
  21. simtools/applications/production_derive_statistics.py +1 -2
  22. simtools/applications/production_generate_grid.py +1 -1
  23. simtools/applications/simulate_flasher.py +74 -72
  24. simtools/applications/simulate_illuminator.py +52 -186
  25. simtools/applications/{simulate_calibration_events.py → simulate_pedestals.py} +9 -55
  26. simtools/applications/submit_model_parameter_from_external.py +0 -1
  27. simtools/applications/validate_camera_efficiency.py +0 -1
  28. simtools/applications/validate_camera_fov.py +1 -2
  29. simtools/applications/validate_cumulative_psf.py +2 -3
  30. simtools/applications/validate_file_using_schema.py +20 -12
  31. simtools/applications/validate_optics.py +2 -2
  32. simtools/camera/camera_efficiency.py +8 -11
  33. simtools/configuration/commandline_parser.py +1 -7
  34. simtools/configuration/configurator.py +0 -2
  35. simtools/corsika/corsika_config.py +9 -11
  36. simtools/corsika/corsika_histograms.py +82 -1
  37. simtools/data_model/model_data_writer.py +87 -25
  38. simtools/data_model/schema.py +61 -2
  39. simtools/data_model/validate_data.py +1 -1
  40. simtools/db/db_handler.py +103 -48
  41. simtools/db/db_model_upload.py +247 -16
  42. simtools/io/io_handler.py +31 -83
  43. simtools/job_execution/job_manager.py +45 -0
  44. simtools/layout/array_layout_utils.py +1 -5
  45. simtools/model/array_model.py +93 -42
  46. simtools/model/model_parameter.py +20 -9
  47. simtools/model/model_repository.py +197 -109
  48. simtools/model/model_utils.py +21 -6
  49. simtools/model/telescope_model.py +20 -0
  50. simtools/production_configuration/derive_corsika_limits.py +1 -1
  51. simtools/ray_tracing/incident_angles.py +7 -7
  52. simtools/ray_tracing/mirror_panel_psf.py +1 -1
  53. simtools/ray_tracing/psf_parameter_optimisation.py +1106 -565
  54. simtools/ray_tracing/ray_tracing.py +1 -3
  55. simtools/reporting/docs_read_parameters.py +171 -101
  56. simtools/resources/array_elements.yml +26 -0
  57. simtools/runners/corsika_simtel_runner.py +11 -17
  58. simtools/runners/runner_services.py +5 -6
  59. simtools/runners/simtools_runner.py +0 -2
  60. simtools/schemas/application_workflow.metaschema.yml +1 -1
  61. simtools/schemas/common_definitions.schema.yml +39 -0
  62. simtools/schemas/model_parameter.metaschema.yml +19 -13
  63. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +6 -12
  64. simtools/schemas/model_parameters/adjust_gain.schema.yml +0 -5
  65. simtools/schemas/model_parameters/altitude.schema.yml +0 -5
  66. simtools/schemas/model_parameters/array_coordinates.schema.yml +0 -5
  67. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +0 -5
  68. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +0 -7
  69. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +0 -7
  70. simtools/schemas/model_parameters/array_layouts.schema.yml +0 -5
  71. simtools/schemas/model_parameters/array_triggers.schema.yml +0 -5
  72. simtools/schemas/model_parameters/array_window.schema.yml +0 -7
  73. simtools/schemas/model_parameters/asum_clipping.schema.yml +0 -3
  74. simtools/schemas/model_parameters/asum_offset.schema.yml +0 -7
  75. simtools/schemas/model_parameters/asum_shaping.schema.yml +0 -7
  76. simtools/schemas/model_parameters/asum_threshold.schema.yml +0 -7
  77. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +0 -5
  78. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +0 -5
  79. simtools/schemas/model_parameters/axes_offsets.schema.yml +0 -7
  80. simtools/schemas/model_parameters/calibration_devices.schema.yml +30 -0
  81. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +0 -7
  82. simtools/schemas/model_parameters/camera_body_shape.schema.yml +0 -7
  83. simtools/schemas/model_parameters/camera_config_file.schema.yml +0 -7
  84. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +0 -7
  85. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +0 -7
  86. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +0 -7
  87. simtools/schemas/model_parameters/camera_depth.schema.yml +0 -7
  88. simtools/schemas/model_parameters/camera_filter.schema.yml +0 -7
  89. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +0 -3
  90. simtools/schemas/model_parameters/camera_pixels.schema.yml +0 -7
  91. simtools/schemas/model_parameters/camera_transmission.schema.yml +0 -7
  92. simtools/schemas/model_parameters/channels_per_chip.schema.yml +0 -7
  93. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +0 -7
  94. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +0 -5
  95. simtools/schemas/model_parameters/dark_events.schema.yml +4 -3
  96. simtools/schemas/model_parameters/default_trigger.schema.yml +0 -7
  97. simtools/schemas/model_parameters/design_model.schema.yml +0 -7
  98. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +0 -7
  99. simtools/schemas/model_parameters/disc_bins.schema.yml +0 -7
  100. simtools/schemas/model_parameters/disc_start.schema.yml +0 -7
  101. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +0 -7
  102. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +0 -7
  103. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +0 -7
  104. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +0 -7
  105. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +0 -7
  106. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +0 -7
  107. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +0 -7
  108. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +0 -7
  109. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +0 -7
  110. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +0 -7
  111. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +0 -7
  112. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -9
  113. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +0 -7
  114. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +0 -7
  115. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +0 -7
  116. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +0 -7
  117. simtools/schemas/model_parameters/dish_shape_length.schema.yml +0 -5
  118. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -5
  119. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +0 -3
  120. simtools/schemas/model_parameters/dsum_offset.schema.yml +0 -3
  121. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +0 -3
  122. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +0 -3
  123. simtools/schemas/model_parameters/dsum_prescale.schema.yml +0 -3
  124. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +0 -3
  125. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +0 -3
  126. simtools/schemas/model_parameters/dsum_shaping.schema.yml +0 -3
  127. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +0 -3
  128. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -12
  129. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +0 -3
  130. simtools/schemas/model_parameters/effective_focal_length.schema.yml +0 -7
  131. simtools/schemas/model_parameters/epsg_code.schema.yml +0 -5
  132. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +0 -7
  133. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -9
  134. simtools/schemas/model_parameters/fadc_bins.schema.yml +0 -7
  135. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +0 -7
  136. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +0 -2
  137. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +0 -7
  138. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +0 -7
  139. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -9
  140. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +0 -7
  141. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +0 -2
  142. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +0 -7
  143. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +0 -7
  144. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +0 -7
  145. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +0 -2
  146. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +0 -7
  147. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +0 -7
  148. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +0 -7
  149. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +0 -7
  150. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +0 -7
  151. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +0 -7
  152. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +0 -3
  153. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +0 -3
  154. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +0 -3
  155. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +0 -7
  156. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +0 -2
  157. simtools/schemas/model_parameters/fadc_mhz.schema.yml +0 -7
  158. simtools/schemas/model_parameters/fadc_noise.schema.yml +0 -7
  159. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +0 -7
  160. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +0 -7
  161. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +0 -7
  162. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +0 -7
  163. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +0 -7
  164. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +0 -7
  165. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +0 -7
  166. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +0 -7
  167. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +0 -3
  168. simtools/schemas/model_parameters/flasher_angular_distribution.schema.yml +32 -0
  169. simtools/schemas/model_parameters/flasher_angular_distribution_width.schema.yml +32 -0
  170. simtools/schemas/model_parameters/flasher_bunch_size.schema.yml +28 -0
  171. simtools/schemas/model_parameters/flasher_external_trigger.schema.yml +32 -0
  172. simtools/schemas/model_parameters/flasher_photons.schema.yml +34 -0
  173. simtools/schemas/model_parameters/flasher_position.schema.yml +43 -0
  174. simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +29 -0
  175. simtools/schemas/model_parameters/flasher_pulse_offset.schema.yml +35 -0
  176. simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +30 -0
  177. simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +32 -0
  178. simtools/schemas/model_parameters/flasher_type.schema.yml +28 -0
  179. simtools/schemas/model_parameters/flasher_var_photons.schema.yml +31 -0
  180. simtools/schemas/model_parameters/flasher_wavelength.schema.yml +33 -0
  181. simtools/schemas/model_parameters/flatfielding.schema.yml +0 -7
  182. simtools/schemas/model_parameters/focal_length.schema.yml +0 -7
  183. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +0 -3
  184. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +0 -3
  185. simtools/schemas/model_parameters/focus_offset.schema.yml +0 -7
  186. simtools/schemas/model_parameters/gain_variation.schema.yml +0 -7
  187. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +2 -7
  188. simtools/schemas/model_parameters/geomag_rotation.schema.yml +2 -7
  189. simtools/schemas/model_parameters/geomag_vertical.schema.yml +2 -7
  190. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +0 -5
  191. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +0 -7
  192. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +0 -7
  193. simtools/schemas/model_parameters/laser_events.schema.yml +4 -3
  194. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +4 -3
  195. simtools/schemas/model_parameters/laser_photons.schema.yml +4 -3
  196. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +4 -3
  197. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +4 -3
  198. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +4 -3
  199. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +4 -3
  200. simtools/schemas/model_parameters/laser_var_photons.schema.yml +4 -3
  201. simtools/schemas/model_parameters/laser_wavelength.schema.yml +4 -3
  202. simtools/schemas/model_parameters/led_events.schema.yml +4 -3
  203. simtools/schemas/model_parameters/led_photons.schema.yml +4 -3
  204. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +4 -3
  205. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +4 -3
  206. simtools/schemas/model_parameters/led_var_photons.schema.yml +4 -3
  207. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +0 -7
  208. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +0 -7
  209. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +0 -7
  210. simtools/schemas/model_parameters/min_photons.schema.yml +0 -7
  211. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +0 -5
  212. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +0 -7
  213. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +0 -7
  214. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -9
  215. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +0 -7
  216. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +0 -5
  217. simtools/schemas/model_parameters/mirror_list.schema.yml +0 -7
  218. simtools/schemas/model_parameters/mirror_offset.schema.yml +0 -7
  219. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +0 -7
  220. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +0 -7
  221. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +0 -7
  222. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +0 -7
  223. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +0 -7
  224. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +0 -3
  225. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +0 -7
  226. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +0 -7
  227. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +0 -5
  228. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +0 -5
  229. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +0 -5
  230. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +0 -5
  231. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +0 -5
  232. simtools/schemas/model_parameters/num_gains.schema.yml +0 -7
  233. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +0 -7
  234. simtools/schemas/model_parameters/optics_properties.schema.yml +0 -7
  235. simtools/schemas/model_parameters/parabolic_dish.schema.yml +0 -3
  236. simtools/schemas/model_parameters/pedestal_events.schema.yml +4 -7
  237. simtools/schemas/model_parameters/photon_delay.schema.yml +0 -7
  238. simtools/schemas/model_parameters/photons_per_run.schema.yml +4 -4
  239. simtools/schemas/model_parameters/pixel_cells.schema.yml +0 -3
  240. simtools/schemas/model_parameters/pixels_parallel.schema.yml +0 -3
  241. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +0 -7
  242. simtools/schemas/model_parameters/pm_average_gain.schema.yml +0 -5
  243. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +0 -5
  244. simtools/schemas/model_parameters/pm_gain_index.schema.yml +0 -5
  245. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +0 -7
  246. simtools/schemas/model_parameters/pm_transit_time.schema.yml +4 -9
  247. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +0 -5
  248. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +0 -7
  249. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +0 -3
  250. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +0 -3
  251. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +0 -3
  252. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +0 -3
  253. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +0 -3
  254. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +0 -3
  255. simtools/schemas/model_parameters/qe_variation.schema.yml +0 -7
  256. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +0 -7
  257. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -7
  258. simtools/schemas/model_parameters/random_generator.schema.yml +0 -7
  259. simtools/schemas/model_parameters/random_mono_probability.schema.yml +0 -7
  260. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +0 -5
  261. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +0 -5
  262. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +0 -5
  263. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +0 -5
  264. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +0 -5
  265. simtools/schemas/model_parameters/sampled_output.schema.yml +0 -7
  266. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +0 -7
  267. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +0 -3
  268. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +0 -3
  269. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +0 -3
  270. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +0 -3
  271. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +0 -3
  272. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +0 -3
  273. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +0 -3
  274. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +0 -3
  275. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +0 -3
  276. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +0 -3
  277. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +0 -3
  278. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +0 -3
  279. simtools/schemas/model_parameters/stars.schema.yml +0 -5
  280. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +0 -7
  281. simtools/schemas/model_parameters/tailcut_scale.schema.yml +0 -7
  282. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +0 -7
  283. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +0 -7
  284. simtools/schemas/model_parameters/telescope_random_error.schema.yml +0 -7
  285. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +0 -7
  286. simtools/schemas/model_parameters/telescope_transmission.schema.yml +0 -7
  287. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +0 -7
  288. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +0 -7
  289. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +0 -7
  290. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +0 -7
  291. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +0 -7
  292. simtools/schemas/model_parameters/transit_time_error.schema.yml +0 -7
  293. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +0 -7
  294. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +0 -7
  295. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +0 -7
  296. simtools/schemas/model_parameters/trigger_pixels.schema.yml +0 -7
  297. simtools/schemas/production_tables.schema.yml +8 -8
  298. simtools/schemas/simulation_models_info.schema.yml +78 -0
  299. simtools/simtel/simtel_config_writer.py +88 -14
  300. simtools/simtel/simulator_array.py +44 -74
  301. simtools/simtel/simulator_light_emission.py +336 -629
  302. simtools/simtel/simulator_ray_tracing.py +2 -2
  303. simtools/simulator.py +46 -18
  304. simtools/testing/configuration.py +4 -2
  305. simtools/testing/sim_telarray_metadata.py +4 -4
  306. simtools/utils/geometry.py +34 -0
  307. simtools/version.py +111 -0
  308. simtools/{corsika/corsika_histograms_visualize.py → visualization/plot_corsika_histograms.py} +109 -0
  309. simtools/visualization/plot_psf.py +775 -0
  310. simtools/visualization/plot_simtel_events.py +284 -87
  311. simtools/applications/maintain_simulation_model_add_production_table.py +0 -71
  312. simtools/model/flasher_model.py +0 -106
  313. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/WHEEL +0 -0
  314. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/licenses/LICENSE +0 -0
  315. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,775 @@
1
+ """
2
+ Optical PSF plotting functions for parameter optimization visualization.
3
+
4
+ This module provides plotting functionality for PSF parameter optimization,
5
+ including parameter comparison plots, convergence plots, and PSF diameter vs off-axis plots.
6
+ """
7
+
8
+ import logging
9
+
10
+ import astropy.units as u
11
+ import matplotlib.pyplot as plt
12
+ import numpy as np
13
+ from matplotlib.backends.backend_pdf import PdfPages
14
+
15
+ from simtools.ray_tracing.ray_tracing import RayTracing
16
+ from simtools.visualization import visualize
17
+
18
+ # Constants
19
+ RADIUS = "Radius [cm]"
20
+ CUMULATIVE_PSF = "Cumulative PSF"
21
+ MAX_OFFSET_DEFAULT = 4.5 # Maximum off-axis angle in degrees
22
+ OFFSET_STEPS_DEFAULT = 0.1 # Step size for off-axis angle sampling
23
+ DEFAULT_FRACTION = 0.8 # Default PSF containment fraction
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ def get_psf_diameter_label(fraction, unit="cm"):
29
+ """
30
+ Generate PSF diameter label based on containment fraction.
31
+
32
+ Parameters
33
+ ----------
34
+ fraction : float
35
+ PSF containment fraction (e.g., 0.8 for D80, 0.95 for D95)
36
+ unit : str, optional
37
+ Unit for the label (default: "cm")
38
+
39
+ Returns
40
+ -------
41
+ str
42
+ Formatted PSF diameter label (e.g., "D80 (cm)", "D95 (cm)", "D95")
43
+ """
44
+ percentage = int(fraction * 100)
45
+ if unit:
46
+ return f"D{percentage} ({unit})"
47
+ return f"D{percentage}"
48
+
49
+
50
+ def get_significance_label(p_value):
51
+ """Get significance label for p-value."""
52
+ if p_value > 0.05: # null hypothesis not rejected at the 95% level
53
+ return "GOOD"
54
+ if p_value > 0.01: # null hypothesis rejected at 95% but not at 99% level
55
+ return "FAIR"
56
+ return "POOR" # null hypothesis rejected at 99% level
57
+
58
+
59
+ def _format_metric_text(
60
+ psf_diameter,
61
+ metric,
62
+ fraction=DEFAULT_FRACTION,
63
+ p_value=None,
64
+ use_ks_statistic=False,
65
+ second_metric=None,
66
+ ):
67
+ """
68
+ Format metric text for display in plots.
69
+
70
+ Parameters
71
+ ----------
72
+ psf_diameter : float
73
+ PSF diameter value
74
+ metric : float
75
+ Primary metric value (RMSD or KS statistic)
76
+ fraction : float, optional
77
+ PSF containment fraction (default: 0.8)
78
+ p_value : float, optional
79
+ P-value from KS test
80
+ use_ks_statistic : bool
81
+ If True, metric is KS statistic; if False, metric is RMSD
82
+ second_metric : float, optional
83
+ Second metric value to display alongside the primary metric
84
+
85
+ Returns
86
+ -------
87
+ str
88
+ Formatted metric text
89
+ """
90
+ psf_label = get_psf_diameter_label(fraction, unit="")
91
+ psf_text = f"{psf_label} = {psf_diameter:.5f} cm"
92
+
93
+ # Create metric text based on the optimization method
94
+ if second_metric is not None:
95
+ # Special case: show both RMSD and KS statistic (for final best plot)
96
+ metric_text = f"RMSD = {metric:.4f}" # metric is RMSD in this case
97
+ metric_text += f"\nKS statistic = {second_metric:.4f}" # second_metric is KS statistic
98
+ if p_value is not None:
99
+ metric_text += f"\np-value = {p_value:.4f}"
100
+ elif use_ks_statistic:
101
+ metric_text = f"KS stat = {metric:.6f}"
102
+ if p_value is not None:
103
+ significance = get_significance_label(p_value)
104
+ metric_text += f"\np-value = {p_value:.6f} ({significance})"
105
+ else:
106
+ metric_text = f"RMSD = {metric:.4f}"
107
+
108
+ return f"{psf_text}\n{metric_text}"
109
+
110
+
111
+ def _create_base_plot_figure(data_to_plot, simulated_data=None):
112
+ """
113
+ Create base figure for PSF parameter plots.
114
+
115
+ Parameters
116
+ ----------
117
+ data_to_plot : dict
118
+ Data dictionary for plotting
119
+ simulated_data : array, optional
120
+ Simulated data to add to the plot
121
+
122
+ Returns
123
+ -------
124
+ tuple
125
+ (fig, ax) - figure and axis objects
126
+ """
127
+ plot_data = data_to_plot.copy()
128
+
129
+ if simulated_data is not None:
130
+ plot_data["simulated"] = simulated_data
131
+
132
+ fig = visualize.plot_1d(
133
+ plot_data,
134
+ plot_difference=True,
135
+ no_markers=True,
136
+ )
137
+ ax = fig.get_axes()[0]
138
+ ax.set_ylim(0, 1.05)
139
+ ax.set_ylabel(CUMULATIVE_PSF)
140
+ return fig, ax
141
+
142
+
143
+ def _build_parameter_title(pars, is_best):
144
+ """Build parameter title string for plots."""
145
+ title_prefix = "* " if is_best else ""
146
+ return (
147
+ f"{title_prefix}reflection = "
148
+ f"{pars['mirror_reflection_random_angle'][0]:.5f}, "
149
+ f"{pars['mirror_reflection_random_angle'][1]:.5f}, "
150
+ f"{pars['mirror_reflection_random_angle'][2]:.5f}\n"
151
+ f"align_vertical = {pars['mirror_align_random_vertical'][0]:.5f}, "
152
+ f"{pars['mirror_align_random_vertical'][1]:.5f}, "
153
+ f"{pars['mirror_align_random_vertical'][2]:.5f}, "
154
+ f"{pars['mirror_align_random_vertical'][3]:.5f}\n"
155
+ f"align_horizontal = {pars['mirror_align_random_horizontal'][0]:.5f}, "
156
+ f"{pars['mirror_align_random_horizontal'][1]:.5f}, "
157
+ f"{pars['mirror_align_random_horizontal'][2]:.5f}, "
158
+ f"{pars['mirror_align_random_horizontal'][3]:.5f}"
159
+ )
160
+
161
+
162
+ def _add_metric_text_box(ax, metrics_text, is_best):
163
+ """Add metric text box to plot."""
164
+ psf_color = "red" if is_best else "black"
165
+ psf_weight = "bold" if is_best else "normal"
166
+
167
+ ax.text(
168
+ 0.5,
169
+ 0.3,
170
+ metrics_text,
171
+ verticalalignment="center",
172
+ horizontalalignment="left",
173
+ transform=ax.transAxes,
174
+ color=psf_color,
175
+ weight=psf_weight,
176
+ bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7}
177
+ if is_best
178
+ else None,
179
+ )
180
+
181
+
182
+ def _add_plot_annotations(
183
+ ax,
184
+ fig,
185
+ pars,
186
+ psf_diameter,
187
+ metric,
188
+ is_best,
189
+ fraction=DEFAULT_FRACTION,
190
+ p_value=None,
191
+ use_ks_statistic=False,
192
+ second_metric=None,
193
+ ):
194
+ """
195
+ Add title, text annotations, and best parameter indicators to plot.
196
+
197
+ Parameters
198
+ ----------
199
+ ax : matplotlib.axes.Axes
200
+ The plot axes
201
+ fig : matplotlib.figure.Figure
202
+ The plot figure
203
+ pars : dict
204
+ Parameter set dictionary
205
+ psf_diameter : float
206
+ PSF diameter value
207
+ metric : float
208
+ Primary metric value
209
+ is_best : bool
210
+ Whether this is the best parameter set
211
+ fraction : float, optional
212
+ PSF containment fraction (default: 0.8)
213
+ p_value : float, optional
214
+ P-value from KS test
215
+ use_ks_statistic : bool
216
+ If True, metric is KS statistic; if False, metric is RMSD
217
+ second_metric : float, optional
218
+ Second metric value to display
219
+ """
220
+ title = _build_parameter_title(pars, is_best)
221
+ ax.set_title(title)
222
+
223
+ metrics_text = _format_metric_text(
224
+ psf_diameter, metric, fraction, p_value, use_ks_statistic, second_metric
225
+ )
226
+ _add_metric_text_box(ax, metrics_text, is_best)
227
+
228
+ if is_best:
229
+ fig.text(
230
+ 0.02,
231
+ 0.02,
232
+ "* Best parameter set (lowest RMSD)",
233
+ fontsize=8,
234
+ style="italic",
235
+ color="red",
236
+ )
237
+
238
+
239
+ def create_psf_parameter_plot(
240
+ data_to_plot,
241
+ pars,
242
+ psf_diameter,
243
+ metric,
244
+ is_best,
245
+ pdf_pages,
246
+ fraction=DEFAULT_FRACTION,
247
+ p_value=None,
248
+ use_ks_statistic=False,
249
+ second_metric=None,
250
+ ):
251
+ """
252
+ Create a plot for PSF simulation results.
253
+
254
+ Parameters
255
+ ----------
256
+ data_to_plot : dict
257
+ Data dictionary for plotting.
258
+ pars : dict
259
+ Parameter set dictionary.
260
+ psf_diameter : float
261
+ PSF diameter value.
262
+ metric : float
263
+ RMSD value (if use_ks_statistic=False) or KS statistic (if use_ks_statistic=True).
264
+ is_best : bool
265
+ Whether this is the best parameter set.
266
+ pdf_pages : PdfPages
267
+ PDF pages object for saving plots.
268
+ fraction : float, optional
269
+ PSF containment fraction (default: 0.8).
270
+ p_value : float, optional
271
+ P-value from KS test (only used when use_ks_statistic=True).
272
+ use_ks_statistic : bool, optional
273
+ If True, metric is KS statistic; if False, metric is RMSD.
274
+ second_metric : float, optional
275
+ Second metric value to display alongside the primary metric (for final best plot).
276
+ """
277
+ fig, ax = _create_base_plot_figure(data_to_plot)
278
+
279
+ _add_plot_annotations(
280
+ ax,
281
+ fig,
282
+ pars,
283
+ psf_diameter,
284
+ metric,
285
+ is_best,
286
+ fraction,
287
+ p_value,
288
+ use_ks_statistic,
289
+ second_metric,
290
+ )
291
+
292
+ pdf_pages.savefig(fig, bbox_inches="tight")
293
+ plt.clf()
294
+
295
+
296
+ def create_detailed_parameter_plot(
297
+ pars,
298
+ ks_statistic,
299
+ psf_diameter,
300
+ simulated_data,
301
+ data_to_plot,
302
+ is_best,
303
+ pdf_pages,
304
+ fraction=DEFAULT_FRACTION,
305
+ p_value=None,
306
+ ):
307
+ """
308
+ Create a detailed plot for a parameter set showing all parameter values.
309
+
310
+ Parameters
311
+ ----------
312
+ pars : dict
313
+ Parameter set dictionary
314
+ ks_statistic : float
315
+ KS statistic value for this parameter set
316
+ psf_diameter : float
317
+ PSF diameter value for this parameter set
318
+ simulated_data : array
319
+ Simulated data for plotting
320
+ data_to_plot : dict
321
+ Data dictionary for plotting
322
+ is_best : bool
323
+ Whether this is the best parameter set
324
+ pdf_pages : PdfPages
325
+ PDF pages object to save the plot
326
+ fraction : float, optional
327
+ PSF containment fraction (default: 0.8)
328
+ p_value : float, optional
329
+ P-value from KS test for statistical significance
330
+ """
331
+ # Check if we have valid simulated data for plotting
332
+ if simulated_data is None:
333
+ logger.warning(
334
+ "No simulated data available for plotting this parameter set, skipping plot creation"
335
+ )
336
+ return
337
+
338
+ try:
339
+ fig, ax = _create_base_plot_figure(data_to_plot, simulated_data)
340
+ except (ValueError, RuntimeError, KeyError, TypeError) as e:
341
+ logger.error(f"Failed to create plot for parameters: {e}")
342
+ return
343
+
344
+ _add_plot_annotations(
345
+ ax,
346
+ fig,
347
+ pars,
348
+ psf_diameter,
349
+ ks_statistic,
350
+ is_best,
351
+ fraction,
352
+ p_value,
353
+ use_ks_statistic=True,
354
+ second_metric=None,
355
+ )
356
+
357
+ pdf_pages.savefig(fig, bbox_inches="tight")
358
+ plt.clf()
359
+
360
+
361
+ def create_parameter_progression_plots(
362
+ results, best_pars, data_to_plot, pdf_pages, fraction=DEFAULT_FRACTION
363
+ ):
364
+ """
365
+ Create plots for all parameter sets showing optimization progression.
366
+
367
+ Parameters
368
+ ----------
369
+ results : list
370
+ List of (pars, ks_statistic, p_value, psf_diameter, simulated_data) tuples
371
+ best_pars : dict
372
+ Best parameter set for highlighting
373
+ data_to_plot : dict
374
+ Data dictionary for plotting
375
+ pdf_pages : PdfPages
376
+ PDF pages object to save plots
377
+ fraction : float, optional
378
+ PSF containment fraction (default: 0.8)
379
+ """
380
+ logger.info("Creating plots for all parameter sets...")
381
+
382
+ for i, (pars, ks_statistic, p_value, psf_diameter, simulated_data) in enumerate(results):
383
+ if simulated_data is None:
384
+ logger.warning(f"No simulated data for iteration {i}, skipping plot")
385
+ continue
386
+
387
+ is_best = pars is best_pars
388
+ logger.info(f"Creating plot {i + 1}/{len(results)}{' (BEST)' if is_best else ''}")
389
+
390
+ create_detailed_parameter_plot(
391
+ pars,
392
+ ks_statistic,
393
+ psf_diameter,
394
+ simulated_data,
395
+ data_to_plot,
396
+ is_best,
397
+ pdf_pages,
398
+ fraction,
399
+ p_value,
400
+ )
401
+
402
+
403
+ def create_gradient_descent_convergence_plot(
404
+ gd_results, threshold, output_file, fraction=DEFAULT_FRACTION, use_ks_statistic=False
405
+ ):
406
+ """
407
+ Create convergence plot during gradient descent.
408
+
409
+ Parameters
410
+ ----------
411
+ gd_results : list
412
+ List of (params, metric, p_value, psf_diameter, simulated_data) tuples from gradient descent
413
+ threshold : float
414
+ Optimization metric threshold used for convergence
415
+ output_file : Path
416
+ Output file path for saving the plot
417
+ fraction : float, optional
418
+ PSF containment fraction for labeling (default: 0.8)
419
+ use_ks_statistic : bool
420
+ Whether to use KS statistic or RMSD labels and titles
421
+ """
422
+ logger.info("Creating gradient descent convergence plot...")
423
+
424
+ # Check if results include p-values (for KS statistic mode)
425
+ has_p_values = len(gd_results[0]) >= 4 and gd_results[0][2] is not None
426
+
427
+ if has_p_values and use_ks_statistic:
428
+ _, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 12), tight_layout=True)
429
+ else:
430
+ _, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8), tight_layout=True)
431
+
432
+ iterations = list(range(len(gd_results)))
433
+ metrics = [r[1] for r in gd_results]
434
+ psf_diameters = [r[3] for r in gd_results]
435
+
436
+ metric_name = "KS Statistic" if use_ks_statistic else "RMSD"
437
+ psf_label = get_psf_diameter_label(fraction)
438
+
439
+ # Plot optimization metric progression
440
+ ax1.plot(iterations, metrics, "b.-", linewidth=2, markersize=6)
441
+ ax1.axhline(y=threshold, color="r", linestyle="--", label=f"Threshold: {threshold}")
442
+ ax1.set_xlabel("Iteration")
443
+ ax1.set_ylabel(metric_name)
444
+ ax1.set_title(f"Gradient Descent Convergence - {metric_name}")
445
+ ax1.grid(True, alpha=0.3)
446
+ ax1.legend()
447
+
448
+ # Plot PSF diameter progression
449
+ ax2.plot(iterations, psf_diameters, "g.-", linewidth=2, markersize=6)
450
+ ax2.set_xlabel("Iteration")
451
+ ax2.set_ylabel(psf_label)
452
+ ax2.set_title(f"Gradient Descent Convergence - {get_psf_diameter_label(fraction, unit='')}")
453
+ ax2.grid(True, alpha=0.3)
454
+
455
+ # Plot p-value progression if available and using KS statistic
456
+ if has_p_values and use_ks_statistic:
457
+ p_values = [r[2] for r in gd_results if r[2] is not None]
458
+ p_iterations = [i for i, r in enumerate(gd_results) if r[2] is not None]
459
+
460
+ ax3.plot(p_iterations, p_values, "m.-", linewidth=2, markersize=6)
461
+ ax3.axhline(
462
+ y=0.05, color="orange", linestyle="--", alpha=0.7, label="p = 0.05 (significance)"
463
+ )
464
+ ax3.axhline(
465
+ y=0.01, color="r", linestyle="--", alpha=0.7, label="p = 0.01 (high significance)"
466
+ )
467
+ ax3.set_xlabel("Iteration")
468
+ ax3.set_ylabel("p-value")
469
+ ax3.set_title("Gradient Descent Convergence - Statistical Significance")
470
+ ax3.set_yscale("log") # Log scale for p-values
471
+ ax3.grid(True, alpha=0.3)
472
+ ax3.legend()
473
+
474
+ plt.savefig(output_file, bbox_inches="tight")
475
+ plt.close()
476
+
477
+ logger.info(f"Convergence plot saved to {output_file}")
478
+
479
+
480
+ def create_monte_carlo_uncertainty_plot(
481
+ mc_results, output_file, fraction=DEFAULT_FRACTION, use_ks_statistic=False
482
+ ):
483
+ """
484
+ Create uncertainty analysis plots showing optimization metric and p-value distributions.
485
+
486
+ Parameters
487
+ ----------
488
+ mc_results : tuple
489
+ Results from Monte Carlo analysis: (mean_metric, std_metric, metric_values,
490
+ mean_p, std_p, p_values, mean_psf_diameter, std_psf_diameter, psf_diameter_values)
491
+ output_file : Path
492
+ Output file path for saving the plot
493
+ fraction : float, optional
494
+ PSF containment fraction for labeling (default: 0.8)
495
+ use_ks_statistic : bool, optional
496
+ Whether KS statistic mode is being used (affects filename suffix)
497
+ """
498
+ (
499
+ mean_metric,
500
+ std_metric,
501
+ metric_values,
502
+ mean_p_value,
503
+ _, # std_p_value (unused)
504
+ p_values,
505
+ mean_psf_diameter,
506
+ std_psf_diameter,
507
+ psf_diameter_values,
508
+ ) = mc_results
509
+
510
+ logger.info("Creating Monte Carlo uncertainty analysis plot...")
511
+
512
+ # Check if we have valid p-values to determine if this is KS statistic or RMSD mode
513
+ valid_p_values = [p for p in p_values if p is not None] if p_values else []
514
+ is_ks_mode = len(valid_p_values) > 0
515
+
516
+ # Create subplot layout based on mode
517
+ if is_ks_mode:
518
+ # KS mode: 2x2 layout with all 4 plots
519
+ _, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10), tight_layout=True)
520
+ else:
521
+ # RMSD mode: 1x2 layout with only metric and PSF diameter plots
522
+ _, (ax1, ax3) = plt.subplots(1, 2, figsize=(12, 5), tight_layout=True)
523
+
524
+ # Metric histogram (KS statistic or RMSD)
525
+ metric_name = "KS Statistic" if is_ks_mode else "RMSD"
526
+ psf_label = get_psf_diameter_label(fraction)
527
+
528
+ ax1.hist(metric_values, bins=20, alpha=0.7, color="blue", edgecolor="black")
529
+ ax1.axvline(
530
+ mean_metric, color="red", linestyle="--", linewidth=2, label=f"Mean: {mean_metric:.6f}"
531
+ )
532
+ ax1.axvline(
533
+ mean_metric - std_metric,
534
+ color="orange",
535
+ linestyle=":",
536
+ alpha=0.7,
537
+ label=f"$\\sigma$: {std_metric:.6f}",
538
+ )
539
+ ax1.axvline(mean_metric + std_metric, color="orange", linestyle=":", alpha=0.7)
540
+ ax1.set_xlabel(metric_name)
541
+ ax1.set_ylabel("Counts")
542
+ ax1.set_title(f"{metric_name} Distribution")
543
+ ax1.legend()
544
+ ax1.grid(True, alpha=0.3)
545
+
546
+ # p-value histogram (only for KS statistic mode)
547
+ if is_ks_mode:
548
+ ax2.hist(valid_p_values, bins=20, alpha=0.7, color="magenta", edgecolor="black")
549
+ if mean_p_value is not None:
550
+ ax2.axvline(
551
+ mean_p_value,
552
+ color="red",
553
+ linestyle="--",
554
+ linewidth=2,
555
+ label=f"Mean: {mean_p_value:.6f}",
556
+ )
557
+ ax2.axvline(0.05, color="orange", linestyle="--", alpha=0.7, label="p = 0.05")
558
+ ax2.axvline(0.01, color="red", linestyle="--", alpha=0.7, label="p = 0.01")
559
+ ax2.set_xlabel("p-value")
560
+ ax2.set_ylabel("Counts")
561
+ ax2.set_title("p-value Distribution")
562
+ ax2.legend()
563
+ ax2.grid(True, alpha=0.3)
564
+
565
+ # PSF diameter histogram
566
+ ax3.hist(psf_diameter_values, bins=20, alpha=0.7, color="green", edgecolor="black")
567
+ ax3.axvline(
568
+ mean_psf_diameter,
569
+ color="red",
570
+ linestyle="--",
571
+ linewidth=2,
572
+ label=f"Mean: {mean_psf_diameter:.4f} cm",
573
+ )
574
+ ax3.axvline(
575
+ mean_psf_diameter - std_psf_diameter,
576
+ color="orange",
577
+ linestyle=":",
578
+ alpha=0.7,
579
+ label=f"$\\sigma$: {std_psf_diameter:.4f} cm",
580
+ )
581
+ ax3.axvline(mean_psf_diameter + std_psf_diameter, color="orange", linestyle=":", alpha=0.7)
582
+ ax3.set_xlabel(psf_label)
583
+ ax3.set_ylabel("Counts")
584
+ ax3.set_title(f"{get_psf_diameter_label(fraction, unit='')} Distribution")
585
+ ax3.legend()
586
+ ax3.grid(True, alpha=0.3)
587
+
588
+ # Scatter plot: Metric vs p-value (only for KS statistic mode)
589
+ if is_ks_mode:
590
+ ax4.scatter(metric_values, valid_p_values, alpha=0.6, color="purple")
591
+ ax4.axhline(y=0.05, color="orange", linestyle="--", alpha=0.7, label="p = 0.05")
592
+ ax4.axhline(y=0.01, color="red", linestyle="--", alpha=0.7, label="p = 0.01")
593
+ ax4.set_xlabel(metric_name)
594
+ ax4.set_ylabel("p-value")
595
+ ax4.set_title(f"{metric_name} vs p-value Correlation")
596
+ ax4.set_yscale("log")
597
+ ax4.legend()
598
+ ax4.grid(True, alpha=0.3)
599
+
600
+ # Save plot in both PDF and PNG formats with appropriate suffix
601
+ suffix = "_ks" if use_ks_statistic else "_rmsd"
602
+
603
+ # Generate base filename without extension
604
+ base_path = output_file.with_suffix("")
605
+ base_name = str(base_path)
606
+
607
+ # Add suffix and save in both formats
608
+ pdf_file = f"{base_name}{suffix}.pdf"
609
+ png_file = f"{base_name}{suffix}.png"
610
+
611
+ plt.savefig(pdf_file, bbox_inches="tight")
612
+ plt.savefig(png_file, bbox_inches="tight", dpi=150)
613
+ plt.close()
614
+
615
+ logger.info(f"Monte Carlo uncertainty plot saved to {pdf_file}")
616
+ logger.info(f"Monte Carlo uncertainty plot saved to {png_file}")
617
+
618
+
619
+ def create_psf_vs_offaxis_plot(tel_model, site_model, args_dict, best_pars, output_dir):
620
+ """
621
+ Create PSF diameter vs off-axis angle plot using the best parameters.
622
+
623
+ Parameters
624
+ ----------
625
+ tel_model : TelescopeModel
626
+ Telescope model object.
627
+ site_model : SiteModel
628
+ Site model object.
629
+ args_dict : dict
630
+ Dictionary containing parsed command-line arguments.
631
+ best_pars : dict
632
+ Best parameter set.
633
+ output_dir : Path
634
+ Output directory for saving plots.
635
+ """
636
+ fraction = args_dict.get("fraction", DEFAULT_FRACTION)
637
+ psf_label_cm = get_psf_diameter_label(fraction, unit="cm")
638
+ psf_label_deg = get_psf_diameter_label(fraction, unit="degrees")
639
+
640
+ logger.info(f"Creating {psf_label_cm} vs off-axis angle plot with best parameters...")
641
+
642
+ # Apply best parameters to telescope model
643
+ tel_model.change_multiple_parameters(**best_pars)
644
+
645
+ # Create off-axis angle array
646
+ max_offset = args_dict.get("max_offset", MAX_OFFSET_DEFAULT)
647
+ offset_steps = args_dict.get("offset_steps", OFFSET_STEPS_DEFAULT)
648
+ off_axis_angles = np.linspace(
649
+ 0,
650
+ max_offset,
651
+ int(max_offset / offset_steps) + 1,
652
+ )
653
+
654
+ ray = RayTracing(
655
+ telescope_model=tel_model,
656
+ site_model=site_model,
657
+ simtel_path=args_dict["simtel_path"],
658
+ zenith_angle=args_dict["zenith"] * u.deg,
659
+ source_distance=args_dict["src_distance"] * u.km,
660
+ off_axis_angle=off_axis_angles * u.deg,
661
+ )
662
+
663
+ logger.info(f"Running ray tracing for {len(off_axis_angles)} off-axis angles...")
664
+ ray.simulate(test=args_dict.get("test", False), force=True)
665
+ ray.analyze(force=True)
666
+
667
+ for key in ["d80_cm", "d80_deg"]:
668
+ plt.figure(figsize=(10, 6), tight_layout=True)
669
+
670
+ ray.plot(key, marker="o", linestyle="-", color="blue", linewidth=2, markersize=6)
671
+
672
+ parameters_text = (
673
+ f"Best Parameters: \n"
674
+ f"reflection=["
675
+ f"{', '.join(f'{x:.4f}' for x in best_pars['mirror_reflection_random_angle'])}],\n"
676
+ f"align_horizontal=["
677
+ f"{', '.join(f'{x:.4f}' for x in best_pars['mirror_align_random_horizontal'])}]\n"
678
+ f"align_vertical=["
679
+ f"{', '.join(f'{x:.4f}' for x in best_pars['mirror_align_random_vertical'])}]\n"
680
+ )
681
+ plt.title(parameters_text)
682
+ plt.xlabel("Off-axis Angle (degrees)")
683
+ plt.ylabel(psf_label_cm if "_cm" in key else psf_label_deg)
684
+ plt.ylim(bottom=0)
685
+ plt.xticks(rotation=45)
686
+ plt.xlim(0, max_offset)
687
+ plt.grid(True, alpha=0.3)
688
+
689
+ # Create dynamic file name based on fraction
690
+ psf_identifier = get_psf_diameter_label(fraction, unit="").lower() # e.g., "d80", "d95"
691
+ unit_suffix = "cm" if "_cm" in key else "deg"
692
+ plot_file_name = f"{tel_model.name}_best_params_{psf_identifier}_{unit_suffix}.png"
693
+ plot_file = output_dir.joinpath(plot_file_name)
694
+ visualize.save_figure(
695
+ plt, plot_file, figure_format=["png"], log_title=f"{psf_label_cm} vs off-axis ({key})"
696
+ )
697
+
698
+ plt.close("all")
699
+
700
+
701
+ def setup_pdf_plotting(args_dict, output_dir, tel_model_name):
702
+ """
703
+ Set up PDF plotting for gradient descent optimization if requested.
704
+
705
+ Parameters
706
+ ----------
707
+ args_dict : dict
708
+ Dictionary containing command-line arguments with plot_all flag.
709
+ output_dir : Path
710
+ Directory where the PDF file will be saved.
711
+ tel_model_name : str
712
+ Name of the telescope model for filename generation.
713
+
714
+ Returns
715
+ -------
716
+ PdfPages or None
717
+ PdfPages object for saving plots if plotting is requested, None otherwise.
718
+
719
+ Notes
720
+ -----
721
+ Creates a PDF file for saving cumulative PSF plots during gradient descent
722
+ optimization. Returns None if plotting is not requested via the plot_all flag.
723
+ """
724
+ if not args_dict.get("plot_all", False):
725
+ return None
726
+ pdf_filename = output_dir / f"psf_gradient_descent_plots_{tel_model_name}.pdf"
727
+ pdf_pages = PdfPages(pdf_filename)
728
+ logger.info(f"Creating cumulative PSF plots for each iteration (saving to {pdf_filename})")
729
+ return pdf_pages
730
+
731
+
732
+ def create_optimization_plots(args_dict, gd_results, tel_model, data_to_plot, output_dir):
733
+ """
734
+ Create optimization plots for all iterations if requested.
735
+
736
+ Parameters
737
+ ----------
738
+ args_dict : dict
739
+ Dictionary containing command-line arguments with save_plots flag.
740
+ gd_results : list
741
+ List of (params, rmsd, _, psf_diameter, _) tuples from gradient descent optimization.
742
+ tel_model : TelescopeModel
743
+ Telescope model object for naming files.
744
+ data_to_plot : dict
745
+ Dictionary containing measured PSF data.
746
+ output_dir : Path
747
+ Directory where the PDF file will be saved.
748
+
749
+ Notes
750
+ -----
751
+ Creates a PDF file with plots for optimization iterations. Only creates plots
752
+ every 5 iterations plus the final iteration to avoid excessively large files.
753
+ Returns early if save_plots flag is not set.
754
+ """
755
+ if not args_dict.get("save_plots", False):
756
+ return
757
+
758
+ fraction = args_dict.get("fraction", DEFAULT_FRACTION)
759
+ pdf_filename = output_dir.joinpath(f"psf_optimization_results_{tel_model.name}.pdf")
760
+ pdf_pages = PdfPages(pdf_filename)
761
+ logger.info(f"Creating PSF plots for each optimization iteration (saving to {pdf_filename})")
762
+
763
+ for i, (params, rmsd, _, psf_diameter, _) in enumerate(gd_results):
764
+ if i % 5 == 0 or i == len(gd_results) - 1:
765
+ create_psf_parameter_plot(
766
+ data_to_plot,
767
+ params,
768
+ psf_diameter,
769
+ rmsd,
770
+ is_best=(i == len(gd_results) - 1),
771
+ pdf_pages=pdf_pages,
772
+ fraction=fraction,
773
+ use_ks_statistic=False,
774
+ )
775
+ pdf_pages.close()