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
simtools/utils/general.py CHANGED
@@ -1,12 +1,10 @@
1
1
  """General functions useful across different parts of the code."""
2
2
 
3
- import copy
4
3
  import datetime
5
4
  import glob
6
- import json
7
5
  import logging
8
6
  import os
9
- import tempfile
7
+ import tarfile
10
8
  import time
11
9
  import urllib.error
12
10
  import urllib.request
@@ -14,54 +12,21 @@ from pathlib import Path
14
12
  from urllib.parse import urlparse
15
13
 
16
14
  import numpy as np
17
- import yaml
18
15
 
19
16
  __all__ = [
20
- "InvalidConfigDataError",
21
17
  "change_dict_keys_case",
22
18
  "clear_default_sim_telarray_cfg_directories",
23
- "collect_data_from_file",
24
19
  "collect_final_lines",
25
20
  "collect_kwargs",
26
21
  "get_log_excerpt",
27
22
  "get_log_level_from_user",
28
23
  "remove_substring_recursively_from_dict",
29
24
  "set_default_kwargs",
30
- "sort_arrays",
31
25
  ]
32
26
 
33
27
  _logger = logging.getLogger(__name__)
34
28
 
35
29
 
36
- class InvalidConfigDataError(Exception):
37
- """Exception for invalid configuration data."""
38
-
39
-
40
- def join_url_or_path(url_or_path, *args):
41
- """
42
- Join URL or path with additional subdirectories and file.
43
-
44
- This is the equivalent to Path.join(), with extended functionality
45
- working also for URLs.
46
-
47
- Parameters
48
- ----------
49
- url_or_path: str or Path
50
- URL or path to be extended.
51
- args: list
52
- Additional arguments to be added to the URL or path.
53
-
54
- Returns
55
- -------
56
- str or Path
57
- Extended URL or path.
58
-
59
- """
60
- if "://" in str(url_or_path):
61
- return "/".join([url_or_path.rstrip("/"), *args])
62
- return Path(url_or_path).joinpath(*args)
63
-
64
-
65
30
  def is_url(url):
66
31
  """
67
32
  Check if a string is a valid URL.
@@ -106,104 +71,6 @@ def url_exists(url):
106
71
  return False
107
72
 
108
73
 
109
- def collect_data_from_http(url):
110
- """
111
- Download yaml or json file from url and return it contents as dict.
112
-
113
- File is downloaded as a temporary file and deleted afterwards.
114
-
115
- Parameters
116
- ----------
117
- url: str
118
- URL of the yaml/json file.
119
-
120
- Returns
121
- -------
122
- dict
123
- Dictionary containing the file content.
124
-
125
- Raises
126
- ------
127
- TypeError
128
- If url is not a valid URL.
129
- FileNotFoundError
130
- If downloading the yaml file fails.
131
-
132
- """
133
- try:
134
- with tempfile.NamedTemporaryFile(mode="w+t") as tmp_file:
135
- urllib.request.urlretrieve(url, tmp_file.name)
136
- data = _collect_data_from_different_file_types(
137
- tmp_file, url, Path(url).suffix.lower(), None
138
- )
139
- except TypeError as exc:
140
- raise TypeError(f"Invalid url {url}") from exc
141
- except urllib.error.HTTPError as exc:
142
- raise FileNotFoundError(f"Failed to download file from {url}") from exc
143
-
144
- _logger.debug(f"Downloaded file from {url}")
145
- return data
146
-
147
-
148
- def collect_data_from_file(file_name, yaml_document=None):
149
- """
150
- Collect data from file based on its extension.
151
-
152
- Parameters
153
- ----------
154
- file_name: str
155
- Name of the yaml/json/ascii file.
156
- yaml_document: None, int
157
- Return list of yaml documents or a single document (for yaml files with several documents).
158
-
159
- Returns
160
- -------
161
- data: dict or list
162
- Data as dict or list.
163
- """
164
- if is_url(file_name):
165
- return collect_data_from_http(file_name)
166
-
167
- suffix = Path(file_name).suffix.lower()
168
- try:
169
- with open(file_name, encoding="utf-8") as file:
170
- return _collect_data_from_different_file_types(file, file_name, suffix, yaml_document)
171
- # broad exception to catch all possible errors in reading the file
172
- except Exception as exc: # pylint: disable=broad-except
173
- raise type(exc)(f"Failed to read file {file_name}: {exc}") from exc
174
- return None
175
-
176
-
177
- def _collect_data_from_different_file_types(file, file_name, suffix, yaml_document):
178
- """Collect data from different file types."""
179
- if suffix == ".json":
180
- return json.load(file)
181
- if suffix in (".list", ".txt"):
182
- return [line.strip() for line in file.readlines()]
183
- if suffix in [".yml", ".yaml"]:
184
- return _collect_data_from_yaml_file(file, file_name, yaml_document)
185
- raise TypeError(f"File type {suffix} not supported.")
186
-
187
-
188
- def _collect_data_from_yaml_file(file, file_name, yaml_document):
189
- """Collect data from a yaml file (allow for multi-document yaml files)."""
190
- try:
191
- return yaml.safe_load(file)
192
- except yaml.constructor.ConstructorError:
193
- return _load_yaml_using_astropy(file)
194
- except yaml.composer.ComposerError:
195
- pass
196
- file.seek(0)
197
- if yaml_document is None:
198
- return list(yaml.safe_load_all(file))
199
- try:
200
- return list(yaml.safe_load_all(file))[yaml_document]
201
- except IndexError as exc:
202
- raise InvalidConfigDataError(
203
- f"YAML file {file_name} does not contain {yaml_document} documents."
204
- ) from exc
205
-
206
-
207
74
  def collect_kwargs(label, in_kwargs):
208
75
  """
