gammasimtools 0.18.0__py3-none-any.whl → 0.20.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/METADATA +24 -69
  2. gammasimtools-0.20.0.dist-info/RECORD +395 -0
  3. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/entry_points.txt +11 -4
  4. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +16 -3
  6. simtools/applications/calculate_incident_angles.py +182 -0
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +4 -3
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
  9. simtools/applications/db_add_simulation_model_from_repository_to_db.py +17 -14
  10. simtools/applications/db_add_value_from_json_to_db.py +8 -10
  11. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
  12. simtools/applications/db_generate_compound_indexes.py +65 -0
  13. simtools/applications/db_get_file_from_db.py +12 -24
  14. simtools/applications/db_get_parameter_from_db.py +4 -4
  15. simtools/applications/db_inspect_databases.py +20 -10
  16. simtools/applications/derive_mirror_rnda.py +17 -11
  17. simtools/applications/derive_psf_parameters.py +59 -309
  18. simtools/applications/derive_trigger_rates.py +91 -0
  19. simtools/applications/docs_produce_array_element_report.py +1 -1
  20. simtools/applications/docs_produce_calibration_reports.py +1 -1
  21. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  22. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  23. simtools/applications/generate_corsika_histograms.py +1 -1
  24. simtools/applications/generate_default_metadata.py +8 -24
  25. simtools/applications/generate_simtel_event_data.py +11 -11
  26. simtools/applications/maintain_simulation_model_add_production_table.py +71 -0
  27. simtools/applications/maintain_simulation_model_compare_productions.py +98 -0
  28. simtools/applications/{verify_simulation_model_production_tables.py → maintain_simulation_model_verify_production_tables.py} +9 -1
  29. simtools/applications/merge_tables.py +2 -2
  30. simtools/applications/plot_array_layout.py +3 -3
  31. simtools/applications/plot_simtel_events.py +421 -0
  32. simtools/applications/plot_tabular_data.py +9 -2
  33. simtools/applications/plot_tabular_data_for_model_parameter.py +2 -1
  34. simtools/applications/print_version.py +8 -9
  35. simtools/applications/production_derive_corsika_limits.py +6 -7
  36. simtools/applications/production_derive_statistics.py +1 -1
  37. simtools/applications/production_generate_grid.py +2 -2
  38. simtools/applications/production_merge_corsika_limits.py +214 -0
  39. simtools/applications/run_application.py +47 -113
  40. simtools/applications/simulate_calibration_events.py +166 -0
  41. simtools/applications/simulate_flasher.py +141 -0
  42. simtools/applications/{simulate_light_emission.py → simulate_illuminator.py} +35 -99
  43. simtools/applications/simulate_prod.py +6 -24
  44. simtools/applications/simulate_prod_htcondor_generator.py +7 -0
  45. simtools/applications/submit_array_layouts.py +2 -1
  46. simtools/applications/submit_model_parameter_from_external.py +1 -1
  47. simtools/applications/validate_camera_efficiency.py +30 -12
  48. simtools/applications/validate_camera_fov.py +1 -1
  49. simtools/applications/validate_cumulative_psf.py +1 -1
  50. simtools/applications/validate_file_using_schema.py +9 -5
  51. simtools/applications/validate_optics.py +1 -1
  52. simtools/camera/camera_efficiency.py +61 -45
  53. simtools/camera/single_photon_electron_spectrum.py +1 -1
  54. simtools/configuration/commandline_parser.py +46 -11
  55. simtools/configuration/configurator.py +4 -4
  56. simtools/corsika/corsika_config.py +45 -25
  57. simtools/corsika/corsika_histograms.py +6 -5
  58. simtools/data_model/data_reader.py +2 -3
  59. simtools/data_model/metadata_collector.py +32 -36
  60. simtools/data_model/metadata_model.py +15 -12
  61. simtools/data_model/model_data_writer.py +13 -32
  62. simtools/data_model/schema.py +74 -24
  63. simtools/data_model/validate_data.py +42 -12
  64. simtools/db/db_handler.py +125 -62
  65. simtools/db/db_model_upload.py +14 -19
  66. simtools/dependencies.py +98 -30
  67. simtools/io/ascii_handler.py +279 -0
  68. simtools/{io_operations → io}/io_handler.py +25 -3
  69. simtools/job_execution/htcondor_script_generator.py +15 -4
  70. simtools/layout/array_layout.py +1 -1
  71. simtools/layout/array_layout_utils.py +51 -12
  72. simtools/model/array_model.py +41 -5
  73. simtools/model/flasher_model.py +106 -0
  74. simtools/model/model_parameter.py +4 -4
  75. simtools/model/model_repository.py +197 -2
  76. simtools/model/site_model.py +25 -0
  77. simtools/model/telescope_model.py +3 -1
  78. simtools/production_configuration/derive_corsika_limits.py +336 -427
  79. simtools/production_configuration/derive_production_statistics_handler.py +7 -6
  80. simtools/production_configuration/generate_production_grid.py +9 -11
  81. simtools/production_configuration/merge_corsika_limits.py +528 -0
  82. simtools/ray_tracing/incident_angles.py +706 -0
  83. simtools/ray_tracing/mirror_panel_psf.py +1 -0
  84. simtools/ray_tracing/psf_parameter_optimisation.py +792 -0
  85. simtools/ray_tracing/ray_tracing.py +6 -2
  86. simtools/reporting/docs_read_parameters.py +150 -62
  87. simtools/runners/corsika_runner.py +1 -1
  88. simtools/runners/corsika_simtel_runner.py +14 -5
  89. simtools/runners/runner_services.py +10 -5
  90. simtools/runners/simtools_runner.py +267 -0
  91. simtools/schemas/application_workflow.metaschema.yml +101 -68
  92. simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +1 -1
  93. simtools/schemas/input/single_pe_spectrum.schema.yml +1 -1
  94. simtools/schemas/metadata.metaschema.yml +577 -3
  95. simtools/schemas/model_parameter.metaschema.yml +6 -6
  96. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +4 -4
  97. simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
  98. simtools/schemas/model_parameters/altitude.schema.yml +1 -1
  99. simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
  100. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +1 -1
  101. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +1 -1
  102. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
  103. simtools/schemas/model_parameters/array_layouts.schema.yml +1 -1
  104. simtools/schemas/model_parameters/array_triggers.schema.yml +1 -1
  105. simtools/schemas/model_parameters/array_window.schema.yml +1 -1
  106. simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
  107. simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
  108. simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
  109. simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
  110. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
  111. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +1 -1
  112. simtools/schemas/model_parameters/axes_offsets.schema.yml +1 -1
  113. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
  114. simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
  115. simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
  116. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
  117. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
  118. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
  119. simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
  120. simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
  121. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +1 -1
  122. simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
  123. simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
  124. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  125. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  126. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +1 -1
  127. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +1 -1
  128. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +1 -1
  129. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
  130. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
  131. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
  132. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +1 -1
  133. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
  134. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +1 -1
  135. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +3 -3
  136. simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
  137. simtools/schemas/model_parameters/default_trigger.schema.yml +1 -1
  138. simtools/schemas/model_parameters/design_model.schema.yml +1 -1
  139. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +1 -1
  140. simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
  141. simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
  142. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
  143. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
  144. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
  145. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
  146. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
  147. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
  148. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +1 -1
  149. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
  150. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
  151. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
  152. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
  153. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
  154. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
  155. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
  156. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
  157. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
  158. simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
  159. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
  160. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
  161. simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
  162. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +1 -1
  163. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
  164. simtools/schemas/model_parameters/dsum_prescale.schema.yml +1 -1
  165. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
  166. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
  167. simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
  168. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +1 -1
  169. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -2
  170. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
  171. simtools/schemas/model_parameters/effective_focal_length.schema.yml +1 -1
  172. simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
  173. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +1 -1
  174. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
  175. simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
  176. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  177. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  178. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
  179. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
  180. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
  181. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  182. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  183. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
  184. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
  185. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
  186. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
  187. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
  188. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
  189. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
  190. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
  191. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
  192. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
  193. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +35 -0
  194. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +41 -0
  195. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +38 -0
  196. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
  197. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
  198. simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
  199. simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
  200. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
  201. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +1 -1
  202. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
  203. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
  204. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
  205. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
  206. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
  207. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
  208. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  209. simtools/schemas/model_parameters/flatfielding.schema.yml +1 -1
  210. simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
  211. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +1 -1
  212. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
  213. simtools/schemas/model_parameters/focus_offset.schema.yml +1 -1
  214. simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
  215. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
  216. simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
  217. simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
  218. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
  219. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  220. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  221. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  222. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
  223. simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
  224. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
  225. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
  226. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
  227. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
  228. simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
  229. simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
  230. simtools/schemas/model_parameters/led_events.schema.yml +1 -1
  231. simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
  232. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
  233. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
  234. simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
  235. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  236. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +50 -1
  237. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  238. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  239. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
  240. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +1 -1
  241. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +1 -1
  242. simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
  243. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
  244. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
  245. simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
  246. simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
  247. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +1 -1
  248. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +1 -1
  249. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +1 -1
  250. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
  251. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +1 -1
  252. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +1 -1
  253. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
  254. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +1 -1
  255. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
  256. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +2 -2
  257. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
  258. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
  259. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +1 -1
  260. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +23 -30
  261. simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
  262. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +1 -1
  263. simtools/schemas/model_parameters/optics_properties.schema.yml +1 -1
  264. simtools/schemas/model_parameters/parabolic_dish.schema.yml +1 -1
  265. simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
  266. simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
  267. simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
  268. simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
  269. simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
  270. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
  271. simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
  272. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
  273. simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
  274. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +1 -1
  275. simtools/schemas/model_parameters/pm_transit_time.schema.yml +1 -1
  276. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
  277. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
  278. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
  279. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
  280. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +11 -1
  281. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +1 -1
  282. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
  283. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
  284. simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
  285. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +1 -1
  286. simtools/schemas/model_parameters/random_focal_length.schema.yml +1 -1
  287. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  288. simtools/schemas/model_parameters/random_mono_probability.schema.yml +1 -1
  289. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
  290. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +1 -1
  291. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +1 -1
  292. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
  293. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
  294. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  295. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  296. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +1 -1
  297. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
  298. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  299. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
  300. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
  301. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +11 -1
  302. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +1 -1
  303. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
  304. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +11 -1
  305. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
  306. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
  307. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
  308. simtools/schemas/model_parameters/stars.schema.yml +2 -2
  309. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  310. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  311. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
  312. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
  313. simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
  314. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
  315. simtools/schemas/model_parameters/telescope_transmission.schema.yml +1 -1
  316. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
  317. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
  318. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
  319. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
  320. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
  321. simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
  322. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
  323. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
  324. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +1 -1
  325. simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
  326. simtools/schemas/plot_configuration.metaschema.yml +5 -2
  327. simtools/schemas/production_configuration_metrics.schema.yml +12 -2
  328. simtools/schemas/production_tables.schema.yml +7 -2
  329. simtools/simtel/simtel_config_reader.py +2 -2
  330. simtools/simtel/simtel_config_writer.py +33 -23
  331. simtools/simtel/simtel_io_event_histograms.py +483 -0
  332. simtools/simtel/simtel_io_event_reader.py +65 -43
  333. simtools/simtel/simtel_io_event_writer.py +40 -20
  334. simtools/simtel/simtel_io_metadata.py +1 -1
  335. simtools/simtel/simtel_table_reader.py +95 -13
  336. simtools/simtel/simulator_array.py +138 -10
  337. simtools/simtel/simulator_camera_efficiency.py +32 -23
  338. simtools/simtel/simulator_light_emission.py +437 -271
  339. simtools/simtel/simulator_ray_tracing.py +1 -1
  340. simtools/simulator.py +105 -147
  341. simtools/telescope_trigger_rates.py +119 -0
  342. simtools/testing/configuration.py +24 -26
  343. simtools/testing/helpers.py +2 -2
  344. simtools/testing/log_inspector.py +52 -0
  345. simtools/testing/validate_output.py +87 -37
  346. simtools/utils/general.py +125 -255
  347. simtools/utils/geometry.py +56 -0
  348. simtools/utils/names.py +1 -1
  349. simtools/visualization/legend_handlers.py +180 -264
  350. simtools/visualization/plot_array_layout.py +20 -8
  351. simtools/visualization/plot_incident_angles.py +431 -0
  352. simtools/visualization/plot_pixels.py +1 -1
  353. simtools/visualization/plot_simtel_event_histograms.py +376 -0
  354. simtools/visualization/plot_simtel_events.py +816 -0
  355. simtools/visualization/plot_tables.py +133 -37
  356. simtools/visualization/visualize.py +1 -100
  357. gammasimtools-0.18.0.dist-info/RECORD +0 -376
  358. simtools/applications/calculate_trigger_rate.py +0 -187
  359. simtools/applications/generate_sim_telarray_histograms.py +0 -196
  360. simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
  361. simtools/simtel/simtel_io_histogram.py +0 -621
  362. simtools/simtel/simtel_io_histograms.py +0 -552
  363. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/WHEEL +0 -0
  364. {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/top_level.txt +0 -0
  365. /simtools/{io_operations → io}/hdf5_handler.py +0 -0
  366. /simtools/{io_operations → io}/legacy_data_handler.py +0 -0
  367. /simtools/{io_operations/io_table_handler.py → io/table_handler.py} +0 -0
@@ -1,6 +1,7 @@
1
- """Simulation using the light emission package for calibration."""
1
+ """Simulation using the light emission package for calibration devices."""
2
2
 
3
3
  import logging
4
+ import shutil
4
5
  import stat
5
6
  import subprocess
6
7
  from pathlib import Path
@@ -8,11 +9,9 @@ from pathlib import Path
8
9
  import astropy.units as u
9
10
  import numpy as np
10
11
 
11
- from simtools.corsika.corsika_histograms_visualize import save_figs_to_pdf
12
- from simtools.io_operations import io_handler
12
+ from simtools.io import io_handler
13
13
  from simtools.runners.simtel_runner import SimtelRunner
14
14
  from simtools.utils.general import clear_default_sim_telarray_cfg_directories
15
- from simtools.visualization.visualize import plot_simtel_ctapipe
16
15
 
17
16
  __all__ = ["SimulatorLightEmission"]
18
17
 
@@ -26,47 +25,104 @@ class SimulatorLightEmission(SimtelRunner):
26
25
 
27
26
  def __init__(
28
27
  self,
28
+ *,
29
29
  telescope_model,
30
- calibration_model,
31
- site_model,
32
- light_emission_config,
33
- le_application,
34
- simtel_path,
35
- light_source_type,
30
+ calibration_model=None,
31
+ flasher_model=None,
32
+ site_model=None,
33
+ light_emission_config=None,
34
+ light_source_setup=None,
35
+ simtel_path=None,
36
+ light_source_type=None,
36
37
  label=None,
37
38
  test=False,
38
39
  ):
39
- """Initialize SimtelRunner."""
40
- super().__init__(label=label, simtel_path=simtel_path)
40
+ """Initialize SimtelRunner.
41
+
42
+ Parameters
43
+ ----------
44
+ telescope_model : TelescopeModel
45
+ Model of the telescope to be simulated
46
+ calibration_model : CalibrationModel, optional
47
+ Model of the calibration device to be simulated
48
+ flasher_model : FlasherModel, optional
49
+ Model of the flasher device to be simulated
50
+ site_model : SiteModel, optional
51
+ Model of the site
52
+ light_emission_config : dict, optional
53
+ Configuration for the light emission
54
+ light_source_setup : str, optional
55
+ Setup for light source positioning ("variable" or "layout")
56
+ simtel_path : Path, optional
57
+ Path to the sim_telarray installation
58
+ light_source_type : str, optional
59
+ Type of light source: 'illuminator', or 'flasher'
60
+ label : str, optional
61
+ Label for the simulation
62
+ test : bool, optional
63
+ Whether this is a test run
64
+ """
65
+ super().__init__(simtel_path=simtel_path, label=label, corsika_config=None)
41
66
 
42
67
  self._logger = logging.getLogger(__name__)
43
- self._logger.debug("Init SimtelRunnerLightEmission")
44
- self._simtel_path = simtel_path
45
68
 
46
69
  self._telescope_model = telescope_model
47
-
48
- self.label = label if label is not None else self._telescope_model.label
49
- self.test = test
50
-
51
70
  self._calibration_model = calibration_model
71
+ self._flasher_model = flasher_model
52
72
  self._site_model = site_model
73
+ self.light_emission_config = light_emission_config or {}
74
+ self.light_source_setup = light_source_setup
75
+ self.light_source_type = light_source_type or "illuminator"
76
+ self.test = test
77
+
78
+ # IO
53
79
  self.io_handler = io_handler.IOHandler()
54
80
  self.output_directory = self.io_handler.get_output_directory(self.label)
55
81
 
56
- # LightEmission - default parameters
57
- self._rep_number = 0
58
- self.runs = 1
59
- self.photons_per_run = (
60
- (self._calibration_model.get_parameter_value("photons_per_run"))
61
- if not self.test
62
- else 1e8
63
- )
82
+ self.number_events = self.light_emission_config["number_events"]
83
+
84
+ # photons per run
85
+ if self._calibration_model is not None:
86
+ self.photons_per_run = (
87
+ self._calibration_model.get_parameter_value("photons_per_run")
88
+ if not self.test
89
+ else 1e8
90
+ )
91
+ elif self._flasher_model is not None:
92
+ self.photons_per_run = (
93
+ self._flasher_model.get_parameter_value("photons_per_flasher")
94
+ if not self.test
95
+ else 1e8
96
+ )
97
+ else:
98
+ self.photons_per_run = 1e8
64
99
 
65
- self.le_application = le_application
66
- self.light_emission_config = light_emission_config
100
+ # Ensure sim_telarray config exists on disk
101
+ if hasattr(self._telescope_model, "write_sim_telarray_config_file"):
102
+ self._telescope_model.write_sim_telarray_config_file(additional_model=site_model)
103
+
104
+ # Runtime variables
67
105
  self.distance = None
68
- self.light_source_type = light_source_type
69
- self._telescope_model.write_sim_telarray_config_file(additional_model=site_model)
106
+
107
+ def _get_prefix(self) -> str:
108
+ prefix = self.light_emission_config.get("output_prefix", "")
109
+ if prefix is not None:
110
+ return f"{prefix}_"
111
+ return ""
112
+
113
+ def _infer_application(self) -> tuple[str, str]:
114
+ """Infer the LightEmission application and mode from type/setup.
115
+
116
+ Returns
117
+ -------
118
+ tuple[str, str]
119
+ (app_name, mode)
120
+ """
121
+ if self.light_source_type == "flasher":
122
+ return ("ff-1m", "flasher")
123
+ # default to illuminator xyzls, mode from setup
124
+ mode = self.light_source_setup or "layout"
125
+ return ("xyzls", mode)
70
126
 
71
127
  @staticmethod
72
128
  def light_emission_default_configuration():
@@ -112,6 +168,79 @@ class SimulatorLightEmission(SimtelRunner):
112
168
  },
