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/db/db_handler.py CHANGED
@@ -13,9 +13,9 @@ from packaging.version import Version
13
13
  from pymongo import MongoClient
14
14
 
15
15
  from simtools.data_model import validate_data
16
- from simtools.io_operations import io_handler
16
+ from simtools.io import ascii_handler, io_handler
17
17
  from simtools.simtel import simtel_table_reader
18
- from simtools.utils import general, names, value_conversion
18
+ from simtools.utils import names, value_conversion
19
19
 
20
20
  __all__ = ["DatabaseHandler"]
21
21
 
@@ -81,6 +81,9 @@ class DatabaseHandler:
81
81
 
82
82
  self._set_up_connection()
83
83
  self._find_latest_simulation_model_db()
84
+ self.db_name = (
85
+ self.mongo_db_config.get("db_simulation_model", None) if self.mongo_db_config else None
86
+ )
84
87
 
85
88
  def _set_up_connection(self):
86
89
  """Open the connection to MongoDB."""
@@ -179,6 +182,28 @@ class DatabaseHandler:
179
182
  else:
180
183
  raise ValueError("Found LATEST in the DB name but no matching versions found in DB.")
181
184
 
185
+ def generate_compound_indexes(self, db_name=None):
186
+ """
187
+ Generate compound indexes for the MongoDB collections.
188
+
189
+ Indexes based on the typical query patterns.
190
+ """
191
+ db_name = db_name or self.db_name
192
+ collection_names = [
193
+ "telescopes",
194
+ "sites",
195
+ "configuration_sim_telarray",
196
+ "configuration_corsika",
197
+ "calibration_devices",
198
+ ]
199
+ for collection_name in collection_names:
200
+ db_collection = self.get_collection(collection_name, db_name=db_name)
201
+ db_collection.create_index(
202
+ [("instrument", 1), ("site", 1), ("parameter", 1), ("parameter_version", 1)]
203
+ )
204
+ db_collection = self.get_collection("production_tables", db_name=db_name)
205
+ db_collection.create_index([("collection", 1), ("model_version", 1)])
206
+
182
207
  def get_model_parameter(
183
208
  self,
184
209
  parameter,
@@ -333,24 +358,23 @@ class DatabaseHandler:
333
358
  )
334
359
  return DatabaseHandler.model_parameters_cached[cache_key]
335
360
 
336
- def get_collection(self, db_name, collection_name):
361
+ def get_collection(self, collection_name, db_name=None):
337
362
  """
338
363
  Get a collection from the DB.
339
364
 
340
365
  Parameters
341
366
  ----------
342
- db_name: str
343
- Name of the DB.
344
367
  collection_name: str
345
368
  Name of the collection.
369
+ db_name: str
370
+ Name of the DB.
346
371
 
347
372
  Returns
348
373
  -------
349
374
  pymongo.collection.Collection
350
375
  The collection from the DB.
351
-
352
376
  """
353
- db_name = self._get_db_name(db_name)
377
+ db_name = db_name or self.db_name
354
378
  return DatabaseHandler.db_client[db_name][collection_name]
355
379
 
356
380
  def get_collections(self, db_name=None, model_collections_only=False):
@@ -370,7 +394,7 @@ class DatabaseHandler:
370
394
  List of collection names
371
395
 
372
396
  """
373
- db_name = db_name or self._get_db_name()
397
+ db_name = db_name or self.db_name
374
398
  if db_name not in self.list_of_collections:
375
399
  self.list_of_collections[db_name] = DatabaseHandler.db_client[
376
400
  db_name
@@ -451,7 +475,7 @@ class DatabaseHandler:
451
475
  file_id: dict of GridOut._id
452
476
  Dict of database IDs of files.
453
477
  """
454
- db_name = self._get_db_name(db_name)
478
+ db_name = db_name or self.db_name
455
479
 
456
480
  if file_names:
457
481
  file_names = [file_names] if not isinstance(file_names, list) else file_names
@@ -467,8 +491,8 @@ class DatabaseHandler:
467
491
  if Path(dest).joinpath(file_name).exists():
468
492
  instance_ids[file_name] = "file exists"
469
493
  else:
470
- file_path_instance = self._get_file_mongo_db(self._get_db_name(), file_name)
471
- self._write_file_from_mongo_to_disk(self._get_db_name(), dest, file_path_instance)
494
+ file_path_instance = self._get_file_mongo_db(db_name, file_name)
495
+ self._write_file_from_mongo_to_disk(db_name, dest, file_path_instance)
472
496
  instance_ids[file_name] = file_path_instance._id # pylint: disable=protected-access