209
76
  Collect kwargs of the type label_* and return them as a dict.
@@ -340,26 +207,27 @@ def get_log_level_from_user(log_level):
340
207
  return possible_levels[log_level_lower]
341
208
 
342
209
 
343
- def copy_as_list(value):
210
+ def ensure_iterable(value):
344
211
  """
345
- Copy value and, if it is not a list, turn it into a list with a single entry.
212
+ Return input value as iterable.
213
+
214
+ - Single values will return as a list with a single element.
215
+ - None values will return as empty list.
216
+ - Values of list or tuple type are not changed.
346
217
 
347
218
  Parameters
348
219
  ----------
349
- value single variable of any type or list
220
+ value: any
221
+ Input value to be converted to a iterable.
350
222
 
351
223
  Returns
352
224
  -------
353
- value: list
354
- Copy of value if it is a list of [value] otherwise.
225
+ list or tuple
226
+ Converted value as list or tuple.
355
227
  """
356
- if isinstance(value, str):
357
- return [value]
358
-
359
- try:
360
- return list(value)
361
- except TypeError:
362
- return [value]
228
+ if not value:
229
+ return []
230
+ return value if isinstance(value, list | tuple) else [value]
363
231
 
364
232
 
365
233
  def program_is_executable(program):
@@ -396,17 +264,17 @@ def _search_directory(directory, filename, rec=False):
396
264
  _logger.debug(f"Directory {directory} does not exist")
397
265
  return None
398
266
 
399
- file = Path(directory).joinpath(filename)
400
- if file.exists():
267
+ _file = Path(directory).joinpath(filename)
268
+ if _file.exists():
401
269
  _logger.debug(f"File {filename} found in {directory}")
402
- return file
270
+ return _file
403
271
 
404
272
  if rec:
405
273
  for subdir in Path(directory).iterdir():
406
274
  if subdir.is_dir():
407
- file = _search_directory(subdir, filename, True)
408
- if file:
409
- return file
275
+ _file = _search_directory(subdir, filename, True)
276
+ if _file:
277
+ return _file
410
278
  return None
411
279
 
412
280
 
@@ -434,15 +302,15 @@ def find_file(name, loc):
434
302
  all_locations = [loc] if not isinstance(loc, list) else loc
435
303
 
436
304
  # Searching file locally
437
- file = _search_directory(".", name)
438
- if file:
439
- return file
305
+ _file = _search_directory(".", name)
306
+ if _file:
307
+ return _file
440
308
 
