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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/METADATA +26 -69
  2. gammasimtools-0.19.0.dist-info/RECORD +393 -0
  3. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/entry_points.txt +9 -2
  4. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +16 -3
  6. simtools/applications/calculate_trigger_rate.py +1 -1
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +4 -3
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
  9. simtools/applications/db_add_value_from_json_to_db.py +2 -1
  10. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
  11. simtools/applications/db_generate_compound_indexes.py +61 -0
  12. simtools/applications/db_get_file_from_db.py +1 -1
  13. simtools/applications/db_get_parameter_from_db.py +4 -4
  14. simtools/applications/db_inspect_databases.py +20 -10
  15. simtools/applications/derive_mirror_rnda.py +17 -11
  16. simtools/applications/derive_psf_parameters.py +59 -309
  17. simtools/applications/docs_produce_array_element_report.py +1 -1
  18. simtools/applications/docs_produce_calibration_reports.py +1 -1
  19. simtools/applications/docs_produce_model_parameter_reports.py +1 -1
  20. simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
  21. simtools/applications/generate_corsika_histograms.py +1 -1
  22. simtools/applications/generate_default_metadata.py +8 -24
  23. simtools/applications/generate_sim_telarray_histograms.py +1 -1
  24. simtools/applications/generate_simtel_event_data.py +11 -11
  25. simtools/applications/maintain_simulation_model_add_production_table.py +71 -0
  26. simtools/applications/maintain_simulation_model_compare_productions.py +98 -0
  27. simtools/applications/{verify_simulation_model_production_tables.py → maintain_simulation_model_verify_production_tables.py} +9 -1
  28. simtools/applications/merge_tables.py +2 -2
  29. simtools/applications/plot_array_layout.py +3 -3
  30. simtools/applications/plot_simtel_events.py +379 -0
  31. simtools/applications/plot_tabular_data.py +9 -2
  32. simtools/applications/plot_tabular_data_for_model_parameter.py +2 -1
  33. simtools/applications/print_version.py +8 -9
  34. simtools/applications/production_derive_corsika_limits.py +6 -7
  35. simtools/applications/production_derive_statistics.py +1 -1
  36. simtools/applications/production_generate_grid.py +2 -2
  37. simtools/applications/production_merge_corsika_limits.py +214 -0
  38. simtools/applications/run_application.py +47 -113
  39. simtools/applications/simulate_calibration_events.py +166 -0
  40. simtools/applications/simulate_flasher.py +141 -0
  41. simtools/applications/{simulate_light_emission.py → simulate_illuminator.py} +35 -99
  42. simtools/applications/simulate_prod.py +6 -24
  43. simtools/applications/simulate_prod_htcondor_generator.py +7 -0
  44. simtools/applications/submit_array_layouts.py +2 -1
  45. simtools/applications/submit_model_parameter_from_external.py +1 -1
  46. simtools/applications/validate_camera_efficiency.py +30 -12
  47. simtools/applications/validate_camera_fov.py +1 -1
  48. simtools/applications/validate_cumulative_psf.py +1 -1
  49. simtools/applications/validate_file_using_schema.py +2 -1
  50. simtools/applications/validate_optics.py +1 -1
  51. simtools/camera/camera_efficiency.py +61 -45
  52. simtools/camera/single_photon_electron_spectrum.py +1 -1
  53. simtools/configuration/commandline_parser.py +29 -0
  54. simtools/configuration/configurator.py +4 -4
  55. simtools/corsika/corsika_config.py +45 -25
  56. simtools/corsika/corsika_histograms.py +6 -5
  57. simtools/data_model/data_reader.py +2 -3
  58. simtools/data_model/metadata_collector.py +32 -36
  59. simtools/data_model/metadata_model.py +15 -12
  60. simtools/data_model/model_data_writer.py +13 -32
  61. simtools/data_model/schema.py +74 -24
  62. simtools/data_model/validate_data.py +34 -9
  63. simtools/db/db_handler.py +43 -37
  64. simtools/db/db_model_upload.py +3 -3
  65. simtools/dependencies.py +88 -25
  66. simtools/io/ascii_handler.py +279 -0
  67. simtools/{io_operations → io}/io_handler.py +25 -3
  68. simtools/job_execution/htcondor_script_generator.py +15 -4
  69. simtools/layout/array_layout.py +1 -1
  70. simtools/layout/array_layout_utils.py +14 -7
  71. simtools/model/array_model.py +23 -4
  72. simtools/model/flasher_model.py +106 -0
  73. simtools/model/model_parameter.py +4 -4
  74. simtools/model/model_repository.py +197 -2
  75. simtools/model/telescope_model.py +3 -1
  76. simtools/production_configuration/derive_corsika_limits.py +361 -427
  77. simtools/production_configuration/derive_production_statistics_handler.py +7 -6
  78. simtools/production_configuration/generate_production_grid.py +9 -11
  79. simtools/production_configuration/merge_corsika_limits.py +528 -0
  80. simtools/ray_tracing/mirror_panel_psf.py +1 -0
  81. simtools/ray_tracing/psf_parameter_optimisation.py +792 -0
  82. simtools/ray_tracing/ray_tracing.py +6 -2
  83. simtools/reporting/docs_read_parameters.py +150 -62
  84. simtools/runners/corsika_runner.py +1 -1
  85. simtools/runners/corsika_simtel_runner.py +14 -5
  86. simtools/runners/runner_services.py +10 -5
  87. simtools/runners/simtools_runner.py +267 -0
  88. simtools/schemas/application_workflow.metaschema.yml +101 -68
  89. simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +1 -1
  90. simtools/schemas/input/single_pe_spectrum.schema.yml +1 -1
  91. simtools/schemas/metadata.metaschema.yml +577 -3
  92. simtools/schemas/model_parameter.metaschema.yml +6 -6
  93. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +2 -2
  94. simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
  95. simtools/schemas/model_parameters/altitude.schema.yml +1 -1
  96. simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
  97. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +1 -1
  98. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +1 -1
  99. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
  100. simtools/schemas/model_parameters/array_layouts.schema.yml +1 -1
  101. simtools/schemas/model_parameters/array_triggers.schema.yml +1 -1
  102. simtools/schemas/model_parameters/array_window.schema.yml +1 -1
  103. simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
  104. simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
  105. simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
  106. simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
  107. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
  108. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +1 -1
  109. simtools/schemas/model_parameters/axes_offsets.schema.yml +1 -1
  110. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
  111. simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
  112. simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
  113. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
  114. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
  115. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
  116. simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
  117. simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
  118. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +1 -1
  119. simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
  120. simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
  121. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  122. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  123. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +1 -1
  124. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +1 -1
  125. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +1 -1
  126. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
  127. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
  128. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
  129. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +1 -1
  130. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
  131. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +1 -1
  132. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +3 -3
  133. simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
  134. simtools/schemas/model_parameters/default_trigger.schema.yml +1 -1
  135. simtools/schemas/model_parameters/design_model.schema.yml +1 -1
  136. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +1 -1
  137. simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
  138. simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
  139. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
  140. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
  141. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
  142. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
  143. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
  144. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
  145. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +1 -1
  146. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
  147. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
  148. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
  149. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
  150. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
  151. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
  152. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
  153. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
  154. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
  155. simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
  156. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
  157. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
  158. simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
  159. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +1 -1
  160. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
  161. simtools/schemas/model_parameters/dsum_prescale.schema.yml +1 -1
  162. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
  163. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
  164. simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
  165. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +1 -1
  166. simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -2
  167. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
  168. simtools/schemas/model_parameters/effective_focal_length.schema.yml +1 -1
  169. simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
  170. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +1 -1
  171. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
  172. simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
  173. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  174. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  175. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
  176. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
  177. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
  178. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  179. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  180. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
  181. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
  182. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
  183. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
  184. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
  185. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
  186. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
  187. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
  188. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
  189. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
  190. simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +35 -0
  191. simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +41 -0
  192. simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +38 -0
  193. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
  194. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
  195. simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
  196. simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
  197. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
  198. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +1 -1
  199. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
  200. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
  201. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
  202. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
  203. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
  204. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
  205. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  206. simtools/schemas/model_parameters/flatfielding.schema.yml +1 -1
  207. simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
  208. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +1 -1
  209. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
  210. simtools/schemas/model_parameters/focus_offset.schema.yml +1 -1
  211. simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
  212. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
  213. simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
  214. simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
  215. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
  216. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  217. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  218. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  219. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
  220. simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
  221. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
  222. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
  223. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
  224. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
  225. simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
  226. simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
  227. simtools/schemas/model_parameters/led_events.schema.yml +1 -1
  228. simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
  229. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
  230. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
  231. simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
  232. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  233. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +50 -1
  234. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  235. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  236. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
  237. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +1 -1
  238. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +1 -1
  239. simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
  240. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
  241. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
  242. simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
  243. simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
  244. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +1 -1
  245. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +1 -1
  246. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +1 -1
  247. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
  248. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +1 -1
  249. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +1 -1
  250. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
  251. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +1 -1
  252. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
  253. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +1 -1
  254. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
  255. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
  256. simtools/schemas/model_parameters/nsb_sky_map.schema.yml +1 -1
  257. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +1 -1
  258. simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
  259. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +1 -1
  260. simtools/schemas/model_parameters/optics_properties.schema.yml +1 -1
  261. simtools/schemas/model_parameters/parabolic_dish.schema.yml +1 -1
  262. simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
  263. simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
  264. simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
  265. simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
  266. simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
  267. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
  268. simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
  269. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
  270. simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
  271. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +1 -1
  272. simtools/schemas/model_parameters/pm_transit_time.schema.yml +1 -1
  273. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
  274. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
  275. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
  276. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
  277. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +11 -1
  278. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +1 -1
  279. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
  280. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
  281. simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
  282. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +1 -1
  283. simtools/schemas/model_parameters/random_focal_length.schema.yml +1 -1
  284. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  285. simtools/schemas/model_parameters/random_mono_probability.schema.yml +1 -1
  286. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
  287. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +1 -1
  288. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +1 -1
  289. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
  290. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
  291. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  292. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  293. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +1 -1
  294. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
  295. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  296. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
  297. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
  298. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +11 -1
  299. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +1 -1
  300. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
  301. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +11 -1
  302. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
  303. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
  304. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
  305. simtools/schemas/model_parameters/stars.schema.yml +1 -1
  306. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  307. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  308. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
  309. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
  310. simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
  311. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
  312. simtools/schemas/model_parameters/telescope_transmission.schema.yml +1 -1
  313. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
  314. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
  315. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
  316. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
  317. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
  318. simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
  319. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
  320. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
  321. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +1 -1
  322. simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
  323. simtools/schemas/plot_configuration.metaschema.yml +5 -2
  324. simtools/schemas/production_configuration_metrics.schema.yml +12 -2
  325. simtools/schemas/production_tables.schema.yml +2 -2
  326. simtools/simtel/simtel_config_reader.py +2 -2
  327. simtools/simtel/simtel_config_writer.py +16 -4
  328. simtools/simtel/simtel_io_event_histograms.py +746 -0
  329. simtools/simtel/simtel_io_event_reader.py +15 -42
  330. simtools/simtel/simtel_io_event_writer.py +9 -9
  331. simtools/simtel/simtel_io_histogram.py +3 -1
  332. simtools/simtel/simtel_io_histograms.py +7 -3
  333. simtools/simtel/simtel_table_reader.py +92 -10
  334. simtools/simtel/simulator_array.py +138 -10
  335. simtools/simtel/simulator_camera_efficiency.py +32 -23
  336. simtools/simtel/simulator_light_emission.py +437 -271
  337. simtools/simtel/simulator_ray_tracing.py +1 -1
  338. simtools/simulator.py +105 -147
  339. simtools/testing/configuration.py +24 -26
  340. simtools/testing/helpers.py +2 -2
  341. simtools/testing/log_inspector.py +50 -0
  342. simtools/testing/validate_output.py +87 -37
  343. simtools/utils/general.py +125 -255
  344. simtools/utils/geometry.py +36 -0
  345. simtools/utils/names.py +1 -1
  346. simtools/visualization/legend_handlers.py +180 -264
  347. simtools/visualization/plot_array_layout.py +20 -8
  348. simtools/visualization/plot_pixels.py +1 -1
  349. simtools/visualization/plot_tables.py +133 -37
  350. simtools/visualization/simtel_event_plots.py +816 -0
  351. simtools/visualization/visualize.py +4 -101
  352. gammasimtools-0.18.0.dist-info/RECORD +0 -376
  353. simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
  354. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/WHEEL +0 -0
  355. {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/top_level.txt +0 -0
  356. /simtools/{io_operations → io}/hdf5_handler.py +0 -0
  357. /simtools/{io_operations → io}/legacy_data_handler.py +0 -0
  358. /simtools/{io_operations/io_table_handler.py → io/table_handler.py} +0 -0
@@ -97,11 +97,14 @@ class IOHandler(metaclass=IOHandlerSingleton):
97
97
  path.joinpath(label_dir) if sub_dir is None else path.joinpath(label_dir, sub_dir)
98
98
  )
