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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/METADATA +2 -3
  2. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/RECORD +313 -296
  3. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/entry_points.txt +3 -2
  4. simtools/_version.py +2 -2
  5. simtools/applications/calculate_incident_angles.py +1 -4
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +1 -2
  7. simtools/applications/convert_model_parameter_from_simtel.py +0 -1
  8. simtools/applications/db_generate_compound_indexes.py +4 -17
  9. simtools/applications/db_upload_model_repository.py +122 -0
  10. simtools/applications/derive_psf_parameters.py +71 -42
  11. simtools/applications/docs_produce_array_element_report.py +1 -1
  12. simtools/applications/docs_produce_calibration_reports.py +1 -1
  13. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  14. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  15. simtools/applications/generate_corsika_histograms.py +8 -185
  16. simtools/applications/maintain_simulation_model_add_production.py +81 -0
  17. simtools/applications/merge_tables.py +1 -1
  18. simtools/applications/plot_array_layout.py +1 -2
  19. simtools/applications/plot_simtel_events.py +2 -228
  20. simtools/applications/print_version.py +8 -7
  21. simtools/applications/production_derive_statistics.py +1 -2
  22. simtools/applications/production_generate_grid.py +1 -1
  23. simtools/applications/simulate_flasher.py +74 -72
  24. simtools/applications/simulate_illuminator.py +52 -186
  25. simtools/applications/{simulate_calibration_events.py → simulate_pedestals.py} +9 -55
  26. simtools/applications/submit_model_parameter_from_external.py +0 -1
  27. simtools/applications/validate_camera_efficiency.py +0 -1
  28. simtools/applications/validate_camera_fov.py +1 -2
  29. simtools/applications/validate_cumulative_psf.py +2 -3
  30. simtools/applications/validate_file_using_schema.py +20 -12
  31. simtools/applications/validate_optics.py +2 -2
  32. simtools/camera/camera_efficiency.py +8 -11
  33. simtools/configuration/commandline_parser.py +1 -7
  34. simtools/configuration/configurator.py +0 -2
  35. simtools/corsika/corsika_config.py +9 -11
  36. simtools/corsika/corsika_histograms.py +82 -1
  37. simtools/data_model/model_data_writer.py +87 -25
  38. simtools/data_model/schema.py +61 -2
  39. simtools/data_model/validate_data.py +1 -1
  40. simtools/db/db_handler.py +103 -48
  41. simtools/db/db_model_upload.py +247 -16
  42. simtools/io/io_handler.py +31 -83
  43. simtools/job_execution/job_manager.py +45 -0
  44. simtools/layout/array_layout_utils.py +1 -5
  45. simtools/model/array_model.py +93 -42
  46. simtools/model/model_parameter.py +20 -9
  47. simtools/model/model_repository.py +197 -109
  48. simtools/model/model_utils.py +21 -6
  49. simtools/model/telescope_model.py +20 -0
  50. simtools/production_configuration/derive_corsika_limits.py +1 -1
  51. simtools/ray_tracing/incident_angles.py +7 -7
  52. simtools/ray_tracing/mirror_panel_psf.py +1 -1
  53. simtools/ray_tracing/psf_parameter_optimisation.py +1106 -565
  54. simtools/ray_tracing/ray_tracing.py +1 -3
  55. simtools/reporting/docs_read_parameters.py +171 -101
  56. simtools/resources/array_elements.yml +26 -0
  57. simtools/runners/corsika_simtel_runner.py +11 -17
  58. simtools/runners/runner_services.py +5 -6
  59. simtools/runners/simtools_runner.py +0 -2
  60. simtools/schemas/application_workflow.metaschema.yml +1 -1
  61. simtools/schemas/common_definitions.schema.yml +39 -0
  62. simtools/schemas/model_parameter.metaschema.yml +19 -13
  63. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +6 -12
  64. simtools/schemas/model_parameters/adjust_gain.schema.yml +0 -5
  65. simtools/schemas/model_parameters/altitude.schema.yml +0 -5
  66. simtools/schemas/model_parameters/array_coordinates.schema.yml +0 -5
  67. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +0 -5
  68. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +0 -7
  69. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +0 -7
  70. simtools/schemas/model_parameters/array_layouts.schema.yml +0 -5
  71. simtools/schemas/model_parameters/array_triggers.schema.yml +0 -5
  72. simtools/schemas/model_parameters/array_window.schema.yml +0 -7
  73. simtools/schemas/model_parameters/asum_clipping.schema.yml +0 -3
  74. simtools/schemas/model_parameters/asum_offset.schema.yml +0 -7
  75. simtools/schemas/model_parameters/asum_shaping.schema.yml +0 -7
  76. simtools/schemas/model_parameters/asum_threshold.schema.yml +0 -7
  77. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +0 -5
  78. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +0 -5
  79. simtools/schemas/model_parameters/axes_offsets.schema.yml +0 -7
  80. simtools/schemas/model_parameters/calibration_devices.schema.yml +30 -0
  81. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +0 -7
  82. simtools/schemas/model_parameters/camera_body_shape.schema.yml +0 -7
  83. simtools/schemas/model_parameters/camera_config_file.schema.yml +0 -7
  84. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +0 -7
  85. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +0 -7
  86. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +0 -7
  87. simtools/schemas/model_parameters/camera_depth.schema.yml +0 -7
  88. simtools/schemas/model_parameters/camera_filter.schema.yml +0 -7
  89. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +0 -3
  90. simtools/schemas/model_parameters/camera_pixels.schema.yml +0 -7
  91. simtools/schemas/model_parameters/camera_transmission.schema.yml +0 -7
  92. simtools/schemas/model_parameters/channels_per_chip.schema.yml +0 -7
  93. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +0 -7
  94. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +0 -5
  95. simtools/schemas/model_parameters/dark_events.schema.yml +4 -3
  96. simtools/schemas/model_parameters/default_trigger.schema.yml +0 -7
  97. simtools/schemas/model_parameters/design_model.schema.yml +0 -7
  98. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +0 -7
  99. simtools/schemas/model_parameters/disc_bins.schema.yml +0 -7
  100. simtools/schemas/model_parameters/disc_start.schema.yml +0 -7
  101. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +0 -7
  102. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +0 -7
  103. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +0 -7
  104. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +0 -7
  105. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +0 -7
  106. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +0 -7
  107. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +0 -7
  108. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +0 -7
  109. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +0 -7
  110. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +0 -7
  111. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +0 -7
  112. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -9
  113. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +0 -7
  114. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +0 -7
  115. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +0 -7
  116. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +0 -7
  117. simtools/schemas/model_parameters/dish_shape_length.schema.yml +0 -5
  118. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -5
  119. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +0 -3
  120. simtools/schemas/model_parameters/dsum_offset.schema.yml +0 -3
  121. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +0 -3
  122. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +0 -3
  123. simtools/schemas/model_parameters/dsum_prescale.schema.yml +0 -3
  124. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +0 -3
  125. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +0 -3
  126. simtools/schemas/model_parameters/dsum_shaping.schema.yml +0 -3
  127. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +0 -3
  128. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -12
  129. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +0 -3
  130. simtools/schemas/model_parameters/effective_focal_length.schema.yml +0 -7
  131. simtools/schemas/model_parameters/epsg_code.schema.yml +0 -5
  132. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +0 -7
  133. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -9
  134. simtools/schemas/model_parameters/fadc_bins.schema.yml +0 -7
  135. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +0 -7
  136. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +0 -2
  137. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +0 -7
  138. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +0 -7
  139. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -9
  140. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +0 -7
  141. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +0 -2
  142. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +0 -7
  143. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +0 -7
  144. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +0 -7
  145. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +0 -2
  146. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +0 -7
  147. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +0 -7
  148. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +0 -7
  149. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +0 -7
  150. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +0 -7
  151. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +0 -7
  152. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +0 -3
  153. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +0 -3
  154. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +0 -3
  155. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +0 -7
  156. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +0 -2
  157. simtools/schemas/model_parameters/fadc_mhz.schema.yml +0 -7
  158. simtools/schemas/model_parameters/fadc_noise.schema.yml +0 -7
  159. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +0 -7
  160. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +0 -7
  161. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +0 -7
  162. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +0 -7
  163. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +0 -7
  164. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +0 -7
  165. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +0 -7
  166. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +0 -7
  167. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +0 -3
  168. simtools/schemas/model_parameters/flasher_angular_distribution.schema.yml +32 -0
  169. simtools/schemas/model_parameters/flasher_angular_distribution_width.schema.yml +32 -0
  170. simtools/schemas/model_parameters/flasher_bunch_size.schema.yml +28 -0
  171. simtools/schemas/model_parameters/flasher_external_trigger.schema.yml +32 -0
  172. simtools/schemas/model_parameters/flasher_photons.schema.yml +34 -0
  173. simtools/schemas/model_parameters/flasher_position.schema.yml +43 -0
  174. simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +29 -0
  175. simtools/schemas/model_parameters/flasher_pulse_offset.schema.yml +35 -0
  176. simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +30 -0
  177. simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +32 -0
  178. simtools/schemas/model_parameters/flasher_type.schema.yml +28 -0
  179. simtools/schemas/model_parameters/flasher_var_photons.schema.yml +31 -0
  180. simtools/schemas/model_parameters/flasher_wavelength.schema.yml +33 -0
  181. simtools/schemas/model_parameters/flatfielding.schema.yml +0 -7
  182. simtools/schemas/model_parameters/focal_length.schema.yml +0 -7
  183. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +0 -3
  184. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +0 -3
  185. simtools/schemas/model_parameters/focus_offset.schema.yml +0 -7
  186. simtools/schemas/model_parameters/gain_variation.schema.yml +0 -7
  187. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +2 -7
  188. simtools/schemas/model_parameters/geomag_rotation.schema.yml +2 -7
  189. simtools/schemas/model_parameters/geomag_vertical.schema.yml +2 -7
  190. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +0 -5
  191. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +0 -7
  192. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +0 -7
  193. simtools/schemas/model_parameters/laser_events.schema.yml +4 -3
  194. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +4 -3
  195. simtools/schemas/model_parameters/laser_photons.schema.yml +4 -3
  196. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +4 -3
  197. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +4 -3
  198. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +4 -3
  199. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +4 -3
  200. simtools/schemas/model_parameters/laser_var_photons.schema.yml +4 -3
  201. simtools/schemas/model_parameters/laser_wavelength.schema.yml +4 -3
  202. simtools/schemas/model_parameters/led_events.schema.yml +4 -3
  203. simtools/schemas/model_parameters/led_photons.schema.yml +4 -3
  204. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +4 -3
  205. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +4 -3
  206. simtools/schemas/model_parameters/led_var_photons.schema.yml +4 -3
  207. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +0 -7
  208. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +0 -7
  209. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +0 -7
  210. simtools/schemas/model_parameters/min_photons.schema.yml +0 -7
  211. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +0 -5
  212. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +0 -7
  213. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +0 -7
  214. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -9
  215. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +0 -7
  216. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +0 -5
  217. simtools/schemas/model_parameters/mirror_list.schema.yml +0 -7
  218. simtools/schemas/model_parameters/mirror_offset.schema.yml +0 -7
  219. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +0 -7
  220. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +0 -7
  221. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +0 -7
  222. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +0 -7
  223. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +0 -7
  224. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +0 -3
  225. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +0 -7
  226. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +0 -7
  227. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +0 -5
  228. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +0 -5
  229. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +0 -5
  230. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +0 -5
  231. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +0 -5
  232. simtools/schemas/model_parameters/num_gains.schema.yml +0 -7
  233. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +0 -7
  234. simtools/schemas/model_parameters/optics_properties.schema.yml +0 -7
  235. simtools/schemas/model_parameters/parabolic_dish.schema.yml +0 -3
  236. simtools/schemas/model_parameters/pedestal_events.schema.yml +4 -7
  237. simtools/schemas/model_parameters/photon_delay.schema.yml +0 -7
  238. simtools/schemas/model_parameters/photons_per_run.schema.yml +4 -4
  239. simtools/schemas/model_parameters/pixel_cells.schema.yml +0 -3
  240. simtools/schemas/model_parameters/pixels_parallel.schema.yml +0 -3
  241. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +0 -7
  242. simtools/schemas/model_parameters/pm_average_gain.schema.yml +0 -5
  243. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +0 -5
  244. simtools/schemas/model_parameters/pm_gain_index.schema.yml +0 -5
  245. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +0 -7
  246. simtools/schemas/model_parameters/pm_transit_time.schema.yml +4 -9
  247. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +0 -5
  248. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +0 -7
  249. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +0 -3
  250. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +0 -3
  251. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +0 -3
  252. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +0 -3
  253. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +0 -3
  254. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +0 -3
  255. simtools/schemas/model_parameters/qe_variation.schema.yml +0 -7
  256. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +0 -7
  257. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -7
  258. simtools/schemas/model_parameters/random_generator.schema.yml +0 -7
  259. simtools/schemas/model_parameters/random_mono_probability.schema.yml +0 -7
  260. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +0 -5
  261. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +0 -5
  262. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +0 -5
  263. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +0 -5
  264. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +0 -5
  265. simtools/schemas/model_parameters/sampled_output.schema.yml +0 -7
  266. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +0 -7
  267. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +0 -3
  268. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +0 -3
  269. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +0 -3
  270. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +0 -3
  271. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +0 -3
  272. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +0 -3
  273. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +0 -3
  274. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +0 -3
  275. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +0 -3
  276. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +0 -3
  277. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +0 -3
  278. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +0 -3
  279. simtools/schemas/model_parameters/stars.schema.yml +0 -5
  280. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +0 -7
  281. simtools/schemas/model_parameters/tailcut_scale.schema.yml +0 -7
  282. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +0 -7
  283. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +0 -7
  284. simtools/schemas/model_parameters/telescope_random_error.schema.yml +0 -7
  285. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +0 -7
  286. simtools/schemas/model_parameters/telescope_transmission.schema.yml +0 -7
  287. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +0 -7
  288. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +0 -7
  289. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +0 -7
  290. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +0 -7
  291. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +0 -7
  292. simtools/schemas/model_parameters/transit_time_error.schema.yml +0 -7
  293. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +0 -7
  294. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +0 -7
  295. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +0 -7
  296. simtools/schemas/model_parameters/trigger_pixels.schema.yml +0 -7
  297. simtools/schemas/production_tables.schema.yml +8 -8
  298. simtools/schemas/simulation_models_info.schema.yml +78 -0
  299. simtools/simtel/simtel_config_writer.py +88 -14
  300. simtools/simtel/simulator_array.py +44 -74
  301. simtools/simtel/simulator_light_emission.py +336 -629
  302. simtools/simtel/simulator_ray_tracing.py +2 -2
  303. simtools/simulator.py +46 -18
  304. simtools/testing/configuration.py +4 -2
  305. simtools/testing/sim_telarray_metadata.py +4 -4
  306. simtools/utils/geometry.py +34 -0
  307. simtools/version.py +111 -0
  308. simtools/{corsika/corsika_histograms_visualize.py → visualization/plot_corsika_histograms.py} +109 -0
  309. simtools/visualization/plot_psf.py +775 -0
  310. simtools/visualization/plot_simtel_events.py +284 -87
  311. simtools/applications/maintain_simulation_model_add_production_table.py +0 -71
  312. simtools/model/flasher_model.py +0 -106
  313. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/WHEEL +0 -0
  314. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/licenses/LICENSE +0 -0
  315. {gammasimtools-0.20.0.dist-info → gammasimtools-0.22.0.dist-info}/top_level.txt +0 -0