113
169
  }
114
170
 
171
+ @staticmethod
172
+ def flasher_default_configuration():
173
+ """
174
+ Get default flasher configuration.
175
+
176
+ Returns
177
+ -------
178
+ dict
179
+ Default configuration for flasher devices.
180
+ """
181
+ return {
182
+ "number_events": {
183
+ "len": 1,
184
+ "unit": None,
185
+ "default": 1,
186
+ "names": ["number_events"],
187
+ },
188
+ "photons_per_flasher": {
189
+ "len": 1,
190
+ "unit": None,
191
+ "default": 2.5e6,
192
+ "names": ["photons"],
193
+ },
194
+ "bunch_size": {
195
+ "len": 1,
196
+ "unit": None,
197
+ "default": 1.0,
198
+ "names": ["bunchsize"],
199
+ },
200
+ "flasher_position": {
201
+ "len": 2,
202
+ "unit": u.Unit("cm"),
203
+ "default": [0.0, 0.0] * u.cm,
204
+ "names": ["xy", "position"],
205
+ },
206
+ "flasher_depth": {
207
+ "len": 1,
208
+ "unit": u.Unit("cm"),
209
+ "default": 60 * u.cm,
210
+ "names": ["depth", "distance"],
211
+ },
212
+ "flasher_inclination": {
213
+ "len": 1,
214
+ "unit": u.Unit("deg"),
215
+ "default": 0.0 * u.deg,
216
+ "names": ["inclination"],
217
+ },
218
+ "spectrum": {
219
+ "len": 1,
220
+ "unit": u.Unit("nm"),
221
+ "default": 400 * u.nm,
222
+ "names": ["wavelength"],
223
+ },
224
+ "lightpulse": {
225
+ "len": 1,
226
+ "unit": None,
227
+ "default": "Simple:0",
228
+ "names": ["pulse"],
229
+ },
230
+ "angular_distribution": {
231
+ "len": 1,
232
+ "unit": None,
233
+ "default": "isotropic",
234
+ "names": ["angular"],
235
+ },
236
+ "flasher_pattern": {
237
+ "len": 1,
238
+ "unit": None,
239
+ "default": "all",
240
+ "names": ["fire", "pattern"],
241
+ },
242
+ }
243
+
115
244
  def calibration_pointing_direction(self):
