gammasimtools 0.21.0__py3-none-any.whl → 0.23.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 (370) hide show
  1. {gammasimtools-0.21.0.dist-info → gammasimtools-0.23.0.dist-info}/METADATA +3 -3
  2. gammasimtools-0.23.0.dist-info/RECORD +414 -0
  3. {gammasimtools-0.21.0.dist-info → gammasimtools-0.23.0.dist-info}/entry_points.txt +2 -1
  4. simtools/_version.py +2 -2
  5. simtools/application_control.py +118 -0
  6. simtools/applications/calculate_incident_angles.py +17 -25
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +29 -45
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +26 -45
  9. simtools/applications/convert_model_parameter_from_simtel.py +21 -42
  10. simtools/applications/db_add_file_to_db.py +12 -13
  11. simtools/applications/db_add_simulation_model_from_repository_to_db.py +20 -33
  12. simtools/applications/db_add_value_from_json_to_db.py +28 -23
  13. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +19 -34
  14. simtools/applications/db_generate_compound_indexes.py +12 -27
  15. simtools/applications/db_get_array_layouts_from_db.py +19 -39
  16. simtools/applications/db_get_file_from_db.py +15 -17
  17. simtools/applications/db_get_parameter_from_db.py +33 -35
  18. simtools/applications/db_inspect_databases.py +10 -11
  19. simtools/applications/db_upload_model_repository.py +104 -0
  20. simtools/applications/derive_ctao_array_layouts.py +16 -21
  21. simtools/applications/derive_mirror_rnda.py +9 -14
  22. simtools/applications/derive_photon_electron_spectrum.py +7 -10
  23. simtools/applications/derive_psf_parameters.py +24 -21
  24. simtools/applications/derive_trigger_rates.py +6 -9
  25. simtools/applications/docs_produce_array_element_report.py +22 -23
  26. simtools/applications/docs_produce_calibration_reports.py +26 -24
  27. simtools/applications/docs_produce_model_parameter_reports.py +15 -22
  28. simtools/applications/docs_produce_simulation_configuration_report.py +21 -22
  29. simtools/applications/generate_array_config.py +14 -33
  30. simtools/applications/generate_corsika_histograms.py +22 -43
  31. simtools/applications/generate_default_metadata.py +15 -36
  32. simtools/applications/generate_regular_arrays.py +11 -15
  33. simtools/applications/generate_simtel_event_data.py +23 -33
  34. simtools/applications/maintain_simulation_model_add_production.py +17 -48
  35. simtools/applications/maintain_simulation_model_compare_productions.py +10 -12
  36. simtools/applications/maintain_simulation_model_verify_production_tables.py +8 -11
  37. simtools/applications/merge_tables.py +15 -24
  38. simtools/applications/plot_array_layout.py +77 -55
  39. simtools/applications/plot_simtel_events.py +11 -13
  40. simtools/applications/plot_tabular_data.py +17 -38
  41. simtools/applications/plot_tabular_data_for_model_parameter.py +16 -23
  42. simtools/applications/print_version.py +14 -42
  43. simtools/applications/production_derive_corsika_limits.py +5 -9
  44. simtools/applications/production_derive_statistics.py +12 -26
  45. simtools/applications/production_generate_grid.py +20 -48
  46. simtools/applications/production_merge_corsika_limits.py +17 -21
  47. simtools/applications/run_application.py +12 -32
  48. simtools/applications/simulate_flasher.py +79 -81
  49. simtools/applications/simulate_illuminator.py +56 -197
  50. simtools/applications/{simulate_calibration_events.py → simulate_pedestals.py} +22 -68
  51. simtools/applications/simulate_prod.py +21 -33
  52. simtools/applications/simulate_prod_htcondor_generator.py +11 -25
  53. simtools/applications/submit_array_layouts.py +15 -18
  54. simtools/applications/submit_data_from_external.py +18 -34
  55. simtools/applications/submit_model_parameter_from_external.py +27 -41
  56. simtools/applications/validate_camera_efficiency.py +23 -22
  57. simtools/applications/validate_camera_fov.py +21 -27
  58. simtools/applications/validate_cumulative_psf.py +28 -37
  59. simtools/applications/validate_file_using_schema.py +35 -45
  60. simtools/applications/validate_optics.py +27 -33
  61. simtools/camera/camera_efficiency.py +8 -13
  62. simtools/configuration/commandline_parser.py +33 -11
  63. simtools/configuration/configurator.py +0 -7
  64. simtools/corsika/corsika_config.py +9 -16
  65. simtools/corsika/corsika_histograms.py +1 -1
  66. simtools/data_model/data_reader.py +0 -2
  67. simtools/data_model/metadata_collector.py +0 -2
  68. simtools/data_model/model_data_writer.py +87 -27
  69. simtools/data_model/schema.py +61 -2
  70. simtools/data_model/validate_data.py +1 -3
  71. simtools/db/db_handler.py +58 -39
  72. simtools/db/db_model_upload.py +210 -5
  73. simtools/io/hdf5_handler.py +0 -5
  74. simtools/io/io_handler.py +31 -83
  75. simtools/io/legacy_data_handler.py +0 -5
  76. simtools/job_execution/job_manager.py +43 -1
  77. simtools/layout/array_layout.py +0 -2
  78. simtools/layout/array_layout_utils.py +1 -5
  79. simtools/layout/telescope_position.py +0 -2
  80. simtools/model/array_model.py +95 -46
  81. simtools/model/calibration_model.py +0 -2
  82. simtools/model/camera.py +0 -2
  83. simtools/model/mirrors.py +0 -2
  84. simtools/model/model_parameter.py +50 -16
  85. simtools/model/model_repository.py +139 -106
  86. simtools/model/model_utils.py +21 -11
  87. simtools/model/site_model.py +0 -2
  88. simtools/model/telescope_model.py +20 -2
  89. simtools/production_configuration/calculate_statistical_uncertainties_grid_point.py +0 -2
  90. simtools/production_configuration/derive_corsika_limits.py +1 -1
  91. simtools/production_configuration/derive_production_statistics.py +0 -2
  92. simtools/production_configuration/interpolation_handler.py +0 -2
  93. simtools/ray_tracing/incident_angles.py +7 -7
  94. simtools/ray_tracing/mirror_panel_psf.py +1 -1
  95. simtools/ray_tracing/psf_analysis.py +0 -2
  96. simtools/ray_tracing/psf_parameter_optimisation.py +180 -73
  97. simtools/ray_tracing/ray_tracing.py +1 -5
  98. simtools/reporting/docs_auto_report_generator.py +108 -0
  99. simtools/reporting/docs_read_parameters.py +168 -104
  100. simtools/resources/array_elements.yml +26 -0
  101. simtools/runners/corsika_runner.py +0 -2
  102. simtools/runners/corsika_simtel_runner.py +11 -19
  103. simtools/runners/runner_services.py +5 -6
  104. simtools/runners/simtel_runner.py +0 -2
  105. simtools/runners/simtools_runner.py +0 -2
  106. simtools/schemas/application_workflow.metaschema.yml +1 -1
  107. simtools/schemas/common_definitions.schema.yml +39 -0
  108. simtools/schemas/model_parameter.metaschema.yml +19 -13
  109. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +6 -12
  110. simtools/schemas/model_parameters/adjust_gain.schema.yml +0 -5
  111. simtools/schemas/model_parameters/altitude.schema.yml +0 -5
  112. simtools/schemas/model_parameters/array_coordinates.schema.yml +0 -5
  113. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +0 -5
  114. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +0 -7
  115. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +0 -7
  116. simtools/schemas/model_parameters/array_layouts.schema.yml +0 -5
  117. simtools/schemas/model_parameters/array_triggers.schema.yml +0 -5
  118. simtools/schemas/model_parameters/array_window.schema.yml +0 -7
  119. simtools/schemas/model_parameters/asum_clipping.schema.yml +0 -3
  120. simtools/schemas/model_parameters/asum_offset.schema.yml +0 -7
  121. simtools/schemas/model_parameters/asum_shaping.schema.yml +0 -7
  122. simtools/schemas/model_parameters/asum_threshold.schema.yml +0 -7
  123. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +0 -5
  124. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +0 -5
  125. simtools/schemas/model_parameters/axes_offsets.schema.yml +0 -7
  126. simtools/schemas/model_parameters/calibration_devices.schema.yml +30 -0
  127. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +0 -7
  128. simtools/schemas/model_parameters/camera_body_shape.schema.yml +0 -7
  129. simtools/schemas/model_parameters/camera_config_file.schema.yml +0 -7
  130. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +0 -7
  131. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +0 -7
  132. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +0 -7
  133. simtools/schemas/model_parameters/camera_depth.schema.yml +0 -7
  134. simtools/schemas/model_parameters/camera_filter.schema.yml +0 -7
  135. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +0 -3
  136. simtools/schemas/model_parameters/camera_pixels.schema.yml +0 -7
  137. simtools/schemas/model_parameters/camera_transmission.schema.yml +0 -7
  138. simtools/schemas/model_parameters/channels_per_chip.schema.yml +0 -7
  139. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +0 -7
  140. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +0 -5
  141. simtools/schemas/model_parameters/dark_events.schema.yml +4 -3
  142. simtools/schemas/model_parameters/default_trigger.schema.yml +0 -7
  143. simtools/schemas/model_parameters/design_model.schema.yml +0 -7
  144. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +0 -7
  145. simtools/schemas/model_parameters/disc_bins.schema.yml +0 -7
  146. simtools/schemas/model_parameters/disc_start.schema.yml +0 -7
  147. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +0 -7
  148. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +0 -7
  149. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +0 -7
  150. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +0 -7
  151. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +0 -7
  152. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +0 -7
  153. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +0 -7
  154. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +0 -7
  155. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +0 -7
  156. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +0 -7
  157. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +0 -7
  158. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -9
  159. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +0 -7
  160. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +0 -7
  161. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +0 -7
  162. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +0 -7
  163. simtools/schemas/model_parameters/dish_shape_length.schema.yml +0 -5
  164. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -5
  165. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +0 -3
  166. simtools/schemas/model_parameters/dsum_offset.schema.yml +0 -3
  167. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +0 -3
  168. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +0 -3
  169. simtools/schemas/model_parameters/dsum_prescale.schema.yml +0 -3
  170. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +0 -3
  171. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +0 -3
  172. simtools/schemas/model_parameters/dsum_shaping.schema.yml +0 -3
  173. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +0 -3
  174. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -12
  175. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +0 -3
  176. simtools/schemas/model_parameters/effective_focal_length.schema.yml +0 -7
  177. simtools/schemas/model_parameters/epsg_code.schema.yml +0 -5
  178. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +0 -7
  179. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -9
  180. simtools/schemas/model_parameters/fadc_bins.schema.yml +0 -7
  181. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +0 -7
  182. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +0 -2
  183. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +0 -7
  184. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +0 -7
  185. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -9
  186. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +0 -7
  187. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +0 -2
  188. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +0 -7
  189. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +0 -7
  190. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +0 -7
  191. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +0 -2
  192. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +0 -7
  193. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +0 -7
  194. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +0 -7
  195. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +0 -7
  196. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +0 -7
  197. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +0 -7
  198. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +0 -3
  199. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +0 -3
  200. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +0 -3
  201. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +0 -7
  202. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +0 -2
  203. simtools/schemas/model_parameters/fadc_mhz.schema.yml +0 -7
  204. simtools/schemas/model_parameters/fadc_noise.schema.yml +0 -7
  205. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +0 -7
  206. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +0 -7
  207. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +0 -7
  208. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +0 -7
  209. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +0 -7
  210. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +0 -7
  211. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +0 -7
  212. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +0 -7
  213. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +0 -3
  214. simtools/schemas/model_parameters/flasher_angular_distribution.schema.yml +32 -0
  215. simtools/schemas/model_parameters/flasher_angular_distribution_width.schema.yml +32 -0
  216. simtools/schemas/model_parameters/flasher_bunch_size.schema.yml +28 -0
  217. simtools/schemas/model_parameters/flasher_external_trigger.schema.yml +32 -0
  218. simtools/schemas/model_parameters/flasher_photons.schema.yml +34 -0
  219. simtools/schemas/model_parameters/flasher_position.schema.yml +43 -0
  220. simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +29 -0
  221. simtools/schemas/model_parameters/flasher_pulse_offset.schema.yml +35 -0
  222. simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +30 -0
  223. simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +32 -0
  224. simtools/schemas/model_parameters/flasher_type.schema.yml +28 -0
  225. simtools/schemas/model_parameters/flasher_var_photons.schema.yml +31 -0
  226. simtools/schemas/model_parameters/flasher_wavelength.schema.yml +33 -0
  227. simtools/schemas/model_parameters/flatfielding.schema.yml +0 -7
  228. simtools/schemas/model_parameters/focal_length.schema.yml +0 -7
  229. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +0 -3
  230. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +0 -3
  231. simtools/schemas/model_parameters/focus_offset.schema.yml +0 -7
  232. simtools/schemas/model_parameters/gain_variation.schema.yml +0 -7
  233. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +2 -7
  234. simtools/schemas/model_parameters/geomag_rotation.schema.yml +2 -7
  235. simtools/schemas/model_parameters/geomag_vertical.schema.yml +2 -7
  236. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +0 -5
  237. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +0 -7
  238. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +0 -7
  239. simtools/schemas/model_parameters/laser_events.schema.yml +4 -3
  240. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +4 -3
  241. simtools/schemas/model_parameters/laser_photons.schema.yml +4 -3
  242. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +4 -3
  243. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +4 -3
  244. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +4 -3
  245. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +4 -3
  246. simtools/schemas/model_parameters/laser_var_photons.schema.yml +4 -3
  247. simtools/schemas/model_parameters/laser_wavelength.schema.yml +4 -3
  248. simtools/schemas/model_parameters/led_events.schema.yml +4 -3
  249. simtools/schemas/model_parameters/led_photons.schema.yml +4 -3
  250. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +4 -3
  251. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +4 -3
  252. simtools/schemas/model_parameters/led_var_photons.schema.yml +4 -3
  253. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +0 -7
  254. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +0 -7
  255. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +0 -7
  256. simtools/schemas/model_parameters/min_photons.schema.yml +0 -7
  257. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +0 -5
  258. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +0 -7
  259. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +0 -7
  260. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -9
  261. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +0 -7
  262. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +0 -5
  263. simtools/schemas/model_parameters/mirror_list.schema.yml +0 -7
  264. simtools/schemas/model_parameters/mirror_offset.schema.yml +0 -7
  265. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +0 -7
  266. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +0 -7
  267. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +0 -7
  268. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +0 -7
  269. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +0 -7
  270. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +0 -3
  271. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +0 -7
  272. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +0 -7
  273. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +0 -5
  274. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +0 -5
  275. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +0 -5
  276. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +0 -5
  277. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +0 -5
  278. simtools/schemas/model_parameters/num_gains.schema.yml +0 -7
  279. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +0 -7
  280. simtools/schemas/model_parameters/optics_properties.schema.yml +0 -7
  281. simtools/schemas/model_parameters/parabolic_dish.schema.yml +0 -3
  282. simtools/schemas/model_parameters/pedestal_events.schema.yml +4 -7
  283. simtools/schemas/model_parameters/photon_delay.schema.yml +0 -7
  284. simtools/schemas/model_parameters/photons_per_run.schema.yml +4 -4
  285. simtools/schemas/model_parameters/pixel_cells.schema.yml +0 -3
  286. simtools/schemas/model_parameters/pixels_parallel.schema.yml +0 -3
  287. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +0 -7
  288. simtools/schemas/model_parameters/pm_average_gain.schema.yml +0 -5
  289. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +0 -5
  290. simtools/schemas/model_parameters/pm_gain_index.schema.yml +0 -5
  291. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +0 -7
  292. simtools/schemas/model_parameters/pm_transit_time.schema.yml +4 -9
  293. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +0 -5
  294. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +0 -7
  295. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +0 -3
  296. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +0 -3
  297. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +0 -3
  298. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +0 -3
  299. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +0 -3
  300. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +0 -3
  301. simtools/schemas/model_parameters/qe_variation.schema.yml +0 -7
  302. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +0 -7
  303. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -7
  304. simtools/schemas/model_parameters/random_generator.schema.yml +0 -7
  305. simtools/schemas/model_parameters/random_mono_probability.schema.yml +0 -7
  306. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +0 -5
  307. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +0 -5
  308. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +0 -5
  309. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +0 -5
  310. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +0 -5
  311. simtools/schemas/model_parameters/sampled_output.schema.yml +0 -7
  312. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +0 -7
  313. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +0 -3
  314. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +0 -3
  315. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +0 -3
  316. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +0 -3
  317. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +0 -3
  318. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +0 -3
  319. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +0 -3
  320. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +0 -3
  321. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +0 -3
  322. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +0 -3
  323. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +0 -3
  324. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +0 -3
  325. simtools/schemas/model_parameters/stars.schema.yml +0 -5
  326. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +0 -7
  327. simtools/schemas/model_parameters/tailcut_scale.schema.yml +0 -7
  328. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +0 -7
  329. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +0 -7
  330. simtools/schemas/model_parameters/telescope_random_error.schema.yml +0 -7
  331. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +0 -7
  332. simtools/schemas/model_parameters/telescope_transmission.schema.yml +0 -7
  333. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +0 -7
  334. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +0 -7
  335. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +0 -7
  336. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +0 -7
  337. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +0 -7
  338. simtools/schemas/model_parameters/transit_time_error.schema.yml +0 -7
  339. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +0 -7
  340. simtools/schemas/model_parameters/transit_time_random.schema.yml +29 -0
  341. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +0 -7
  342. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +0 -7
  343. simtools/schemas/model_parameters/trigger_pixels.schema.yml +0 -7
  344. simtools/schemas/production_tables.schema.yml +8 -8
  345. simtools/schemas/simulation_models_info.schema.yml +78 -0
  346. simtools/simtel/simtel_config_reader.py +0 -2
  347. simtools/simtel/simtel_config_writer.py +118 -26
  348. simtools/simtel/simtel_io_metadata.py +3 -3
  349. simtools/simtel/simulator_array.py +43 -85
  350. simtools/simtel/simulator_camera_efficiency.py +0 -2
  351. simtools/simtel/simulator_light_emission.py +336 -631
  352. simtools/simtel/simulator_ray_tracing.py +2 -4
  353. simtools/simulator.py +45 -19
  354. simtools/testing/assertions.py +2 -2
  355. simtools/testing/configuration.py +21 -6
  356. simtools/testing/sim_telarray_metadata.py +4 -4
  357. simtools/utils/general.py +5 -13
  358. simtools/utils/geometry.py +34 -5
  359. simtools/utils/names.py +1 -13
  360. simtools/version.py +83 -0
  361. simtools/visualization/plot_array_layout.py +129 -23
  362. simtools/visualization/plot_incident_angles.py +0 -2
  363. simtools/visualization/plot_psf.py +163 -61
  364. simtools/visualization/plot_simtel_events.py +1 -12
  365. simtools/visualization/visualize.py +0 -12
  366. gammasimtools-0.21.0.dist-info/RECORD +0 -396
  367. simtools/model/flasher_model.py +0 -106
  368. {gammasimtools-0.21.0.dist-info → gammasimtools-0.23.0.dist-info}/WHEEL +0 -0
  369. {gammasimtools-0.21.0.dist-info → gammasimtools-0.23.0.dist-info}/licenses/LICENSE +0 -0
  370. {gammasimtools-0.21.0.dist-info → gammasimtools-0.23.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- """Simulation using the light emission package for calibration devices."""
1
+ """Light emission simulation (e.g. illuminators or flashers)."""
2
2
 
3
3
  import logging
4
4
  import shutil
@@ -10,261 +10,200 @@ import astropy.units as u
10
10
  import numpy as np
11
11
 
12
12
  from simtools.io import io_handler
13
+ from simtools.model.model_utils import initialize_simulation_models
13
14
  from simtools.runners.simtel_runner import SimtelRunner
14
15
  from simtools.utils.general import clear_default_sim_telarray_cfg_directories
15
-
16
- __all__ = ["SimulatorLightEmission"]
16
+ from simtools.utils.geometry import fiducial_radius_from_shape
17
17
 
18
18
 
19
19
  class SimulatorLightEmission(SimtelRunner):
20
20
  """
21
- Interface with sim_telarray to perform light emission package simulations.
21
+ Light emission simulation (e.g. illuminators or flashers).
22
22
 
23
- The light emission package is used to simulate an artificial light source, used for calibration.
24
- """
23
+ Uses the sim_telarray LightEmission package to simulate the light emission.
25
24
 
26
- def __init__(
27
- self,
28
- *,
29
- telescope_model,
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,
37
- label=None,
38
- test=False,
39
- ):
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)
25
+ Parameters
26
+ ----------
27
+ light_emission_config : dict, optional
28
+ Configuration for the light emission (e.g. number of events, model names)
29
+ label : str, optional
30
+ Label for the simulation
31
+ """
66
32
 
33
+ def __init__(self, light_emission_config, db_config=None, label=None):
34
+ """Initialize SimulatorLightEmission."""
67
35
  self._logger = logging.getLogger(__name__)
36
+ self.io_handler = io_handler.IOHandler()
37
+
38
+ super().__init__(
39
+ simtel_path=light_emission_config.get("simtel_path"), label=label, corsika_config=None
40
+ )
68
41
 
69
- self._telescope_model = telescope_model
70
- self._calibration_model = calibration_model
71
- self._flasher_model = flasher_model
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
42
+ self.output_directory = self.io_handler.get_output_directory()
77
43
 
78
- # IO
79
- self.io_handler = io_handler.IOHandler()
80
- self.output_directory = self.io_handler.get_output_directory(self.label)
44
+ self.telescope_model, self.site_model, self.calibration_model = (
45
+ initialize_simulation_models(
46
+ label=label,
47
+ db_config=db_config,
48
+ site=light_emission_config.get("site"),
49
+ telescope_name=light_emission_config.get("telescope"),
50
+ calibration_device_name=light_emission_config.get("light_source"),
51
+ model_version=light_emission_config.get("model_version"),
52
+ )
53
+ )
54
+ self.telescope_model.write_sim_telarray_config_file(additional_models=self.site_model)
81
55
 
82
- self.number_events = self.light_emission_config["number_events"]
56
+ self.light_emission_config = self._initialize_light_emission_configuration(
57
+ light_emission_config
58
+ )
83
59
 
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
60
+ def _initialize_light_emission_configuration(self, config):
61
+ """Initialize light emission configuration."""
62
+ if self.calibration_model.get_parameter_value("flasher_type"):
63
+ config["light_source_type"] = self.calibration_model.get_parameter_value(
64
+ "flasher_type"
65
+ ).lower()
66
+
67
+ config["flasher_photons"] = (
68
+ self.calibration_model.get_parameter_value("flasher_photons")
69
+ if not config.get("test", False)
70
+ else 1e8
71
+ )
72
+
73
+ if config.get("light_source_position") is not None:
74
+ config["light_source_position"] = (
75
+ np.array(config["light_source_position"], dtype=float) * u.m
90
76
  )
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
77
+
78
+ return config
79
+
80
+ def simulate(self):
81
+ """
82
+ Simulate light emission.
83
+
84
+ Returns
85
+ -------
86
+ Path
87
+ The output simtel file path.
88
+ """
89
+ run_script = self.prepare_script()
90
+ log_path = Path(self.output_directory) / "logfile.log"
91
+ with open(log_path, "w", encoding="utf-8") as fh:
92
+ subprocess.run(
93
+ run_script,
94
+ shell=False,
95
+ check=False,
96
+ text=True,
97
+ stdout=fh,
98
+ stderr=fh,
96
99
  )
97
- else:
98
- self.photons_per_run = 1e8
100
+ out = Path(self._get_simulation_output_filename())
101
+ if not out.exists():
102
+ self._logger.warning(f"Expected sim_telarray output not found: {out}")
103
+ return out
104
+
105
+ def prepare_script(self):
106
+ """
107
+ Build and return bash run script containing the light-emission command.
108
+
109
+ Returns
110
+ -------
111
+ Path
112
+ Full path of the run script.
113
+ """
114
+ script_dir = self.output_directory.joinpath("scripts")
115
+ script_dir.mkdir(parents=True, exist_ok=True)
116
+
117
+ app_name = self._get_light_emission_application_name()
118
+ script_file = script_dir / f"{app_name}-light_emission.sh"
119
+ self._logger.debug(f"Run bash script - {script_file}")
99
120
 
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)
121
+ target_out = Path(self._get_simulation_output_filename())
122
+ if target_out.exists():
123
+ raise FileExistsError(
124
+ f"sim_telarray output file exists, cancelling simulation: {target_out}"
125
+ )
126
+
127
+ lines = [
128
+ "#!/usr/bin/env bash\n",
129
+ f"{self._make_light_emission_script()}\n\n",
130
+ (
131
+ f"[ -s '{self.output_directory}/{app_name}.iact.gz' ] || "
132
+ f"{{ echo 'LightEmission did not produce IACT file' >&2; exit 1; }}\n\n"
133
+ ),
134
+ f"{self._make_simtel_script()}\n\n",
135
+ f"rm -f '{self.output_directory}/{app_name}.iact.gz'\n\n",
136
+ ]
103
137
 
104
- # Runtime variables
105
- self.distance = None
138
+ script_file.write_text("".join(lines), encoding="utf-8")
139
+ script_file.chmod(script_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
140
+ return script_file
106
141
 
107
- def _get_prefix(self) -> str:
142
+ def _get_prefix(self):
108
143
  prefix = self.light_emission_config.get("output_prefix", "")
109
144
  if prefix is not None:
110
145
  return f"{prefix}_"
111
146
  return ""
112
147
 
113
- def _infer_application(self) -> tuple[str, str]:
114
- """Infer the LightEmission application and mode from type/setup.
148
+ def _get_light_emission_application_name(self):
149
+ """
150
+ Return the LightEmission application and mode from type.
115
151
 
116
152
  Returns
117
153
  -------
118
- tuple[str, str]
119
- (app_name, mode)
154
+ str
155
+ app_name
120
156
  """