473
497
  return instance_ids
474
498
 
@@ -509,8 +533,7 @@ class DatabaseHandler:
509
533
  ValueError
510
534
  if query returned no results.
511
535
  """
512
- db_name = self._get_db_name()
513
- collection = self.get_collection(db_name, collection_name)
536
+ collection = self.get_collection(collection_name, db_name=self.db_name)
514
537
  posts = list(collection.find(query))
515
538
  if not posts:
516
539
  raise ValueError(
@@ -547,7 +570,7 @@ class DatabaseHandler:
547
570
  pass
548
571
 
549
572
  query = {"model_version": model_version, "collection": collection_name}
550
- collection = self.get_collection(self._get_db_name(), "production_tables")
573
+ collection = self.get_collection("production_tables", db_name=self.db_name)
551
574
  post = collection.find_one(query)
552
575
  if not post:
553
576
  raise ValueError(f"The following query returned zero results: {query}")
@@ -574,7 +597,7 @@ class DatabaseHandler:
574
597
  list
575
598
  List of model versions
576
599
  """
577
- collection = self.get_collection(self._get_db_name(), "production_tables")
600
+ collection = self.get_collection("production_tables", db_name=self.db_name)
578
601
  return sorted(
579
602
  {post["model_version"] for post in collection.find({"collection": collection_name})}
580
603
  )
@@ -762,8 +785,7 @@ class DatabaseHandler:
762
785
  production_table: dict
763
786
  The production table to add to the DB.
764
787
  """
765
- db_name = self._get_db_name(db_name)
766
- collection = self.get_collection(db_name, "production_tables")
788
+ collection = self.get_collection("production_tables", db_name=db_name or self.db_name)
767
789
  self._logger.debug(f"Adding production for {production_table.get('collection')} to to DB")
768
790
  collection.insert_one(production_table)
769
791
  DatabaseHandler.production_table_cached.clear()
@@ -794,8 +816,8 @@ class DatabaseHandler:
794
816
  """
795
817
  par_dict = validate_data.DataValidator.validate_model_parameter(par_dict)
796
818
 
797
- db_name = self._get_db_name(db_name)
798
- collection = self.get_collection(db_name, collection_name)
819
+ db_name = db_name or self.db_name
820
+ collection = self.get_collection(collection_name, db_name=db_name)
799
821
 
800
822
  par_dict["value"], _base_unit, _ = value_conversion.get_value_unit_type(
801
823
  value=par_dict["value"], unit_str=par_dict.get("unit", None)
@@ -810,7 +832,7 @@ class DatabaseHandler:
810
832
  f"corresponding to the {par_dict['parameter']} parameter, must be provided."
811
833
  )
812
834
  file_path = Path(file_prefix).joinpath(par_dict["value"])
813
- if not general.is_utf8_file(file_path):
835
+ if not ascii_handler.is_utf8_file(file_path):
814
836
  raise ValueError(f"File is not UTF-8 encoded: {file_path}")
815
837
  files_to_add_to_db.add(f"{file_path}")
816
838
 
@@ -825,22 +847,6 @@ class DatabaseHandler:
825
847
 
826
848
  self._reset_parameter_cache()
827
849
 
828
- def _get_db_name(self, db_name=None):
829
- """
830
- Return database name. If not provided, return the default database name.
831
-
832
- Parameters
833
- ----------
834
- db_name: str
835
- Database name
836
-
837
- Returns
838
- -------
839
- str
840
- Database name
841
- """
842
- return self.mongo_db_config["db_simulation_model"] if db_name is None else db_name
843
-
844
850
  def insert_file_to_db(self, file_name, db_name=None, **kwargs):
845
851
  """
846
852
  Insert a file to the DB.
@@ -863,7 +869,7 @@ class DatabaseHandler:
863
869
  "newly created DB GridOut._id.
864
870
 
865
871
  """
866
- db_name = self._get_db_name(db_name)
872
+ db_name = db_name or self.db_name
867
873
  db = DatabaseHandler.db_client[db_name]
868
874
  file_system = gridfs.GridFS(db)
869
875
 
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  from pathlib import Path
5
5
 
6
- import simtools.utils.general as gen
6
+ from simtools.io import ascii_handler
7
7
  from simtools.utils import names
8
8
 
9
9
  logger = logging.getLogger(__name__)