116
245
  """
117
246
  Calculate the pointing of the calibration device towards the telescope.
@@ -145,16 +274,16 @@ class SimulatorLightEmission(SimtelRunner):
145
274
  tel_phi = 180 - np.round(
146
275
  np.rad2deg(np.arctan2(direction_vector[1], direction_vector[0])), 6
147
276
  )
148
-
149
- # Calculate laser beam theta and phi angles
277
+ # Calculate source beam theta and phi angles
150
278
  direction_vector_inv = direction_vector * -1
151
- laser_theta = np.round(
152
- np.rad2deg(np.arccos(direction_vector_inv[2] / np.linalg.norm(direction_vector_inv))), 6
279
+ source_theta = np.round(
280
+ np.rad2deg(np.arccos(direction_vector_inv[2] / np.linalg.norm(direction_vector_inv))),
281
+ 6,
153
282
  )
154
- laser_phi = np.round(
283
+ source_phi = np.round(
155
284
  np.rad2deg(np.arctan2(direction_vector_inv[1], direction_vector_inv[0])), 6
156
285
  )
157
- return pointing_vector.tolist(), [tel_theta, tel_phi, laser_theta, laser_phi]
286
+ return pointing_vector.tolist(), [tel_theta, tel_phi, source_theta, source_phi]
158
287
 
159
288
  def _write_telpos_file(self):
160
289
  """
