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
simtools/model/camera.py CHANGED
@@ -1,18 +1,13 @@
1
+ """Definition and modeling of camera."""
2
+
1
3
  import logging
4
+ from pathlib import Path
2
5
 
3
6
  import astropy.units as u
4
- import matplotlib as mlp
5
- import matplotlib.colors as mcolors
6
- import matplotlib.patches as mpatches
7
- import matplotlib.pyplot as plt
8
7
  import numpy as np
9
- from matplotlib.collections import PatchCollection
10
8
  from scipy.spatial import cKDTree as KDTree
11
9
  from scipy.spatial import distance
12
10
 
13
- import simtools.visualization.legend_handlers as leg_h
14
- from simtools.model.model_utils import is_two_mirror_telescope
15
- from simtools.utils import names
16
11
  from simtools.utils.geometry import rotate
17
12
 
18
13
  __all__ = ["Camera"]
@@ -20,57 +15,59 @@ __all__ = ["Camera"]
20
15
 
21
16
  class Camera:
22
17
  """
23
- Camera class, defining pixel layout including rotation, finding neighbour pixels, calculating\
24
- FoV and plotting the camera.
18
+ Camera class, defining pixel layout.
19
+
20
+ This includes rotation, finding neighbor pixels, calculating FoV and plotting the camera.
25
21
 
26
22
  Parameters
27
23
  ----------
28
- telescope_model_name: string
29
- As provided by the telescope model method TelescopeModel (ex South-LST-1).
30
- camera_config_file: string
24
+ telescope_model_name: str
25
+ As provided by the telescope model method TelescopeModel (e.g., LSTN-01)
26
+ camera_config_file: str or Path
31
27
  The sim_telarray file name.
32
28
  focal_length: float
33
29
  The focal length of the camera in (preferably the effective focal length), assumed to be \
34
30
  in the same unit as the pixel positions in the camera_config_file, usually cm.
35
31
  """
36
32
 
37
- # Constants for finding neighbour pixels.
33
+ # Constants for finding neighbor pixels.
38
34
  PMT_NEIGHBOR_RADIUS_FACTOR = 1.1
39
35
  SIPM_NEIGHBOR_RADIUS_FACTOR = 1.4
40
36
  SIPM_ROW_COLUMN_DIST_FACTOR = 0.2
41
37
 
42
- def __init__(self, telescope_model_name, camera_config_file, focal_length):
43
- """
44
- Initialize Camera class, defining pixel layout including rotation, finding neighbour pixels,
45
- calculating FoV and plotting the camera.
38
+ def __init__(
39
+ self, telescope_model_name: str, camera_config_file: str | Path, focal_length: float
40
+ ):
46
41
  """
42
+ Initialize Camera class, defining pixel layout.
47
43
 
44
+ This includes rotation, finding neighbor pixels, calculating FoV and plotting the camera.
45
+ """
48
46
  self._logger = logging.getLogger(__name__)
49
47
 
50
- self._telescope_model_name = telescope_model_name
51
- _, self._camera_name, _ = names.split_telescope_model_name(telescope_model_name)
48
+ self.telescope_model_name = telescope_model_name
52
49
  self._camera_config_file = camera_config_file
53
- self._focal_length = focal_length
54
- if self._focal_length <= 0:
50
+ self.focal_length = focal_length
51
+ if self.focal_length <= 0:
55
52
  raise ValueError("The focal length must be larger than zero")
56
- self._pixels = self.read_pixel_list(camera_config_file)
53
+ self.pixels = self.read_pixel_list(camera_config_file)
57
54
 
58
- self._pixels = self._rotate_pixels(self._pixels)
55
+ self.pixels = self._rotate_pixels(self.pixels)
59
56
 
60
- # Initialize an empty list of neighbours, to be calculated only when necessary.
61
- self._neighbours = None
57
+ # Initialize an empty list of neighbors, to be calculated only when necessary.
58
+ self._neighbors = None
62
59
 
63
60
  # Initialize an empty list of edge pixels, to be calculated only when necessary.
64
61
  self._edge_pixel_indices = None
65
62
 
66
63
  @staticmethod
67
- def read_pixel_list(camera_config_file):
64
+ def read_pixel_list(camera_config_file: str | Path) -> dict:
68
65
  """
69
66
  Read the pixel layout from the camera config file, assumed to be in a sim_telarray format.
70
67
 
71
68
  Parameters
72
69
  ----------
73
- camera_config_file: string
70
+ camera_config_file: str or Path
74
71
  The sim_telarray file name.
75
72
 
76
73
  Returns
@@ -85,65 +82,112 @@ class Camera:
85
82
  The hexagonal shapes differ in their orientation, where those denoted as 3 are rotated
86
83
  clockwise by 30 degrees with respect to those denoted as 1.