@@ -26,7 +26,7 @@ def add_values_from_json_to_db(file, collection, db, db_name, file_prefix):
26
26
  file_prefix : str
27
27
  Path to location of all additional files to be uploaded.
28
28
  """
29
- par_dict = gen.collect_data_from_file(file_name=file)
29
+ par_dict = ascii_handler.collect_data_from_file(file_name=file)
30
30
  logger.debug(
31
31
  f"Adding the following parameter to the DB: {par_dict['parameter']} "
32
32
  f"version {par_dict['parameter_version']} "
@@ -119,7 +119,7 @@ def _read_production_table(model_dict, file, model_name):
119
119
  "design_model": {},
120
120
  },
121
121
  )
122
- parameter_dict = gen.collect_data_from_file(file_name=file)
122
+ parameter_dict = ascii_handler.collect_data_from_file(file_name=file)
123
123
  logger.debug(f"Reading production table for {array_element} (collection {collection})")
124
124
  try:
125
125
  if array_element in ("configuration_corsika", "configuration_sim_telarray"):
simtools/dependencies.py CHANGED
@@ -14,18 +14,37 @@ import re
14
14
  import subprocess
15
15
  from pathlib import Path
16
16
 
17
- import simtools.utils.general as gen
17
+ import yaml
18
+
18
19
  from simtools.db.db_handler import DatabaseHandler
20
+ from simtools.io import ascii_handler
19
21
 
20
22
  _logger = logging.getLogger(__name__)
21
23
 
22
24
 
23
- def get_version_string(db_config=None):
24
- """Print the versions of the dependencies."""
25
+ def get_version_string(db_config=None, run_time=None):
26
+ """
27
+ Print the versions of the dependencies.
28
+
29
+ Parameters
30
+ ----------
31
+ db_config : dict, optional
32
+ Database configuration dictionary.
33
+ run_time : list, optional
34
+ Runtime environment command (e.g., Docker).
35
+
36
+ Returns
37
+ -------
38
+ str
39
+ String containing the versions of the dependencies.
40
+
41
+ """
25
42
  return (
26
43
  f"Database version: {get_database_version(db_config)}\n"
27
- f"sim_telarray version: {get_sim_telarray_version()}\n"
28
- f"CORSIKA version: {get_corsika_version()}\n"
44
+ f"sim_telarray version: {get_sim_telarray_version(run_time)}\n"
45
+ f"CORSIKA version: {get_corsika_version(run_time)}\n"
46
+ f"Build options: {get_build_options(run_time)}\n"
47
+ f"Runtime environment: {run_time if run_time else 'None'}\n"
29
48
  )
30
49
 
31
50
 
@@ -50,10 +69,15 @@ def get_database_version(db_config):
50
69
  return db.mongo_db_config.get("db_simulation_model")
51
70
 
52
71
 
53
- def get_sim_telarray_version():
72
+ def get_sim_telarray_version(run_time):
54
73
  """
55
74
  Get the version of the sim_telarray package using 'sim_telarray --version'.
56
75
 
76
+ Parameters
77
+ ----------
78
+ run_time : list, optional
79
+ Runtime environment command (e.g., Docker).
80
+
57
81
  Returns
58
82
  -------
59
83
  str
@@ -65,24 +89,33 @@ def get_sim_telarray_version():
65
89
  return None
66
90
  sim_telarray_path = Path(sim_telarray_path) / "sim_telarray" / "bin" / "sim_telarray"
67
91
 
92
+ if run_time is None:
93
+ command = [str(sim_telarray_path), "--version"]
94
+ else:
95
+ command = [*run_time, str(sim_telarray_path), "--version"]
96
+
97
+ _logger.debug(f"Running command: {command}")
98
+ result = subprocess.run(command, capture_output=True, text=True, check=False)
99
+
68
100
  # expect stdout with e.g. a line 'Release: 2024.271.0 from 2024-09-27'
69
- result = subprocess.run(
70
- [sim_telarray_path, "--version"],
71
- capture_output=True,
72
- text=True,
73
- check=False,
74
- )
75
101
  match = re.search(r"^Release:\s+(.+)", result.stdout, re.MULTILINE)
76
-
77
102
  if match:
78
103
  return match.group(1).split()[0]
104
+
105
+ _logger.debug(f"Command output stdout: {result.stdout} stderr: {result.stderr}")
106
+
79
107
  raise ValueError(f"sim_telarray release not found in {result.stdout}")
80
108
 
81
109
 