simtools/io/io_handler.py CHANGED
@@ -1,15 +1,8 @@
1
- """Handle input and output paths."""
1
+ """Handle input and output directories and file paths."""
2
2
 
3
- import datetime
4
3
  import logging
5
4
  from pathlib import Path
6
5
 
7
- __all__ = ["IOHandler", "IOHandlerSingleton"]
8
-
9
-
10
- class IncompleteIOHandlerInitError(Exception):
11
- """Exception raised when IOHandler is not initialized."""
12
-
13
6
 
14
7
  class IOHandlerSingleton(type):
15
8
  """Singleton base class."""
@@ -17,32 +10,24 @@ class IOHandlerSingleton(type):
17
10
  _instances = {}
18
11
 
19
12
  def __call__(cls, *args, **kwargs):
20
- """
21
- Ensure a single instance of the IOHandlerSingleton class.
22
-
23
- Creates a new instance if it doesn't exist, otherwise returns the existing instance.
24
- """
13
+ """Create a new instance if it doesn't exist, otherwise returns the existing instance."""
25
14
  if cls not in cls._instances:
26
15
  cls._instances[cls] = super().__call__(*args, **kwargs)
27
16
  return cls._instances[cls]
28
17
 
29
18
 
30
19
  class IOHandler(metaclass=IOHandlerSingleton):
