gammasimtools 0.6.1__py3-none-any.whl → 0.8.2__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 (355) hide show
  1. gammasimtools-0.8.2.dist-info/METADATA +173 -0
  2. gammasimtools-0.8.2.dist-info/RECORD +345 -0
  3. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/WHEEL +1 -1
  4. gammasimtools-0.8.2.dist-info/entry_points.txt +31 -0
  5. simtools/_dev_version/__init__.py +9 -0
  6. simtools/_version.py +2 -2
  7. simtools/applications/calculate_trigger_rate.py +210 -0
  8. simtools/applications/convert_all_model_parameters_from_simtel.py +372 -0
  9. simtools/applications/{print_array_elements.py → convert_geo_coordinates_of_array_elements.py} +58 -63
  10. simtools/applications/convert_model_parameter_from_simtel.py +119 -0
  11. simtools/applications/{add_file_to_db.py → db_add_file_to_db.py} +70 -60
  12. simtools/applications/db_add_model_parameters_from_repository_to_db.py +184 -0
  13. simtools/applications/db_add_value_from_json_to_db.py +105 -0
  14. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +180 -0
  15. simtools/applications/db_get_array_layouts_from_db.py +162 -0
  16. simtools/applications/{get_file_from_db.py → db_get_file_from_db.py} +30 -34
  17. simtools/applications/db_get_parameter_from_db.py +131 -0
  18. simtools/applications/db_inspect_databases.py +52 -0
  19. simtools/applications/derive_mirror_rnda.py +39 -255
  20. simtools/applications/derive_psf_parameters.py +441 -0
  21. simtools/applications/generate_array_config.py +82 -0
  22. simtools/applications/generate_corsika_histograms.py +52 -52
  23. simtools/applications/generate_default_metadata.py +5 -8
  24. simtools/applications/generate_regular_arrays.py +117 -0
  25. simtools/applications/generate_simtel_array_histograms.py +97 -56
  26. simtools/applications/plot_array_layout.py +345 -115
  27. simtools/applications/production_generate_simulation_config.py +158 -0
  28. simtools/applications/production_scale_events.py +168 -0
  29. simtools/applications/simulate_light_emission.py +478 -0
  30. simtools/applications/simulate_prod.py +97 -175
  31. simtools/applications/submit_data_from_external.py +9 -12
  32. simtools/applications/submit_model_parameter_from_external.py +122 -0
  33. simtools/applications/validate_camera_efficiency.py +35 -102
  34. simtools/applications/validate_camera_fov.py +20 -19
  35. simtools/applications/{compare_cumulative_psf.py → validate_cumulative_psf.py} +45 -44
  36. simtools/applications/validate_file_using_schema.py +111 -47
  37. simtools/applications/validate_optics.py +17 -22
  38. simtools/camera_efficiency.py +193 -202
  39. simtools/configuration/commandline_parser.py +384 -96
  40. simtools/configuration/configurator.py +55 -71
  41. simtools/constants.py +5 -5
  42. simtools/corsika/corsika_config.py +482 -342
  43. simtools/corsika/corsika_histograms.py +226 -204
  44. simtools/corsika/corsika_histograms_visualize.py +23 -24
  45. simtools/corsika/primary_particle.py +159 -0
  46. simtools/data_model/data_reader.py +25 -20
  47. simtools/data_model/format_checkers.py +52 -0
  48. simtools/data_model/metadata_collector.py +211 -185
  49. simtools/data_model/metadata_model.py +115 -37
  50. simtools/data_model/model_data_writer.py +335 -26
  51. simtools/data_model/validate_data.py +366 -154
  52. simtools/db/db_array_elements.py +130 -0
  53. simtools/db/db_from_repo_handler.py +106 -0
  54. simtools/db/db_handler.py +1246 -0
  55. simtools/io_operations/hdf5_handler.py +3 -1
  56. simtools/io_operations/io_handler.py +32 -57
  57. simtools/job_execution/job_manager.py +82 -69
  58. simtools/layout/array_layout.py +325 -537
  59. simtools/layout/geo_coordinates.py +8 -11
  60. simtools/layout/telescope_position.py +163 -86
  61. simtools/model/array_model.py +312 -259
  62. simtools/model/calibration_model.py +50 -0
  63. simtools/model/camera.py +277 -523
  64. simtools/model/mirrors.py +68 -49
  65. simtools/model/model_parameter.py +602 -0
  66. simtools/model/model_utils.py +11 -39
  67. simtools/model/site_model.py +161 -0
  68. simtools/model/telescope_model.py +143 -633
  69. simtools/production_configuration/calculate_statistical_errors_grid_point.py +454 -0
  70. simtools/production_configuration/event_scaler.py +146 -0
  71. simtools/production_configuration/generate_simulation_config.py +193 -0
  72. simtools/production_configuration/interpolation_handler.py +197 -0
  73. simtools/ray_tracing/__init__.py +0 -0
  74. simtools/ray_tracing/mirror_panel_psf.py +280 -0
  75. simtools/{psf_analysis.py → ray_tracing/psf_analysis.py} +133 -47
  76. simtools/ray_tracing/ray_tracing.py +646 -0
  77. simtools/runners/__init__.py +0 -0
  78. simtools/runners/corsika_runner.py +240 -0
  79. simtools/runners/corsika_simtel_runner.py +225 -0
  80. simtools/runners/runner_services.py +307 -0
  81. simtools/runners/simtel_runner.py +224 -0
  82. simtools/schemas/array_elements.yml +137 -0
  83. simtools/schemas/integration_tests_config.metaschema.yml +93 -0
  84. simtools/schemas/metadata.metaschema.yml +6 -0
  85. simtools/schemas/model_parameter.metaschema.yml +78 -0
  86. simtools/schemas/{data.metaschema.yml → model_parameter_and_data_schema.metaschema.yml} +27 -44
  87. simtools/schemas/model_parameters/adjust_gain.schema.yml +37 -0
  88. simtools/schemas/model_parameters/altitude.schema.yml +37 -0
  89. simtools/schemas/model_parameters/array_coordinates.schema.yml +33 -0
  90. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +77 -0
  91. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +39 -0
  92. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +39 -0
  93. simtools/schemas/model_parameters/array_layouts.schema.yml +48 -0
  94. simtools/schemas/model_parameters/array_triggers.schema.yml +93 -0
  95. simtools/schemas/model_parameters/asum_clipping.schema.yml +38 -0
  96. simtools/schemas/model_parameters/asum_offset.schema.yml +35 -0
  97. simtools/schemas/model_parameters/asum_shaping.schema.yml +35 -0
  98. simtools/schemas/model_parameters/asum_threshold.schema.yml +38 -0
  99. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +32 -0
  100. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +35 -0
  101. simtools/schemas/model_parameters/axes_offsets.schema.yml +53 -0
  102. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +40 -0
  103. simtools/schemas/model_parameters/camera_body_shape.schema.yml +45 -0
  104. simtools/schemas/model_parameters/camera_config_file.schema.yml +40 -0
  105. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +36 -0
  106. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +43 -0
  107. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +42 -0
  108. simtools/schemas/model_parameters/camera_depth.schema.yml +42 -0
  109. simtools/schemas/model_parameters/camera_filter.schema.yml +45 -0
  110. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +29 -0
  111. simtools/schemas/model_parameters/camera_pixels.schema.yml +36 -0
  112. simtools/schemas/model_parameters/camera_transmission.schema.yml +41 -0
  113. simtools/schemas/model_parameters/channels_per_chip.schema.yml +36 -0
  114. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +35 -0
  115. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +27 -0
  116. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +38 -0
  117. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +28 -0
  118. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +23 -0
  119. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +27 -0
  120. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +28 -0
  121. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +27 -0
  122. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +38 -0
  123. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +52 -0
  124. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +27 -0
  125. simtools/schemas/model_parameters/dark_events.schema.yml +32 -0
  126. simtools/schemas/model_parameters/default_trigger.schema.yml +35 -0
  127. simtools/schemas/model_parameters/design_model.schema.yml +31 -0
  128. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +32 -0
  129. simtools/schemas/model_parameters/disc_bins.schema.yml +39 -0
  130. simtools/schemas/model_parameters/disc_start.schema.yml +41 -0
  131. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +42 -0
  132. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +41 -0
  133. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +41 -0
  134. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +39 -0
  135. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +40 -0
  136. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +41 -0
  137. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +33 -0
  138. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +42 -0
  139. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +37 -0
  140. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +44 -0
  141. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +36 -0
  142. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +45 -0
  143. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +40 -0
  144. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +41 -0
  145. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +38 -0
  146. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +38 -0
  147. simtools/schemas/model_parameters/dish_shape_length.schema.yml +41 -0
  148. simtools/schemas/model_parameters/dsum_clipping.schema.yml +38 -0
  149. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +38 -0
  150. simtools/schemas/model_parameters/dsum_offset.schema.yml +37 -0
  151. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +33 -0
  152. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +39 -0
  153. simtools/schemas/model_parameters/dsum_prescale.schema.yml +44 -0
  154. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +38 -0
  155. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +45 -0
  156. simtools/schemas/model_parameters/dsum_shaping.schema.yml +44 -0
  157. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +32 -0
  158. simtools/schemas/model_parameters/dsum_threshold.schema.yml +43 -0
  159. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +42 -0
  160. simtools/schemas/model_parameters/effective_focal_length.schema.yml +61 -0
  161. simtools/schemas/model_parameters/epsg_code.schema.yml +37 -0
  162. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +35 -0
  163. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +46 -0
  164. simtools/schemas/model_parameters/fadc_bins.schema.yml +40 -0
  165. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +50 -0
  166. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +38 -0
  167. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +42 -0
  168. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +49 -0
  169. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +47 -0
  170. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +51 -0
  171. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +37 -0
  172. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +43 -0
  173. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +49 -0
  174. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +43 -0
  175. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +39 -0
  176. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +42 -0
  177. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +40 -0
  178. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +50 -0
  179. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +42 -0
  180. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +41 -0
  181. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +42 -0
  182. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +43 -0
  183. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +39 -0
  184. simtools/schemas/model_parameters/fadc_mhz.schema.yml +31 -0
  185. simtools/schemas/model_parameters/fadc_noise.schema.yml +41 -0
  186. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +40 -0
  187. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +39 -0
  188. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +50 -0
  189. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +43 -0
  190. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +43 -0
  191. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +42 -0
  192. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +41 -0
  193. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +42 -0
  194. simtools/schemas/model_parameters/flatfielding.schema.yml +37 -0
  195. simtools/schemas/model_parameters/focal_length.schema.yml +45 -0
  196. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +158 -0
  197. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +29 -0
  198. simtools/schemas/model_parameters/focus_offset.schema.yml +66 -0
  199. simtools/schemas/model_parameters/gain_variation.schema.yml +43 -0
  200. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +34 -0
  201. simtools/schemas/model_parameters/geomag_rotation.schema.yml +37 -0
  202. simtools/schemas/model_parameters/geomag_vertical.schema.yml +34 -0
  203. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +36 -0
  204. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +34 -0
  205. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +34 -0
  206. simtools/schemas/model_parameters/laser_events.schema.yml +36 -0
  207. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +35 -0
  208. simtools/schemas/model_parameters/laser_photons.schema.yml +32 -0
  209. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +34 -0
  210. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +34 -0
  211. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +33 -0
  212. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +33 -0
  213. simtools/schemas/model_parameters/laser_var_photons.schema.yml +33 -0
  214. simtools/schemas/model_parameters/laser_wavelength.schema.yml +33 -0
  215. simtools/schemas/model_parameters/led_events.schema.yml +34 -0
  216. simtools/schemas/model_parameters/led_photons.schema.yml +34 -0
  217. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +32 -0
  218. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +33 -0
  219. simtools/schemas/model_parameters/led_var_photons.schema.yml +34 -0
  220. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +41 -0
  221. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +43 -0
  222. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +35 -0
  223. simtools/schemas/model_parameters/min_photons.schema.yml +32 -0
  224. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +36 -0
  225. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +64 -0
  226. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +64 -0
  227. simtools/schemas/model_parameters/mirror_class.schema.yml +41 -0
  228. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +51 -0
  229. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +42 -0
  230. simtools/schemas/model_parameters/mirror_list.schema.yml +38 -0
  231. simtools/schemas/model_parameters/mirror_offset.schema.yml +41 -0
  232. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +39 -0
  233. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +61 -0
  234. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +40 -0
  235. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +46 -0
  236. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +51 -0
  237. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +37 -0
  238. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +79 -0
  239. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +47 -0
  240. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +34 -0
  241. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +33 -0
  242. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +35 -0
  243. simtools/schemas/model_parameters/nsb_skymap.schema.yml +39 -0
  244. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +50 -0
  245. simtools/schemas/model_parameters/num_gains.schema.yml +34 -0
  246. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +33 -0
  247. simtools/schemas/model_parameters/optics_properties.schema.yml +31 -0
  248. simtools/schemas/model_parameters/parabolic_dish.schema.yml +32 -0
  249. simtools/schemas/model_parameters/pedestal_events.schema.yml +32 -0
  250. simtools/schemas/model_parameters/photon_delay.schema.yml +38 -0
  251. simtools/schemas/model_parameters/photons_per_run.schema.yml +33 -0
  252. simtools/schemas/model_parameters/pixel_cells.schema.yml +35 -0
  253. simtools/schemas/model_parameters/pixels_parallel.schema.yml +54 -0
  254. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +40 -0
  255. simtools/schemas/model_parameters/pm_average_gain.schema.yml +34 -0
  256. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +40 -0
  257. simtools/schemas/model_parameters/pm_gain_index.schema.yml +36 -0
  258. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +41 -0
  259. simtools/schemas/model_parameters/pm_transit_time.schema.yml +63 -0
  260. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +39 -0
  261. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +42 -0
  262. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +33 -0
  263. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +33 -0
  264. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +29 -0
  265. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +168 -0
  266. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +36 -0
  267. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +34 -0
  268. simtools/schemas/model_parameters/qe_variation.schema.yml +43 -0
  269. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +42 -0
  270. simtools/schemas/model_parameters/random_focal_length.schema.yml +45 -0
  271. simtools/schemas/model_parameters/random_generator.schema.yml +36 -0
  272. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +35 -0
  273. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +36 -0
  274. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +36 -0
  275. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +34 -0
  276. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +34 -0
  277. simtools/schemas/model_parameters/sampled_output.schema.yml +31 -0
  278. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +34 -0
  279. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +79 -0
  280. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +42 -0
  281. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +41 -0
  282. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +33 -0
  283. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +36 -0
  284. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +29 -0
  285. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +168 -0
  286. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +36 -0
  287. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +35 -0
  288. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +37 -0
  289. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +40 -0
  290. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +40 -0
  291. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +41 -0
  292. simtools/schemas/model_parameters/tailcut_scale.schema.yml +40 -0
  293. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +31 -0
  294. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +35 -0
  295. simtools/schemas/model_parameters/telescope_random_error.schema.yml +34 -0
  296. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +37 -0
  297. simtools/schemas/model_parameters/telescope_transmission.schema.yml +113 -0
  298. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +41 -0
  299. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +36 -0
  300. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +36 -0
  301. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +37 -0
  302. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +38 -0
  303. simtools/schemas/model_parameters/transit_time_error.schema.yml +45 -0
  304. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +36 -0
  305. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +32 -0
  306. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +53 -0
  307. simtools/schemas/model_parameters/trigger_pixels.schema.yml +40 -0
  308. simtools/simtel/simtel_config_reader.py +353 -0
  309. simtools/simtel/simtel_config_writer.py +244 -63
  310. simtools/simtel/{simtel_events.py → simtel_io_events.py} +26 -25
  311. simtools/simtel/simtel_io_histogram.py +661 -0
  312. simtools/simtel/simtel_io_histograms.py +569 -0
  313. simtools/simtel/simulator_array.py +145 -0
  314. simtools/simtel/{simtel_runner_camera_efficiency.py → simulator_camera_efficiency.py} +76 -52
  315. simtools/simtel/simulator_light_emission.py +473 -0
  316. simtools/simtel/simulator_ray_tracing.py +262 -0
  317. simtools/simulator.py +220 -446
  318. simtools/testing/__init__.py +0 -0
  319. simtools/testing/assertions.py +151 -0
  320. simtools/testing/configuration.py +226 -0
  321. simtools/testing/helpers.py +42 -0
  322. simtools/testing/validate_output.py +240 -0
  323. simtools/utils/general.py +340 -437
  324. simtools/utils/geometry.py +12 -12
  325. simtools/utils/names.py +257 -644
  326. simtools/utils/value_conversion.py +176 -0
  327. simtools/version.py +3 -1
  328. simtools/visualization/legend_handlers.py +135 -152
  329. simtools/visualization/plot_camera.py +379 -0
  330. simtools/visualization/visualize.py +346 -167
  331. gammasimtools-0.6.1.dist-info/METADATA +0 -180
  332. gammasimtools-0.6.1.dist-info/RECORD +0 -91
  333. gammasimtools-0.6.1.dist-info/entry_points.txt +0 -23
  334. simtools/_dev_version/scm_version.py +0 -10
  335. simtools/applications/db_development_tools/add_new_parameter_to_db.py +0 -81
  336. simtools/applications/db_development_tools/add_unit_to_parameter_in_db.py +0 -59
  337. simtools/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py +0 -102
  338. simtools/applications/get_parameter.py +0 -92
  339. simtools/applications/make_regular_arrays.py +0 -160
  340. simtools/applications/produce_array_config.py +0 -136
  341. simtools/applications/production.py +0 -313
  342. simtools/applications/sim_showers_for_trigger_rates.py +0 -187
  343. simtools/applications/tune_psf.py +0 -334
  344. simtools/corsika/corsika_default_config.py +0 -282
  345. simtools/corsika/corsika_runner.py +0 -450
  346. simtools/corsika_simtel/corsika_simtel_runner.py +0 -197
  347. simtools/db_handler.py +0 -1480
  348. simtools/ray_tracing.py +0 -525
  349. simtools/simtel/simtel_histograms.py +0 -414
  350. simtools/simtel/simtel_runner.py +0 -244
  351. simtools/simtel/simtel_runner_array.py +0 -293
  352. simtools/simtel/simtel_runner_ray_tracing.py +0 -277
  353. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/LICENSE +0 -0
  354. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/top_level.txt +0 -0
  355. /simtools/{corsika_simtel → db}/__init__.py +0 -0