82
- def get_corsika_version():
110
+ def get_corsika_version(run_time=None):
83
111
  """
84
112
  Get the version of the CORSIKA package.
85
113
 
114
+ Parameters
115
+ ----------
116
+ run_time : list, optional
117
+ Runtime environment command (e.g., Docker).
118
+
86
119
  Returns
87
120
  -------
88
121
  str
@@ -95,10 +128,15 @@ def get_corsika_version():
95
128
  return None
96
129
  corsika_command = Path(sim_telarray_path) / "corsika-run" / "corsika"
97
130
 
131
+ if run_time is None:
132
+ command = [str(corsika_command)]
133
+ else:
134
+ command = [*run_time, str(corsika_command)]
135
+
98
136
  # Below I do not use the standard context manager because
99
137
  # it makes mocking in the tests significantly more difficult
100
138
  process = subprocess.Popen( # pylint: disable=consider-using-with
101
- corsika_command,
139
+ command,
102
140
  stdout=subprocess.PIPE,
103
141
  stderr=subprocess.PIPE,
104
142
  stdin=subprocess.PIPE,
@@ -123,25 +161,50 @@ def get_corsika_version():
123
161
  if version and re.match(r"\d+\.\d+", version):
124
162
  return version
125
163
  try:
126
- build_opts = get_build_options()
127
- except (FileNotFoundError, TypeError):
164
+ build_opts = get_build_options(run_time)
165
+ except (FileNotFoundError, TypeError, ValueError):
128
166
  _logger.warning("Could not get CORSIKA version.")
129
167
  return None
130
168
  _logger.debug("Getting the CORSIKA version from the build options.")
131
169
  return build_opts.get("corsika_version")
132
170
 
133
171
 
134
- def get_build_options():
172
+ def get_build_options(run_time=None):
135
173
  """
136
174
  Return CORSIKA / sim_telarray build options.
137
175
 
138
176
  Expects a build_opts.yml file in the sim_telarray directory.