99
99
 
100
+ return self._mkdir(path)
101
+
102
+ def _mkdir(self, path):
103
+ """Create a directory and return path."""
100
104
  try:
101
105
  path.mkdir(parents=True, exist_ok=True)
102
- except FileNotFoundError:
103
- self._logger.error(f"Error creating directory {path!s}")
104
- raise
106
+ except FileNotFoundError as exc:
107
+ raise FileNotFoundError(f"Error creating directory {path!s}") from exc
105
108
 
106
109
  return path.absolute()
107
110
 
@@ -156,3 +159,22 @@ class IOHandler(metaclass=IOHandlerSingleton):
156
159
  else:
157
160
  raise IncompleteIOHandlerInitError
158
161
  return file_prefix.joinpath(file_name).absolute()
162
+
163
+ def get_model_configuration_directory(self, label, model_version):
164
+ """
165
+ Get path of the simulation model configuration directory.
166
+
167
+ This is the directory where the sim_telarray configuration files will be stored.
168
+
169
+ Parameters
170
+ ----------
171
+ label: str
172
+ Instance label.
173
+ model_version: str
174
+ Model version.
175
+
176
+ Returns
177
+ -------
178
+ Path
179
+ """
180
+ return self._mkdir(self.get_output_directory(label=label).joinpath("model", model_version))
@@ -17,13 +17,12 @@ def generate_submission_script(args_dict):
17
17
  args_dict: dict