121
- if self.light_source_type == "flasher":
122
- return ("ff-1m", "flasher")
157
+ if self.light_emission_config["light_source_type"] == "flat_fielding":
158
+ return "ff-1m"
123
159
  # default to illuminator xyzls, mode from setup
124
- mode = self.light_source_setup or "layout"
125
- return ("xyzls", mode)
160
+ return "xyzls"
126
161
 
127
- @staticmethod
128
- def light_emission_default_configuration():
162
+ def _get_telescope_pointing(self):
129
163
  """
130
- Get default light emission configuration.
164
+ Return telescope pointing based on light source type.
131
165
 
132
- Returns
133
- -------
134
- dict
135
- Default configuration light emission.
136
-
137
- """
138
- return {
139
- "zenith_angle": {
140
- "len": 1,
141
- "unit": u.Unit("deg"),
142
- "default": 0.0 * u.deg,
143
- "names": ["zenith", "theta"],
144
- },
145
- "azimuth_angle": {
146
- "len": 1,
147
- "unit": u.Unit("deg"),
148
- "default": 0.0 * u.deg,
149
- "names": ["azimuth", "phi"],
150
- },
151
- "source_distance": {
152
- "len": 1,
153
- "unit": u.Unit("m"),
154
- "default": 1000 * u.m,
155
- "names": ["sourcedist", "srcdist"],
156
- },
157
- "off_axis_angle": {
158
- "len": 1,
159
- "unit": u.Unit("deg"),
160
- "default": 0 * u.deg,
161
- "names": ["off_axis"],
162
- },
163
- "fadc_bins": {
164
- "len": 1,
165
- "unit": u.dimensionless_unscaled,
166
- "default": 128,
167
- "names": ["fadc_bins"],
168
- },
169
- }
170
-
171
- @staticmethod
172
- def flasher_default_configuration():
173
- """
174
- Get default flasher configuration.
166
+ For flat_fielding sims, avoid calibration pointing entirely; default angles to (0,0).
175
167
 
176
168
  Returns
177
169
  -------
178
- dict
179
- Default configuration for flasher devices.
170
+ tuple
171
+ The telescope pointing angles (theta, phi).
172
+
180
173
  """
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
-
244
- def calibration_pointing_direction(self):
174
+ if self.light_emission_config["light_source_type"] == "flat_fielding":
175
+ return 0.0, 0.0
176
+ if self.light_emission_config.get("light_source_position") is not None:
177
+ self._logger.info("Using fixed (vertical up) telescope pointing.")
178
+ return 0.0, 0.0
179
+ _, angles = self._calibration_pointing_direction()
180
+ return angles[0], angles[1]
181
+
182
+ def _calibration_pointing_direction(self, x_cal=None, y_cal=None, z_cal=None):
245
183
  """
246
184
  Calculate the pointing of the calibration device towards the telescope.
247
185
 
186
+ This is for calibration devices not installed on telescopes (e.g. illuminators).
187
+
248
188
  Returns
249
189
  -------
250
190
  list
251
191
  The pointing vector from the calibration device to the telescope.
252
192
  """