@@ -180,6 +309,30 @@ class SimulatorLightEmission(SimtelRunner):
180
309
 
181
310
  return telpos_file
182
311
 
312
+ def _prepare_flasher_atmosphere_files(self, config_directory: Path) -> int:
313
+ """Prepare canonical atmosphere aliases for ff-1m and return model id 1."""
314
+ atmo_name = self._site_model.get_parameter_value("atmospheric_profile")
315
+ self._logger.debug(f"Using atmosphere profile: {atmo_name}")
316
+
317
+ src_path = config_directory.joinpath(atmo_name)
318
+ for canonical in ("atmprof1.dat", "atm_profile_model_1.dat"):
319
+ dst = config_directory.joinpath(canonical)
320
+ if dst.exists() or dst.is_symlink():
321
+ try:
322
+ dst.unlink()
323
+ except OSError:
324
+ pass
325
+ try:
326
+ dst.symlink_to(src_path)
327
+ except OSError:
328
+ try:
329
+ shutil.copy2(src_path, dst)
330
+ except OSError as copy_err:
331
+ self._logger.warning(
332
+ f"Failed to create atmosphere alias {dst.name}: {copy_err}"
333
+ )
334
+ return 1
335
+
183
336
  def _make_light_emission_script(self):
184
337
  """
185
338
  Create the light emission script to run the light emission package.
@@ -192,92 +345,151 @@ class SimulatorLightEmission(SimtelRunner):
192
345
  str
193
346
  The commands to run the Light Emission package
194
347
  """
195
- x_cal, y_cal, z_cal = self._calibration_model.get_parameter_value_with_unit(
196
- "array_element_position_ground"
197
- )
198
348
  x_tel, y_tel, z_tel = self._telescope_model.get_parameter_value_with_unit(
199
349
  "array_element_position_ground"
200
350
  )
201
351
 