31
- """Handle input and output paths."""
20
+ """Handle input and output directories and file paths."""
32
21
 
33
22
  def __init__(self):
34
23
  """Initialize IOHandler."""
35
- self._logger = logging.getLogger(__name__)
36
- self._logger.debug("Init IOHandler")
24
+ self.logger = logging.getLogger(__name__)
25
+ self.logger.debug("Init IOHandler")
37
26
 
38
27
  self.output_path = None
39
- self.use_plain_output_path = False
40
- self.data_path = None
41
28
  self.model_path = None
42
29
 
43
- def set_paths(
44
- self, output_path=None, data_path=None, model_path=None, use_plain_output_path=False
45
- ):
30
+ def set_paths(self, output_path=None, model_path=None):
46
31
  """
47
32
  Set paths for input and output.
48
33
 
@@ -50,28 +35,19 @@ class IOHandler(metaclass=IOHandlerSingleton):
50
35
  ----------
51
36
  output_path: str or Path
52
37
  Path pointing to the output directory.
53
- data_path: str or Path
54
- Path pointing to the data files (e.g., CORSIKA or sim_telarray output).
55
38
  model_path: str or Path
56
39
  Path pointing to the model file directory.
57
- use_plain_output_path: bool
58
- Use plain output path without adding tool name and date
59
-
60
40
  """