253
- x_cal, y_cal, z_cal = self._calibration_model.get_parameter_value_with_unit(
254
- "array_element_position_ground"
255
- )
193
+ if x_cal is None or y_cal is None or z_cal is None:
194
+ x_cal, y_cal, z_cal = self.calibration_model.get_parameter_value_with_unit(
195
+ "array_element_position_ground"
196
+ )
256
197
  x_cal, y_cal, z_cal = [coord.to(u.m).value for coord in (x_cal, y_cal, z_cal)]
257
198
  cal_vect = np.array([x_cal, y_cal, z_cal])
258
- x_tel, y_tel, z_tel = self._telescope_model.get_parameter_value_with_unit(
199
+ x_tel, y_tel, z_tel = self.telescope_model.get_parameter_value_with_unit(
259
200
  "array_element_position_ground"
260
201
  )
261
202
  x_tel, y_tel, z_tel = [coord.to(u.m).value for coord in (x_tel, y_tel, z_tel)]
262
-
263
203
  tel_vect = np.array([x_tel, y_tel, z_tel])
264
204
 
265
205
  direction_vector = tel_vect - cal_vect
266
206
  # pointing vector from calibration device to telescope
267
-
268
207
  pointing_vector = np.round(direction_vector / np.linalg.norm(direction_vector), 6)
269
208
 
270
209
  # Calculate telescope theta and phi angles
@@ -285,53 +224,52 @@ class SimulatorLightEmission(SimtelRunner):
285
224
  )