441
309
  # Searching file in given locations
442
310
  for location in all_locations:
443
- file = _search_directory(location, name, True)
444
- if file:
445
- return file
311
+ _file = _search_directory(location, name, True)
312
+ if _file:
313
+ return _file
446
314
 
447
315
  msg = f"File {name} could not be found in {all_locations}"
448
316
  _logger.error(msg)
@@ -477,6 +345,29 @@ def resolve_file_patterns(file_names):
477
345
  return _files
478
346
 
479
347
 
348
+ def pack_tar_file(tar_file_name, file_list):
349
+ """
350
+ Pack files into a tar.gz archive.
351
+
352
+ Parameters
353
+ ----------
354
+ tar_file_name: str
355
+ Name of the output tar.gz file.
356
+ file_list: list
357
+ List of files to include in the archive.
358
+ """
359
+ file_list = [Path(f) for f in file_list]
360
+ base = Path(os.path.commonpath([f.resolve() for f in file_list]))
361
+ base_resolved = base.resolve()
362
+ for f in file_list:
363
+ if not f.is_file() or not f.resolve().is_relative_to(base_resolved):
364
+ raise ValueError(f"Unsafe file path: {f}")
365
+
366
+ with tarfile.open(tar_file_name, "w:gz") as tar:
367
+ for file in file_list:
368
+ tar.add(file, arcname=file.name)
369
+
370
+
480
371
  def get_log_excerpt(log_file, n_last_lines=30):
481
372
  """
482
373
  Get an excerpt from a log file, namely the n_last_lines of the file.
@@ -615,29 +506,6 @@ def remove_substring_recursively_from_dict(data_dict, substring="\n"):
615
506
  return data_dict
616
507
 
617
508
 
618
- def sort_arrays(*args):
619
- """Sort arrays.
620
-
621
- Parameters
622
- ----------
623
- *args
624
- Arguments to be sorted.
625
-
626
- Returns
627
- -------
628
- list
629
- Sorted args.
630
- """
631
- if len(args) == 0:
632
- return args
633
- order_array = copy.copy(args[0])
634
- new_args = []
635
- for arg in args:
636
- _, value = zip(*sorted(zip(order_array, arg)))
637
- new_args.append(list(value))
638
- return new_args
639
-
640
-
641
509
  def user_confirm():
642
510
  """
643
511
  Ask the user to enter y or n (case-insensitive) on the command line.