61
41
  self.output_path = output_path
62
- self.use_plain_output_path = use_plain_output_path
63
- self.data_path = data_path
64
42
  self.model_path = model_path
65
43
 
66
- def get_output_directory(self, label=None, sub_dir=None):
44
+ def get_output_directory(self, sub_dir=None):
67
45
  """
68
- Return path to output directory.
46
+ Create and get path of an output directory.
69
47
 
70
48
  Parameters
71
49
  ----------
72
- label: str
73
- Instance label.
74
- sub_dir: str
50
+ sub_dir: str or list of str, optional
75
51
  Name of the subdirectory (ray_tracing, model etc)
76
52
 
77
53
  Returns
@@ -81,34 +57,24 @@ class IOHandler(metaclass=IOHandlerSingleton):
81
57
  Raises
82
58
  ------
83
59
  FileNotFoundError
84
- if error creating directory
85
- TypeError
86
- raised for errors while creating directory name
60
+ if the directory cannot be created
87
61
  """
88
- path = Path(self.output_path)
89
- if not self.use_plain_output_path:
90
- path = (
91
- path
92
- if str(self.output_path).endswith("-output")
93
- else path.joinpath("simtools-output")
94
- )
95
- label_dir = label if label is not None else "d-" + str(datetime.date.today())
96
- path = (
97
- path.joinpath(label_dir) if sub_dir is None else path.joinpath(label_dir, sub_dir)
98
- )
99
-
100
- return self._mkdir(path)
101
-
102
- def _mkdir(self, path):
103
- """Create a directory and return path."""
62
+ if sub_dir is None:
63
+ parts = []
64
+ elif isinstance(sub_dir, list | tuple):
65
+ parts = sub_dir
66
+ else:
67
+ parts = [sub_dir]
68
+ path = Path(self.output_path, *parts)
69
+
104
70
  try:
105
71
  path.mkdir(parents=True, exist_ok=True)
106
72
  except FileNotFoundError as exc:
107
73
  raise FileNotFoundError(f"Error creating directory {path!s}") from exc
108
74
 
109
- return path.absolute()
75
+ return path.resolve()
110
76
 
111
- def get_output_file(self, file_name, label=None, sub_dir=None):
77
+ def get_output_file(self, file_name, sub_dir=None):
112
78
  """
113
79
  Get path of an output file.
114
80
 
@@ -116,51 +82,31 @@ class IOHandler(metaclass=IOHandlerSingleton):
116
82
  ----------
117
83
  files_name: str
118
84
  File name.
119
- label: str
120
- Instance label.
121
- sub_dir: str
85
+ sub_dir: sub_dir: str or list of str, optional
122
86
  Name of the subdirectory (ray_tracing, model etc)
123
87
 
124
88
  Returns
125
89
  -------
126
90
  Path
127
91
  """
128
- return (
129
- self.get_output_directory(label=label, sub_dir=sub_dir).joinpath(file_name).absolute()
130
- )
92
+ return self.get_output_directory(sub_dir).joinpath(file_name).absolute()
131
93
 
132
- def get_input_data_file(self, parent_dir=None, file_name=None, test=False):
94
+ def get_test_data_file(self, file_name=None):
133
95
  """
134
- Get path of a data file, using data_path.
96
+ Get path of a data file in the test resources directory.
135
97
 
136
98
  Parameters
137
99
  ----------
138
- parent_dir: str
139
- Parent directory of the file.
140
100
  files_name: str
141
101
  File name.
142
- test: bool
143
- If true, return test resources location
144
102
 
145
103
  Returns
146
104
  -------
147
105
  Path
148
-
149
- Raises
150
- ------
151
- IncompleteIOHandlerInitError
152
- if data_path is not set
153
-
154
106
  """
155
- if test:
156
- file_prefix = Path("tests/resources/")
157
- elif self.data_path is not None:
158
- file_prefix = Path(self.data_path).joinpath(parent_dir)
159
- else:
160
- raise IncompleteIOHandlerInitError
161
- return file_prefix.joinpath(file_name).absolute()
107
+ return Path("tests/resources", file_name).resolve()
162
108
 
163
- def get_model_configuration_directory(self, label, model_version):
109
+ def get_model_configuration_directory(self, model_version, sub_dir=None):
164
110
  """
165
111
  Get path of the simulation model configuration directory.
166
112
 
@@ -168,13 +114,15 @@ class IOHandler(metaclass=IOHandlerSingleton):
168
114
 
169
115
  Parameters
170
116
  ----------
171
- label: str
172
- Instance label.
173
117
  model_version: str
174
118
  Model version.
119
+ sub_dir: str
120
+ subdirectory
175
121
 
176
122
  Returns
177
123
  -------
178
124
  Path
179
125
  """
180
- return self._mkdir(self.get_output_directory(label=label).joinpath("model", model_version))
126
+ return self.get_output_directory(
127
+ sub_dir=[sub_dir, "model", model_version] if sub_dir else ["model", model_version]
128
+ )
@@ -2,6 +2,7 @@
2
2
 
3
3
  import logging
4
4
  import subprocess
5
+ import time
5
6
  from pathlib import Path
6
7
 
7
8
  import simtools.utils.general as gen
@@ -9,10 +10,54 @@ import simtools.utils.general as gen
9
10
  __all__ = ["JobExecutionError", "JobManager"]
10
11
 
11
12
 
13
+ logger = logging.getLogger(__name__)
14
+
15
+
12
16
  class JobExecutionError(Exception):
13
17
  """Job execution error."""
14
18
 
15
19
 
20
+ def retry_command(command, max_attempts=3, delay=10):
21
+ """
22
+ Execute a shell command with retry logic for network-related failures.
23
+
24
+ Parameters
25
+ ----------
26
+ command : str
27
+ Shell command to execute.
28
+ max_attempts : int
29
+ Maximum number of retry attempts (default: 3).
30
+ delay : int
31
+ Delay in seconds between attempts (default: 10).
32
+
33
+ Returns
34
+ -------
35
+ bool
36
+ True if command succeeded, False if all attempts failed.
37
+
38
+ Raises
39
+ ------
40
+ subprocess.CalledProcessError
41
+ If command fails after all retry attempts.
42
+ """
43
+ for attempt in range(1, max_attempts + 1):
44
+ logger.info(f"Attempt {attempt} of {max_attempts}: {command}")
45
+ try:
46
+ subprocess.run(command, shell=True, check=True, text=True)
47
+ logger.info(f"Command succeeded on attempt {attempt}")
48
+ return True
49
+ except subprocess.CalledProcessError as exc:
50
+ logger.warning(f"Command failed on attempt {attempt}")
51
+ if attempt < max_attempts:
52
+ logger.info(f"Waiting {delay}s before retry...")
53
+ time.sleep(delay)
54
+ else:
55
+ logger.error(f"Command failed after {max_attempts} attempts")
56
+ raise exc from None
57
+
58
+ return False
59
+
60
+
16
61
  class JobManager:
17
62
  """
18
63
  Job manager for submitting jobs to a compute node.
@@ -154,10 +154,7 @@ def write_array_layouts(array_layouts, args_dict, db_config):
154
154
  _logger.info(f"Writing updated array layouts to the database for site {site}.")
155
155
 
156
156
  io_handler_instance = io_handler.IOHandler()
157
- io_handler_instance.set_paths(
158
- output_path=args_dict["output_path"],
159
- use_plain_output_path=args_dict["use_plain_output_path"],
160
- )
157
+ io_handler_instance.set_paths(output_path=args_dict["output_path"])
161
158
  output_file = io_handler_instance.get_output_file(
162
159
  f"array-layouts-{args_dict['updated_parameter_version']}.json"
163
160
  )
@@ -168,7 +165,6 @@ def write_array_layouts(array_layouts, args_dict, db_config):
168
165
  instrument=site,
169
166
  parameter_version=args_dict.get("updated_parameter_version"),
170
167
  output_file=output_file,
171
- use_plain_output_path=args_dict["use_plain_output_path"],
172
168
  db_config=db_config,
173
169
  )
174
170
  MetadataCollector.dump(
@@ -9,6 +9,7 @@ from astropy.table import QTable
9
9
  from simtools.data_model import data_reader, schema
10
10
  from simtools.db import db_handler
11
11
  from simtools.io import io_handler
12
+ from simtools.model.calibration_model import CalibrationModel
12
13
  from simtools.model.site_model import SiteModel
13
14
  from simtools.model.telescope_model import TelescopeModel
14
15
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
@@ -40,18 +41,21 @@ class ArrayModel:
40
41
  Dictionary with configuration for sim_telarray random instrument setup.
41
42
  simtel_path: str, Path, optional
42
43
  Path to the sim_telarray installation directory.
44
+ calibration_device_types: List[str], optional
45
+ List of calibration device types (e.g., 'flat_fielding') attached to each telescope.
43
46
  """
44
47
 
45
48
  def __init__(
46
49
  self,
47
- mongo_db_config: dict,
48
- model_version: str,
49
- label: str | None = None,
50
- site: str | None = None,
51
- layout_name: str | None = None,
52
- array_elements: str | Path | list[str] | None = None,
53
- sim_telarray_seeds: dict | None = None,
54
- simtel_path: str | Path | None = None,
50
+ mongo_db_config,
51
+ model_version,
52
+ label=None,
53
+ site=None,
54
+ layout_name=None,
55
+ array_elements=None,
56
+ sim_telarray_seeds=None,
57
+ simtel_path=None,
58
+ calibration_device_types=None,
55
59
  ):
56
60
  """Initialize ArrayModel."""
57
61
  self._logger = logging.getLogger(__name__)
@@ -69,8 +73,8 @@ class ArrayModel:
69
73
  self.io_handler = io_handler.IOHandler()
70
74
  self.db = db_handler.DatabaseHandler(mongo_db_config=mongo_db_config)
71
75
 
72
- self.array_elements, self.site_model, self.telescope_model = self._initialize(
73
- site, array_elements
76
+ self.array_elements, self.site_model, self.telescope_models, self.calibration_models = (
77
+ self._initialize(site, array_elements, calibration_device_types)
74
78
  )
75
79
 
76
80
  self._telescope_model_files_exported = False
@@ -78,7 +82,7 @@ class ArrayModel:
78
82
  self.sim_telarray_seeds = sim_telarray_seeds
79
83
  self.simtel_path = simtel_path
80
84
 
81
- def _initialize(self, site: str, array_elements_config: str | Path | list[str]):
85
+ def _initialize(self, site, array_elements_config, calibration_device_types):
82
86
  """
83
87
  Initialize ArrayModel taking different configuration options into account.
84
88
 
@@ -88,6 +92,8 @@ class ArrayModel:
88
92
  Site name.
89
93
  array_elements_config: Union[str, Path, List[str]]
90
94
  Array element definitions.
95
+ calibration_device_types: str
96
+ Calibration device types.
91
97
 
92
98
  Returns
93
99
  -------
@@ -106,7 +112,6 @@ class ArrayModel:
106
112
  label=self.label,
107
113
  )
108
114
 
109
- array_elements = {}
110
115
  # Case 1: array_elements is a file name
111
116
  if isinstance(array_elements_config, str | Path):
112
117
  array_elements = self._load_array_element_positions_from_file(
@@ -120,16 +125,20 @@ class ArrayModel:
120
125
  array_elements = self._get_array_elements_from_list(
121
126
  site_model.get_array_elements_for_layout(self.layout_name)
122
127
  )
123
- if not array_elements:
128
+ else:
124
129
  raise ValueError(
125
130
  "No array elements found. "
126
131
  "Possibly missing valid layout name or missing telescope list."
127
132
  )
128
- telescope_model = self._build_telescope_models(site_model, array_elements)
129
- return array_elements, site_model, telescope_model
133
+
134
+ telescope_models, calibration_models = self._build_telescope_models(
135
+ site_model, array_elements, calibration_device_types
136
+ )
137
+
138
+ return array_elements, site_model, telescope_models, calibration_models
130
139
 
131
140
  @property
132
- def config_file_path(self) -> Path:
141
+ def config_file_path(self):
133
142
  """
134
143
  Return the path of the array config file for sim_telarray.
135
144
 
@@ -149,7 +158,7 @@ class ArrayModel:
149
158
  return self._config_file_path
150
159
 
151
160
  @property
152
- def number_of_telescopes(self) -> int:
161
+ def number_of_telescopes(self):
153
162
  """
154
163
  Return the number of telescopes.
155
164
 
@@ -158,7 +167,7 @@ class ArrayModel:
158
167
  int
159
168
  Number of telescopes.
160
169
  """
161
- return len(self.telescope_model)
170
+ return len(self.telescope_models)
162
171
 
163
172
  @property
164
173
  def site(self) -> str:
@@ -200,12 +209,16 @@ class ArrayModel:
200
209
  )