177
+
178
+ Parameters
179
+ ----------
180
+ run_time : list, optional
181
+ Runtime environment command (e.g., Docker).
182
+
183
+ Returns
184
+ -------
185
+ dict
186
+ Build options from build_opts.yml file.
139
187
  """
188
+ sim_telarray_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
189
+ if sim_telarray_path is None:
190
+ raise ValueError("SIMTOOLS_SIMTEL_PATH not defined.")
191
+
192
+ build_opts_path = Path(sim_telarray_path) / "build_opts.yml"
193
+
194
+ if run_time is None:
195
+ try:
196
+ return ascii_handler.collect_data_from_file(build_opts_path)
197
+ except FileNotFoundError as exc:
198
+ raise FileNotFoundError("No build_opts.yml file found.") from exc
199
+
200
+ command = [*run_time, "cat", str(build_opts_path)]
201
+ _logger.debug(f"Reading build_opts.yml with command: {command}")
202
+
203
+ result = subprocess.run(command, capture_output=True, text=True, check=False)
204
+ if result.returncode:
205
+ raise FileNotFoundError(f"No build_opts.yml file found in container: {result.stderr}")
206
+
140
207
  try:
141
- return gen.collect_data_from_file(
142
- Path(os.getenv("SIMTOOLS_SIMTEL_PATH")) / "build_opts.yml"
143
- )
144
- except FileNotFoundError as exc:
145
- raise FileNotFoundError("No build_opts.yml file found.") from exc
146
- except TypeError as exc:
147
- raise TypeError("SIMTOOLS_SIMTEL_PATH not defined.") from exc
208
+ return yaml.safe_load(result.stdout)
209
+ except yaml.YAMLError as exc:
210
+ raise ValueError(f"Error parsing build_opts.yml from container: {exc}") from exc
@@ -0,0 +1,279 @@
1
+ """Helper module for ASCII file operations."""
2
+
3
+ import json
4
+ import logging
5
+ import tempfile
6
+ import urllib.request
7
+ from pathlib import Path
8
+
9
+ import astropy.units as u
10
+ import numpy as np
11
+ import yaml
12
+
13
+ from simtools.utils.general import is_url
14
+
15
+ _logger = logging.getLogger(__name__)
16
+
17
+
18
+ def collect_data_from_file(file_name, yaml_document=None):
19
+ """
20
+ Collect data from file based on its extension.
21
+
22
+ Parameters
23
+ ----------
24
+ file_name: str
25
+ Name of the yaml/json/ascii file.
26
+ yaml_document: None, int
27
+ Return list of yaml documents or a single document (for yaml files with several documents).
28
+
29
+ Returns
30
+ -------
31
+ data: dict or list
32
+ Data as dict or list.
33
+ """
34
+ if is_url(file_name):
35
+ return collect_data_from_http(file_name)
36
+
37
+ suffix = Path(file_name).suffix.lower()
38
+ try:
39
+ with open(file_name, encoding="utf-8") as file:
40
+ return _collect_data_from_different_file_types(file, file_name, suffix, yaml_document)
41
+ # broad exception to catch all possible errors in reading the file
42
+ except Exception as exc: # pylint: disable=broad-except
43
+ raise type(exc)(f"Failed to read file {file_name}: {exc}") from exc
44
+
45
+
46
+ def _collect_data_from_different_file_types(file, file_name, suffix, yaml_document):
47
+ """Collect data from different file types."""
48
+ if suffix == ".json":
49
+ return json.load(file)
50
+ if suffix in (".list", ".txt"):
51
+ return [line.strip() for line in file.readlines()]
52
+ if suffix in [".yml", ".yaml"]:
53
+ return _collect_data_from_yaml_file(file, file_name, yaml_document)
54
+ raise TypeError(f"File type {suffix} not supported.")
55
+
56
+
57
+ def _collect_data_from_yaml_file(file, file_name, yaml_document):
58
+ """Collect data from a yaml file (allow for multi-document yaml files)."""
59
+ try:
60
+ return yaml.safe_load(file)
61
+ except yaml.constructor.ConstructorError:
62
+ return _load_yaml_using_astropy(file)
63
+ except yaml.composer.ComposerError:
64
+ pass
65
+ file.seek(0)
66
+ if yaml_document is None:
67
+ return list(yaml.safe_load_all(file))
68
+ try:
69
+ return list(yaml.safe_load_all(file))[yaml_document]
70
+ except IndexError as exc:
71
+ raise IndexError(
72
+ f"Failed to read file {file_name}: YAML document index {yaml_document} is out of range."
73
+ ) from exc
74
+
75
+
76
+ def _load_yaml_using_astropy(file):
77
+ """
78
+ Load a yaml file using astropy's yaml loader.
79
+
80
+ Parameters
81
+ ----------
82
+ file: file
83
+ File to be loaded.
84
+
85
+ Returns
86
+ -------
87
+ dict
88
+ Dictionary containing the file content.
89
+ """
90
+ # pylint: disable=import-outside-toplevel
91
+ import astropy.io.misc.yaml as astropy_yaml
92
+
93
+ file.seek(0)
94
+ return astropy_yaml.load(file)
95
+
96
+
97
+ def collect_data_from_http(url):
98
+ """
99
+ Download yaml or json file from url and return it contents as dict.
100
+
101
+ File is downloaded as a temporary file and deleted afterwards.
102
+
103
+ Parameters
104
+ ----------
105
+ url: str
106
+ URL of the yaml/json file.
107
+
108
+ Returns
109
+ -------
110
+ dict
111
+ Dictionary containing the file content.
112
+
113
+ Raises
114
+ ------
115
+ TypeError
116
+ If url is not a valid URL.
117
+ FileNotFoundError
118
+ If downloading the yaml file fails.
119
+
120
+ """
121
+ try:
122
+ with tempfile.NamedTemporaryFile(mode="w+t") as tmp_file:
123
+ urllib.request.urlretrieve(url, tmp_file.name)
124
+ data = _collect_data_from_different_file_types(
125
+ tmp_file, url, Path(url).suffix.lower(), None
126
+ )
127
+ except TypeError as exc:
128
+ raise TypeError(f"Invalid url {url}") from exc
129
+ except urllib.error.HTTPError as exc:
130
+ raise FileNotFoundError(f"Failed to download file from {url}") from exc
131
+
132
+ _logger.debug(f"Downloaded file from {url}")
133
+ return data
134
+
135
+
136
+ def read_file_encoded_in_utf_or_latin(file_name):
137
+ """
138
+ Read a file encoded in UTF-8 or Latin-1.
139
+
140
+ Parameters
141
+ ----------
142
+ file_name: str
143
+ Name of the file to be read.
144
+
145
+ Returns
146
+ -------
147
+ list
148
+ List of lines read from the file.
149
+
150
+ Raises
151
+ ------
152
+ UnicodeDecodeError
153
+ If the file cannot be decoded using UTF-8 or Latin-1.
154
+ """
155
+ try:
156
+ with open(file_name, encoding="utf-8") as file:
157
+ lines = file.readlines()
158
+ except UnicodeDecodeError:
159
+ _logger.debug("Unable to decode file using UTF-8. Trying Latin-1.")
160
+ try:
161
+ with open(file_name, encoding="latin-1") as file:
162
+ lines = file.readlines()
163
+ except UnicodeDecodeError as exc:
164
+ msg = f"Unable to decode file {file_name} using UTF-8 or Latin-1."
165
+ raise UnicodeDecodeError(exc.encoding, exc.object, exc.start, exc.end, msg) from exc
166
+
167
+ return lines
168
+
169
+
170
+ def is_utf8_file(file_name):
171
+ """
172
+ Check if a file is encoded in UTF-8.
173
+
174
+ Parameters
175
+ ----------
176
+ file_name: str, Path
177
+ Name of the file to be checked.
178
+
179
+ Returns
180
+ -------
181
+ bool
182
+ True if the file is encoded in UTF-8, False otherwise.
183
+ """
184
+ try:
185
+ with open(file_name, encoding="utf-8") as file:
186
+ file.read()
187
+ return True
188
+ except UnicodeDecodeError:
189
+ return False
190
+
191
+
192
+ def write_data_to_file(data, output_file, sort_keys=False, numpy_types=False):
193
+ """
194
+ Write structured data to JSON or YAML file.
195
+
196
+ The file type is determined by the file extension.
197
+
198
+ Parameters
199
+ ----------
200
+ data: dict or list
201
+ Data to be written to the file.
202
+ output_file: str or Path
203
+ Name of the file to be written.
204
+ sort_keys: bool, optional
205
+ If True, sort the keys.
206
+ numpy_types: bool, optional
207
+ If True, convert numpy types to native Python types.
208
+ """
209
+ output_file = Path(output_file)
210
+ if output_file.suffix.lower() == ".json":
211
+ return _write_to_json(data, output_file, sort_keys, numpy_types)
212
+ if output_file.suffix.lower() in [".yml", ".yaml"]:
213
+ return _write_to_yaml(data, output_file, sort_keys)
214
+
215
+ raise ValueError(
216
+ f"Unsupported file type {output_file.suffix}. Only .json, .yml, and .yaml are supported."
217
+ )
218
+
219
+
220
+ def _write_to_json(data, output_file, sort_keys, numpy_types):
221
+ """
222
+ Write data to a JSON file.
223
+
224
+ Parameters
225
+ ----------
226
+ data: dict or list
227
+ Data to be written to the file.
228
+ output_file: Path
229
+ Name of the file to be written.
230
+ sort_keys: bool
231
+ If True, sort the keys.
232
+ numpy_types: bool
233
+ If True, convert numpy types to native Python types.
234
+ """
235
+ with open(output_file, "w", encoding="utf-8") as file:
236
+ json.dump(
237
+ data,
238
+ file,
239
+ indent=4,
240
+ sort_keys=sort_keys,
241
+ cls=JsonNumpyEncoder if numpy_types else None,
242
+ )
243
+ file.write("\n")
244
+
245
+
246
+ def _write_to_yaml(data, output_file, sort_keys):
247
+ """
248
+ Write data to a YAML file.
249
+
250
+ Parameters
251
+ ----------
252
+ data: dict or list
253
+ Data to be written to the file.
254
+ output_file: Path
255
+ Name of the file to be written.
256
+ sort_keys: bool
257
+ If True, sort the keys.
258
+
259
+ """
260
+ with open(output_file, "w", encoding="utf-8") as file:
261
+ yaml.dump(data, file, indent=4, sort_keys=sort_keys, explicit_start=True)
262
+
263
+
264
+ class JsonNumpyEncoder(json.JSONEncoder):
265
+ """Convert numpy to python types as accepted by json.dump."""
266
+
267
+ def default(self, o):
268
+ """Return default encoder."""
269
+ if isinstance(o, np.floating):
270
+ return float(o)
271
+ if isinstance(o, np.integer):
272
+ return int(o)
273
+ if isinstance(o, np.ndarray):
274
+ return o.tolist()
275
+ if isinstance(o, u.core.CompositeUnit | u.core.IrreducibleUnit | u.core.Unit):
276
+ return str(o) if o != u.dimensionless_unscaled else None
277
+ if np.issubdtype(type(o), np.bool_):
278
+ return bool(o)
279
+ return super().default(o)