202
- config_directory = self.io_handler.get_output_directory(
203
- label=self.label, sub_dir=f"model/{self._site_model.model_version}"
352
+ config_directory = self.io_handler.get_model_configuration_directory(
353
+ label=self.label, model_version=self._site_model.model_version
204
354
  )
205
-
206
355
  telpos_file = self._write_telpos_file()
207
356
 
208
- command = f"rm {self.output_directory}/"
209
- command += f"{self.le_application[0]}_{self.le_application[1]}.simtel.gz\n"
210
- command += str(self._simtel_path.joinpath("sim_telarray/LightEmission/"))
211
- command += f"/{self.le_application[0]}"
357
+ app_name, _ = self._infer_application()
358
+
359
+ parts: list[str] = []
360
+ # application path
361
+ parts.append(str(self._simtel_path.joinpath("sim_telarray/LightEmission/")))
362
+ parts.append(f"/{app_name}")
363
+
212
364
  corsika_observation_level = self._site_model.get_parameter_value_with_unit(
213
365
  "corsika_observation_level"
214
366
  )
215
- command += f" -h {corsika_observation_level.to(u.m).value}"
216
- command += f" --telpos-file {telpos_file}"
217
-
218
- if self.light_source_type == "led":
219
- if self.le_application[1] == "variable":
220
- command += f" -x {self.light_emission_config['x_pos']['default'].to(u.cm).value}"
221
- command += f" -y {self.light_emission_config['y_pos']['default'].to(u.cm).value}"
222
- command += f" -z {self.light_emission_config['z_pos']['default'].to(u.cm).value}"
223
- command += (
224
- f" -d {','.join(map(str, self.light_emission_config['direction']['default']))}"
225
- )
226
- command += f" -n {self.photons_per_run}"
227
-
228
- elif self.le_application[1] == "layout":
229
- command += f" -x {x_cal.to(u.cm).value}"
230
- command += f" -y {y_cal.to(u.cm).value}"
231
- command += f" -z {z_cal.to(u.cm).value}"
232
- pointing_vector = self.calibration_pointing_direction()[0]
233
- command += f" -d {','.join(map(str, pointing_vector))}"
234
-
235
- command += f" -n {self.photons_per_run}"
236
- self._logger.info(f"Photons per run: {self.photons_per_run} ")
237
-
238
- laser_wavelength = self._calibration_model.get_parameter_value_with_unit(
239
- "laser_wavelength"
240
- )
241
- command += f" -s {int(laser_wavelength.to(u.nm).value)}"
242
-
243
- led_pulse_sigtime = self._calibration_model.get_parameter_value_with_unit(
244
- "led_pulse_sigtime"
245
- )
246
- command += f" -p Gauss:{led_pulse_sigtime.to(u.ns).value}"
247
- command += " -a isotropic"
248
-
249
- command += f" -A {config_directory}/"
250
- command += f"{self._telescope_model.get_parameter_value('atmospheric_profile')}"
251
-
252
- elif self.light_source_type == "laser":
253
- command += " --events 1"
254
- command += " --bunches 2500000"
255
- command += " --step 0.1"
256
- command += " --bunchsize 1"
257
- spectrum = self._calibration_model.get_parameter_value_with_unit("laser_wavelength")
258
- command += f" --spectrum {int(spectrum.to(u.nm).value)}"
259
- command += " --lightpulse Gauss:"
260
- pulse_sigtime = self._calibration_model.get_parameter_value_with_unit(
261
- "laser_pulse_sigtime"
367
+ parts.append(
368
+ self._build_altitude_atmo_block(
369
+ app_name, config_directory, corsika_observation_level, telpos_file
262
370
  )
263
- command += f"{pulse_sigtime.to(u.ns).value}"
264
- x_origin = x_cal - x_tel
265
- y_origin = y_cal - y_tel
266
- z_origin = z_cal - z_tel
267
- _, angles = self.calibration_pointing_direction()
268
- angle_theta = angles[0]
269
- angle_phi = angles[1]
270
- positions = x_origin.to(u.cm).value, y_origin.to(u.cm).value, z_origin.to(u.cm).value
271
- command += f" --laser-position '{positions[0]},{positions[1]},{positions[2]}'"
272
-
273
- command += f" --telescope-theta {angle_theta}"
274
- command += f" --telescope-phi {angle_phi}"
275
- command += f" --laser-theta {90 - angles[2]}"
276
- command += f" --laser-phi {angles[3]}"
277
- command += f" --atmosphere {config_directory}/"
278
- command += f"{self._telescope_model.get_parameter_value('atmospheric_profile')}"
279
- command += f" -o {self.output_directory}/{self.le_application[0]}.iact.gz"
280
- command += "\n"
371
+ )
372
+
373
+ parts.append(self._build_source_specific_block(x_tel, y_tel, z_tel, config_directory))
374
+
375
+ if self.light_source_type == "illuminator":
376
+ parts.append(f" -A {config_directory}/")
377
+ parts.append(f"{self._telescope_model.get_parameter_value('atmospheric_profile')}")
378
+
379
+ parts.append(f" -o {self.output_directory}/{app_name}.iact.gz")
380
+ parts.append("\n")
381
+
382
+ return "".join(parts)
383
+
384
+ def _build_altitude_atmo_block(
385
+ self, app_name, config_directory: Path, corsika_observation_level, telpos_file: Path
386
+ ) -> str:
387
+ """Return CLI segment for altitude/atmosphere and telpos handling."""
388
+ if app_name in ("ff-1m",):
389
+ seg = []
390
+ seg.append(" -I.")
391
+ seg.append(f" -I{self._simtel_path.joinpath('sim_telarray/cfg')}")
392
+ seg.append(f" -I{config_directory}")
393
+ seg.append(f" --altitude {corsika_observation_level.to(u.m).value}")
394
+ atmo_id = self._prepare_flasher_atmosphere_files(config_directory)
395
+ seg.append(f" --atmosphere {atmo_id}")
396
+ return "".join(seg)
397
+ # default path (not used for flasher now, but kept for completeness)
398
+ return f" -h {corsika_observation_level.to(u.m).value} --telpos-file {telpos_file}"
399
+
400
+ def _build_source_specific_block(self, _x_tel, _y_tel, _z_tel, _config_directory: Path) -> str:
401
+ """Return CLI segment for light-source specific flags."""
402
+ if self.light_source_type == "flasher":
403
+ return self._add_flasher_command_options("")
404
+ if self.light_source_type == "illuminator":
405
+ return self._add_illuminator_command_options("")
406
+ self._logger.warning("Unknown light_source_type '%s'", self.light_source_type)
407
+ return ""
408
+
409
+ def _add_flasher_command_options(self, command):
410
+ """Add flasher-specific options to the script (uniform ff-1m)."""
411
+ return self._add_flasher_options(command)
412
+
413
+ def _add_flasher_options(self, command):
414
+ """Add flasher options for all telescope types (ff-1m style)."""
415
+ # For MST/LST we used to use ff-1m; now apply same for all telescopes
416
+ flasher_xy = self._flasher_model.get_parameter_value_with_unit("flasher_position")
417
+ flasher_distance = self._flasher_model.get_parameter_value_with_unit("flasher_depth")
418
+ # Camera radius required for application, Radius of fiducial sphere enclosing camera
419
+ camera_radius = (
420
+ self._telescope_model.get_parameter_value_with_unit("camera_body_diameter")
421
+ .to(u.cm)
422
+ .value
423
+ / 2
424
+ )
425
+ spectrum = self._flasher_model.get_parameter_value_with_unit("spectrum")
426
+ pulse = self._flasher_model.get_parameter_value("lightpulse")
427
+ angular = self._flasher_model.get_parameter_value("angular_distribution")
428
+ bunch_size = self._flasher_model.get_parameter_value("bunch_size")
429
+
430
+ # Convert to plain numbers for CLI
431
+ fx = flasher_xy[0].to(u.cm).value
432
+ fy = flasher_xy[1].to(u.cm).value
433
+ dist_cm = flasher_distance.to(u.cm).value
434
+ spec_nm = int(spectrum.to(u.nm).value)
435
+
436
+ command += f" --events {self.number_events}"
437
+ command += f" --photons {self.photons_per_run}"
438
+ command += f" --bunchsize {bunch_size}"
439
+ command += f" --xy {fx},{fy}"
440
+ command += f" --distance {dist_cm}"
441
+ command += f" --camera-radius {camera_radius}"
442
+ command += f" --spectrum {spec_nm}"
443
+ command += f" --lightpulse {pulse}"
444
+ command += f" --angular-distribution {angular}"
445
+ return command
446
+
447
+ def _add_illuminator_command_options(self, command):
448
+ """
449
+ Add illuminator-specific command options to the light emission script.
450
+
451
+ Parameters
452
+ ----------
453
+ command : str
454
+ The command string to add options to
455
+
456
+ Returns
457
+ -------
458
+ str
459
+ The updated command string
460
+ """
461
+ if self.light_source_setup == "variable":
462
+ command += f" -x {self.light_emission_config['x_pos']['default'].to(u.cm).value}"
463
+ command += f" -y {self.light_emission_config['y_pos']['default'].to(u.cm).value}"
464
+ command += f" -z {self.light_emission_config['z_pos']['default'].to(u.cm).value}"
465
+ command += (
466
+ f" -d {','.join(map(str, self.light_emission_config['direction']['default']))}"
467
+ )
468
+ command += f" -n {self.photons_per_run}"
469
+
470
+ elif self.light_source_setup == "layout":
471
+ x_cal, y_cal, z_cal = self._calibration_model.get_parameter_value_with_unit(
472
+ "array_element_position_ground"
473
+ )
474
+ command += f" -x {x_cal.to(u.cm).value}"
475
+ command += f" -y {y_cal.to(u.cm).value}"
476
+ command += f" -z {z_cal.to(u.cm).value}"
477
+ pointing_vector = self.calibration_pointing_direction()[0]
478
+ command += f" -d {','.join(map(str, pointing_vector))}"
479
+
480
+ command += f" -n {self.photons_per_run}"
481
+ self._logger.info(f"Photons per run: {self.photons_per_run} ")
482
+
483
+ laser_wavelength = self._calibration_model.get_parameter_value_with_unit(
484
+ "laser_wavelength"
485
+ )
486
+ command += f" -s {int(laser_wavelength.to(u.nm).value)}"
487
+
488
+ led_pulse_sigtime = self._calibration_model.get_parameter_value_with_unit(
489
+ "led_pulse_sigtime"
490
+ )
491
+ command += f" -p Gauss:{led_pulse_sigtime.to(u.ns).value}"
492
+ command += " -a isotropic"
281
493
 
282
494
  return command
283
495
 
@@ -290,17 +502,22 @@ class SimulatorLightEmission(SimtelRunner):
290
502
  str
291
503
  The command to run sim_telarray
292
504
  """
293
- # LightEmission
294
- _, angles = self.calibration_pointing_direction()
505
+ # For flasher sims, avoid calibration pointing entirely; default angles to (0,0)
506
+ if self.light_source_type == "flasher":
507
+ angles = [0, 0]
508
+ else:
509
+ _, angles = self.calibration_pointing_direction()
295
510
 
296
- command = f"{self._simtel_path.joinpath('sim_telarray/bin/sim_telarray/')}"
297
- command += " -I"
298
- command += f" -I{self._telescope_model.config_file_directory}"
299
- command += f" -c {self._telescope_model.config_file_path}"
511
+ simtel_bin = self._simtel_path.joinpath("sim_telarray/bin/sim_telarray/")
512
+ # Build command without prefix; caller will add SIM_TELARRAY_CONFIG_PATH once
513
+ command = f"{simtel_bin} "
514
+ command += f"-I{self._telescope_model.config_file_directory} "
515
+ command += f"-I{simtel_bin} "
516
+ command += f"-c {self._telescope_model.config_file_path} "
300
517
  self._remove_line_from_config(self._telescope_model.config_file_path, "array_triggers")
301
518
  self._remove_line_from_config(self._telescope_model.config_file_path, "axes_offsets")
302
519
 
303
- command += " -DNUM_TELESCOPES=1"
520
+ command += "-DNUM_TELESCOPES=1 "
304
521
 
305
522
  command += super().get_config_option(
306
523
  "altitude",
@@ -310,7 +527,7 @@ class SimulatorLightEmission(SimtelRunner):
310
527
  )
311
528
  command += super().get_config_option(
312
529
  "atmospheric_transmission",
313
- self._telescope_model.get_parameter_value("atmospheric_transmission"),
530
+ self._site_model.get_parameter_value("atmospheric_transmission"),
314
531
  )
315
532
  command += super().get_config_option("TRIGGER_TELESCOPES", "1")
316
533
 
@@ -318,25 +535,38 @@ class SimulatorLightEmission(SimtelRunner):
318
535
  command += super().get_config_option("PULSE_ANALYSIS", "-30")
319
536
  command += super().get_config_option("MAXIMUM_TELESCOPES", 1)
320
537
 
321
- if self.le_application[1] == "variable":
538
+ if self.light_source_type == "variable":
322
539
  command += super().get_config_option("telescope_theta", 0)
323
540
  command += super().get_config_option("telescope_phi", 0)
324
541
  else:
325
542
  command += super().get_config_option("telescope_theta", f"{angles[0]}")
326
543
  command += super().get_config_option("telescope_phi", f"{angles[1]}")
327
544
 
545
+ # For flasher runs, bypass reflections on primary mirror
546
+ if self.light_source_type == "flasher":
547
+ command += super().get_config_option("Bypass_Optics", "1")
548
+
328
549
  command += super().get_config_option("power_law", "2.68")
550
+ app_name, app_mode = self._infer_application()
551
+ pref = self._get_prefix()
329
552
  command += super().get_config_option(
330
- "input_file", f"{self.output_directory}/{self.le_application[0]}.iact.gz"
553
+ "input_file", f"{self.output_directory}/{app_name}.iact.gz"
331
554
  )
555
+ dist_suffix = ""
556
+ if self.light_source_setup == "variable":
557
+ try:
558
+ dist_val = int(self._get_distance_for_plotting().to_value(u.m))
559
+ dist_suffix = f"_d_{dist_val}"
560
+ except Exception: # pylint:disable=broad-except
561
+ dist_suffix = ""
562
+
332
563
  command += super().get_config_option(
333
564
  "output_file",
334
- f"{self.output_directory}/{self.le_application[0]}_{self.le_application[1]}.simtel.gz",
565
+ f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.simtel.zst",
335
566
  )
336
567
  command += super().get_config_option(
337
568
  "histogram_file",
338
- f"{self.output_directory}/"
339
- f"{self.le_application[0]}_{self.le_application[1]}.ctsim.hdata\n",
569
+ f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.ctsim.hdata\n",
340
570
  )
341
571
 
342
572
  # Remove the default sim_telarray configuration directories
@@ -362,65 +592,10 @@ class SimulatorLightEmission(SimtelRunner):
362
592
  if not line.startswith(line_prefix):
363
593
  file.write(line)
364
594
 
365
- def _create_postscript(self, **kwargs):
366
- """
367
- Write out post-script file using read_cta in hessioxxx/bin/read_cta.
368
-
369
- parts from the documentation
370
- -r level (Use 10/5 tail-cut image cleaning and redo reconstruction.)
371
- level >= 1: show parameters from sim_hessarray.
372
- level >= 2: redo shower reconstruction
373
- level >= 3: redo image cleaning (and shower reconstruction
374
- with new image parameters)
375
- level >= 4: redo amplitude summation
376
- level >= 5: PostScript file includes original and
377
- new shower reconstruction.
378
- --integration-window w,o[,ps] *(Set integration window width and offset.)
379
- For some integration schemes there is a pulse shaping option.
380
-
381
-
382
- Returns
383
- -------
384
- str
385
- Command to create the postscript file
386
- """
387
- postscript_dir = self.output_directory.joinpath("postscripts")
388
- postscript_dir.mkdir(parents=True, exist_ok=True)
389
-
390
- command = str(self._simtel_path.joinpath("hessioxxx/bin/read_cta"))
391
- command += " --min-tel 1 --min-trg-tel 1"
392
- command += " -q --integration-scheme 4"
393
- command += " --integration-window "
394
- command += f"{kwargs['integration_window'][0]},{kwargs['integration_window'][1]}"
395
- command += f" -r {kwargs['level']}"
396
- command += " --plot-with-sum-only"
397
- command += " --plot-with-pixel-amp --plot-with-pixel-id"
398
- command += (
399
- f" -p {postscript_dir}/"
400
- f"{self.le_application[0]}_{self.le_application[1]}_"
401
- f"d_{int(self.distance.to(u.m).value)}.ps"
402
- )
403
- command += (
404
- f" {self.output_directory}/"
405
- f"{self.le_application[0]}_{self.le_application[1]}.simtel.gz\n"
406
- )
407
- # ps2pdf required, now only store postscripts
408
- # command += f"ps2pdf {self.output_directory}/{self.le_application}.ps
409
- # {self.output_directory}/{self.le_application}.pdf"
410
- return command
411
-
412
- def prepare_script(self, generate_postscript=False, **kwargs):
595
+ def prepare_script(self):
413
596
  """
414
597
  Build and return bash run script containing the light-emission command.
415
598
 
416
- Parameters
417
- ----------
418
- plot: bool
419
- If output should be plotted.
420
-
421
- generate_postscript: bool
422
- If postscript should be generated with read_cta.
423
-
424
599
  Returns
425
600
  -------
426
601
  Path
@@ -430,97 +605,98 @@ class SimulatorLightEmission(SimtelRunner):
430
605
 
431
606
  _script_dir = self.output_directory.joinpath("scripts")
432
607
  _script_dir.mkdir(parents=True, exist_ok=True)
433
- _script_file = _script_dir.joinpath(f"{self.le_application[0]}-lightemission.sh")
608
+ _script_file = _script_dir.joinpath(f"{self._infer_application()[0]}-lightemission.sh")
434
609
  self._logger.debug(f"Run bash script - {_script_file}")
435
610
 
611
+ target_out = Path(self._get_simulation_output_filename())
612
+ if target_out.exists():
613
+ msg = f"Simtel output file exists already, cancelling simulation: {target_out}"
614
+ self._logger.error(msg)
615
+ raise FileExistsError(msg)
616
+
436
617
  command_le = self._make_light_emission_script()
437
618
  command_simtel = self._make_simtel_script()
438
619
 
439
620
  with _script_file.open("w", encoding="utf-8") as file:
440
- file.write("#!/usr/bin/env bash\n\n")
621
+ file.write("#!/usr/bin/env bash\n")
441
622
 
442
623
  file.write(f"{command_le}\n\n")
624
+ app_name, _ = self._infer_application()
625
+ file.write(
626
+ f"[ -s '{self.output_directory}/{app_name}.iact.gz' ] || "
627
+ f"{{ echo 'LightEmission did not produce IACT file' >&2; exit 1; }}\n\n"
628
+ )
443
629
  file.write(f"{command_simtel}\n\n")
444
630
 
445
- if generate_postscript:
446
- self._logger.debug("Write out postscript file")
447
- command_plot = self._create_postscript(**kwargs)
448
- file.write("# Generate postscript\n\n")
449
- file.write(f"{command_plot}\n\n")
450
- file.write("# End\n\n")
631
+ # Cleanup intermediate IACT file at the end of the run
632
+ file.write(f"rm -f '{self.output_directory}/{app_name}.iact.gz'\n\n")
451
633
 
452
634
  _script_file.chmod(_script_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
453
635
  return _script_file
454
636
 
455
- def process_simulation_output(self, args_dict, figures):
456
- """Process the simulation output, including plotting and saving figures."""
457
- try:
458
- filename = self._get_simulation_output_filename()
459
- distance = self._get_distance_for_plotting()
460
-
461
- fig = self._plot_simulation_output(
462
- filename,
463
- args_dict["boundary_thresh"],
464
- args_dict["picture_thresh"],
465
- args_dict["min_neighbors"],
466
- distance,
467
- args_dict["return_cleaned"],
468
- )
469
- figures.append(fig)
470
-
471
- except AttributeError:
472
- msg = (
473
- f"Telescope not triggered at distance of "
474
- f"{self.light_emission_config['z_pos']['default']}"
475
- )
476
- self._logger.warning(msg)
477
-
478
637
  def _get_simulation_output_filename(self):
479
638
  """Get the filename of the simulation output."""
480
- return (
481
- f"{self.output_directory}/{self.le_application[0]}_{self.le_application[1]}.simtel.gz"
482
- )
639
+ dist_suffix = ""
640
+ if self.light_source_setup == "variable":
641
+ try:
642
+ dist_val = int(self._get_distance_for_plotting().to_value(u.m))
643
+ dist_suffix = f"_d_{dist_val}"
644
+ except Exception: # pylint:disable=broad-except
645
+ dist_suffix = ""
646
+ app_name, app_mode = self._infer_application()
647
+ pref = self._get_prefix()
648
+ return f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.simtel.zst"
483
649
 
484
650
  def _get_distance_for_plotting(self):
485
- """Get the distance to be used for plotting."""
486
- try:
487
- return self.light_emission_config["z_pos"]["default"]
488
- except KeyError:
489
- return round(self.distance, 2)
490
-
491
- def _plot_simulation_output(
492
- self, filename, boundary_thresh, picture_thresh, min_neighbors, distance, return_cleaned
493
- ):
494
- """Plot the simulation output."""
495
- return plot_simtel_ctapipe(
496
- filename,
497
- cleaning_args=[boundary_thresh, picture_thresh, min_neighbors],
498
- distance=distance,
499
- return_cleaned=return_cleaned,
500
- )
501
-
502
- def save_figures_to_pdf(self, figures, telescope):
503
- """Save the generated figures to a PDF file."""
504
- save_figs_to_pdf(
505
- figures,
506
- f"{self.output_directory}/"
507
- f"{telescope}_{self.le_application[0]}_{self.le_application[1]}.pdf",
508
- )
651
+ """Get the distance to be used for plotting as an astropy Quantity.
509
652
 
510
- def run_simulation(self, args_dict, figures):
511
- """Run the light emission simulation."""
512
- run_script = self.prepare_script(generate_postscript=True, **args_dict)
513
- log_file = Path(self.output_directory) / "logfile.log"
514
- with open(log_file, "w", encoding="utf-8") as log_file:
653
+ For flasher runs, use the flasher_depth (cm) from the flasher model.
654
+ For illuminator runs, use the configured z_pos quantity.
655
+ Otherwise, fall back to self.distance if set, or 0 m.
656
+ """
657
+ if self.light_source_type == "flasher" and self._flasher_model is not None:
658
+ return self._flasher_model.get_parameter_value_with_unit("flasher_depth").to(u.m)
659
+
660
+ def _as_meters(val):
661
+ if isinstance(val, u.Quantity):
662
+ return val.to(u.m)
663
+ try:
664
+ return float(val) * u.m
665
+ except (TypeError, ValueError):
666
+ return None
667
+
668
+ cfg = self.light_emission_config or {}
669
+ z = cfg.get("z_pos")
670
+ if isinstance(z, dict):
671
+ z_def = z.get("default")
672
+ z_val = z_def[0] if isinstance(z_def, list | tuple) and z_def else z_def
673
+ z_q = _as_meters(z_val)
674
+ if z_q is not None:
675
+ return z_q
676
+
677
+ d_q = _as_meters(getattr(self, "distance", None))
678
+ if d_q is not None:
679
+ return d_q
680
+
681
+ return 0 * u.m
682
+
683
+ def run_simulation(self) -> Path:
684
+ """Run the light emission simulation and return the output simtel file path."""
685
+ run_script = self.prepare_script()
686
+ log_path = Path(self.output_directory) / "logfile.log"
687
+ with open(log_path, "w", encoding="utf-8") as fh:
515
688
  subprocess.run(
516
689
  run_script,
517
690
  shell=False,
518
691
  check=False,
519
692
  text=True,
520
- stdout=log_file,
521
- stderr=log_file,
693
+ stdout=fh,
694
+ stderr=fh,
522
695
  )
523
- self.process_simulation_output(args_dict, figures)
696
+ out = Path(self._get_simulation_output_filename())
697
+ if not out.exists():
698
+ self._logger.warning(f"Expected simtel output not found: {out}")
699
+ return out
524
700
 
525
701
  def distance_list(self, arg):
526
702
  """
@@ -558,15 +734,8 @@ class SimulatorLightEmission(SimtelRunner):
558
734
  raise KeyError(f"Key '{key}' not found in light emission configuration.")
559
735
 
560
736
  def calculate_distance_telescope_calibration_device(self):
561
- """
562
- Calculate the distance(s) between the telescope and the calibration device.
563
-
564
- Returns
565
- -------
566
- list of astropy.Quantity
567
- A list of distances for variable positions or a single distance for layout positions.
568
- """
569
- if not self.light_emission_config:
737
+ """Calculate distance(s) between telescope and calibration device."""
738
+ if self.light_source_setup == "layout":
570
739
  # Layout positions: Use DB coordinates
571
740
  x_cal, y_cal, z_cal = self._calibration_model.get_parameter_value_with_unit(
572
741
  "array_element_position_ground"
@@ -579,7 +748,7 @@ class SimulatorLightEmission(SimtelRunner):
579
748
  tel_vect = np.array([x_tel, y_tel, z_tel])
580
749
  cal_vect = np.array([x_cal, y_cal, z_cal])
581
750
  distance = np.linalg.norm(cal_vect - tel_vect)
582
- print("Distance between telescope and calibration device:", distance * u.m)
751
+ self._logger.info(f"Distance between telescope and calibration device: {distance} m")
583
752
  return [distance * u.m]
584
753
 
585
754
  # Variable positions: Calculate distances for all positions
@@ -595,7 +764,7 @@ class SimulatorLightEmission(SimtelRunner):
595
764
  return distances
596
765
 
597
766
  def simulate_variable_distances(self, args_dict):
598
- """Simulate light emission for variable distances."""
767
+ """Simulate light emission for variable distances and return output files list."""
599
768
  if args_dict["distances_ls"] is not None:
600
769
  self.update_light_emission_config(
601
770
  "z_pos", self.distance_list(args_dict["distances_ls"])
@@ -603,8 +772,7 @@ class SimulatorLightEmission(SimtelRunner):
603
772
  self._logger.info(
604
773
  f"Simulating for distances: {self.light_emission_config['z_pos']['default']}"
605
774
  )
606
-
607
- figures = []
775
+ outputs: list[Path] = []
608
776
  distances = self.calculate_distance_telescope_calibration_device()
609
777
 
610
778
  for current_distance, z_pos in zip(
@@ -612,15 +780,13 @@ class SimulatorLightEmission(SimtelRunner):
612
780
  ):
613
781
  self.update_light_emission_config("z_pos", z_pos)
614
782
  self.distance = current_distance
615
- self.run_simulation(args_dict, figures)
616
-
617
- self.save_figures_to_pdf(figures, args_dict["telescope"])
618
-
619
- def simulate_layout_positions(self, args_dict):
620
- """Simulate light emission for layout positions."""
621
- figures = []
622
- self.distance = self.calculate_distance_telescope_calibration_device()[
623
- 0
624
- ] # Single distance for layout
625
- self.run_simulation(args_dict, figures)
626
- self.save_figures_to_pdf(figures, args_dict["telescope"])
783
+ outputs.append(self.run_simulation())
784
+ return outputs
785
+
786
+ def simulate_layout_positions(self, args_dict): # pylint: disable=unused-argument
787
+ """Simulate light emission for layout positions and return output files list."""
788
+ # args_dict kept for API symmetry; explicitly mark as unused
789
+ del args_dict
790
+ self.distance = self.calculate_distance_telescope_calibration_device()[0]
791
+ # Single distance for layout
792
+ return [self.run_simulation()]