@@ -814,123 +682,125 @@ def convert_string_to_list(data_string, is_float=True, force_comma_separation=Fa
814
682
  return data_string
815
683
 
816
684
 
817
- def _load_yaml_using_astropy(file):
685
+ def get_structure_array_from_table(table, column_names):
818
686
  """
819
- Load a yaml file using astropy's yaml loader.
687
+ Get a structured array from an astropy table for a selected list of columns.
820
688
 
821
689
  Parameters
822
690
  ----------
823
- file: file
824
- File to be loaded.
691
+ table: astropy.table.Table
692
+ Table to be converted.
693
+ column_names: list
694
+ List of column names to be included in the structured array.
825
695
 
826
696
  Returns
827
697
  -------
828
- dict
829
- Dictionary containing the file content.
698
+ numpy.ndarray
699
+ Structured array containing the table data.
830
700
  """
831
- # pylint: disable=import-outside-toplevel
832
- import astropy.io.misc.yaml as astropy_yaml
833
-
834
- file.seek(0)
835
- return astropy_yaml.load(file)
701
+ return np.array(
702
+ list(zip(*[np.array(table[col]) for col in column_names if col in table.colnames])),
703
+ dtype=[(col, np.array(table[col]).dtype) for col in column_names if col in table.colnames],
704
+ )
836
705
 
837
706
 
838
- def is_utf8_file(file_name):
707
+ def convert_keys_in_dict_to_lowercase(data):
839
708
  """
840
- Check if a file is encoded in UTF-8.
709
+ Recursively convert all dictionary keys to lowercase.
841
710
 
842
711
  Parameters
843
712
  ----------
844
- file_name: str, Path
845
- Name of the file to be checked.
713
+ data: dict
714
+ Dictionary to be converted.
846
715
 
847
716
  Returns
848
717
  -------
849
- bool
850
- True if the file is encoded in UTF-8, False otherwise.
718
+ dict
719
+ Dictionary with all keys converted to lowercase.
851
720
  """
852
- try:
853
- with open(file_name, encoding="utf-8") as file:
854
- file.read()
855
- return True
856
- except UnicodeDecodeError:
857
- return False
721
+ if isinstance(data, dict):
722
+ return {k.lower(): convert_keys_in_dict_to_lowercase(v) for k, v in data.items()}
723
+ if isinstance(data, list):
724
+ return [convert_keys_in_dict_to_lowercase(i) for i in data]
725
+ return data
858
726
 
859
727
 
860
- def read_file_encoded_in_utf_or_latin(file_name):
728
+ def remove_key_from_dict(data, key_to_remove):
861
729
  """
862
- Read a file encoded in UTF-8 or Latin-1.
730
+ Remove a specific key from a dictionary recursively.
863
731
 
864
732
  Parameters
865
733
  ----------
866
- file_name: str
867
- Name of the file to be read.
734
+ data: dict
735
+ Dictionary to be processed.
736
+ key_to_remove: str
737
+ Key to be removed from the dictionary.
868
738
 
869
739
  Returns
870
740
  -------
871
- list
872
- List of lines read from the file.
873
-
874
- Raises
875
- ------
876
- UnicodeDecodeError
877
- If the file cannot be decoded using UTF-8 or Latin-1.
741
+ dict
742
+ Dictionary with the specified key removed.
878
743
  """
879
- try:
880
- with open(file_name, encoding="utf-8") as file:
881
- lines = file.readlines()
882
- except UnicodeDecodeError:
883
- logging.debug("Unable to decode file using UTF-8. Trying Latin-1.")
884
- try:
885
- with open(file_name, encoding="latin-1") as file:
886
- lines = file.readlines()
887
- except UnicodeDecodeError as exc:
888
- raise UnicodeDecodeError("Unable to decode file using UTF-8 or Latin-1.") from exc
744
+ if isinstance(data, dict):
745
+ return {
746
+ k: remove_key_from_dict(v, key_to_remove) for k, v in data.items() if k != key_to_remove
747
+ }
748
+ if isinstance(data, list):
749
+ return [remove_key_from_dict(i, key_to_remove) for i in data]
750
+ return data
751
+
889
752
 
890
- return lines
753
+ def _find_differences_dict(obj1, obj2, path, diffs):
754
+ """Recursively find differences between two dictionaries."""
755
+ for key in sorted(set(obj1) | set(obj2)):
756
+ subpath = f"{path}['{key}']" if path else f"['{key}']"
757
+ if key not in obj1:
758
+ diffs.append(f"{subpath}: added in second object")
759
+ elif key not in obj2:
760
+ diffs.append(f"{subpath}: removed in second object")
761
+ else:
762
+ diffs.extend(find_differences_in_json_objects(obj1[key], obj2[key], subpath))
891
763
 
892
764
 
893
- def get_structure_array_from_table(table, column_names):
765
+ def find_differences_in_json_objects(obj1, obj2, path=""):
894
766
  """
895
- Get a structured array from an astropy table for a selected list of columns.
767
+ Recursively find differences between two JSON-like objects.
896
768
 
897
769
  Parameters
898
770
  ----------
899
- table: astropy.table.Table
900
- Table to be converted.
901
- column_names: list
902
- List of column names to be included in the structured array.
771
+ obj1: dict, list, or any
772
+ First object to compare.
773
+ obj2: dict, list, or any
774
+ Second object to compare.
775
+ path: str
776
+ Path to the current object in the JSON structure, used for reporting differences.
903
777
 
904
778
  Returns
905
779
  -------
906
- numpy.ndarray
907
- Structured array containing the table data.
780
+ list
781
+ List of differences found between the two objects, with paths indicating where the
782
+ differences occur.
908
783
  """
909
- return np.array(
910
- list(zip(*[np.array(table[col]) for col in column_names if col in table.colnames])),
911
- dtype=[(col, np.array(table[col]).dtype) for col in column_names if col in table.colnames],
912
- )
784
+ diffs = []
913
785
 
786
+ if not isinstance(obj1, type(obj2)):
787
+ diffs.append(f"{path}: type changed from {type(obj1).__name__} to {type(obj2).__name__}")
788
+ return diffs
914
789
 
915
- def convert_keys_in_dict_to_lowercase(data):
916
- """
917
- Recursively convert all dictionary keys to lowercase.
790
+ if isinstance(obj1, dict):
791
+ _find_differences_dict(obj1, obj2, path, diffs)
918
792
 
919
- Parameters
920
- ----------
921
- data: dict
922
- Dictionary to be converted.
793
+ elif isinstance(obj1, list):
794
+ if len(obj1) != len(obj2):
795
+ diffs.append(f"{path}: list length changed from {len(obj1)} to {len(obj2)}")
796
+ for i, (a, b) in enumerate(zip(obj1, obj2)):
797
+ subpath = f"{path}[{i}]" if path else f"[{i}]"
798
+ diffs.extend(find_differences_in_json_objects(a, b, subpath))
923
799
 
924
- Returns
925
- -------
926
- dict
927
- Dictionary with all keys converted to lowercase.
928
- """
929
- if isinstance(data, dict):
930
- return {k.lower(): convert_keys_in_dict_to_lowercase(v) for k, v in data.items()}
931
- if isinstance(data, list):
932
- return [convert_keys_in_dict_to_lowercase(i) for i in data]
933
- return data
800
+ elif obj1 != obj2:
801
+ diffs.append(f"{path}: value changed from {obj1} to {obj2}")
802
+
803
+ return diffs
934
804
 
935
805
 
936
806
  def clear_default_sim_telarray_cfg_directories(command):
@@ -180,3 +180,39 @@ def calculate_circular_mean(angles):
180
180
  sin_sum = np.sum(np.sin(angles))
181
181
  cos_sum = np.sum(np.cos(angles))
182
182
  return np.arctan2(sin_sum, cos_sum)
183
+
184
+
185
+ def transform_ground_to_shower_coordinates(x_ground, y_ground, z_ground, azimuth, altitude):
186
+ """
187
+ Transform ground to shower coordinates.
188
+
189
+ Assume ground to be of type 'North-West-Up' (NWU) coordinates.
190
+
191
+ Parameters
192
+ ----------
193
+ x_ground: numpy.array
194
+ Ground x coordinate.
195
+ y_ground: numpy.array
196
+ Ground y coordinate.
197
+ z_ground: numpy.array
198
+ Ground z coordinate.
199
+ azimuth: numpy.array
200
+ Azimuth angle of the shower (in radians).
201
+ altitude: numpy.array
202
+ Altitude angle of the shower (in radians).
203
+
204
+ Returns
205
+ -------
206
+ tuple
207
+ Transformed shower coordinates (x', y', z').
208
+ """
209
+ x, y, z, az, alt = np.broadcast_arrays(x_ground, y_ground, z_ground, azimuth, altitude)
210
+
211
+ ca, sa = np.cos(az), np.sin(az)
212
+ cz, sz = np.sin(alt), np.cos(alt)
213
+
214
+ x_s = ca * cz * x - sa * y + ca * sz * z
215
+ y_s = sa * cz * x + ca * y + sa * sz * z
216
+ z_s = -sz * x + cz * z
217
+
218
+ return x_s, y_s, z_s
simtools/utils/names.py CHANGED
@@ -829,6 +829,6 @@ def file_name_with_version(file_name, suffix):
829
829
  if file_name is None or suffix is None:
830
830
  return None
831
831
  file_name = str(file_name)
832
- if bool(re.search(r"\d+\.\d+\.\d+$", file_name)):
832
+ if re.search(r"\d{1,8}\.\d{1,8}\.\d{1,8}\Z", file_name):
833
833
  return Path(file_name + suffix)
834
834
  return Path(file_name).with_suffix(suffix)