18
18
  Arguments dictionary.
19
19
  """
20
- _logger.info("Generating HT Condor submission scripts ")
21
-
22
20
  work_dir = Path(args_dict["output_path"])
23
21
  log_dir = work_dir / "logs"
24
22
  work_dir.mkdir(parents=True, exist_ok=True)
25
23
  log_dir.mkdir(parents=True, exist_ok=True)
26
24
  submit_file_name = "simulate_prod.submit"
25
+ _logger.info(f"Generating HT Condor submission scripts (path: {work_dir})")
27
26
 
28
27
  with open(work_dir / f"{submit_file_name}.condor", "w", encoding="utf-8") as submit_file_handle:
29
28
  submit_file_handle.write(
@@ -101,9 +100,20 @@ def _get_submit_script(args_dict):
101
100
  )
102
101
  core_scatter = args_dict["core_scatter"]
103
102
  core_scatter_string = f'"{core_scatter[0]} {core_scatter[1].to(u.m).value} m"'
103
+ view_cone = args_dict["view_cone"]
104
+ view_cone_string = f'"{view_cone[0].to(u.deg)} {view_cone[1].to(u.deg)}"'
104
105
 
105
106
  label = args_dict["label"] if args_dict["label"] else "simulate-prod"
106
107
 
108
+ array_layout_name = (
109
+ args_dict["array_layout_name"][0]
110
+ if isinstance(args_dict["array_layout_name"], list)
111
+ and len(args_dict["array_layout_name"]) == 1
112
+ else args_dict["array_layout_name"]
113
+ )
114
+
115
+ run_number_offset = args_dict["run_number_offset"] or 1
116
+
107
117
  return f"""#!/usr/bin/env bash