@@ -0,0 +1,1246 @@
1
+ """Module to handle interaction with DB."""
2
+
3
+ import logging
4
+ import re
5
+ from pathlib import Path
6
+ from threading import Lock
7
+
8
+ import gridfs
9
+ from bson.objectid import ObjectId
10
+ from packaging.version import Version
11
+ from pymongo import ASCENDING, MongoClient
12
+ from pymongo.errors import BulkWriteError
13
+
14
+ from simtools.db import db_array_elements, db_from_repo_handler
15
+ from simtools.io_operations import io_handler
16
+ from simtools.utils import names, value_conversion
17
+
18
+ __all__ = ["DatabaseHandler"]
19
+
20
+ logging.getLogger("pymongo").setLevel(logging.WARNING)
21
+
22
+
23
+ # pylint: disable=unsubscriptable-object
24
+ # The above comment is because pylint does not know that DatabaseHandler.db_client is subscriptable
25
+
26
+
27
+ class DatabaseHandler:
28
+ """
29
+ DatabaseHandler provides the interface to the DB.
30
+
31
+ Parameters
32
+ ----------
33
+ mongo_db_config: dict
34
+ Dictionary with the MongoDB configuration with the following entries:
35
+ "db_server" - DB server address
36
+ "db_api_port" - Port to use
37
+ "db_api_user" - API username
38
+ "db_api_pw" - Password for the API user
39
+ "db_api_authentication_database" - DB with user info (optional, default is "admin")
40
+ "db_simulation_model" - Name of simulation model database
41
+ """
42
+
43
+ DB_CTA_SIMULATION_MODEL_DESCRIPTIONS = "CTA-Simulation-Model-Descriptions"
44
+ # DB collection with updates field names
45
+ DB_DERIVED_VALUES = "Staging-CTA-Simulation-Model-Derived-Values"
46
+
47
+ ALLOWED_FILE_EXTENSIONS = [".dat", ".txt", ".lis", ".cfg", ".yml", ".yaml", ".ecsv"]
48
+
49
+ db_client = None
50
+ site_parameters_cached = {}
51
+ model_parameters_cached = {}
52
+ model_versions_cached = {}
53
+ corsika_configuration_parameters_cached = {}
54
+
55
+ def __init__(self, mongo_db_config=None):
56
+ """Initialize the DatabaseHandler class."""
57
+ self._logger = logging.getLogger(__name__)
58
+
59
+ self.mongo_db_config = mongo_db_config
60
+ self.io_handler = io_handler.IOHandler()
61
+ self.list_of_collections = {}
62
+
63
+ self._set_up_connection()
64
+ self._find_latest_simulation_model_db()
65
+
66
+ def _set_up_connection(self):
67
+ """Open the connection to MongoDB."""
68
+ if self.mongo_db_config and DatabaseHandler.db_client is None:
69
+ lock = Lock()
70
+ with lock:
71
+ DatabaseHandler.db_client = self._open_mongo_db()
72
+
73
+ def _open_mongo_db(self):
74
+ """
75
+ Open a connection to MongoDB and return the client to read/write to the DB with.
76
+
77
+ Returns
78
+ -------
79
+ A PyMongo DB client
80
+
81
+ Raises
82
+ ------
83
+ KeyError
84
+ If the DB configuration is invalid
85
+ """
86
+ try:
87
+ direct_connection = self.mongo_db_config["db_server"] in (
88
+ "localhost",
89
+ "simtools-mongodb",
90
+ )
91
+ return MongoClient(
92
+ self.mongo_db_config["db_server"],
93
+ port=self.mongo_db_config["db_api_port"],
94
+ username=self.mongo_db_config["db_api_user"],
95
+ password=self.mongo_db_config["db_api_pw"],
96
+ authSource=self.mongo_db_config.get("db_api_authentication_database", "admin"),
97
+ directConnection=direct_connection,
98
+ ssl=not direct_connection,
99
+ tlsallowinvalidhostnames=True,
100
+ tlsallowinvalidcertificates=True,
101
+ )
102
+ except KeyError:
103
+ self._logger.error("Invalid setting of DB configuration")
104
+ raise
105
+
106
+ def _find_latest_simulation_model_db(self):
107
+ """
108
+ Find the latest released version of the simulation model and update the DB config.
109
+
110
+ This is indicated by adding "LATEST" to the name of the simulation model database
111
+ (field "db_simulation_model" in the database configuration dictionary).
112
+ Only released versions are considered, pre-releases are ignored.
113
+
114
+ Raises
115
+ ------
116
+ ValueError
117
+ If the "LATEST" version is requested but no versions are found in the DB.
118
+
119
+ """
120
+ try:
121
+ if not self.mongo_db_config["db_simulation_model"].endswith("LATEST"):
122
+ return
123
+ except TypeError:
124
+ return
125
+
126
+ prefix = self.mongo_db_config["db_simulation_model"].replace("LATEST", "")
127
+ list_of_db_names = self.db_client.list_database_names()
128
+ filtered_list_of_db_names = [s for s in list_of_db_names if s.startswith(prefix)]
129
+ versioned_strings = []
130
+ version_pattern = re.compile(
131
+ rf"{re.escape(prefix)}v?(\d+)-(\d+)-(\d+)(?:-([a-zA-Z0-9_.]+))?"
132
+ )
133
+
134
+ for s in filtered_list_of_db_names:
135
+ match = version_pattern.search(s)
136
+ # A version is considered a pre-release if it contains a '-' character (re group 4)
137
+ if match and match.group(4) is None:
138
+ version_str = match.group(1) + "." + match.group(2) + "." + match.group(3)
139
+ version = Version(version_str)
140
+ versioned_strings.append((s, version))
141
+
142
+ if versioned_strings:
143
+ latest_string, _ = max(versioned_strings, key=lambda x: x[1])
144
+ self.mongo_db_config["db_simulation_model"] = latest_string
145
+ self._logger.info(
146
+ f"Updated the DB simulation model to the latest version {latest_string}"
147
+ )
148
+ else:
149
+ raise ValueError("Found LATEST in the DB name but no matching versions found in DB.")
150
+
151
+ def get_model_parameters(
152
+ self,
153
+ site,
154
+ array_element_name,
155
+ model_version,
156
+ collection="telescope",
157
+ only_applicable=False,
158
+ ):
159
+ """
160
+ Get parameters from MongoDB or simulation model repository for an array element.
161
+
162
+ An array element can be e.g., a telescope or a calibration device.
163
+ Read parameters for design and for the specified array element (if necessary). This allows
164
+ to overwrite design parameters with specific parameters without having to copy
165
+ all model parameters when changing only a few.
166
+
167
+ Parameters
168
+ ----------
169
+ site: str
170
+ Site name.
171
+ array_element_name: str
172
+ Name of the array element model (e.g. LSTN-01, MSTS-design)
173
+ model_version: str
174
+ Version of the model.
175
+ collection: str
176
+ collection of array element (e.g. telescopes, calibration_devices)
177
+ only_applicable: bool
178
+ If True, only applicable parameters will be read.
179
+
180
+ Returns
181
+ -------
182
+ dict containing the parameters
183
+
184
+ """
185
+ _site, _array_element_name, _model_version = self._validate_model_input(
186
+ site, array_element_name, model_version
187
+ )
188
+ array_element_list = db_array_elements.get_array_element_list_for_db_query(
189
+ _array_element_name, self, _model_version, collection
190
+ )
191
+ pars = {}
192
+ for array_element in array_element_list:
193
+ _array_elements_cache_key = self._parameter_cache_key(
194
+ site, array_element, model_version, collection
195
+ )
196
+ try:
197
+ pars.update(DatabaseHandler.model_parameters_cached[_array_elements_cache_key])
198
+ except KeyError:
199
+ pars.update(
200
+ self.read_mongo_db(
201
+ self.mongo_db_config.get("db_simulation_model", None),
202
+ array_element_name=array_element,
203
+ model_version=_model_version,
204
+ collection_name=collection,
205
+ run_location=None,
206
+ write_files=False,
207
+ only_applicable=only_applicable,
208
+ )
209
+ )
210
+ if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
211
+ pars = db_from_repo_handler.update_model_parameters_from_repo(
212
+ parameters=pars,
213
+ site=_site,
214
+ parameter_collection=collection,
215
+ array_element_name=array_element,
216
+ model_version=_model_version,
217
+ db_simulation_model_url=self.mongo_db_config.get("db_simulation_model_url"),
218
+ )
219
+ DatabaseHandler.model_parameters_cached[_array_elements_cache_key] = pars
220
+
221
+ return pars
222
+
223
+ def get_collection(self, db_name, collection_name):
224
+ """
225
+ Get a collection from the DB.
226
+
227
+ Parameters
228
+ ----------
229
+ db_name: str
230
+ Name of the DB.
231
+ collection_name: str
232
+ Name of the collection.
233
+
234
+ Returns
235
+ -------
236
+ pymongo.collection.Collection
237
+ The collection from the DB.
238
+
239
+ """
240
+ db_name = self._get_db_name(db_name)
241
+ return DatabaseHandler.db_client[db_name][collection_name]
242
+
243
+ def export_file_db(self, db_name, dest, file_name):
244
+ """
245
+ Get file from the DB and write to disk.
246
+
247
+ Parameters
248
+ ----------
249
+ db_name: str
250
+ Name of the DB to search in.
251
+ dest: str or Path
252
+ Location where to write the file to.
253
+ file_name: str
254
+ Name of the file to get.
255
+
256
+ Returns
257
+ -------
258
+ file_id: GridOut._id
259
+ the database ID the file was assigned when it was inserted to the DB.
260
+
261
+ Raises
262
+ ------
263
+ FileNotFoundError
264
+ If the desired file is not found.
265
+
266
+ """
267
+ db_name = self._get_db_name(db_name)
268
+
269
+ self._logger.debug(f"Getting {file_name} from {db_name} and writing it to {dest}")
270
+ file_path_instance = self._get_file_mongo_db(db_name, file_name)
271
+ self._write_file_from_mongo_to_disk(db_name, dest, file_path_instance)
272
+ return file_path_instance._id # pylint: disable=protected-access;
273
+
274
+ def export_model_files(self, parameters, dest):
275
+ """
276
+ Export all the files in a model from the DB and write them to disk.
277
+
278
+ Parameters
279
+ ----------
280
+ parameters: dict
281
+ Dict of model parameters
282
+ dest: str or Path
283
+ Location where to write the files to.
284
+
285
+ Raises
286
+ ------
287
+ FileNotFoundError
288
+ if a file in parameters.values is not found
289
+
290
+ """
291
+ if self.mongo_db_config:
292
+ for info in parameters.values():
293
+ if not info or not info.get("file") or info["value"] is None:
294
+ continue
295
+ if Path(dest).joinpath(info["value"]).exists():
296
+ continue
297
+ file = self._get_file_mongo_db(self._get_db_name(), info["value"])
298
+ self._write_file_from_mongo_to_disk(self._get_db_name(), dest, file)
299
+ if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
300
+ self._logger.warning(
301
+ "Exporting model files from simulation model repository not yet implemented"
302
+ )
303
+
304
+ @staticmethod
305
+ def _is_file(value):
306
+ """Verify if a parameter value is a file name."""
307
+ return any(ext in str(value) for ext in DatabaseHandler.ALLOWED_FILE_EXTENSIONS)
308
+
309
+ def read_mongo_db(
310
+ self,
311
+ db_name,
312
+ array_element_name,
313
+ model_version,
314
+ run_location,
315
+ collection_name,
316
+ write_files=True,
317
+ only_applicable=False,
318
+ ):
319
+ """
320
+ Build and execute query to Read the MongoDB for a specific array element.
321
+
322
+ Also writes the files listed in the parameter values into the sim_telarray run location
323
+
324
+ Parameters
325
+ ----------
326
+ db_name: str
327
+ the name of the DB
328
+ array_element_name: str
329
+ Name of the array element model (e.g. MSTN-design ...)
330
+ model_version: str
331
+ Version of the model.
332
+ run_location: Path or str
333
+ The sim_telarray run location to write the tabulated data files into.
334
+ collection_name: str
335
+ The name of the collection to read from.
336
+ write_files: bool
337
+ If true, write the files to the run_location.
338
+ only_applicable: bool
339
+ If True, only applicable parameters will be read.
340
+
341
+ Returns
342
+ -------
343
+ dict containing the parameters
344
+
345
+ Raises
346
+ ------
347
+ ValueError
348
+ if query returned zero results.
349
+
350
+ """
351
+ collection = self.get_collection(db_name, collection_name)
352
+ _parameters = {}
353
+
354
+ query = {
355
+ "instrument": array_element_name,
356
+ "version": self.model_version(model_version, db_name),
357
+ }
358
+
359
+ if only_applicable:
360
+ query["applicable"] = True
361
+ if collection.count_documents(query) < 1:
362
+ raise ValueError(
363
+ "The following query returned zero results! Check the input data and rerun.\n",
364
+ query,
365
+ )
366
+ for post in collection.find(query).sort("parameter", ASCENDING):
367
+ par_now = post["parameter"]
368
+ _parameters[par_now] = post
369
+ _parameters[par_now].pop("parameter", None)
370
+ _parameters[par_now].pop("instrument", None)
371
+ _parameters[par_now]["entry_date"] = ObjectId(post["_id"]).generation_time
372
+ if _parameters[par_now]["file"] and write_files:
373
+ file = self._get_file_mongo_db(db_name, _parameters[par_now]["value"])
374
+ self._write_file_from_mongo_to_disk(db_name, run_location, file)
375
+
376
+ return _parameters
377
+
378
+ def get_site_parameters(
379
+ self,
380
+ site,
381
+ model_version,
382
+ only_applicable=False,
383
+ ):
384
+ """
385
+ Get parameters from either MongoDB or simulation model repository for a specific site.
386
+
387
+ Parameters
388
+ ----------
389
+ site: str
390
+ Site name.
391
+ model_version: str
392
+ Version of the model.
393
+ only_applicable: bool
394
+ If True, only applicable parameters will be read.
395
+
396
+ Returns
397
+ -------
398
+ dict containing the parameters
399
+
400
+ """
401
+ _site, _, _model_version = self._validate_model_input(site, None, model_version)
402
+ _db_name = self._get_db_name()
403
+ _site_cache_key = self._parameter_cache_key(site, None, model_version)
404
+ try:
405
+ return DatabaseHandler.site_parameters_cached[_site_cache_key]
406
+ except KeyError:
407
+ pass
408
+
409
+ _pars = self._get_site_parameters_mongo_db(
410
+ _db_name,
411
+ _site,
412
+ _model_version,
413
+ only_applicable,
414
+ )
415
+ # update simulation model using repository
416
+ if self.mongo_db_config.get("db_simulation_model_url", None) is not None:
417
+ _pars = db_from_repo_handler.update_model_parameters_from_repo(
418
+ parameters=_pars,
419
+ site=_site,
420
+ array_element_name=None,
421
+ parameter_collection="site",
422
+ model_version=_model_version,
423
+ db_simulation_model_url=self.mongo_db_config.get("db_simulation_model_url", None),
424
+ )
425
+
426
+ DatabaseHandler.site_parameters_cached[_site_cache_key] = _pars
427
+ return DatabaseHandler.site_parameters_cached[_site_cache_key]
428
+
429
+ def _get_site_parameters_mongo_db(self, db_name, site, model_version, only_applicable=False):
430
+ """
431
+ Get parameters from MongoDB for a specific site.
432
+
433
+ Parameters
434
+ ----------
435
+ db_name: str
436
+ The name of the DB.
437
+ site: str
438
+ Site name.
439
+ model_version: str
440
+ Version of the model.
441
+ only_applicable: bool
442
+ If True, only applicable parameters will be read.
443
+
444
+ Returns
445
+ -------
446
+ dict containing the parameters
447
+
448
+ Raises
449
+ ------
450
+ ValueError
451
+ if query returned zero results.
452
+
453
+ """
454
+ collection = self.get_collection(db_name, "sites")
455
+ _parameters = {}
456
+
457
+ query = {
458
+ "site": site,
459
+ "version": model_version,
460
+ }
461
+ if only_applicable:
462
+ query["applicable"] = True
463
+ if collection.count_documents(query) < 1:
464
+ raise ValueError(
465
+ "The following query returned zero results! Check the input data and rerun.\n",
466
+ query,
467
+ )
468
+ for post in collection.find(query).sort("parameter", ASCENDING):
469
+ par_now = post["parameter"]
470
+ _parameters[par_now] = post
471
+ _parameters[par_now].pop("parameter", None)
472
+ _parameters[par_now].pop("site", None)
473
+ _parameters[par_now]["entry_date"] = ObjectId(post["_id"]).generation_time
474
+
475
+ return _parameters
476
+
477
+ def get_derived_values(self, site, array_element_name, model_version):
478
+ """
479
+ Get all derived values from the DB for a specific array element.
480
+
481
+ Parameters
482
+ ----------
483
+ site: str
484
+ Site name.
485
+ array_element_name: str
486
+ Name of the array element model (e.g. MSTN, SSTS).
487
+ model_version: str
488
+ Version of the model.
489
+
490
+ Returns
491
+ -------
492
+ dict containing the parameters
493
+
494
+ """
495
+ _, _array_element_name, _model_version = self._validate_model_input(
496
+ site, array_element_name, model_version
497
+ )
498
+
499
+ return self.read_mongo_db(
500
+ DatabaseHandler.DB_DERIVED_VALUES,
501
+ _array_element_name,
502
+ _model_version,
503
+ run_location=None,
504
+ collection_name="derived_values",
505
+ write_files=False,
506
+ )
507
+
508
+ def get_simulation_configuration_parameters(
509
+ self, simulation_software, site, array_element_name, model_version
510
+ ):
511
+ """
512
+ Get simulation configuration parameters from the DB.
513
+
514
+ Parameters
515
+ ----------
516
+ simulation_software: str
517
+ Name of the simulation software.
518
+ site: str
519
+ Site name.
520
+ array_element_name: str
521
+ Name of the array element model (e.g. MSTN, SSTS).
522
+ model_version: str
523
+ Version of the model.
524
+
525
+ Returns
526
+ -------
527
+ dict containing the parameters
528
+
529
+ Raises
530
+ ------
531
+ ValueError
532
+ if simulation_software is not valid.
533
+ """
534
+ if simulation_software == "corsika":
535
+ return self.get_corsika_configuration_parameters(model_version)
536
+ if simulation_software == "simtel":
537
+ if site and array_element_name:
538
+ return self.get_model_parameters(
539
+ site, array_element_name, model_version, collection="configuration_sim_telarray"
540
+ )
541
+ return {}
542
+ raise ValueError(f"Unknown simulation software: {simulation_software}")
543
+
544
+ def get_corsika_configuration_parameters(self, model_version):
545
+ """
546
+ Get CORSIKA configuration parameters from the DB.
547
+
548
+ Parameters
549
+ ----------
550
+ model_version : str
551
+ Version of the model.
552
+
553
+ Returns
554
+ -------
555
+ dict
556
+ Configuration parameters for CORSIKA
557
+ """
558
+ _corsika_cache_key = self._parameter_cache_key(None, None, model_version)
559
+ try:
560
+ return DatabaseHandler.corsika_configuration_parameters_cached[_corsika_cache_key]
561
+ except KeyError:
562
+ pass
563
+ DatabaseHandler.corsika_configuration_parameters_cached[_corsika_cache_key] = (
564
+ self.read_mongo_db(
565
+ db_name=self._get_db_name(),
566
+ array_element_name=None,
567
+ model_version=model_version,
568
+ run_location=None,
569
+ collection_name="configuration_corsika",
570
+ write_files=False,
571
+ )
572
+ )
573
+ return DatabaseHandler.corsika_configuration_parameters_cached[_corsika_cache_key]
574
+
575
+ def _validate_model_input(self, site, array_element_name, model_version):
576
+ """
577
+ Validate input for model parameter queries.
578
+
579
+ site: str
580
+ Site name.
581
+ array_element_name: str
582
+ Name of the array element model (e.g. LSTN-01, MSTS-design)
583
+ model_version: str
584
+ Version of the model.
585
+
586
+ """
587
+ return (
588
+ names.validate_site_name(site),
589
+ names.validate_array_element_name(array_element_name) if array_element_name else None,
590
+ self.model_version(model_version),
591
+ )
592
+
593
+ @staticmethod
594
+ def _get_file_mongo_db(db_name, file_name):
595
+ """
596
+ Extract a file from MongoDB and return GridFS file instance.
597
+
598
+ Parameters
599
+ ----------
600
+ db_name: str
601
+ the name of the DB with files of tabulated data
602
+ file_name: str
603
+ The name of the file requested
604
+
605
+ Returns
606
+ -------
607
+ GridOut
608
+ A file instance returned by GridFS find_one
609
+
610
+ Raises
611
+ ------
612
+ FileNotFoundError
613
+ If the desired file is not found.
614
+
615
+ """
616
+ db = DatabaseHandler.db_client[db_name]
617
+ file_system = gridfs.GridFS(db)
618
+ if file_system.exists({"filename": file_name}):
619
+ return file_system.find_one({"filename": file_name})
620
+
621
+ raise FileNotFoundError(f"The file {file_name} does not exist in the database {db_name}")
622
+
623
+ @staticmethod
624
+ def _write_file_from_mongo_to_disk(db_name, path, file):
625
+ """
626
+ Extract a file from MongoDB and write it to disk.
627
+
628
+ Parameters
629
+ ----------
630
+ db_name: str
631
+ the name of the DB with files of tabulated data
632
+ path: str or Path
633
+ The path to write the file to
634
+ file: GridOut
635
+ A file instance returned by GridFS find_one
636
+ """
637
+ db = DatabaseHandler.db_client[db_name]
638
+ fs_output = gridfs.GridFSBucket(db)
639
+ with open(Path(path).joinpath(file.filename), "wb") as output_file:
640
+ fs_output.download_to_stream_by_name(file.filename, output_file)
641
+
642
+ def copy_array_element(
643
+ self,
644
+ db_name,
645
+ element_to_copy,
646
+ version_to_copy,
647
+ new_array_element_name,
648
+ collection_name="telescopes",
649
+ db_to_copy_to=None,
650
+ collection_to_copy_to=None,
651
+ ):
652
+ """
653
+ Copy a full array element configuration to a new array element name.
654
+
655
+ Only a specific version is copied.
656
+ (This function should be rarely used, probably only during "construction".)
657
+
658
+ Parameters
659
+ ----------
660
+ db_name: str
661
+ the name of the DB to copy from
662
+ element_to_copy: str
663
+ The array element to copy
664
+ version_to_copy: str
665
+ The version of the configuration to copy
666
+ new_array_element_name: str
667
+ The name of the new array element
668
+ collection_name: str
669
+ The name of the collection to copy from.
670
+ db_to_copy_to: str
671
+ The name of the DB to copy to.
672
+ collection_to_copy_to: str
673
+ The name of the collection to copy to.
674
+
675
+ Raises
676
+ ------
677
+ BulkWriteError
678
+
679
+ """
680
+ db_name = self._get_db_name(db_name)
681
+ if db_to_copy_to is None:
682
+ db_to_copy_to = db_name
683
+
684
+ if collection_to_copy_to is None:
685
+ collection_to_copy_to = collection_name
686
+
687
+ self._logger.info(
688
+ f"Copying version {version_to_copy} of {element_to_copy} "
689
+ f"to the new array element {new_array_element_name} in the {db_to_copy_to} DB"
690
+ )
691
+
692
+ collection = self.get_collection(db_name, collection_name)
693
+ db_entries = []
694
+
695
+ _version_to_copy = self.model_version(version_to_copy)
696
+
697
+ query = {
698
+ "instrument": element_to_copy,
699
+ "version": _version_to_copy,
700
+ }
701
+ for post in collection.find(query):
702
+ post["instrument"] = new_array_element_name
703
+ post.pop("_id", None)
704
+ db_entries.append(post)
705
+
706
+ self._logger.info(f"Creating new array element {new_array_element_name}")
707
+ collection = self.get_collection(db_to_copy_to, collection_to_copy_to)
708
+ try:
709
+ collection.insert_many(db_entries)
710
+ except BulkWriteError as exc:
711
+ raise BulkWriteError(str(exc.details)) from exc
712
+
713
+ def copy_documents(self, db_name, collection, query, db_to_copy_to, collection_to_copy_to=None):
714
+ """
715
+ Copy the documents matching to "query" to the DB "db_to_copy_to".
716
+
717
+ The documents are copied to the same collection as in "db_name".
718
+ (This function should be rarely used, probably only during "construction".)
719
+
720
+ Parameters
721
+ ----------
722
+ db_name: str
723
+ the name of the DB to copy from
724
+ collection: str
725
+ the name of the collection to copy from
726
+ query: dict
727
+ A dictionary with a query to search for documents to copy.
728
+ For example, the query below would copy all entries of version 6.0.0
729
+ from telescope LSTN-01 to "db_to_copy_to".
730
+
731
+ .. code-block:: python
732
+
733
+ query = {
734
+ "instrument": "LSTN-01",
735
+ "version": "6.0.0",
736
+ }
737
+ db_to_copy_to: str
738
+ The name of the DB to copy to.
739
+
740
+ Raises
741
+ ------
742
+ BulkWriteError
743
+
744
+ """
745
+ db_name = self._get_db_name(db_name)
746
+
747
+ _collection = self.get_collection(db_name, collection)
748
+ if collection_to_copy_to is None:
749
+ collection_to_copy_to = collection
750
+ db_entries = []
751
+
752
+ for post in _collection.find(query):
753
+ post.pop("_id", None)
754
+ db_entries.append(post)
755
+
756
+ self._logger.info(
757
+ f"Copying documents matching the following query {query}\nto {db_to_copy_to}"
758
+ )
759
+ _collection = self.get_collection(db_to_copy_to, collection_to_copy_to)
760
+ try:
761
+ _collection.insert_many(db_entries)
762
+ except BulkWriteError as exc:
763
+ raise BulkWriteError(str(exc.details)) from exc
764
+
765
+ def delete_query(self, db_name, collection, query):
766
+ """
767
+ Delete all entries from the DB which correspond to the provided query.
768
+
769
+ (This function should be rarely used, if at all.)
770
+
771
+ Parameters
772
+ ----------
773
+ db_name: str
774
+ the name of the DB
775
+ collection: str
776
+ the name of the collection to copy from
777
+ query: dict
778
+ A dictionary listing the fields/values to delete.
779
+ For example, the query below would delete the entire version 6.0.0
780
+ from telescope LSTN-01.
781
+
782
+ .. code-block:: python
783
+
784
+ query = {
785
+ "instrument": "LSTN-01",
786
+ "version": "6.0.0",
787
+ }
788
+
789
+ """
790
+ _collection = self.get_collection(db_name, collection)
791
+
792
+ if "version" in query:
793
+ query["version"] = self.model_version(query["version"])
794
+
795
+ self._logger.info(f"Deleting {_collection.count_documents(query)} entries from {db_name}")
796
+
797
+ _collection.delete_many(query)
798
+
799
+ def update_parameter_field(
800
+ self,
801
+ db_name,
802
+ model_version,
803
+ parameter,
804
+ field,
805
+ new_value,
806
+ array_element_name=None,
807
+ site=None,
808
+ collection_name="telescopes",
809
+ ):
810
+ """
811
+ Update a parameter field value for a specific array element/version.
812
+
813
+ This function only modifies the value of one of the following
814
+ DB entries: Applicable, units, Type, items, minimum, maximum.
815
+ These type of changes should be very rare. However they can
816
+ be done without changing the Object ID of the entry since
817
+ they are generally "harmless".
818
+
819
+ Parameters
820
+ ----------
821
+ db_name: str
822
+ the name of the DB
823
+ model_version: str
824
+ Which model version to update
825
+ parameter: str
826
+ Which parameter to update
827
+ field: str
828
+ Field to update (only options are Applicable, units, Type, items, minimum, maximum).
829
+ If the field does not exist, it will be added.
830
+ new_value: type identical to the original field type
831
+ The new value to set to the field given in "field".
832
+ array_element_name: str
833
+ Which array element to update, if None then update a site parameter
834
+ site: str, North or South
835
+ Update a site parameter (the array_element_name argument must be None)
836
+ collection_name: str
837
+ The name of the collection in which to update the parameter.
838
+
839
+ Raises
840
+ ------
841
+ ValueError
842
+ if field not in allowed fields
843
+
844
+ """
845
+ db_name = self._get_db_name(db_name)
846
+ allowed_fields = ["applicable", "unit", "type", "items", "minimum", "maximum"]
847
+ if field not in allowed_fields:
848
+ raise ValueError(f"The field {field} must be one of {', '.join(allowed_fields)}")
849
+
850
+ collection = self.get_collection(db_name, collection_name)
851
+ _model_version = self.model_version(model_version, db_name)
852
+
853
+ query = {
854
+ "version": _model_version,
855
+ "parameter": parameter,
856
+ }
857
+ if array_element_name is not None:
858
+ query["instrument"] = array_element_name
859
+ logger_info = f"instrument {array_element_name}"
860
+ elif site is not None and site in names.site_names():
861
+ query["site"] = site
862
+ logger_info = f"site {site}"
863
+ else:
864
+ raise ValueError("You need to specify an array element or a site.")
865
+
866
+ par_entry = collection.find_one(query)
867
+ if par_entry is None:
868
+ self._logger.warning(
869
+ f"The query {query} did not return any results. I will not make any changes."
870
+ )
871
+ return
872
+
873
+ if field in par_entry:
874
+ old_field_value = par_entry[field]
875
+
876
+ if old_field_value == new_value:
877
+ self._logger.warning(
878
+ f"The value of the field {field} is already {new_value}. No changes necessary"
879
+ )
880
+ return
881
+
882
+ self._logger.info(
883
+ f"For {logger_info}, version {_model_version}, parameter {parameter}, "
884
+ f"replacing field {field} value from {old_field_value} to {new_value}"
885
+ )
886
+ else:
887
+ self._logger.info(
888
+ f"For {logger_info}, version {_model_version}, parameter {parameter}, "
889
+ f"the field {field} does not exist, adding it"
890
+ )
891
+
892
+ query_update = {"$set": {field: new_value}}
893
+ collection.update_one(query, query_update)
894
+
895
+ self._reset_parameter_cache(
896
+ site=(
897
+ site
898
+ if site is not None
899
+ else names.get_site_from_array_element_name(array_element_name)
900
+ ),
901
+ array_element_name=array_element_name,
902
+ model_version=_model_version,
903
+ )
904
+
905
+ def add_new_parameter(
906
+ self,
907
+ db_name,
908
+ version,
909
+ parameter,
910
+ value,
911
+ array_element_name=None,
912
+ site=None,
913
+ collection_name="telescopes",
914
+ file_prefix=None,
915
+ **kwargs,
916
+ ):
917
+ """
918
+ Add a parameter value for a specific array element.
919
+
920
+ A new document will be added to the DB,
921
+ with all fields taken from the input parameters.
922
+
923
+ Parameters
924
+ ----------
925
+ db_name: str
926
+ the name of the DB
927
+ version: str
928
+ The version of the new parameter value
929
+ parameter: str
930
+ Which parameter to add
931
+ value: can be any type, preferably given in kwargs
932
+ The value to set for the new parameter
933
+ array_element_name: str
934
+ The name of the array element to add a parameter to
935
+ (only used if collection_name is not "sites").
936
+ site: str
937
+ Site name; ignored if collection_name is "telescopes".
938
+ collection_name: str
939
+ The name of the collection to add a parameter to.
940
+ file_prefix: str or Path
941
+ where to find files to upload to the DB
942
+ kwargs: dict
943
+ Any additional fields to add to the parameter
944
+
945
+ Raises
946
+ ------
947
+ ValueError
948
+ If key to collection_name is not valid.
949
+
950
+ """
951
+ db_name = self._get_db_name(db_name)
952
+ collection = self.get_collection(db_name, collection_name)
953
+
954
+ db_entry = {}
955
+ if any(
956
+ key in collection_name
957
+ for key in ["telescopes", "calibration_devices", "configuration_sim_telarray"]
958
+ ):
959
+ db_entry["instrument"] = names.validate_array_element_name(array_element_name)
960
+ elif "sites" in collection_name:
961
+ db_entry["instrument"] = names.validate_site_name(site)
962
+ elif "configuration_corsika" in collection_name:
963
+ db_entry["instrument"] = None
964
+ else:
965
+ raise ValueError(f"Cannot add parameter to collection {collection_name}")
966
+
967
+ db_entry["version"] = version
968
+ db_entry["parameter"] = parameter
969
+ if site is not None:
970
+ db_entry["site"] = names.validate_site_name(site)
971
+
972
+ _base_value, _base_unit, _base_type = value_conversion.get_value_unit_type(
973
+ value=value, unit_str=kwargs.get("unit", None)
974
+ )
975
+ db_entry["value"] = _base_value
976
+ if _base_unit is not None:
977
+ db_entry["unit"] = _base_unit
978
+ db_entry["type"] = kwargs["type"] if "type" in kwargs else _base_type
979
+
980
+ files_to_add_to_db = set()
981
+ db_entry["file"] = False
982
+ if self._is_file(value):
983
+ db_entry["file"] = True
984
+ if file_prefix is None:
985
+ raise FileNotFoundError(
986
+ "The location of the file to upload, "
987
+ f"corresponding to the {parameter} parameter, must be provided."
988
+ )
989
+ file_path = Path(file_prefix).joinpath(value)
990
+ files_to_add_to_db.add(f"{file_path}")
991
+
992
+ kwargs.pop("type", None)
993
+ db_entry.update(kwargs)
994
+
995
+ self._logger.info(
996
+ f"Will add the following entry to DB {db_name} and collection {db_name}:\n{db_entry}"
997
+ )
998
+
999
+ collection.insert_one(db_entry)
1000
+ for file_to_insert_now in files_to_add_to_db:
1001
+ self._logger.info(f"Will also add the file {file_to_insert_now} to the DB")
1002
+ self.insert_file_to_db(file_to_insert_now, db_name)
1003
+
1004
+ self._reset_parameter_cache(site, array_element_name, version)
1005
+
1006
+ def _get_db_name(self, db_name=None):
1007
+ """
1008
+ Return database name. If not provided, return the default database name.
1009
+
1010
+ Parameters
1011
+ ----------
1012
+ db_name: str
1013
+ Database name
1014
+
1015
+ Returns
1016
+ -------
1017
+ str
1018
+ Database name
1019
+ """
1020
+ return self.mongo_db_config["db_simulation_model"] if db_name is None else db_name
1021
+
1022
+ def model_version(self, version, db_name=None):
1023
+ """
1024
+ Return model version and check that it is valid.
1025
+
1026
+ Queries the database for all available model versions and check if the
1027
+ requested version is valid.
1028
+
1029
+ Parameters
1030
+ ----------
1031
+ version : str
1032
+ Model version.
1033
+ db_name : str
1034
+ Database name.
1035
+
1036
+ Returns
1037
+ -------
1038
+ str
1039
+ Model version.
1040
+
1041
+ Raises
1042
+ ------
1043
+ ValueError
1044
+ if version not valid.
1045
+
1046
+ """
1047
+ _all_versions = self.get_all_versions(db_name=db_name)
1048
+ if version in _all_versions:
1049
+ return version
1050
+ if len(_all_versions) == 0:
1051
+ return None
1052
+
1053
+ raise ValueError(
1054
+ f"Invalid model version {version} in DB {self._get_db_name(db_name)} "
1055
+ f"(allowed are {_all_versions})"
1056
+ )
1057
+
1058
+ def insert_file_to_db(self, file_name, db_name=None, **kwargs):
1059
+ """
1060
+ Insert a file to the DB.
1061
+
1062
+ Parameters
1063
+ ----------
1064
+ file_name: str or Path
1065
+ The name of the file to insert (full path).
1066
+ db_name: str
1067
+ the name of the DB
1068
+ **kwargs (optional): keyword arguments for file creation.
1069
+ The full list of arguments can be found in, \
1070
+ https://docs.mongodb.com/manual/core/gridfs/#the-files-collection
1071
+ mostly these are unnecessary though.
1072
+
1073
+ Returns
1074
+ -------
1075
+ file_iD: GridOut._id
1076
+ If the file exists, return its GridOut._id, otherwise insert the file and return its"
1077
+ "newly created DB GridOut._id.
1078
+
1079
+ """
1080
+ db_name = self._get_db_name(db_name)
1081
+
1082
+ db = DatabaseHandler.db_client[db_name]
1083
+ file_system = gridfs.GridFS(db)
1084
+
1085
+ if "content_type" not in kwargs:
1086
+ kwargs["content_type"] = "ascii/dat"
1087
+
1088
+ if "filename" not in kwargs:
1089
+ kwargs["filename"] = Path(file_name).name
1090
+
1091
+ if file_system.exists({"filename": kwargs["filename"]}):
1092
+ self._logger.warning(
1093
+ f"The file {kwargs['filename']} exists in the DB. Returning its ID"
1094
+ )
1095
+ return file_system.find_one( # pylint: disable=protected-access
1096
+ {"filename": kwargs["filename"]}
1097
+ )._id
1098
+ with open(file_name, "rb") as data_file:
1099
+ return file_system.put(data_file, **kwargs)
1100
+
1101
+ def get_all_versions(
1102
+ self,
1103
+ parameter=None,
1104
+ array_element_name=None,
1105
+ site=None,
1106
+ db_name=None,
1107
+ collection=None,
1108
+ ):
1109
+ """
1110
+ Get all version entries in the DB of collection and/or a specific parameter.
1111
+
1112
+ Parameters
1113
+ ----------
1114
+ parameter: str
1115
+ Which parameter to get the versions of
1116
+ array_element_name: str
1117
+ Which array element to get the versions of (in case "collection_name" is not "sites")
1118
+ site: str
1119
+ Site name.
1120
+ db_name: str
1121
+ Database name.
1122
+ collection_name: str
1123
+ The name of the collection in which to update the parameter.
1124
+
1125
+ Returns
1126
+ -------
1127
+ all_versions: list
1128
+ List of all versions found
1129
+
1130
+ Raises
1131
+ ------
1132
+ ValueError
1133
+ If key to collection_name is not valid.
1134
+
1135
+ """
1136
+ db_name = self._get_db_name() if db_name is None else db_name
1137
+ if not db_name:
1138
+ self._logger.warning("No database name defined to determine list of model versions")
1139
+ return []
1140
+ _cache_key = f"model_versions_{db_name}-{collection}"
1141
+
1142
+ query = {}
1143
+ if parameter is not None:
1144
+ query["parameter"] = parameter
1145
+ _cache_key = f"{_cache_key}-{parameter}"
1146
+ if collection in ["telescopes", "calibration_devices"] and array_element_name is not None:
1147
+ query["instrument"] = names.validate_array_element_name(array_element_name)
1148
+ _cache_key = f"{_cache_key}-{query['instrument']}"
1149
+ elif collection == "sites" and site is not None:
1150
+ query["site"] = names.validate_site_name(site)
1151
+ _cache_key = f"{_cache_key}-{query['site']}"
1152
+
1153
+ if _cache_key not in DatabaseHandler.model_versions_cached:
1154
+ all_versions = set()
1155
+ collections_to_query = (
1156
+ [collection] if collection else self.get_collections(db_name, True)
1157
+ )
1158
+ for collection_name in collections_to_query:
1159
+ db_collection = self.get_collection(db_name, collection_name)
1160
+ sorted_posts = db_collection.find(query).sort("version", ASCENDING)
1161
+ all_versions.update(post["version"] for post in sorted_posts)
1162
+ DatabaseHandler.model_versions_cached[_cache_key] = list(all_versions)
1163
+
1164
+ if len(DatabaseHandler.model_versions_cached[_cache_key]) == 0:
1165
+ self._logger.warning(f"The query {query} did not return any results. No versions found")
1166
+
1167
+ return DatabaseHandler.model_versions_cached[_cache_key]
1168
+
1169
+ def _parameter_cache_key(self, site, array_element_name, model_version, collection=None):
1170
+ """
1171
+ Create a cache key for the parameter cache dictionaries.
1172
+
1173
+ Parameters
1174
+ ----------
1175
+ site: str
1176
+ Site name.
1177
+ array_element_name: str
1178
+ Array element name.
1179
+ model_version: str
1180
+ Model version.
1181
+ collection: str
1182
+ DB collection name.
1183
+
1184
+ Returns
1185
+ -------
1186
+ str
1187
+ Cache key.
1188
+ """
1189
+ parts = []
1190
+ if site:
1191
+ parts.append(site)
1192
+ if array_element_name:
1193
+ parts.append(array_element_name)
1194
+ parts.append(model_version)
1195
+ if collection:
1196
+ parts.append(collection)
1197
+ return "-".join(parts)
1198
+
1199
+ def _reset_parameter_cache(self, site, array_element_name, model_version):
1200
+ """
1201
+ Reset the cache for the parameters.
1202
+
1203
+ Parameters
1204
+ ----------
1205
+ site: str
1206
+ Site name.
1207
+ array_element_name: str
1208
+ Array element name.
1209
+ model_version: str
1210
+ Model version.
1211
+ """
1212
+ self._logger.debug(f"Resetting cache for {site} {array_element_name} {model_version}")
1213
+ _cache_key = self._parameter_cache_key(site, array_element_name, model_version)
1214
+ DatabaseHandler.site_parameters_cached.pop(_cache_key, None)
1215
+ DatabaseHandler.model_parameters_cached.pop(_cache_key, None)
1216
+ db_array_elements.get_array_elements.cache_clear()
1217
+
1218
+ def get_collections(self, db_name=None, model_collections_only=False):
1219
+ """
1220
+ List of collections in the DB.
1221
+
1222
+ Parameters
1223
+ ----------
1224
+ db_name: str
1225
+ Database name.
1226
+
1227
+ Returns
1228
+ -------
1229
+ list
1230
+ List of collection names
1231
+ model_collections_only: bool
1232
+ If True, only return model collections (i.e. exclude fs.files, fs.chunks, metadata)
1233
+
1234
+ """
1235
+ db_name = self._get_db_name() if db_name is None else db_name
1236
+ if db_name not in self.list_of_collections:
1237
+ self.list_of_collections[db_name] = DatabaseHandler.db_client[
1238
+ db_name
1239
+ ].list_collection_names()
1240
+ if model_collections_only:
1241
+ return [
1242
+ collection
1243
+ for collection in self.list_of_collections[db_name]
1244
+ if not collection.startswith("fs.") and collection != "metadata"
1245
+ ]
1246
+ return self.list_of_collections[db_name]