87
84
  """
85
+ pixels = Camera.initialize_pixel_dict()
88
86
 
89
- pixels = {}
90
- pixels["pixel_diameter"] = 9999
91
- pixels["pixel_shape"] = 9999
92
- pixels["pixel_spacing"] = 9999
93
- pixels["lightguide_efficiency_angle_file"] = "none"
94
- pixels["lightguide_efficiency_wavelength_file"] = "none"
95
- pixels["rotate_angle"] = 0
96
- pixels["x"] = []
97
- pixels["y"] = []
98
- pixels["pix_id"] = []
99
- pixels["pix_on"] = []
100
-
101
- with open(camera_config_file, "r", encoding="utf-8") as dat_file:
87
+ with open(camera_config_file, encoding="utf-8") as dat_file:
102
88
  for line in dat_file:
103
- pix_info = line.split()
104
- if line.startswith("PixType"):
105
- pixels["pixel_shape"] = int(pix_info[5].strip())
106
- pixels["pixel_diameter"] = float(pix_info[6].strip())
107
- pixels["lightguide_efficiency_angle_file"] = (
108
- pix_info[8].strip().replace('"', "")
109
- )
110
- if len(pix_info) > 9:
111
- pixels["lightguide_efficiency_wavelength_file"] = (
112
- pix_info[9].strip().replace('"', "")
113
- )
114
- if line.startswith("Rotate"):
115
- pixels["rotate_angle"] = np.deg2rad(float(pix_info[1].strip()))
116
- if line.startswith("Pixel"):
117
- pixels["x"].append(float(pix_info[3].strip()))
118
- pixels["y"].append(float(pix_info[4].strip()))
119
- pixels["pix_id"].append(int(pix_info[1].strip()))
120
- if len(pix_info) > 9:
121
- if int(pix_info[9].strip()) != 0:
122
- pixels["pix_on"].append(True)
123
- else:
124
- pixels["pix_on"].append(False)
125
- else:
126
- pixels["pix_on"].append(True)
89
+ Camera.process_line(line, pixels)
90
+
91
+ Camera.validate_pixels(pixels, camera_config_file)
92
+
93
+ return pixels
94
+
95
+ @staticmethod
96
+ def initialize_pixel_dict() -> dict:
97
+ """
98
+ Initialize the pixel dictionary with default values.
99
+
100
+ Returns
101
+ -------
102
+ dict
103
+ A dictionary with default pixel properties.
104
+ """
105
+ return {
106
+ "pixel_diameter": 9999,
107
+ "pixel_shape": 9999,
108
+ "pixel_spacing": 9999,
109
+ "lightguide_efficiency_angle_file": "none",
110
+ "lightguide_efficiency_wavelength_file": "none",
111
+ "rotate_angle": 0,
112
+ "x": [],
113
+ "y": [],
114
+ "pix_id": [],
115
+ "pix_on": [],
116
+ }
117
+
118
+ @staticmethod
119
+ def process_line(line: str, pixels: dict):
120
+ """
121
+ Process a line from the camera config file and update the pixels dictionary.
127
122
 
123
+ Parameters
124
+ ----------
125
+ line: str
126
+ A line from the camera config file.
127
+ pixels: dict
128
+ The dictionary to update with pixel information.
129
+ """
130
+ pix_info = line.split()
131
+
132
+ if line.startswith("PixType"):
133
+ pixels["pixel_shape"] = int(pix_info[5].strip())
134
+ pixels["pixel_diameter"] = float(pix_info[6].strip())
135
+ pixels["lightguide_efficiency_angle_file"] = pix_info[8].strip().replace('"', "")
136
+
137
+ if len(pix_info) > 9:
138
+ pixels["lightguide_efficiency_wavelength_file"] = (
139
+ pix_info[9].strip().replace('"', "")
140
+ )
141
+
142
+ elif line.startswith("Rotate"):
143
+ pixels["rotate_angle"] = np.deg2rad(float(pix_info[1].strip()))
144
+
145
+ elif line.startswith("Pixel"):
146
+ pixels["x"].append(float(pix_info[3].strip()))
147
+ pixels["y"].append(float(pix_info[4].strip()))
148
+ pixels["pix_id"].append(int(pix_info[1].strip()))
149
+
150
+ if len(pix_info) > 9:
151
+ pixels["pix_on"].append(int(pix_info[9].strip()) != 0)
152
+ else:
153
+ pixels["pix_on"].append(True)
154
+
155
+ @staticmethod
156
+ def validate_pixels(pixels: dict, camera_config_file: str | Path):
157
+ """
158
+ Validate the pixel dictionary to ensure all required fields are present.
159
+
160
+ Parameters
161
+ ----------
162
+ pixels: dict
163
+ The pixel dictionary to validate.
164
+ camera_config_file: string
165
+ The sim_telarray file name for error messages.
166
+
167
+ Raises
168
+ ------
169
+ ValueError
170
+ If the pixel diameter or pixel shape is invalid.
171
+ """
128
172
  if pixels["pixel_diameter"] == 9999:
129
173
  raise ValueError(f"Could not read the pixel diameter from {camera_config_file} file")
174
+
130
175
  if pixels["pixel_shape"] not in [1, 2, 3]:
131
176
  raise ValueError(
132
177
  f"Pixel shape in {camera_config_file} unrecognized (has to be 1, 2 or 3)"
133
178
  )
134
179
 
135
- return pixels
136
-
137
- def _rotate_pixels(self, pixels):
180
+ def _rotate_pixels(self, pixels: dict) -> dict:
138
181
  """
139
182
  Rotate the pixels according to the rotation angle given in pixels['rotate_angle'].
183
+
140
184
  Additional rotation is added to get to the camera view of an observer facing the camera.
141
185
  The angle for the axes rotation depends on the coordinate system in which the original
142
186
  data was provided.
143
187
 
144
188
  Parameters
145
189
  ----------
146
- pixels: dictionary
190
+ pixels: dict
147
191
  The dictionary produced by the read_pixel_list method of this class
148
192
 
149
193
  Returns
@@ -153,7 +197,6 @@ class Camera:
153
197
  The pixels orientation for plotting is added to the dictionary in pixels['orientation'].
154
198
  The orientation is determined by the pixel shape (see read_pixel_list for details).
155
199
  """
156
-
157
200
  rotate_angle = pixels["rotate_angle"] * u.rad # So not to change the original angle
158
201
  # The original pixel list is given such that
159
202
  # x -> North, y -> West, z -> Up in the ground system.
@@ -179,19 +222,18 @@ class Camera:
179
222
 
180
223
  return pixels
181
224
 
182
- def get_number_of_pixels(self):
225
+ def get_number_of_pixels(self) -> int:
183
226
  """