108
118
 
109
119
  # Process ID used to generate run number
@@ -116,15 +126,16 @@ simtools-simulate-prod \\
116
126
  --label {label} \\
117
127
  --model_version {args_dict["model_version"]} \\
118
128
  --site {args_dict["site"]} \\
119
- --array_layout_name {args_dict["array_layout_name"]} \\
129
+ --array_layout_name {array_layout_name} \\
120
130
  --primary {args_dict["primary"]} \\
121
131
  --azimuth_angle {azimuth_angle_string} \\
122
132
  --zenith_angle {zenith_angle_string} \\
123
133
  --nshow {args_dict["nshow"]} \\
124
134
  --energy_range {energy_range_string} \\
125
135
  --core_scatter {core_scatter_string} \\
136
+ --view_cone {view_cone_string} \\
126
137
  --run_number $((process_id)) \\
127
- --run_number_offset {args_dict["run_number_offset"]} \\
138
+ --run_number_offset {run_number_offset} \\
128
139
  --number_of_runs 1 \\
129
140
  --data_directory /tmp/simtools-data \\
130
141
  --output_path /tmp/simtools-output \\
@@ -10,7 +10,7 @@ from astropy.table import QTable
10
10
 
11
11
  import simtools.utils.general as gen
12
12
  from simtools.data_model import data_reader, schema
13
- from simtools.io_operations import io_handler
13
+ from simtools.io import io_handler
14
14
  from simtools.layout.geo_coordinates import GeoCoordinates
15
15
  from simtools.layout.telescope_position import TelescopePosition
16
16
  from simtools.model.site_model import SiteModel
@@ -7,7 +7,7 @@ import simtools.utils.general as gen
7
7
  from simtools.data_model import data_reader
8
8
  from simtools.data_model.metadata_collector import MetadataCollector
9
9
  from simtools.data_model.model_data_writer import ModelDataWriter
10
- from simtools.io_operations import io_handler
10
+ from simtools.io import ascii_handler, io_handler
11
11
  from simtools.model.array_model import ArrayModel
12
12
  from simtools.model.site_model import SiteModel
13
13
  from simtools.utils import names
@@ -36,17 +36,19 @@ def retrieve_ctao_array_layouts(site, repository_url, branch_name="main"):
36
36
  _logger.info(f"Retrieving array layouts from {repository_url} on branch {branch_name}.")