201
210
  self._model_version = model_version
202
211
 
203
- def _build_telescope_models(self, site_model: SiteModel, array_elements: dict) -> dict:
212
+ def _build_telescope_models(self, site_model, array_elements, calibration_device_types):
204
213
  """
205
- Build the the telescope models for all telescopes of this array.
214
+ Build telescope models for all telescopes of this array.
206
215
 
207
- Includes reading of telescope model parameters from the DB.
208
- The array is defined in the telescopes dictionary. Array element positions
216
+ Adds calibration device models, if requested through the calibration_device_types argument.
217
+ Calibration device models are stored in a dictionary with the telescope name as key (to
218
+ identify the calibration device model on a given telescope).
219
+
220
+ Includes reading of telescope model parameters from the database.
221
+ The array is defined in the array_elements dictionary. Array element positions
209
222
  are read from the database if no values are given in this dictionary.
210
223
 
211
224
  Parameters
@@ -214,40 +227,78 @@ class ArrayModel:
214
227
  Site model.
215
228
  array_elements: dict
216
229
  Dict with array elements.
230
+ calibration_device_types: List[str]
231
+ List of calibration device types (e.g., 'flat_fielding')
232
+
233
+ Returns
234
+ -------
235
+ dict, dict
236
+ Dictionaries with telescope and calibration models.
237
+ """
238
+ telescope_models, calibration_models = {}, {}
239
+
240
+ for element_name in array_elements:
241
+ if names.get_collection_name_from_array_element_name(element_name) != "telescopes":
242
+ continue
243
+
244
+ telescope_models[element_name] = TelescopeModel(
245
+ site=site_model.site,
246
+ telescope_name=element_name,
247
+ model_version=self.model_version,
248
+ mongo_db_config=self.mongo_db_config,
249
+ label=self.label,
250
+ )
251
+ calibration_models[element_name] = self._build_calibration_models(
252
+ telescope_models[element_name],
253
+ site_model,
254
+ calibration_device_types,
255
+ )
256
+
257
+ return telescope_models, calibration_models
258
+
259
+ def _build_calibration_models(self, telescope_model, site_model, calibration_device_types):
260
+ """
261
+ Build calibration device models for all telescopes in the array.
262
+
263
+ A telescope can have multiple calibration devices of different types.
217
264
 
218
265
  Returns
219
266
  -------
220
267
  dict
221
- Dictionary with telescope models.
222
- """
223
- telescope_model = {}
224
- for element_name, _ in array_elements.items():
225
- collection = names.get_collection_name_from_array_element_name(element_name)
226
- if collection == "telescopes":
227
- telescope_model[element_name] = TelescopeModel(
228
- site=site_model.site,
229
- telescope_name=element_name,
230
- model_version=self.model_version,
231
- mongo_db_config=self.mongo_db_config,
232
- label=self.label,
233
- )
234
- return telescope_model
268
+ Dict with calibration device models.
269
+ """
270
+ calibration_models = {}
271
+ for calibration_device_type in calibration_device_types or []:
272
+ device_name = telescope_model.get_calibration_device_name(calibration_device_type)
273
+ if device_name is None:
274
+ continue
275
+
276
+ calibration_models[device_name] = CalibrationModel(
277
+ site=site_model.site,
278
+ calibration_device_model_name=device_name,
279
+ mongo_db_config=self.mongo_db_config,
280
+ model_version=self.model_version,
281
+ label=self.label,
282
+ )
283
+ return calibration_models
235
284
 
236
285
  def print_telescope_list(self):
237
286
  """Print list of telescopes."""
238
- for tel_name, data in self.telescope_model.items():
287
+ for tel_name, data in self.telescope_models.items():
239
288
  print(f"Name: {tel_name}\t Model: {data.name}")
240
289
 
241
290
  def export_simtel_telescope_config_files(self):
242
291
  """Export sim_telarray configuration files for all telescopes into the model directory."""
243
292
  exported_models = []
244
- for _, tel_model in self.telescope_model.items():
293
+ for tel_model in self.telescope_models.values():
245
294
  name = tel_model.name + (
246
295
  "_" + tel_model.extra_label if tel_model.extra_label != "" else ""
247
296
  )
248
297
  if name not in exported_models:
249
298
  self._logger.debug(f"Exporting configuration file for telescope {name}")
250
- tel_model.write_sim_telarray_config_file()
299
+ tel_model.write_sim_telarray_config_file(
300
+ additional_models=self.calibration_models.get(tel_model.name)
301
+ )
251
302
  exported_models.append(name)
252
303
  else:
253
304
  self._logger.debug(
@@ -270,7 +321,7 @@ class ArrayModel:
270
321
  )
271
322
  simtel_writer.write_array_config_file(
272
323
  config_file_path=self.config_file_path,
273
- telescope_model=self.telescope_model,
324
+ telescope_model=self.telescope_models,
274
325
  site_model=self.site_model,
275
326
  additional_metadata=self._get_additional_simtel_metadata(),
276
327
  )
@@ -298,7 +349,7 @@ class ArrayModel:
298
349
  """