184
- Get the number of pixels in the camera (all pixels, including those defined as "off".
227
+ Get the number of pixels in the camera (all pixels, including those defined as "off").
185
228
 
186
229
  Returns
187
230
  -------
188
231
  int
189
232
  number of pixels.
190
233
  """
234
+ return len(self.pixels["x"])
191
235
 
192
- return len(self._pixels["x"])
193
-
194
- def get_pixel_diameter(self):
236
+ def get_pixel_diameter(self) -> float:
195
237
  """
196
238
  Get pixel diameter contained in _pixels.
197
239
 
@@ -200,10 +242,9 @@ class Camera:
200
242
  float
201
243
  Pixel diameter (usually in cm).
202
244
  """
245
+ return self.pixels["pixel_diameter"]
203
246
 
204
- return self._pixels["pixel_diameter"]
205
-
206
- def get_pixel_active_solid_angle(self):
247
+ def get_pixel_active_solid_angle(self) -> float:
207
248
  """
208
249
  Get the active solid angle of a pixel in sr.
209
250
 
@@ -212,16 +253,17 @@ class Camera:
212
253
  float
213
254
  active solid angle of a pixel in sr.
214
255
  """
215
-
216
256
  pixel_area = self.get_pixel_diameter() ** 2
217
257
  # In case we have hexagonal pixels:
218
258
  if self.get_pixel_shape() == 1 or self.get_pixel_shape() == 3:
219
259
  pixel_area *= np.sqrt(3) / 2
220
- return pixel_area / (self._focal_length**2)
260
+ return pixel_area / (self.focal_length**2)
221
261
 
222
- def get_pixel_shape(self):
262
+ def get_pixel_shape(self) -> int:
223
263
  """
224
- Get pixel shape code 1, 2 or 3, where 1 and 3 are hexagonal pixels, where one is rotated by\
264
+ Get pixel shape code 1, 2 or 3.
265
+
266
+ Where 1 and 3 are hexagonal pixels, where one is rotated by\
225
267
  30 degrees with respect to the other. A square pixel is denoted as 2.
226
268
 
227
269
  Returns
@@ -229,9 +271,9 @@ class Camera:
229
271
  int (1, 2 or 3)
230
272
  Pixel shape.
231
273
  """
232
- return self._pixels["pixel_shape"]
274
+ return self.pixels["pixel_shape"]
233
275
 
234
- def get_lightguide_efficiency_angle_file_name(self):
276
+ def get_lightguide_efficiency_angle_file_name(self) -> str:
235
277
  """
236
278
  Get the file name of the light guide efficiency as a function of incidence angle.
237
279
 
@@ -240,10 +282,9 @@ class Camera:
240
282
  str
241
283
  File name of the light guide efficiency as a function of incidence angle.
242
284
  """
285
+ return self.pixels["lightguide_efficiency_angle_file"]
243
286
 
244
- return self._pixels["lightguide_efficiency_angle_file"]
245
-
246
- def get_lightguide_efficiency_wavelength_file_name(self):
287
+ def get_lightguide_efficiency_wavelength_file_name(self) -> str:
247
288
  """
248
289
  Get the file name of the light guide efficiency as a function of wavelength.
249
290
 
@@ -252,26 +293,28 @@ class Camera:
252
293
  str
253
294
  File name of the light guide efficiency as a function of wavelength.
254
295
  """
255
- return self._pixels["lightguide_efficiency_wavelength_file"]
296
+ return self.pixels["lightguide_efficiency_wavelength_file"]
256
297
 
257
- def get_camera_fill_factor(self):
298
+ def get_camera_fill_factor(self) -> float:
258
299
  """
259
- Calculate the fill factor of the camera, defined as (pixel_diameter/pixel_spacing)**2
300
+ Calculate the fill factor of the camera, defined as (pixel_diameter/pixel_spacing)**2.
260
301
 
261
302
  Returns
262
303
  -------
263
304
  float
264
305
  The camera fill factor.
265
306
  """
266
-
267
- if self._pixels["pixel_spacing"] == 9999:
268
- points = np.array([self._pixels["x"], self._pixels["y"]]).T
307
+ if self.pixels["pixel_spacing"] == 9999:
308
+ points = np.array([self.pixels["x"], self.pixels["y"]]).T
269
309
  pixel_distances = distance.cdist(points, points, "euclidean")
270
- self._pixels["pixel_spacing"] = np.min(pixel_distances[pixel_distances > 0])
310
+ # pylint: disable=unsubscriptable-object
311
+ pixel_distances = pixel_distances[pixel_distances > 0]
312
+ pixel_spacing = np.min(pixel_distances)
313
+ self.pixels["pixel_spacing"] = pixel_spacing
271
314
 
272
- return (self._pixels["pixel_diameter"] / self._pixels["pixel_spacing"]) ** 2
315
+ return (self.pixels["pixel_diameter"] / self.pixels["pixel_spacing"]) ** 2
273
316
 
274
- def calc_fov(self):
317
+ def calc_fov(self) -> tuple[float, float]:
275
318
  """
276
319
  Calculate the FOV of the camera in degrees, taking into account the focal length.
277
320
 
@@ -286,17 +329,21 @@ class Camera:
286
329
  -----
287
330
  The x,y pixel positions and focal length are assumed to have the same unit (usually cm)
288
331
  """
289
-
290
332
  self._logger.debug("Calculating the FoV")
291
-
292
333
  return self._calc_fov(
293
- self._pixels["x"],
294
- self._pixels["y"],
334
+ self.pixels["x"],
335
+ self.pixels["y"],
295
336
  self.get_edge_pixels(),
296
- self._focal_length,
337
+ self.focal_length,
297
338
  )
298
339
 
299
- def _calc_fov(self, x_pixel, y_pixel, edge_pixel_indices, focal_length):
340
+ def _calc_fov(
341
+ self,
342
+ x_pixel: list[float],
343
+ y_pixel: list[float],
344
+ edge_pixel_indices: list[int],
345
+ focal_length: float,
346
+ ) -> tuple[float, float]:
300
347
  """
301
348
  Calculate the FOV of the camera in degrees, taking into account the focal length.
302
349
 
@@ -323,7 +370,6 @@ class Camera:
323
370
  -----
324
371
  The x,y pixel positions and focal length are assumed to have the same unit (usually cm)
325
372
  """
326
-
327
373
  self._logger.debug("Calculating the FoV")
328
374
 
329
375
  average_edge_distance = 0
@@ -336,10 +382,11 @@ class Camera:
336
382
  return fov, average_edge_distance
337
383
 
338
384
  @staticmethod
339
- def _find_neighbours(x_pos, y_pos, radius):
385
+ def _find_neighbors(x_pos: np.ndarray, y_pos: np.ndarray, radius: float) -> list[list[int]]:
340
386
  """
341
- use a KD-Tree to quickly find nearest neighbours (e.g., of the pixels in a camera or mirror\
342
- facets)
387
+ Use a KD-Tree to quickly find nearest neighbors.
388
+
389
+ This applies to e.g., of the pixels in a camera or mirror facets.
343
390
 
344
391
  Parameters
345
392
  ----------
@@ -348,96 +395,120 @@ class Camera:
348
395
  y_pos : numpy.array_like
349
396
  y position of each e.g., pixel
350
397
  radius : float
351
- radius to consider neighbour it should be slightly larger than the pixel diameter or \
398
+ radius to consider neighbor it should be slightly larger than the pixel diameter or \
352
399
  mirror facet.
353
400
 
354
401
  Returns
355
402
  -------
356
- neighbours: numpy.array_like
357
- Array of neighbour indices in a list for each e.g., pixel.
403
+ list of lists
404
+ Array of neighbor indices in a list for each pixel
358
405
  """
406
+ tree = KDTree(np.column_stack([x_pos, y_pos]))
407
+ neighbors = tree.query_ball_tree(tree, radius)
408
+ return [list(np.setdiff1d(neigh, [i])) for i, neigh in enumerate(neighbors)]
359
409
 
360
- points = np.array([x_pos, y_pos]).T
361
- indices = np.arange(len(x_pos))
362
- kdtree = KDTree(points)
363
- neighbours = [kdtree.query_ball_point(p, r=radius) for p in points]
364
-
365
- for neighbour_now, index_now in zip(neighbours, indices):
366
- neighbour_now.remove(index_now) # get rid of the pixel or mirror itself
367
-
368
- return neighbours
369
-
370
- def _find_adjacent_neighbour_pixels(self, x_pos, y_pos, radius, row_coloumn_dist):
410
+ def _find_adjacent_neighbor_pixels(
411
+ self, x_pos: np.ndarray, y_pos: np.ndarray, radius: float, row_column_dist: float
412
+ ) -> list[list[int]]:
371
413
  """
372
- Find adjacent neighbour pixels in cameras with square pixels. Only directly adjacent \
373
- neighbours are allowed, no diagonals.
414
+ Find adjacent neighbor pixels in cameras with square pixels.
415
+
416
+ Only directly adjacent neighbors are allowed, no diagonals.
374
417
 
375
418
  Parameters
376
419
  ----------
377
- x_pos : numpy.array_like
420
+ x_pos: np.ndarray
378
421
  x position of each pixel
379
- y_pos : numpy.array_like
380
- y position of each pixels
381
- radius : float
382
- radius to consider neighbour.
383
- Should be slightly larger than the pixel diameter.
384
- row_coloumn_dist : float
385
- Maximum distance for pixels in the same row/column to consider when looking for a \
386
- neighbour. Should be around 20% of the pixel diameter.
422
+ y_pos: np.ndarray
423
+ y position of each pixel
424
+ radius: float
425
+ Radius within which to find neighbors
426
+ row_column_dist: float
427
+ Distance to consider for row/column adjacency.
428
+ Should be around 20% of the pixel diameter.
387
429
 
388
430
  Returns
389
431
  -------
390
- neighbours: numpy.array_like
391
- Array of neighbour indices in a list for each pixel
432
+ list of lists
433
+ Array of neighbor indices in a list for each pixel
392
434
  """
435
+ # First find the neighbors with the usual method and the original radius
436
+ # which does not allow for diagonal neighbors.
437
+ neighbors = self._find_neighbors(x_pos, y_pos, radius)
393
438
 
394
- # First find the neighbours with the usual method and the original radius
395
- # which does not allow for diagonal neighbours.
396
- neighbours = self._find_neighbours(x_pos, y_pos, radius)
397
- for i_pix, nn in enumerate(neighbours):
439
+ for i_pix, nn in enumerate(neighbors):
398
440
  # Find pixels defined as edge pixels now
399
441
  if len(nn) < 4:
400
442
  # Go over all other pixels and search for ones which are adjacent
401
443
  # but further than sqrt(2) away
402
- for j_pix, _ in enumerate(x_pos):
403
- # No need to look at the pixel itself
404
- # nor at any pixels already in the neighbours list
405
- if j_pix != i_pix and j_pix not in nn:
406
- dist = np.sqrt(
407
- (x_pos[i_pix] - x_pos[j_pix]) ** 2 + (y_pos[i_pix] - y_pos[j_pix]) ** 2
408
- )
409
- # Check if this pixel is in the same row or column
410
- # and allow it to be ~1.68*diameter away (1.4*1.2 = 1.68)
411
- # Need to increase the distance because of the curvature
412
- # of the CHEC camera
413
- if (
414
- abs(x_pos[i_pix] - x_pos[j_pix]) < row_coloumn_dist
415
- or abs(y_pos[i_pix] - y_pos[j_pix]) < row_coloumn_dist
416
- ) and dist < 1.2 * radius:
417
- nn.append(j_pix)
418
-
419
- return neighbours
420
-
421
- def _calc_neighbour_pixels(self, pixels):
422
- """
423
- Find adjacent neighbour pixels in cameras with hexagonal or square pixels. Only directly \
424
- adjacent neighbours are searched for, no diagonals.
444
+ self._add_additional_neighbors(i_pix, nn, x_pos, y_pos, radius, row_column_dist)
445
+
446
+ return neighbors
447
+
448
+ def _add_additional_neighbors(
449
+ self,
450
+ i_pix: int,
451
+ nn: list[int],
452
+ x_pos: np.ndarray,
453
+ y_pos: np.ndarray,
454
+ radius: float,
455
+ row_column_dist: float,
456
+ ):
457
+ """
458
+ Add neighbors for a given pixel if they are not already neighbors and are adjacent.
459
+
460
+ Parameters
461
+ ----------
462
+ i_pix: int
463
+ Index of the pixel to find neighbors for
464
+ nn: list
465
+ Current list of neighbors for the pixel
466
+ x_pos: np.ndarray
467
+ x position of each pixel
468
+ y_pos: np.ndarray
469
+ y position of each pixel
470
+ radius: float
471
+ Radius within which to find neighbors
472
+ row_column_dist: float
473
+ Distance to consider for row/column adjacency
474
+ """
475
+ for j_pix, _ in enumerate(x_pos):
476
+ # No need to look at the pixel itself
477
+ # nor at any pixels already in the neighbors list
478
+ if j_pix != i_pix and j_pix not in nn:
479
+ dist = np.sqrt(
480
+ (x_pos[i_pix] - x_pos[j_pix]) ** 2 + (y_pos[i_pix] - y_pos[j_pix]) ** 2
481
+ )
482
+ # Check if this pixel is in the same row or column
483
+ # and allow it to be ~1.68*diameter away (1.4*1.2 = 1.68)
484
+ # Need to increase the distance because of the curvature
485
+ # of the CHEC camera
486
+ if (
487
+ abs(x_pos[i_pix] - x_pos[j_pix]) < row_column_dist
488
+ or abs(y_pos[i_pix] - y_pos[j_pix]) < row_column_dist
489
+ ) and dist < 1.2 * radius:
490
+ nn.append(j_pix)
491
+
492
+ def _calc_neighbor_pixels(self, pixels: dict) -> list[list[int]]:
493
+ """
494
+ Find adjacent neighbor pixels in cameras with hexagonal or square pixels.
495
+
496
+ Only directly adjacent neighbors are searched for, no diagonals.
425
497
 
426
498
  Parameters
427
499
  ----------
428
- pixels: dictionary
500
+ pixels: dict
429
501
  The dictionary produced by the read_pixel_list method of this class
430
502
 
431
503
  Returns
432
504
  -------
433
- neighbours: numpy.array_like
434
- Array of neighbour indices in a list for each pixel
505
+ neighbors: list of lists
506
+ Array of neighbor indices in a list for each pixel
435
507
  """
436
-
437
- self._logger.debug("Searching for neighbour pixels")
508
+ self._logger.debug("Searching for neighbor pixels")
438
509
 
439
510
  if pixels["pixel_shape"] == 1 or pixels["pixel_shape"] == 3:
440
- self._neighbours = self._find_neighbours(
511
+ self._neighbors = self._find_neighbors(
441
512
  pixels["x"],
442
513
  pixels["y"],
443
514
  self.PMT_NEIGHBOR_RADIUS_FACTOR * pixels["pixel_diameter"],
@@ -445,21 +516,22 @@ class Camera:
445
516
  elif pixels["pixel_shape"] == 2:
446
517
  # Distance increased by 40% to take into account gaps in the SiPM cameras
447
518
  # Pixels in the same row/column can be 20% shifted from one another
448
- # Inside find_adjacent_neighbour_pixels the distance is increased
519
+ # Inside find_adjacent_neighbor_pixels the distance is increased
449
520
  # further for pixels in the same row/column to 1.68*diameter.
450
- self._neighbours = self._find_adjacent_neighbour_pixels(
521
+ self._neighbors = self._find_adjacent_neighbor_pixels(
451
522
  pixels["x"],
452
523
  pixels["y"],
453
524
  self.SIPM_NEIGHBOR_RADIUS_FACTOR * pixels["pixel_diameter"],
454
525
  self.SIPM_ROW_COLUMN_DIST_FACTOR * pixels["pixel_diameter"],
455
526
  )
456
527
 
457
- return self._neighbours
528
+ return self._neighbors
458
529
 
459
- def get_neighbour_pixels(self, pixels=None):
530
+ def get_neighbor_pixels(self, pixels: dict | None = None) -> list[list[int]]:
460
531
  """
461
- Get a list of neighbour pixels by calling calc_neighbour_pixels() when necessary. The \
462
- purpose of this function is to ensure the calculation occurs only once and only when \
532
+ Get a list of neighbor pixels by calling calc_neighbor_pixels() when necessary.
533
+
534
+ The purpose of this function is to ensure the calculation occurs only once and only when
463
535
  necessary.
464
536
 
465
537
  Parameters
@@ -469,51 +541,54 @@ class Camera:
469
541
 
470
542
  Returns
471
543
  -------
472
- neighbours: numpy.array_like
473
- Array of neighbour indices in a list for each pixel.
544
+ neighbors: list of lists
545
+ Array of neighbor indices in a list for each pixel.
474
546
  """
475
-
476
- if self._neighbours is None:
547
+ if self._neighbors is None:
477
548
  if pixels is None:
478
- pixels = self._pixels
479
- return self._calc_neighbour_pixels(pixels)
549
+ pixels = self.pixels
550
+ return self._calc_neighbor_pixels(pixels)
480
551
 
481
- return self._neighbours
552
+ return self._neighbors
482
553
 
483
- def _calc_edge_pixels(self, pixels, neighbours):
554
+ def _calc_edge_pixels(self, pixels: dict, neighbors: list[list[int]]) -> list[int]:
484
555
  """
485
556
  Find the edge pixels of the camera.
486
557
 
487
558
  Parameters
488
559
  ----------
489
- pixels: dictionary
560
+ pixels: dict
490
561
  The dictionary produced by the read_pixel_list method of this class.
491
- neighbours: numpy.array_like
492
- Array of neighbour indices in a list for each pixel.
562
+ neighbors: list of lists
563
+ Array of neighbor indices in a list for each pixel.
493
564
 
494
565
  Returns
495
566
  -------
496
- edge_pixel_indices: numpy.array_like
567
+ edge_pixel_indices: list
497
568
  Array of edge pixel indices.
498
569
  """
499
-
500
570
  self._logger.debug("Searching for edge pixels")
501
-
502
571
  edge_pixel_indices = []
503
572
 
573
+ def is_edge_pixel(i_pix):
574
+ pixel_shape = pixels["pixel_shape"]
575
+ pix_on = pixels["pix_on"][i_pix]
576
+ num_neighbors = len(neighbors[i_pix])
577
+
578
+ shape_condition = (pixel_shape in [1, 3] and num_neighbors < 6) or (
579
+ pixel_shape == 2 and num_neighbors < 4
580
+ )
581
+ return pix_on and shape_condition
582
+
504
583
  for i_pix, _ in enumerate(pixels["x"]):
505
- if pixels["pixel_shape"] == 1 or pixels["pixel_shape"] == 3:
506
- if pixels["pix_on"][i_pix]:
507
- if len(neighbours[i_pix]) < 6:
508
- edge_pixel_indices.append(i_pix)
509
- elif pixels["pixel_shape"] == 2:
510
- if pixels["pix_on"][i_pix]:
511
- if len(neighbours[i_pix]) < 4:
512
- edge_pixel_indices.append(i_pix)
584
+ if is_edge_pixel(i_pix):
585
+ edge_pixel_indices.append(i_pix)
513
586
 
514
587
  return edge_pixel_indices
515
588
 
516
- def get_edge_pixels(self, pixels=None, neighbours=None):
589
+ def get_edge_pixels(
590
+ self, pixels: dict | None = None, neighbors: list[list[int]] | None = None
591
+ ) -> list[int]:
517
592
  """
518
593
  Get the indices of the edge pixels of the camera.
519
594
 
@@ -521,340 +596,19 @@ class Camera:
521
596
  ----------
522
597
  pixels: dict
523
598
  The dictionary produced by the read_pixel_list method of this class.
524
- neighbours: numpy.array_like
525
- Array of neighbour indices in a list for each pixel.
599
+ neighbors: list of lists
600
+ Array of neighbor indices in a list for each pixel.
526
601
 
527
602
  Returns
528
603
  -------
529
- edge_pixel_indices: numpy.array_like
604
+ edge_pixel_indices: list
530
605
  Array of edge pixel indices.
531
606
  """
532
-
533
607
  if self._edge_pixel_indices is None:
534
608
  if pixels is None:
535
- pixels = self._pixels
536
- if neighbours is None:
537
- neighbours = self.get_neighbour_pixels()
538
- return self._calc_edge_pixels(pixels, neighbours)
609
+ pixels = self.pixels
610
+ if neighbors is None:
611
+ neighbors = self.get_neighbor_pixels()
612
+ return self._calc_edge_pixels(pixels, neighbors)
539
613
 
540
614
  return self._edge_pixel_indices
541
-
542
- def _plot_axes_def(self, plot, rotate_angle):
543
- """
544
- Plot three axes definitions on the pyplot.plt instance provided. The three axes are Alt/Az,\
545
- the camera coordinate system and the original coordinate system the pixel list was provided.
546
-
547
- Parameters
548
- ----------
549
- plot: pyplot.plt instance
550
- A pyplot.plt instance where to add the axes definitions.
551
- rotate_angle: float
552
- The rotation angle applied
553
- """
554
-
555
- invert_yaxis = False
556
- x_left = 0.7 # Position of the left most axis
557
- if not is_two_mirror_telescope(self._telescope_model_name):
558
- invert_yaxis = True
559
- x_left = 0.8
560
-
561
- x_title = r"$x_{\!pix}$"
562
- y_title = r"$y_{\!pix}$"
563
- x_pos, y_pos = (x_left, 0.12)
564
- # The rotation of LST (above 100 degrees) raises the axes.
565
- # In this case, lower the starting point.
566
- if np.rad2deg(rotate_angle) > 100:
567
- y_pos -= 0.09
568
- x_pos -= 0.05
569
- kwargs = {
570
- "x_title": x_title,
571
- "y_title": y_title,
572
- "x_pos": x_pos,
573
- "y_pos": y_pos,
574
- "rotate_angle": rotate_angle - (1 / 2.0) * np.pi,
575
- "fc": "black",
576
- "ec": "black",
577
- "invert_yaxis": invert_yaxis,
578
- }
579
- self._plot_one_axis_def(plot, **kwargs)
580
-
581
- x_title = r"$x_{\!cam}$"
582
- y_title = r"$y_{\!cam}$"
583
- x_pos, y_pos = (x_left + 0.15, 0.12)
584
- kwargs = {
585
- "x_title": x_title,
586
- "y_title": y_title,
587
- "x_pos": x_pos,
588
- "y_pos": y_pos,
589
- "rotate_angle": (3 / 2.0) * np.pi,
590
- "fc": "blue",
591
- "ec": "blue",
592
- "invert_yaxis": invert_yaxis,
593
- }
594
- self._plot_one_axis_def(plot, **kwargs)
595
-
596
- x_title = "Alt"
597
- y_title = "Az"
598
- x_pos, y_pos = (x_left + 0.15, 0.25)
599
- kwargs = {
600
- "x_title": x_title,
601
- "y_title": y_title,
602
- "x_pos": x_pos,
603
- "y_pos": y_pos,
604
- "rotate_angle": (3 / 2.0) * np.pi,
605
- "fc": "red",
606
- "ec": "red",
607
- "invert_yaxis": invert_yaxis,
608
- }
609
- self._plot_one_axis_def(plot, **kwargs)
610
-
611
- @staticmethod
612
- def _plot_one_axis_def(plot, **kwargs):
613
- """
614
- Plot an axis on the pyplot.plt instance provided.
615
-
616
- Parameters
617
- ----------
618
- plot: pyplot.plt instance
619
- A pyplot.plt instance where to add the axes definitions.
620
- **kwargs: dict
621
- x_title: str
622
- x-axis title
623
- y_title: str
624
- y-axis title,
625
- x_pos: float
626
- x position of the axis to draw
627
- y_pos: float
628
- y position of the axis to draw
629
- rotate_angle: float
630
- rotation angle of the axis in radians
631
- fc: str
632
- face colour of the axis
633
- ec: str
634
- edge colour of the axis
635
- invert_yaxis: bool
636
- Flag to invert the y-axis (for dual mirror telescopes).
637
- """
638
-
639
- x_title = kwargs["x_title"]
640
- y_title = kwargs["y_title"]
641
- x_pos, y_pos = (kwargs["x_pos"], kwargs["y_pos"])
642
-
643
- r = 0.1 # size of arrow
644
- sign = 1.0
645
- if kwargs["invert_yaxis"]:
646
- sign *= -1.0
647
- x_text1 = x_pos + sign * r * np.cos(kwargs["rotate_angle"])
648
- y_text1 = y_pos + r * np.sin(0 + kwargs["rotate_angle"])
649
- x_text2 = x_pos + sign * r * np.cos(np.pi / 2.0 + kwargs["rotate_angle"])
650
- y_text2 = y_pos + r * np.sin(np.pi / 2.0 + kwargs["rotate_angle"])
651
-
652
- plot.gca().annotate(
653
- x_title,
654
- xy=(x_pos, y_pos),
655
- xytext=(x_text1, y_text1),
656
- xycoords="axes fraction",
657
- ha="center",
658
- va="center",
659
- size="xx-large",
660
- arrowprops={
661
- "arrowstyle": "<|-",
662
- "shrinkA": 0,
663
- "shrinkB": 0,
664
- "fc": kwargs["fc"],
665
- "ec": kwargs["ec"],
666
- },
667
- )
668
-
669
- plot.gca().annotate(
670
- y_title,
671
- xy=(x_pos, y_pos),
672
- xytext=(x_text2, y_text2),
673
- xycoords="axes fraction",
674
- ha="center",
675
- va="center",
676
- size="xx-large",
677
- arrowprops={
678
- "arrowstyle": "<|-",
679
- "shrinkA": 0,
680
- "shrinkB": 0,
681
- "fc": kwargs["fc"],
682
- "ec": kwargs["ec"],
683
- },
684
- )
685
-
686
- def plot_pixel_layout(self, camera_in_sky_coor=False, pixels_id_to_print=50):
687
- """
688
- Plot the pixel layout for an observer facing the camera. Including in the plot edge pixels,\
689
- off pixels, pixel ID for the first 50 pixels, coordinate systems, FOV, focal length and the\
690
- average edge radius.
691
-
692
- Returns
693
- -------
694
- fig: plt.figure instance
695
- Figure with the pixel layout.
696
- """
697
-
698
- self._logger.info(f"Plotting the {self._telescope_model_name} camera")
699
-
700
- fig, ax = plt.subplots()
701
- plt.gcf().set_size_inches(8, 8)
702
-
703
- if not is_two_mirror_telescope(self._telescope_model_name):
704
- if not camera_in_sky_coor:
705
- self._pixels["y"] = [(-1) * y_val for y_val in self._pixels["y"]]
706
-
707
- on_pixels, edge_pixels, off_pixels = [], [], []
708
-
709
- for i_pix, xy_pix_pos in enumerate(zip(self._pixels["x"], self._pixels["y"])):
710
- if self._pixels["pixel_shape"] == 1 or self._pixels["pixel_shape"] == 3:
711
- hexagon = mpatches.RegularPolygon(
712
- (xy_pix_pos[0], xy_pix_pos[1]),
713
- numVertices=6,
714
- radius=self._pixels["pixel_diameter"] / np.sqrt(3),
715
- orientation=np.deg2rad(self._pixels["orientation"]),
716
- )
717
- if self._pixels["pix_on"][i_pix]:
718
- if len(self.get_neighbour_pixels()[i_pix]) < 6:
719
- edge_pixels.append(hexagon)
720
- else:
721
- on_pixels.append(hexagon)
722
- else:
723
- off_pixels.append(hexagon)
724
- elif self._pixels["pixel_shape"] == 2:
725
- square = mpatches.Rectangle(
726
- (
727
- xy_pix_pos[0] - self._pixels["pixel_diameter"] / 2.0,
728
- xy_pix_pos[1] - self._pixels["pixel_diameter"] / 2.0,
729
- ),
730
- width=self._pixels["pixel_diameter"],
731
- height=self._pixels["pixel_diameter"],
732
- )
733
- if self._pixels["pix_on"][i_pix]:
734
- if len(self.get_neighbour_pixels()[i_pix]) < 4:
735
- edge_pixels.append(square)
736
- else:
737
- on_pixels.append(square)
738
- else:
739
- off_pixels.append(square)
740
-
741
- if self._pixels["pix_id"][i_pix] < pixels_id_to_print + 1:
742
- font_size = 4
743
- if names.get_telescope_class(self._telescope_model_name) == "SCT":
744
- font_size = 2
745
- plt.text(
746
- xy_pix_pos[0],
747
- xy_pix_pos[1],
748
- self._pixels["pix_id"][i_pix],
749
- horizontalalignment="center",
750
- verticalalignment="center",
751
- fontsize=font_size,
752
- )
753
-
754
- ax.add_collection(
755
- PatchCollection(on_pixels, facecolor="none", edgecolor="black", linewidth=0.2)
756
- )
757
- ax.add_collection(
758
- PatchCollection(
759
- edge_pixels,
760
- facecolor=mcolors.to_rgb("brown") + (0.5,),
761
- edgecolor=mcolors.to_rgb("black") + (1,),
762
- linewidth=0.2,
763
- )
764
- )
765
- ax.add_collection(
766
- PatchCollection(off_pixels, facecolor="black", edgecolor="black", linewidth=0.2)
767
- )
768
-
769
- legend_objects = [leg_h.PixelObject(), leg_h.EdgePixelObject()]
770
- legend_labels = ["Pixel", "Edge pixel"]
771
- if isinstance(on_pixels[0], mlp.patches.RegularPolygon):
772
- legend_handler_map = {
773
- leg_h.PixelObject: leg_h.HexPixelHandler(),
774
- leg_h.EdgePixelObject: leg_h.HexEdgePixelHandler(),
775
- leg_h.OffPixelObject: leg_h.HexOffPixelHandler(),
776
- }
777
- elif isinstance(on_pixels[0], mlp.patches.Rectangle):
778
- legend_handler_map = {
779
- leg_h.PixelObject: leg_h.SquarePixelHandler(),
780
- leg_h.EdgePixelObject: leg_h.SquareEdgePixelHandler(),
781
- leg_h.OffPixelObject: leg_h.SquareOffPixelHandler(),
782
- }
783
-
784
- if len(off_pixels) > 0:
785
- legend_objects.append(leg_h.OffPixelObject())
786
- legend_labels.append("Disabled pixel")
787
-
788
- plt.axis("equal")
789
- plt.grid(True)
790
- ax.set_axisbelow(True)
791
- plt.axis(
792
- [
793
- min(self._pixels["x"]),
794
- max(self._pixels["x"]),
795
- min(self._pixels["y"]) * 1.42,
796
- max(self._pixels["y"]) * 1.42,
797
- ]
798
- )
799
- plt.xlabel("Horizontal scale [cm]", fontsize=18, labelpad=0)
800
- plt.ylabel("Vertical scale [cm]", fontsize=18, labelpad=0)
801
- ax.set_title(
802
- f"Pixels layout in {self._telescope_model_name:s} camera",
803
- fontsize=15,
804
- y=1.02,
805
- )
806
- plt.tick_params(axis="both", which="major", labelsize=15)
807
-
808
- self._plot_axes_def(plt, self._pixels["rotate_angle"])
809
- description = "For an observer facing the camera"
810
- if camera_in_sky_coor and not is_two_mirror_telescope(self._telescope_model_name):
811
- description = "For an observer behind the camera looking through"
812
- if is_two_mirror_telescope(self._telescope_model_name):
813
- description = "For an observer looking from secondary to camera"
814
- ax.text(
815
- 0.02,
816
- 0.02,
817
- description,
818
- transform=ax.transAxes,
819
- color="black",
820
- fontsize=12,
821
- )
822
-
823
- fov, r_edge_avg = self.calc_fov()
824
- ax.text(
825
- 0.02,
826
- 0.96,
827
- r"$f_{\mathrm{eff}}$ = " + f"{self._focal_length:.3f} cm",
828
- transform=ax.transAxes,
829
- color="black",
830
- fontsize=12,
831
- )
832
- ax.text(
833
- 0.02,
834
- 0.92,
835
- f"Avg. edge radius = {r_edge_avg:.3f} cm",
836
- transform=ax.transAxes,
837
- color="black",
838
- fontsize=12,
839
- )
840
- ax.text(
841
- 0.02,
842
- 0.88,
843
- f"FoV = {fov:.3f} deg",
844
- transform=ax.transAxes,
845
- color="black",
846
- fontsize=12,
847
- )
848
-
849
- plt.legend(
850
- legend_objects,
851
- legend_labels,
852
- handler_map=legend_handler_map,
853
- prop={"size": 11},
854
- loc="upper right",
855
- )
856
-
857
- ax.set_aspect("equal", "datalim")
858
- plt.tight_layout()
859
-
860
- return fig