286
225
  return pointing_vector.tolist(), [tel_theta, tel_phi, source_theta, source_phi]
287
226
 
288
- def _write_telpos_file(self):
227
+ def _write_telescope_position_file(self):
289
228
  """
290
- Write the telescope positions to a telpos file.
229
+ Write the telescope positions to a telescope_position file.
291
230
 
292
231
  The file will contain lines in the format: x y z r in cm
293
232
 
294
233
  Returns
295
234
  -------
296
235
  Path
297
- The path to the generated telpos file.
236
+ The path to the generated telescope_position file.
298
237
  """
299
- telpos_file = self.output_directory.joinpath("telpos.dat")
300
- x_tel, y_tel, z_tel = self._telescope_model.get_parameter_value_with_unit(
238
+ x_tel, y_tel, z_tel = self.telescope_model.get_parameter_value_with_unit(
301
239
  "array_element_position_ground"
302
240
  )
303
241
  x_tel, y_tel, z_tel = [coord.to(u.cm).value for coord in (x_tel, y_tel, z_tel)]
304
242
 
305
- radius = self._telescope_model.get_parameter_value_with_unit("telescope_sphere_radius")
243
+ radius = self.telescope_model.get_parameter_value_with_unit("telescope_sphere_radius")
306
244
  radius = radius.to(u.cm).value # Convert radius to cm
307
- with telpos_file.open("w", encoding="utf-8") as file:
308
- file.write(f"{x_tel} {y_tel} {z_tel} {radius}\n")
309
245
 
310
- return telpos_file
246
+ telescope_position_file = self.output_directory.joinpath("telescope_position.dat")
247
+ telescope_position_file.write_text(f"{x_tel} {y_tel} {z_tel} {radius}\n", encoding="utf-8")
248
+ return telescope_position_file
311
249
 
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}")
250
+ def _prepare_flasher_atmosphere_files(self, config_directory, model_id=1):
251
+ """
252
+ Prepare canonical atmosphere aliases for ff-1m and return model id.
316
253
 
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
254
+ The ff-1m tool requires atmosphere files atmprof1.dat or atm_profile_model_1.dat and
255
+ as configuration parameter the atmosphere id ('--atmosphere id').
256
+
257
+ """
258
+ src_path = config_directory / self.site_model.get_parameter_value("atmospheric_profile")
259
+ self._logger.debug(f"Using atmosphere profile: {src_path}")
260
+
261
+ for name in (f"atmprof{model_id}.dat", f"atm_profile_model_{model_id}.dat"):
262
+ dst = config_directory / name
325
263
  try:
326
- dst.symlink_to(src_path)
327
- except OSError:
264
+ if dst.exists() or dst.is_symlink():
265
+ dst.unlink()
328
266
  try:
267
+ dst.symlink_to(src_path)
268
+ except OSError:
329
269
  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
270
+ except OSError as copy_err:
271
+ self._logger.warning(f"Failed to create atmosphere alias {dst.name}: {copy_err}")
272
+ return model_id
335
273
 
336
274
  def _make_light_emission_script(self):
337
275
  """
@@ -345,153 +283,109 @@ class SimulatorLightEmission(SimtelRunner):
345
283
  str
346
284
  The commands to run the Light Emission package
347
285
  """
348
- x_tel, y_tel, z_tel = self._telescope_model.get_parameter_value_with_unit(
349
- "array_element_position_ground"
350
- )
351
-
352
286
  config_directory = self.io_handler.get_model_configuration_directory(
353
- label=self.label, model_version=self._site_model.model_version
287
+ model_version=self.site_model.model_version
354
288
  )
355
- telpos_file = self._write_telpos_file()
356
-
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
-
364
- corsika_observation_level = self._site_model.get_parameter_value_with_unit(
289
+ app_name = self._get_light_emission_application_name()
290
+ corsika_observation_level = self.site_model.get_parameter_value_with_unit(
365
291
  "corsika_observation_level"
366
292
  )
367
- parts.append(
368
- self._build_altitude_atmo_block(
369
- app_name, config_directory, corsika_observation_level, telpos_file
370
- )
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
293
 
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."""
294
+ parts = [str(self._simtel_path / "sim_telarray/LightEmission") + f"/{app_name}"]
295
+ parts.extend(self._get_site_command(app_name, config_directory, corsika_observation_level))
296
+ parts.extend(self._get_light_source_command())
297
+ if self.light_emission_config["light_source_type"] == "illuminator":
298
+ parts += [
299
+ "-A",
300
+ (
301
+ f"{config_directory}/"
302
+ f"{self.telescope_model.get_parameter_value('atmospheric_profile')}"
303
+ ),
304
+ ]
305
+ parts += [f"-o {self.output_directory}/{app_name}.iact.gz", "\n"]
306
+ return " ".join(parts)
307
+
308
+ def _get_site_command(self, app_name, config_directory, corsika_observation_level):
309
+ """Return site command with altitude, atmosphere and telescope_position handling."""
388
310
  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
311
  atmo_id = self._prepare_flasher_atmosphere_files(config_directory)
395
- seg.append(f" --atmosphere {atmo_id}")
396
- return "".join(seg)
312
+ return [
313
+ "-I.",
314
+ f"-I{self._simtel_path / 'sim_telarray/cfg'}",
315
+ f"-I{config_directory}",
316
+ f"--altitude {corsika_observation_level.to(u.m).value}",
317
+ f"--atmosphere {atmo_id}",
318
+ ]
397
319
  # 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)
320
+ return [
321
+ f"-h {corsika_observation_level.to(u.m).value} ",
322
+ f"--telpos-file {self._write_telescope_position_file()}",
323
+ ]
324
+
325
+ def _get_light_source_command(self):
326
+ """Return light-source specific command options."""
327
+ if self.light_emission_config["light_source_type"] == "flat_fielding":
328
+ return self._add_flasher_command_options()
329
+ if self.light_emission_config["light_source_type"] == "illuminator":
330
+ return self._add_illuminator_command_options()
331
+ raise ValueError(
332
+ f"Unknown light_source_type '{self.light_emission_config['light_source_type']}'"
333
+ )
412
334
 
413
- def _add_flasher_options(self, command):
335
+ def _add_flasher_command_options(self):
414
336
  """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")
337
+ flasher_xyz = self.calibration_model.get_parameter_value_with_unit("flasher_position")
338
+ camera_radius = fiducial_radius_from_shape(
339
+ self.telescope_model.get_parameter_value_with_unit("camera_body_diameter")
421
340
  .to(u.cm)
422
- .value
423
- / 2
341
+ .value,
342
+ self.telescope_model.get_parameter_value("camera_body_shape"),
424
343
  )
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(
344
+ flasher_wavelength = self.calibration_model.get_parameter_value_with_unit(
345
+ "flasher_wavelength"
346
+ )
347
+ dist_cm = self.calculate_distance_focal_plane_calibration_device().to(u.cm).value
348
+ angular_distribution = self._get_angular_distribution_string_for_sim_telarray()
349
+
350
+ return [
351
+ f"--events {self.light_emission_config['number_of_events']}",
352
+ f"--photons {self.light_emission_config['flasher_photons']}",
353
+ f"--bunchsize {self.calibration_model.get_parameter_value('flasher_bunch_size')}",
354
+ f"--xy {flasher_xyz[0].to(u.cm).value},{flasher_xyz[1].to(u.cm).value}",
355
+ f"--distance {dist_cm}",
356
+ f"--camera-radius {camera_radius}",
357
+ f"--spectrum {int(flasher_wavelength.to(u.nm).value)}",
358
+ f"--lightpulse {self._get_pulse_shape_string_for_sim_telarray()}",
359
+ f"--angular-distribution {angular_distribution}",
360
+ ]
361
+
362
+ def _add_illuminator_command_options(self):
363
+ """Get illuminator-specific command options for light emission script."""
364
+ pos = self.light_emission_config.get("light_source_position")
365
+ if pos is None:
366
+ pos = self.calibration_model.get_parameter_value_with_unit(
472
367
  "array_element_position_ground"
473
368
  )
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"
493
-
494
- return command
369
+ x_cal, y_cal, z_cal = pos
370
+ if self.light_emission_config.get("light_source_pointing"):
371
+ pointing_vector = self.light_emission_config["light_source_pointing"]
372
+ else:
373
+ pointing_vector = self._calibration_pointing_direction(x_cal, y_cal, z_cal)[0]
374
+ flasher_wavelength = self.calibration_model.get_parameter_value_with_unit(
375
+ "flasher_wavelength"
376
+ )
377
+ angular_distribution = self._get_angular_distribution_string_for_sim_telarray()
378
+
379
+ return [
380
+ f"-x {x_cal.to(u.cm).value}",
381
+ f"-y {y_cal.to(u.cm).value}",
382
+ f"-z {z_cal.to(u.cm).value}",
383
+ f"-d {','.join(map(str, pointing_vector))}",
384
+ f"-n {self.light_emission_config['flasher_photons']}",
385
+ f"-s {int(flasher_wavelength.to(u.nm).value)}",
386
+ f"-p {self._get_pulse_shape_string_for_sim_telarray()}",
387
+ f"-a {angular_distribution}",
388
+ ]
495
389
 
496
390
  def _make_simtel_script(self):
497
391
  """
@@ -502,291 +396,102 @@ class SimulatorLightEmission(SimtelRunner):
502
396
  str
503
397
  The command to run sim_telarray
504
398
  """
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()
399
+ theta, phi = self._get_telescope_pointing()
510
400
 
511
401
  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} "
517
- self._remove_line_from_config(self._telescope_model.config_file_path, "array_triggers")
518
- self._remove_line_from_config(self._telescope_model.config_file_path, "axes_offsets")
519
-
520
- command += "-DNUM_TELESCOPES=1 "
521
-
522
- command += super().get_config_option(
523
- "altitude",
524
- self._site_model.get_parameter_value_with_unit("corsika_observation_level")
525
- .to(u.m)
526
- .value,
527
- )
528
- command += super().get_config_option(
529
- "atmospheric_transmission",
530
- self._site_model.get_parameter_value("atmospheric_transmission"),
531
- )
532
- command += super().get_config_option("TRIGGER_TELESCOPES", "1")
533
-
534
- command += super().get_config_option("TELTRIG_MIN_SIGSUM", "2")
535
- command += super().get_config_option("PULSE_ANALYSIS", "-30")
536
- command += super().get_config_option("MAXIMUM_TELESCOPES", 1)
537
402
 
538
- if self.light_source_type == "variable":
539
- command += super().get_config_option("telescope_theta", 0)
540
- command += super().get_config_option("telescope_phi", 0)
541
- else:
542
- command += super().get_config_option("telescope_theta", f"{angles[0]}")
543
- command += super().get_config_option("telescope_phi", f"{angles[1]}")
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
-
549
- command += super().get_config_option("power_law", "2.68")
550
- app_name, app_mode = self._infer_application()
403
+ parts = [
404
+ f"{simtel_bin}",
405
+ f"-I{self.telescope_model.config_file_directory}",
406
+ f"-I{simtel_bin}",
407
+ f"-c {self.telescope_model.config_file_path}",
408
+ "-DNUM_TELESCOPES=1",
409
+ super().get_config_option(
410
+ "altitude",
411
+ self.site_model.get_parameter_value_with_unit("corsika_observation_level")
412
+ .to(u.m)
413
+ .value,
414
+ ),
415
+ super().get_config_option(
416
+ "atmospheric_transmission",
417
+ self.site_model.get_parameter_value("atmospheric_transmission"),
418
+ ),
419
+ super().get_config_option("TRIGGER_TELESCOPES", "1"),
420
+ super().get_config_option("TELTRIG_MIN_SIGSUM", "2"),
421
+ super().get_config_option("PULSE_ANALYSIS", "-30"),
422
+ super().get_config_option("MAXIMUM_TELESCOPES", 1),
423
+ super().get_config_option("telescope_theta", f"{theta}"),
424
+ super().get_config_option("telescope_phi", f"{phi}"),
425
+ ]
426
+
427
+ if self.light_emission_config["light_source_type"] == "flat_fielding":
428
+ parts.append(super().get_config_option("Bypass_Optics", "1"))
429
+
430
+ app_name = self._get_light_emission_application_name()
551
431
  pref = self._get_prefix()
552
- command += super().get_config_option(
553
- "input_file", f"{self.output_directory}/{app_name}.iact.gz"
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
-
563
- command += super().get_config_option(
564
- "output_file",
565
- f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.simtel.zst",
566
- )
567
- command += super().get_config_option(
568
- "histogram_file",
569
- f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.ctsim.hdata\n",
570
- )
571
-
572
- # Remove the default sim_telarray configuration directories
573
- return clear_default_sim_telarray_cfg_directories(command)
574
-
575
- def _remove_line_from_config(self, file_path, line_prefix):
576
- """
577
- Remove lines starting with a specific prefix from the config.
578
-
579
- Parameters
580
- ----------
581
- file_path : Path
582
- The path to the configuration file.
583
- line_prefix : str
584
- The prefix of lines to be removed.
585
- """
586
- file_path = Path(file_path)
587
- with file_path.open("r", encoding="utf-8") as file:
588
- lines = file.readlines()
589
-
590
- with file_path.open("w", encoding="utf-8") as file:
591
- for line in lines:
592
- if not line.startswith(line_prefix):
593
- file.write(line)
594
-
595
- def prepare_script(self):
596
- """
597
- Build and return bash run script containing the light-emission command.
598
-
599
- Returns
600
- -------
601
- Path
602
- Full path of the run script.
603
- """
604
- self._logger.debug("Creating run bash script")
605
-
606
- _script_dir = self.output_directory.joinpath("scripts")
607
- _script_dir.mkdir(parents=True, exist_ok=True)
608
- _script_file = _script_dir.joinpath(f"{self._infer_application()[0]}-lightemission.sh")
609
- self._logger.debug(f"Run bash script - {_script_file}")
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
-
617
- command_le = self._make_light_emission_script()
618
- command_simtel = self._make_simtel_script()
619
-
620
- with _script_file.open("w", encoding="utf-8") as file:
621
- file.write("#!/usr/bin/env bash\n")
622
-
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
- )
629
- file.write(f"{command_simtel}\n\n")
630
-
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")
633
-
634
- _script_file.chmod(_script_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
635
- return _script_file
432
+ parts += [
433
+ super().get_config_option("power_law", "2.68"),
434
+ super().get_config_option("input_file", f"{self.output_directory}/{app_name}.iact.gz"),
435
+ super().get_config_option(
436
+ "output_file", f"{self.output_directory}/{pref}{app_name}.simtel.zst"
437
+ ),
438
+ super().get_config_option(
439
+ "histogram_file", f"{self.output_directory}/{pref}{app_name}.ctsim.hdata\n"
440
+ ),
441
+ ]
442
+
443
+ return clear_default_sim_telarray_cfg_directories(" ".join(parts))
636
444
 
637
445
  def _get_simulation_output_filename(self):
638
446
  """Get the filename of the simulation output."""
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()
447
+ app_name = self._get_light_emission_application_name()
647
448
  pref = self._get_prefix()
648
- return f"{self.output_directory}/{pref}{app_name}_{app_mode}{dist_suffix}.simtel.zst"
449
+ return f"{self.output_directory}/{pref}{app_name}.simtel.zst"
649
450
 
650
- def _get_distance_for_plotting(self):
651
- """Get the distance to be used for plotting as an astropy Quantity.
652
-
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.
451
+ def calculate_distance_focal_plane_calibration_device(self):
656
452
  """
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)
453
+ Calculate distance between focal plane and calibration device.
659
454
 
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:
688
- subprocess.run(
689
- run_script,
690
- shell=False,
691
- check=False,
692
- text=True,
693
- stdout=fh,
694
- stderr=fh,
695
- )
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
455
+ For flasher-type light sources. Flasher position is given in mirror coordinates,
456
+ with positive z pointing towards the camera, so the distance is focal_length - flasher_z.
700
457
 
701
- def distance_list(self, arg):
458
+ Returns
459
+ -------
460
+ astropy.units.Quantity
461
+ Distance between calibration device and focal plane.
702
462
  """
703
- Convert distance list to astropy quantities.
463
+ focal_length = self.telescope_model.get_parameter_value_with_unit("focal_length").to(u.m)
464
+ flasher_z = self.calibration_model.get_parameter_value_with_unit("flasher_position")[2].to(
465
+ u.m
466
+ )
467
+ return focal_length - flasher_z
704
468
 
705
- Parameters
706
- ----------
707
- arg: list
708
- List of distances.
469
+ def _get_angular_distribution_string_for_sim_telarray(self):
470
+ """
471
+ Get the angular distribution string for sim_telarray.
709
472
 
710
473
  Returns
711
474
  -------
712
- values: list
713
- List of distances as astropy quantities.
475
+ str
476
+ The angular distribution string.
714
477
  """
715
- try:
716
- return [float(x) * u.m for x in arg]
717
- except ValueError as exc:
718
- raise ValueError("Distances must be numeric values") from exc
478
+ option_string = self.calibration_model.get_parameter_value(
479
+ "flasher_angular_distribution"
480
+ ).lower()
481
+ width = self.calibration_model.get_parameter_value_with_unit(
482
+ "flasher_angular_distribution_width"
483
+ )
484
+ return f"{option_string}:{width.to(u.deg).value}" if width is not None else option_string
719
485
 
720
- def update_light_emission_config(self, key: str, value):
721
- """
722
- Update the light emission configuration.
723
-
724
- Parameters
725
- ----------
726
- key : str
727
- The key in the configuration to update.
728
- value : Any
729
- The new value to set for the key.
486
+ def _get_pulse_shape_string_for_sim_telarray(self):
730
487
  """
731
- if key in self.light_emission_config:
732
- self.light_emission_config[key]["default"] = value
733
- else:
734
- raise KeyError(f"Key '{key}' not found in light emission configuration.")
488
+ Get the pulse shape string for sim_telarray.
735
489
 
736
- def calculate_distance_telescope_calibration_device(self):
737
- """Calculate distance(s) between telescope and calibration device."""
738
- if self.light_source_setup == "layout":
739
- # Layout positions: Use DB coordinates
740
- x_cal, y_cal, z_cal = self._calibration_model.get_parameter_value_with_unit(
741
- "array_element_position_ground"
742
- )
743
- x_cal, y_cal, z_cal = [coord.to(u.m).value for coord in (x_cal, y_cal, z_cal)]
744
- x_tel, y_tel, z_tel = self._telescope_model.get_parameter_value_with_unit(
745
- "array_element_position_ground"
746
- )
747
- x_tel, y_tel, z_tel = [coord.to(u.m).value for coord in (x_tel, y_tel, z_tel)]
748
- tel_vect = np.array([x_tel, y_tel, z_tel])
749
- cal_vect = np.array([x_cal, y_cal, z_cal])
750
- distance = np.linalg.norm(cal_vect - tel_vect)
751
- self._logger.info(f"Distance between telescope and calibration device: {distance} m")
752
- return [distance * u.m]
753
-
754
- # Variable positions: Calculate distances for all positions
755
- x_tel = self.light_emission_config["x_pos"]["default"].to(u.m).value
756
- y_tel = self.light_emission_config["y_pos"]["default"].to(u.m).value
757
- z_positions = self.light_emission_config["z_pos"]["default"]
758
-
759
- distances = []
760
- for z in z_positions:
761
- tel_vect = np.array([x_tel, y_tel, z.to(u.m).value])
762
- cal_vect = np.array([0, 0, 0]) # Calibration device at origin
763
- distances.append(np.linalg.norm(cal_vect - tel_vect) * u.m)
764
- return distances
765
-
766
- def simulate_variable_distances(self, args_dict):
767
- """Simulate light emission for variable distances and return output files list."""
768
- if args_dict["distances_ls"] is not None:
769
- self.update_light_emission_config(
770
- "z_pos", self.distance_list(args_dict["distances_ls"])
771
- )
772
- self._logger.info(
773
- f"Simulating for distances: {self.light_emission_config['z_pos']['default']}"
774
- )
775
- outputs: list[Path] = []
776
- distances = self.calculate_distance_telescope_calibration_device()
777
-
778
- for current_distance, z_pos in zip(
779
- distances, self.light_emission_config["z_pos"]["default"]
780
- ):
781
- self.update_light_emission_config("z_pos", z_pos)
782
- self.distance = current_distance
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()]
490
+ Returns
491
+ -------
492
+ str
493
+ The pulse shape string.
494
+ """
495
+ option_string = self.calibration_model.get_parameter_value("flasher_pulse_shape").lower()
496
+ width = self.calibration_model.get_parameter_value_with_unit("flasher_pulse_width")
497
+ return f"{option_string}:{width.to(u.ns).value}" if width is not None else option_string