299
350
  if self._config_file_directory is None:
300
351
  self._config_file_directory = self.io_handler.get_model_configuration_directory(
301
- self.label, self.model_version
352
+ model_version=self.model_version
302
353
  )
303
354
  return self._config_file_directory
304
355
 
@@ -459,7 +510,7 @@ class ArrayModel:
459
510
  table = QTable(meta={"array_name": self.layout_name, "site": self.site_model.site})
460
511
 
461
512
  name, pos_x, pos_y, pos_z, tel_r = [], [], [], [], []
462
- for tel_name, data in self.telescope_model.items():
513
+ for tel_name, data in self.telescope_models.items():
463
514
  name.append(tel_name)
464
515
  xyz = data.position(coordinate_system=coordinate_system)
465
516
  pos_x.append(xyz[0])
@@ -234,7 +234,9 @@ class ModelParameter:
234
234
  return [_value[i] * _astropy_units[i] for i in range(len(_value))]
235
235
 
236
236
  except (KeyError, TypeError, AttributeError) as exc:
237
- self._logger.debug(f"{exc} encountered, returning only value without units.")
237
+ self._logger.debug(
238
+ f"{exc} encountered for parameter {par_name}, returning only value without units."
239
+ )
238
240
  return _value # if unit is NoneType
239
241
 
240
242
  def get_parameter_type(self, par_name):
@@ -308,7 +310,7 @@ class ModelParameter:
308
310
  return
309
311
 
310
312
  self._config_file_directory = self.io_handler.get_model_configuration_directory(
311
- label=self.label, model_version=self.model_version
313
+ model_version=self.model_version
312
314
  )
313
315
 
314
316
  # Setting file name and the location
@@ -495,23 +497,19 @@ class ModelParameter:
495
497
  )
496
498
  self._is_exported_model_files_up_to_date = True
497
499
 
498
- def write_sim_telarray_config_file(self, additional_model=None):
500
+ def write_sim_telarray_config_file(self, additional_models=None):
499
501
  """
500
502
  Write the sim_telarray configuration file.
501
503
 
502
504
  Parameters
503
505
  ----------
504
- additional_model: TelescopeModel or SiteModel
506
+ additional_models: TelescopeModel or SiteModel
505
507
  Model object for additional parameter to be written to the config file.
506
508
  """
507
509
  self.parameters.update(self._simulation_config_parameters.get("sim_telarray", {}))
508
510
  self.export_model_files(update_if_necessary=True)
509
511
 
510
- if additional_model:
511
- self.parameters.update(additional_model.parameters)
512
- additional_model.export_model_files(
513
- self.config_file_directory, update_if_necessary=True
514
- )
512
+ self._add_additional_models(additional_models)
515
513
 
516
514
  self._load_simtel_config_writer()
517
515
  self.simtel_config_writer.write_telescope_config_file(
@@ -519,6 +517,19 @@ class ModelParameter:
519
517
  parameters=self.parameters,
520
518
  )
521
519
 
520
+ def _add_additional_models(self, additional_models):
521
+ """Add additional models to the current model parameters."""
522
+ if additional_models is None:
523
+ return
524
+
525
+ if isinstance(additional_models, dict):
526
+ for additional_model in additional_models.values():
527
+ self._add_additional_models(additional_model)
528
+ return
529
+
530
+ self.parameters.update(additional_models.parameters)
531
+ additional_models.export_model_files(self.config_file_directory, update_if_necessary=True)
532
+
522
533
  @property
523
534
  def config_file_directory(self):
524
535
  """Directory for configuration files. Configure if not yet set."""