37
37
 
38
38
  if gen.is_url(repository_url):
39
- array_element_ids = gen.collect_data_from_http(
39
+ array_element_ids = ascii_handler.collect_data_from_http(
40
40
  url=f"{repository_url}/{branch_name}/array-element-ids.json"
41
41
  )
42
- sub_arrays = gen.collect_data_from_http(
42
+ sub_arrays = ascii_handler.collect_data_from_http(
43
43
  url=f"{repository_url}/{branch_name}/subarray-ids.json"
44
44
  )
45
45
  else:
46
- array_element_ids = gen.collect_data_from_file(
46
+ array_element_ids = ascii_handler.collect_data_from_file(
47
47
  Path(repository_url) / "array-element-ids.json"
48
48
  )
49
- sub_arrays = gen.collect_data_from_file(Path(repository_url) / "subarray-ids.json")
49
+ sub_arrays = ascii_handler.collect_data_from_file(
50
+ Path(repository_url) / "subarray-ids.json"
51
+ )
50
52
 
51
53
  return _get_ctao_layouts_per_site(site, sub_arrays, array_element_ids)
52
54
 
@@ -232,7 +234,7 @@ def get_array_layouts_from_parameter_file(
232
234
  list
233
235
  List of dictionaries containing array layout names and their elements.
234
236
  """
235
- array_layouts = gen.collect_data_from_file(file_path)
237
+ array_layouts = ascii_handler.collect_data_from_file(file_path)
236
238
  try:
237
239
  value = array_layouts["value"]
238
240
  except KeyError as exc:
@@ -243,7 +245,12 @@ def get_array_layouts_from_parameter_file(
243
245
  for layout in value:
244
246
  layouts.append(
245
247
  _get_array_layout_dict(
246
- db_config, model_version, site, None, layout["name"], coordinate_system
248
+ db_config,
249
+ model_version,
250
+ site,
251
+ layout.get("elements"),
252
+ layout["name"],
253
+ coordinate_system,
247
254
  )
248
255
  )
249
256
  return layouts
@@ -8,11 +8,11 @@ from astropy.table import QTable
8
8
 
9
9
  from simtools.data_model import data_reader, schema
10
10
  from simtools.db import db_handler
11
- from simtools.io_operations import io_handler
11
+ from simtools.io import io_handler
12
12
  from simtools.model.site_model import SiteModel
13
13
  from simtools.model.telescope_model import TelescopeModel
14
14
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
15
- from simtools.utils import names
15
+ from simtools.utils import general, names
16
16
 
17
17
  __all__ = ["ArrayModel"]
18
18
 
@@ -297,11 +297,30 @@ class ArrayModel:
297
297
  Path of the config directory path for sim_telarray.
298
298
  """
299
299
  if self._config_file_directory is None:
300
- self._config_file_directory = self.io_handler.get_output_directory(
301
- self.label, f"model/{self.model_version}"
300
+ self._config_file_directory = self.io_handler.get_model_configuration_directory(
301
+ self.label, self.model_version
302
302
  )
303
303
  return self._config_file_directory
304
304
 
305
+ def pack_model_files(self):
306
+ """
307
+ Pack all model files into a tar.gz archive.
308
+
309
+ Returns
310
+ -------
311
+ Path
312
+ Path of the packed model files archive.
313
+ """
314
+ model_files = list(Path(self.get_config_directory()).rglob("*"))
315
+ if not model_files:
316
+ self._logger.warning("No model files found to pack.")
317
+ return None
318
+
319
+ archive_name = self.get_config_directory() / "model_files.tar.gz"
320
+ general.pack_tar_file(archive_name, model_files)
321
+ self._logger.info(f"Packed model files into {archive_name}")
322
+ return archive_name
323
+
305
324
  def _load_array_element_positions_from_file(
306
325
  self, array_elements_file: str | Path, site: str
307
326
  ) -> dict:
@@ -0,0 +1,106 @@
1
+ """Definition and modeling of a flasher device."""
2
+
3
+ import logging
4
+
5
+ from simtools.model.model_parameter import ModelParameter
6
+
7
+ __all__ = ["FlasherModel"]
8
+
9
+
10
+ class FlasherModel(ModelParameter):
11
+ """
12
+ FlasherModel represents the MC model of an individual flasher device.
13
+
14
+ It provides functionality to read the required parameters from the DB.
15
+ Flasher devices are used for flat fielding of the camera pixels.
16
+
17
+ Parameters
18
+ ----------
19
+ site: str
20
+ Site name (e.g., South or North).
21
+ flasher_device_model_name: str
22
+ Flasher device model name (ex. FLSN-01, FLSS-01, ...).
23
+ mongo_db_config: dict
24
+ MongoDB configuration.
25
+ model_version: str
26
+ Model version.
27
+ label: str, optional
28
+ Instance label. Important for output file naming.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ site: str,
34
+ flasher_device_model_name: str,
35
+ mongo_db_config: dict,
36
+ model_version: str,
37
+ label: str | None = None,
38
+ ):
39
+ # Minimal dummy DB to avoid real DB access
40
+ class _DummyDB: # pylint: disable=too-few-public-methods
41
+ def get_design_model(self, *_, **__):
42
+ return {}
43
+
44
+ def get_model_parameters(self, *_, **__):
45
+ return {}
46
+
47
+ def get_simulation_configuration_parameters(self, *_, **__):
48
+ return {}
49
+
50
+ def export_model_files(self, *_, **__):
51
+ return None
52
+
53
+ super().__init__(
54
+ site=site,
55
+ array_element_name=None, # bypass validation (no flasher in array_elements)
56
+ collection="flasher_devices",
57
+ mongo_db_config=mongo_db_config,
58
+ model_version=model_version,
59
+ db=_DummyDB(), # do not query DB
60
+ label=label,
61
+ )
62
+
63
+ self._logger = logging.getLogger(__name__)
64
+ self._logger.debug(f"Init FlasherModel {site} {flasher_device_model_name}")
65
+
66
+ # Keep provided flasher name for reference/logging only
67
+ self._flasher_device_model_name = flasher_device_model_name
68
+
69
+ # Inject defaults for MST (single-mirror) when DB entries are missing.
70
+ self._inject_mst_defaults_if_missing()
71
+
72
+ def _inject_mst_defaults_if_missing(self):
73
+ """Provide dummy defaults (here NectarCam) when flasher collection is absent."""
74
+ defaults = {
75
+ "photons_per_flasher": {"value": 2.5e6, "type": "float"},
76
+ # Position near optical axis (cm)
77
+ "flasher_position": {
78
+ "value": [0.0, 0.0],
79
+ "unit": "cm,cm",
80
+ "type": "float_list",
81
+ },
82
+ # Distance flasher window to Winston cones (16.75 m)
83
+ "flasher_depth": {"value": 1675.0, "unit": "cm", "type": "float"},
84
+ # Received wavelength at PMT (nm)
85
+ "spectrum": {"value": 392, "unit": "nm", "type": "float"},
86
+ # Simple Gaussian pulse width (ns)
87
+ "lightpulse": {"value": "Gauss:3.0", "type": "string"},
88
+ # Store rise/decay for future use
89
+ "rise_time_10_90": {"value": 2.5, "unit": "ns", "type": "float"},
90
+ "decay_time_90_10": {"value": 5.0, "unit": "ns", "type": "float"},
91
+ # Angular distribution width ~11 deg around axis
92
+ "angular_distribution": {"value": "gauss:11", "type": "string"},
93
+ "centroid_offset_deg": {"value": 0.5, "unit": "deg", "type": "float"},
94
+ # Bunch size for LE
95
+ "bunch_size": {"value": 1.0, "type": "float"},
96
+ # Placeholder for future spectral file usage
97
+ "spectrum_file": {"value": None, "type": "string"},
98
+ }
99
+
100
+ missing = []
101
+ for key, entry in defaults.items():
102
+ if key not in self.parameters:
103
+ self.parameters[key] = entry
104
+ missing.append(key)
105
+ if missing:
106
+ self._logger.info(f"Using built-in MST flasher defaults for: {', '.join(missing)}")
@@ -9,7 +9,7 @@ import astropy.units as u
9
9
 
10
10
  import simtools.utils.general as gen
11
11
  from simtools.db import db_handler
12
- from simtools.io_operations import io_handler
12
+ from simtools.io import ascii_handler, io_handler
13
13
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
14
14
  from simtools.utils import names
15
15
 
@@ -307,8 +307,8 @@ class ModelParameter:
307
307
  if self.name is None and self.site is None:
308
308
  return
309
309
 
310
- self._config_file_directory = self.io_handler.get_output_directory(
311
- label=self.label, sub_dir=f"model/{self.model_version}"
310
+ self._config_file_directory = self.io_handler.get_model_configuration_directory(
311
+ label=self.label, model_version=self.model_version
312
312
  )
313
313
 
314
314
  # Setting file name and the location
@@ -431,7 +431,7 @@ class ModelParameter:
431
431
  "Insufficient validation of parameters."
432
432
  )
433
433
  self._logger.debug(f"Changing parameters from file {file_name}")
434
- self.change_multiple_parameters(**gen.collect_data_from_file(file_name=file_name))
434
+ self.change_multiple_parameters(**ascii_handler.collect_data_from_file(file_name=file_name))
435
435
 
436
436
  def change_multiple_parameters(self, **kwargs):
437
437
  """
@@ -5,10 +5,12 @@ a gitlab repository ('SimulationModels'). This module provides service
5
5
  functions to interact with and verify the repository.
6
6
  """
7
7
 
8
+ import json
8
9
  import logging
10
+ import shutil
9
11
  from pathlib import Path
10
12
 
11
- from simtools.utils import general as gen
13
+ from simtools.io import ascii_handler
12
14
  from simtools.utils import names
13
15
 
14
16
  _logger = logging.getLogger(__name__)
@@ -76,7 +78,7 @@ def _verify_model_parameters_for_production(simulation_models_path, production_f
76
78
  tuple
77
79
  (missing_files_list, total_checked_count)
78
80
  """
79
- production_table = gen.collect_data_from_file(production_file)
81
+ production_table = ascii_handler.collect_data_from_file(production_file)
80
82
  missing_files = []
81
83
  total_checked = 0
82
84
 
@@ -132,3 +134,196 @@ def _get_model_parameter_file_path(
132
134
  / parameter_name
133
135
  / f"{parameter_name}-{parameter_version}.json"
134
136
  )
137
+
138
+
139
+ def copy_and_update_production_table(args_dict):
140
+ """
141
+ Copy and update simulation model production tables.
142
+
143
+ Parameters
144
+ ----------
145
+ args_dict: dict
146
+ Dictionary containing the arguments for copying and updating production tables.
147
+ """
148
+ modifications = ascii_handler.collect_data_from_file(args_dict["modifications"])
149
+ changes = modifications.get("changes", {})
150
+ model_version = modifications["model_version"]
151
+
152
+ simulation_models_path = Path(args_dict["simulation_models_path"])
153
+ source_prod_table_path = (
154
+ simulation_models_path / "productions" / args_dict["source_prod_table_dir"]
155
+ )
156
+ target_prod_table_path = simulation_models_path / "productions" / model_version
157
+ model_parameters_dir = simulation_models_path / "model_parameters"
158
+
159
+ _logger.info(
160
+ f"Copying production tables from {source_prod_table_path} to {target_prod_table_path}"
161
+ )
162
+
163
+ if Path(target_prod_table_path).exists():
164
+ raise FileExistsError(
165
+ f"The target production table directory '{target_prod_table_path}' already exists."
166
+ )
167
+ shutil.copytree(source_prod_table_path, target_prod_table_path)
168
+
169
+ _apply_changes_to_production_tables(target_prod_table_path, changes, model_version)
170
+
171
+ for telescope, parameters in changes.items():
172
+ for param, param_data in parameters.items():
173
+ if param_data.get("value"):
174
+ _create_new_parameter_entry(telescope, param, param_data, model_parameters_dir)
175
+
176
+
177
+ def _apply_changes_to_production_tables(target_prod_table_path, changes, model_version):
178
+ """Apply changes to the production tables in the target directory."""
179
+ for file_path in Path(target_prod_table_path).rglob("*.json"):
180
+ if file_path.name.startswith("configuration"):
181
+ continue
182
+ data = ascii_handler.collect_data_from_file(file_path)
183
+ _apply_changes_to_production_table(data, changes, model_version)
184
+ with file_path.open("w", encoding="utf-8") as f:
185
+ json.dump(data, f, indent=4, sort_keys=True)
186
+ f.write("\n")
187
+
188
+
189
+ def _apply_changes_to_production_table(data, changes, model_version):
190
+ """
191
+ Recursively apply changes to the new production tables.
192
+
193
+ Parameters
194
+ ----------
195
+ data: dict or list
196
+ The JSON data to be updated.
197
+ changes: dict
198
+ The changes to be applied.
199
+ model_version: str
200
+ The model version to be set in the JSON data.
201
+ """
202
+ if isinstance(data, dict):
203
+ if "model_version" in data:
204
+ data["model_version"] = model_version
205
+ _update_parameters(data.get("parameters", {}), changes)
206
+
207
+ elif isinstance(data, list):
208
+ for item in data:
209
+ _apply_changes_to_production_table(item, changes, model_version)
210
+
211
+
212
+ def _update_parameters(params, changes):
213
+ """Update parameters in the given dictionary based on changes."""
214
+ for telescope, updates in changes.items():
215
+ if telescope not in params:
216
+ continue
217
+ for param, param_data in updates.items():
218
+ if param in params[telescope]:
219
+ old = params[telescope][param]
220
+ new = param_data["version"]
221
+ _logger.info(f"Updating '{telescope} - {param}' from {old} to {new}")
222
+ params[telescope][param] = new
223
+ else:
224
+ _logger.info(
225
+ f"Adding new parameter '{telescope} - {param}' "
226
+ f"with version {param_data['version']}"
227
+ )
228
+ params[telescope][param] = param_data["version"]
229
+
230
+
231
+ def _create_new_parameter_entry(telescope, param, param_data, model_parameters_dir):
232
+ """
233
+ Create new model parameter JSON file by copying the latest version and updating fields.
234
+
235
+ Parameters
236
+ ----------
237
+ telescope: str
238
+ Name of the telescope.
239
+ param: str
240
+ Name of the parameter.
241
+ param_data: dict
242
+ Dictionary containing the parameter data including version and value.
243
+ model_parameters_dir: str
244
+ Path to the model parameters directory.
245
+ """
246
+ telescope_dir = Path(model_parameters_dir) / telescope
247
+ if not telescope_dir.exists():
248
+ raise FileNotFoundError(
249
+ f"Directory for telescope '{telescope}' does not exist in '{model_parameters_dir}'."
250
+ )
251
+
252
+ param_dir = telescope_dir / param
253
+ if not param_dir.exists():
254
+ raise FileNotFoundError(
255
+ f"Directory for parameter '{param}' does not exist in '{telescope}'."
256
+ )
257
+
258
+ latest_file = _get_latest_model_parameter_file(param_dir, param)
259
+ if not latest_file:
260
+ raise FileNotFoundError(
261
+ f"No files found for parameter '{param}' in directory '{param_dir}'."
262
+ )
263
+
264
+ json_data = ascii_handler.collect_data_from_file(latest_file)
265
+
266
+ json_data["parameter_version"] = _update_model_parameter_version(
267
+ json_data, param_data, param, telescope
268
+ )
269
+ json_data["value"] = param_data["value"]
270
+
271
+ new_file_name = f"{param}-{param_data['version']}.json"
272
+ new_file_path = param_dir / new_file_name
273
+
274
+ with new_file_path.open("w", encoding="utf-8") as f:
275
+ json.dump(json_data, f, indent=4)
276
+ f.write("\n")
277
+ _logger.info(f"Created new model parameter JSON file: {new_file_path}")
278
+
279
+
280
+ def _get_latest_model_parameter_file(directory, parameter):
281
+ """
282
+ Get the latest model parameter JSON file for a parameter in the given directory.
283
+
284
+ Assume files are named in the format 'parameter-version.json'.
285
+
286
+ Parameters
287
+ ----------
288
+ directory: str
289
+ Path to the directory containing parameter JSON files.
290
+ parameter: str
291
+ Name of the parameter to find.
292
+
293
+ Returns
294
+ -------
295
+ str
296
+ Path to the latest JSON file for the parameter.
297
+
298
+ Raises
299
+ ------
300
+ FileNotFoundError
301
+ If no files for the parameter are found in the directory.
302
+ """
303
+ directory_path = Path(directory)
304
+ files = list(directory_path.glob(f"{parameter}-*.json"))
305
+ if not files:
306
+ raise FileNotFoundError(
307
+ f"No JSON files found for parameter '{parameter}' in directory '{directory}'."
308
+ )
309
+
310
+ # Sort files by version number (assumes version is part of the filename)
311
+ def safe_parse_version(filename):
312
+ version_str = filename.stem.split("-")[-1]
313
+ parts = version_str.split(".")
314
+ return tuple(part.zfill(8) for part in parts)
315
+
316
+ files.sort(key=safe_parse_version)
317
+ return str(files[-1])
318
+
319
+
320
+ def _update_model_parameter_version(json_data, param_data, param, telescope):
321
+ """Check for major version jump and print a warning if necessary."""
322
+ latest_version = int(json_data.get("parameter_version", "0").split(".")[0])
323
+ new_version = int(param_data["version"].split(".")[0])
324
+ if new_version > latest_version + 1:
325
+ _logger.warning(
326
+ f"Major version jump from {latest_version} to {new_version} "
327
+ f"for parameter '{param}' in telescope '{telescope}'."
328
+ )
329
+ return param_data["version"]
@@ -174,7 +174,9 @@ class TelescopeModel(ModelParameter):
174
174
  eff_focal_length = eff_focal_length.to(unit).value
175
175
  except AttributeError:
176
176
  eff_focal_length = 0.0
177
- if return_focal_length_if_zero and (eff_focal_length is None or eff_focal_length == 0.0):
177
+ if return_focal_length_if_zero and (
178
+ eff_focal_length is None or np.isclose(eff_focal_length, 0.0)
179
+ ):
178
180
  self._logger.warning("Using focal_length because effective_focal_length is 0")
179
181
  return self.get_parameter_value_with_unit("focal_length").to(unit).value
180
182
  return eff_focal_length