gammasimtools 0.6.1__py3-none-any.whl → 0.8.1__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 (353) hide show
  1. gammasimtools-0.8.1.dist-info/METADATA +172 -0
  2. gammasimtools-0.8.1.dist-info/RECORD +346 -0
  3. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.1.dist-info}/WHEEL +1 -1
  4. gammasimtools-0.8.1.dist-info/entry_points.txt +31 -0
  5. simtools/_version.py +2 -2
  6. simtools/applications/calculate_trigger_rate.py +210 -0
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +372 -0
  8. simtools/applications/{print_array_elements.py → convert_geo_coordinates_of_array_elements.py} +58 -63
  9. simtools/applications/convert_model_parameter_from_simtel.py +119 -0
  10. simtools/applications/{add_file_to_db.py → db_add_file_to_db.py} +70 -60
  11. simtools/applications/db_add_model_parameters_from_repository_to_db.py +184 -0
  12. simtools/applications/db_add_value_from_json_to_db.py +105 -0
  13. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +180 -0
  14. simtools/applications/db_get_array_layouts_from_db.py +162 -0
  15. simtools/applications/{get_file_from_db.py → db_get_file_from_db.py} +30 -34
  16. simtools/applications/db_get_parameter_from_db.py +131 -0
  17. simtools/applications/db_inspect_databases.py +52 -0
  18. simtools/applications/derive_mirror_rnda.py +39 -255
  19. simtools/applications/derive_psf_parameters.py +441 -0
  20. simtools/applications/generate_array_config.py +82 -0
  21. simtools/applications/generate_corsika_histograms.py +52 -52
  22. simtools/applications/generate_default_metadata.py +5 -8
  23. simtools/applications/generate_regular_arrays.py +117 -0
  24. simtools/applications/generate_simtel_array_histograms.py +97 -56
  25. simtools/applications/plot_array_layout.py +345 -115
  26. simtools/applications/production_generate_simulation_config.py +158 -0
  27. simtools/applications/production_scale_events.py +168 -0
  28. simtools/applications/simulate_light_emission.py +478 -0
  29. simtools/applications/simulate_prod.py +97 -175
  30. simtools/applications/submit_data_from_external.py +9 -12
  31. simtools/applications/submit_model_parameter_from_external.py +122 -0
  32. simtools/applications/validate_camera_efficiency.py +35 -102
  33. simtools/applications/validate_camera_fov.py +20 -19
  34. simtools/applications/{compare_cumulative_psf.py → validate_cumulative_psf.py} +45 -44
  35. simtools/applications/validate_file_using_schema.py +113 -47
  36. simtools/applications/validate_optics.py +17 -22
  37. simtools/camera_efficiency.py +193 -202
  38. simtools/configuration/commandline_parser.py +384 -96
  39. simtools/configuration/configurator.py +55 -71
  40. simtools/constants.py +5 -5
  41. simtools/corsika/corsika_config.py +482 -342
  42. simtools/corsika/corsika_histograms.py +226 -204
  43. simtools/corsika/corsika_histograms_visualize.py +23 -24
  44. simtools/corsika/primary_particle.py +159 -0
  45. simtools/data_model/data_reader.py +25 -20
  46. simtools/data_model/format_checkers.py +52 -0
  47. simtools/data_model/metadata_collector.py +210 -184
  48. simtools/data_model/metadata_model.py +115 -37
  49. simtools/data_model/model_data_writer.py +335 -26
  50. simtools/data_model/validate_data.py +366 -154
  51. simtools/db/db_array_elements.py +130 -0
  52. simtools/db/db_from_repo_handler.py +106 -0
  53. simtools/db/db_handler.py +1246 -0
  54. simtools/io_operations/hdf5_handler.py +3 -1
  55. simtools/io_operations/io_handler.py +32 -57
  56. simtools/job_execution/job_manager.py +82 -69
  57. simtools/layout/array_layout.py +325 -537
  58. simtools/layout/geo_coordinates.py +8 -11
  59. simtools/layout/telescope_position.py +163 -86
  60. simtools/model/array_model.py +305 -256
  61. simtools/model/calibration_model.py +50 -0
  62. simtools/model/camera.py +233 -493
  63. simtools/model/mirrors.py +61 -44
  64. simtools/model/model_parameter.py +602 -0
  65. simtools/model/model_utils.py +7 -35
  66. simtools/model/site_model.py +161 -0
  67. simtools/model/telescope_model.py +127 -621
  68. simtools/production_configuration/calculate_statistical_errors_grid_point.py +454 -0
  69. simtools/production_configuration/event_scaler.py +146 -0
  70. simtools/production_configuration/generate_simulation_config.py +193 -0
  71. simtools/production_configuration/interpolation_handler.py +197 -0
  72. simtools/ray_tracing/__init__.py +0 -0
  73. simtools/ray_tracing/mirror_panel_psf.py +280 -0
  74. simtools/{psf_analysis.py → ray_tracing/psf_analysis.py} +133 -47
  75. simtools/ray_tracing/ray_tracing.py +646 -0
  76. simtools/runners/__init__.py +0 -0
  77. simtools/runners/corsika_runner.py +240 -0
  78. simtools/runners/corsika_simtel_runner.py +225 -0
  79. simtools/runners/runner_services.py +307 -0
  80. simtools/runners/simtel_runner.py +224 -0
  81. simtools/schemas/array_elements.yml +137 -0
  82. simtools/schemas/integration_tests_config.metaschema.yml +93 -0
  83. simtools/schemas/metadata.metaschema.yml +6 -0
  84. simtools/schemas/model_parameter.metaschema.yml +78 -0
  85. simtools/schemas/{data.metaschema.yml → model_parameter_and_data_schema.metaschema.yml} +27 -44
  86. simtools/schemas/model_parameters/adjust_gain.schema.yml +37 -0
  87. simtools/schemas/model_parameters/altitude.schema.yml +37 -0
  88. simtools/schemas/model_parameters/array_coordinates.schema.yml +33 -0
  89. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +77 -0
  90. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +39 -0
  91. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +39 -0
  92. simtools/schemas/model_parameters/array_layouts.schema.yml +48 -0
  93. simtools/schemas/model_parameters/array_triggers.schema.yml +93 -0
  94. simtools/schemas/model_parameters/asum_clipping.schema.yml +38 -0
  95. simtools/schemas/model_parameters/asum_offset.schema.yml +35 -0
  96. simtools/schemas/model_parameters/asum_shaping.schema.yml +35 -0
  97. simtools/schemas/model_parameters/asum_threshold.schema.yml +38 -0
  98. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +32 -0
  99. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +35 -0
  100. simtools/schemas/model_parameters/axes_offsets.schema.yml +53 -0
  101. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +40 -0
  102. simtools/schemas/model_parameters/camera_body_shape.schema.yml +45 -0
  103. simtools/schemas/model_parameters/camera_config_file.schema.yml +40 -0
  104. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +36 -0
  105. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +43 -0
  106. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +42 -0
  107. simtools/schemas/model_parameters/camera_depth.schema.yml +42 -0
  108. simtools/schemas/model_parameters/camera_filter.schema.yml +45 -0
  109. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +29 -0
  110. simtools/schemas/model_parameters/camera_pixels.schema.yml +36 -0
  111. simtools/schemas/model_parameters/camera_transmission.schema.yml +41 -0
  112. simtools/schemas/model_parameters/channels_per_chip.schema.yml +36 -0
  113. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +35 -0
  114. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +27 -0
  115. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +38 -0
  116. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +28 -0
  117. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +23 -0
  118. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +27 -0
  119. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +28 -0
  120. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +27 -0
  121. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +38 -0
  122. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +52 -0
  123. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +27 -0
  124. simtools/schemas/model_parameters/dark_events.schema.yml +32 -0
  125. simtools/schemas/model_parameters/default_trigger.schema.yml +35 -0
  126. simtools/schemas/model_parameters/design_model.schema.yml +31 -0
  127. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +32 -0
  128. simtools/schemas/model_parameters/disc_bins.schema.yml +39 -0
  129. simtools/schemas/model_parameters/disc_start.schema.yml +41 -0
  130. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +42 -0
  131. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +41 -0
  132. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +41 -0
  133. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +39 -0
  134. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +40 -0
  135. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +41 -0
  136. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +33 -0
  137. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +42 -0
  138. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +37 -0
  139. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +44 -0
  140. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +36 -0
  141. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +45 -0
  142. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +40 -0
  143. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +41 -0
  144. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +38 -0
  145. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +38 -0
  146. simtools/schemas/model_parameters/dish_shape_length.schema.yml +41 -0
  147. simtools/schemas/model_parameters/dsum_clipping.schema.yml +38 -0
  148. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +38 -0
  149. simtools/schemas/model_parameters/dsum_offset.schema.yml +37 -0
  150. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +33 -0
  151. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +39 -0
  152. simtools/schemas/model_parameters/dsum_prescale.schema.yml +44 -0
  153. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +38 -0
  154. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +45 -0
  155. simtools/schemas/model_parameters/dsum_shaping.schema.yml +44 -0
  156. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +32 -0
  157. simtools/schemas/model_parameters/dsum_threshold.schema.yml +43 -0
  158. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +42 -0
  159. simtools/schemas/model_parameters/effective_focal_length.schema.yml +61 -0
  160. simtools/schemas/model_parameters/epsg_code.schema.yml +37 -0
  161. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +35 -0
  162. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +46 -0
  163. simtools/schemas/model_parameters/fadc_bins.schema.yml +40 -0
  164. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +50 -0
  165. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +38 -0
  166. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +42 -0
  167. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +49 -0
  168. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +47 -0
  169. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +51 -0
  170. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +37 -0
  171. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +43 -0
  172. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +49 -0
  173. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +43 -0
  174. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +39 -0
  175. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +42 -0
  176. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +40 -0
  177. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +50 -0
  178. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +42 -0
  179. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +41 -0
  180. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +42 -0
  181. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +43 -0
  182. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +39 -0
  183. simtools/schemas/model_parameters/fadc_mhz.schema.yml +31 -0
  184. simtools/schemas/model_parameters/fadc_noise.schema.yml +41 -0
  185. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +40 -0
  186. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +39 -0
  187. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +50 -0
  188. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +43 -0
  189. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +43 -0
  190. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +42 -0
  191. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +41 -0
  192. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +42 -0
  193. simtools/schemas/model_parameters/flatfielding.schema.yml +37 -0
  194. simtools/schemas/model_parameters/focal_length.schema.yml +45 -0
  195. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +158 -0
  196. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +29 -0
  197. simtools/schemas/model_parameters/focus_offset.schema.yml +66 -0
  198. simtools/schemas/model_parameters/gain_variation.schema.yml +43 -0
  199. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +34 -0
  200. simtools/schemas/model_parameters/geomag_rotation.schema.yml +37 -0
  201. simtools/schemas/model_parameters/geomag_vertical.schema.yml +34 -0
  202. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +36 -0
  203. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +34 -0
  204. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +34 -0
  205. simtools/schemas/model_parameters/laser_events.schema.yml +36 -0
  206. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +35 -0
  207. simtools/schemas/model_parameters/laser_photons.schema.yml +32 -0
  208. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +34 -0
  209. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +34 -0
  210. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +33 -0
  211. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +33 -0
  212. simtools/schemas/model_parameters/laser_var_photons.schema.yml +33 -0
  213. simtools/schemas/model_parameters/laser_wavelength.schema.yml +33 -0
  214. simtools/schemas/model_parameters/led_events.schema.yml +34 -0
  215. simtools/schemas/model_parameters/led_photons.schema.yml +34 -0
  216. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +32 -0
  217. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +33 -0
  218. simtools/schemas/model_parameters/led_var_photons.schema.yml +34 -0
  219. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +41 -0
  220. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +43 -0
  221. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +35 -0
  222. simtools/schemas/model_parameters/min_photons.schema.yml +32 -0
  223. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +36 -0
  224. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +64 -0
  225. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +64 -0
  226. simtools/schemas/model_parameters/mirror_class.schema.yml +41 -0
  227. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +51 -0
  228. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +42 -0
  229. simtools/schemas/model_parameters/mirror_list.schema.yml +38 -0
  230. simtools/schemas/model_parameters/mirror_offset.schema.yml +41 -0
  231. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +39 -0
  232. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +61 -0
  233. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +40 -0
  234. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +46 -0
  235. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +51 -0
  236. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +37 -0
  237. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +79 -0
  238. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +47 -0
  239. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +34 -0
  240. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +33 -0
  241. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +35 -0
  242. simtools/schemas/model_parameters/nsb_skymap.schema.yml +39 -0
  243. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +50 -0
  244. simtools/schemas/model_parameters/num_gains.schema.yml +34 -0
  245. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +33 -0
  246. simtools/schemas/model_parameters/optics_properties.schema.yml +31 -0
  247. simtools/schemas/model_parameters/parabolic_dish.schema.yml +32 -0
  248. simtools/schemas/model_parameters/pedestal_events.schema.yml +32 -0
  249. simtools/schemas/model_parameters/photon_delay.schema.yml +38 -0
  250. simtools/schemas/model_parameters/photons_per_run.schema.yml +33 -0
  251. simtools/schemas/model_parameters/pixel_cells.schema.yml +35 -0
  252. simtools/schemas/model_parameters/pixels_parallel.schema.yml +54 -0
  253. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +40 -0
  254. simtools/schemas/model_parameters/pm_average_gain.schema.yml +34 -0
  255. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +40 -0
  256. simtools/schemas/model_parameters/pm_gain_index.schema.yml +36 -0
  257. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +41 -0
  258. simtools/schemas/model_parameters/pm_transit_time.schema.yml +63 -0
  259. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +39 -0
  260. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +42 -0
  261. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +33 -0
  262. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +33 -0
  263. simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +29 -0
  264. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +168 -0
  265. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +36 -0
  266. simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +34 -0
  267. simtools/schemas/model_parameters/qe_variation.schema.yml +43 -0
  268. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +42 -0
  269. simtools/schemas/model_parameters/random_focal_length.schema.yml +45 -0
  270. simtools/schemas/model_parameters/random_generator.schema.yml +36 -0
  271. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +35 -0
  272. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +36 -0
  273. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +36 -0
  274. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +34 -0
  275. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +34 -0
  276. simtools/schemas/model_parameters/sampled_output.schema.yml +31 -0
  277. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +34 -0
  278. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +79 -0
  279. simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +42 -0
  280. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +41 -0
  281. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +33 -0
  282. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +36 -0
  283. simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +29 -0
  284. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +168 -0
  285. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +36 -0
  286. simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +35 -0
  287. simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +37 -0
  288. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +40 -0
  289. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +40 -0
  290. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +41 -0
  291. simtools/schemas/model_parameters/tailcut_scale.schema.yml +40 -0
  292. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +31 -0
  293. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +35 -0
  294. simtools/schemas/model_parameters/telescope_random_error.schema.yml +34 -0
  295. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +37 -0
  296. simtools/schemas/model_parameters/telescope_transmission.schema.yml +113 -0
  297. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +41 -0
  298. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +36 -0
  299. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +36 -0
  300. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +37 -0
  301. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +38 -0
  302. simtools/schemas/model_parameters/transit_time_error.schema.yml +45 -0
  303. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +36 -0
  304. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +32 -0
  305. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +53 -0
  306. simtools/schemas/model_parameters/trigger_pixels.schema.yml +40 -0
  307. simtools/simtel/simtel_config_reader.py +353 -0
  308. simtools/simtel/simtel_config_writer.py +244 -63
  309. simtools/simtel/{simtel_events.py → simtel_io_events.py} +26 -25
  310. simtools/simtel/simtel_io_histogram.py +661 -0
  311. simtools/simtel/simtel_io_histograms.py +569 -0
  312. simtools/simtel/simulator_array.py +145 -0
  313. simtools/simtel/{simtel_runner_camera_efficiency.py → simulator_camera_efficiency.py} +76 -52
  314. simtools/simtel/simulator_light_emission.py +473 -0
  315. simtools/simtel/simulator_ray_tracing.py +262 -0
  316. simtools/simulator.py +220 -446
  317. simtools/testing/__init__.py +0 -0
  318. simtools/testing/assertions.py +151 -0
  319. simtools/testing/configuration.py +226 -0
  320. simtools/testing/helpers.py +42 -0
  321. simtools/testing/validate_output.py +240 -0
  322. simtools/utils/general.py +340 -437
  323. simtools/utils/geometry.py +12 -12
  324. simtools/utils/names.py +258 -644
  325. simtools/utils/value_conversion.py +176 -0
  326. simtools/version.py +2 -0
  327. simtools/visualization/legend_handlers.py +135 -152
  328. simtools/visualization/plot_camera.py +379 -0
  329. simtools/visualization/visualize.py +346 -167
  330. gammasimtools-0.6.1.dist-info/METADATA +0 -180
  331. gammasimtools-0.6.1.dist-info/RECORD +0 -91
  332. gammasimtools-0.6.1.dist-info/entry_points.txt +0 -23
  333. simtools/applications/db_development_tools/add_new_parameter_to_db.py +0 -81
  334. simtools/applications/db_development_tools/add_unit_to_parameter_in_db.py +0 -59
  335. simtools/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py +0 -102
  336. simtools/applications/get_parameter.py +0 -92
  337. simtools/applications/make_regular_arrays.py +0 -160
  338. simtools/applications/produce_array_config.py +0 -136
  339. simtools/applications/production.py +0 -313
  340. simtools/applications/sim_showers_for_trigger_rates.py +0 -187
  341. simtools/applications/tune_psf.py +0 -334
  342. simtools/corsika/corsika_default_config.py +0 -282
  343. simtools/corsika/corsika_runner.py +0 -450
  344. simtools/corsika_simtel/corsika_simtel_runner.py +0 -197
  345. simtools/db_handler.py +0 -1480
  346. simtools/ray_tracing.py +0 -525
  347. simtools/simtel/simtel_histograms.py +0 -414
  348. simtools/simtel/simtel_runner.py +0 -244
  349. simtools/simtel/simtel_runner_array.py +0 -293
  350. simtools/simtel/simtel_runner_ray_tracing.py +0 -277
  351. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.1.dist-info}/LICENSE +0 -0
  352. {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.1.dist-info}/top_level.txt +0 -0
  353. /simtools/{corsika_simtel → db}/__init__.py +0 -0
simtools/model/camera.py CHANGED
@@ -1,18 +1,12 @@
1
+ """Definition and modeling of camera."""
2
+
1
3
  import logging
2
4
 
3
5
  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
6
  import numpy as np
9
- from matplotlib.collections import PatchCollection
10
7
  from scipy.spatial import cKDTree as KDTree
11
8
  from scipy.spatial import distance
12
9
 
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
10
  from simtools.utils.geometry import rotate
17
11
 
18
12
  __all__ = ["Camera"]
@@ -20,13 +14,14 @@ __all__ = ["Camera"]
20
14
 
21
15
  class Camera:
22
16
  """
23
- Camera class, defining pixel layout including rotation, finding neighbour pixels, calculating\
24
- FoV and plotting the camera.
17
+ Camera class, defining pixel layout.
18
+
19
+ This includes rotation, finding neighbor pixels, calculating FoV and plotting the camera.
25
20
 
26
21
  Parameters
27
22
  ----------
28
23
  telescope_model_name: string
29
- As provided by the telescope model method TelescopeModel (ex South-LST-1).
24
+ As provided by the telescope model method TelescopeModel (e.g., LSTN-01)
30
25
  camera_config_file: string
31
26
  The sim_telarray file name.
32
27
  focal_length: float
@@ -34,31 +29,30 @@ class Camera:
34
29
  in the same unit as the pixel positions in the camera_config_file, usually cm.
35
30
  """
36
31
 
37
- # Constants for finding neighbour pixels.
32
+ # Constants for finding neighbor pixels.
38
33
  PMT_NEIGHBOR_RADIUS_FACTOR = 1.1
39
34
  SIPM_NEIGHBOR_RADIUS_FACTOR = 1.4
40
35
  SIPM_ROW_COLUMN_DIST_FACTOR = 0.2
41
36
 
42
37
  def __init__(self, telescope_model_name, camera_config_file, focal_length):
43
38
  """
44
- Initialize Camera class, defining pixel layout including rotation, finding neighbour pixels,
45
- calculating FoV and plotting the camera.
46
- """
39
+ Initialize Camera class, defining pixel layout.
47
40
 
41
+ This includes rotation, finding neighbor pixels, calculating FoV and plotting the camera.
42
+ """
48
43
  self._logger = logging.getLogger(__name__)
49
44
 
50
- self._telescope_model_name = telescope_model_name
51
- _, self._camera_name, _ = names.split_telescope_model_name(telescope_model_name)
45
+ self.telescope_model_name = telescope_model_name
52
46
  self._camera_config_file = camera_config_file
53
- self._focal_length = focal_length
54
- if self._focal_length <= 0:
47
+ self.focal_length = focal_length
48
+ if self.focal_length <= 0:
55
49
  raise ValueError("The focal length must be larger than zero")
56
- self._pixels = self.read_pixel_list(camera_config_file)
50
+ self.pixels = self.read_pixel_list(camera_config_file)
57
51
 
58
- self._pixels = self._rotate_pixels(self._pixels)
52
+ self.pixels = self._rotate_pixels(self.pixels)
59
53
 
60
- # Initialize an empty list of neighbours, to be calculated only when necessary.
61
- self._neighbours = None
54
+ # Initialize an empty list of neighbors, to be calculated only when necessary.
55
+ self._neighbors = None
62
56
 
63
57
  # Initialize an empty list of edge pixels, to be calculated only when necessary.
64
58
  self._edge_pixel_indices = None
@@ -85,58 +79,105 @@ class Camera:
85
79
  The hexagonal shapes differ in their orientation, where those denoted as 3 are rotated
86
80
  clockwise by 30 degrees with respect to those denoted as 1.
87
81
  """
82
+ pixels = Camera.initialize_pixel_dict()
88
83
 
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:
84
+ with open(camera_config_file, encoding="utf-8") as dat_file:
102
85
  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)
86
+ Camera.process_line(line, pixels)
87
+
88
+ Camera.validate_pixels(pixels, camera_config_file)
89
+
90
+ return pixels
91
+
92
+ @staticmethod
93
+ def initialize_pixel_dict():
94
+ """
95
+ Initialize the pixel dictionary with default values.
96
+
97
+ Returns
98
+ -------
99
+ dict
100
+ A dictionary with default pixel properties.
101
+ """
102
+ return {
103
+ "pixel_diameter": 9999,
104
+ "pixel_shape": 9999,
105
+ "pixel_spacing": 9999,
106
+ "lightguide_efficiency_angle_file": "none",
107
+ "lightguide_efficiency_wavelength_file": "none",
108
+ "rotate_angle": 0,
109
+ "x": [],
110
+ "y": [],
111
+ "pix_id": [],
112
+ "pix_on": [],
113
+ }
114
+
115
+ @staticmethod
116
+ def process_line(line, pixels):
117
+ """
118
+ Process a line from the camera config file and update the pixel dictionary.
119
+
120
+ Parameters
121
+ ----------
122
+ line: string
123
+ A line from the camera config file.
124
+ pixels: dict
125
+ The pixel dictionary to update.
126
+ """
127
+ pix_info = line.split()
128
+
129
+ if line.startswith("PixType"):
130
+ pixels["pixel_shape"] = int(pix_info[5].strip())
131
+ pixels["pixel_diameter"] = float(pix_info[6].strip())
132
+ pixels["lightguide_efficiency_angle_file"] = pix_info[8].strip().replace('"', "")
133
+
134
+ if len(pix_info) > 9:
135
+ pixels["lightguide_efficiency_wavelength_file"] = (
136
+ pix_info[9].strip().replace('"', "")
137
+ )
138
+
139
+ elif line.startswith("Rotate"):
140
+ pixels["rotate_angle"] = np.deg2rad(float(pix_info[1].strip()))
141
+
142
+ elif line.startswith("Pixel"):
143
+ pixels["x"].append(float(pix_info[3].strip()))
144
+ pixels["y"].append(float(pix_info[4].strip()))
145
+ pixels["pix_id"].append(int(pix_info[1].strip()))
146
+
147
+ if len(pix_info) > 9:
148
+ pixels["pix_on"].append(int(pix_info[9].strip()) != 0)
149
+ else:
150
+ pixels["pix_on"].append(True)
151
+
152
+ @staticmethod
153
+ def validate_pixels(pixels, camera_config_file):
154
+ """
155
+ Validate the pixel dictionary to ensure it has correct values.
127
156
 
157
+ Parameters
158
+ ----------
159
+ pixels: dict
160
+ The pixel dictionary to validate.
161
+ camera_config_file: string
162
+ The sim_telarray file name for error messages.
163
+
164
+ Raises
165
+ ------
166
+ ValueError
167
+ If the pixel diameter or pixel shape is invalid.
168
+ """
128
169
  if pixels["pixel_diameter"] == 9999:
129
170
  raise ValueError(f"Could not read the pixel diameter from {camera_config_file} file")
171
+
130
172
  if pixels["pixel_shape"] not in [1, 2, 3]:
131
173
  raise ValueError(
132
174
  f"Pixel shape in {camera_config_file} unrecognized (has to be 1, 2 or 3)"
133
175
  )
134
176
 
135
- return pixels
136
-
137
177
  def _rotate_pixels(self, pixels):
138
178
  """
139
179
  Rotate the pixels according to the rotation angle given in pixels['rotate_angle'].
180
+
140
181
  Additional rotation is added to get to the camera view of an observer facing the camera.
141
182
  The angle for the axes rotation depends on the coordinate system in which the original
142
183
  data was provided.
@@ -153,7 +194,6 @@ class Camera:
153
194
  The pixels orientation for plotting is added to the dictionary in pixels['orientation'].
154
195
  The orientation is determined by the pixel shape (see read_pixel_list for details).
155
196
  """
156
-
157
197
  rotate_angle = pixels["rotate_angle"] * u.rad # So not to change the original angle
158
198
  # The original pixel list is given such that
159
199
  # x -> North, y -> West, z -> Up in the ground system.
@@ -188,8 +228,7 @@ class Camera:
188
228
  int
189
229
  number of pixels.
190
230
  """
191
-
192
- return len(self._pixels["x"])
231
+ return len(self.pixels["x"])
193
232
 
194
233
  def get_pixel_diameter(self):
195
234
  """
@@ -200,8 +239,7 @@ class Camera:
200
239
  float
201
240
  Pixel diameter (usually in cm).
202
241
  """
203
-
204
- return self._pixels["pixel_diameter"]
242
+ return self.pixels["pixel_diameter"]
205
243
 
206
244
  def get_pixel_active_solid_angle(self):
207
245
  """
@@ -212,16 +250,17 @@ class Camera:
212
250
  float
213
251
  active solid angle of a pixel in sr.
214
252
  """
215
-
216
253
  pixel_area = self.get_pixel_diameter() ** 2
217
254
  # In case we have hexagonal pixels:
218
255
  if self.get_pixel_shape() == 1 or self.get_pixel_shape() == 3:
219
256
  pixel_area *= np.sqrt(3) / 2
220
- return pixel_area / (self._focal_length**2)
257
+ return pixel_area / (self.focal_length**2)
221
258
 
222
259
  def get_pixel_shape(self):
223
260
  """
224
- Get pixel shape code 1, 2 or 3, where 1 and 3 are hexagonal pixels, where one is rotated by\
261
+ Get pixel shape code 1, 2 or 3.
262
+
263
+ Where 1 and 3 are hexagonal pixels, where one is rotated by\
225
264
  30 degrees with respect to the other. A square pixel is denoted as 2.
226
265
 
227
266
  Returns
@@ -229,7 +268,7 @@ class Camera:
229
268
  int (1, 2 or 3)
230
269
  Pixel shape.
231
270
  """
232
- return self._pixels["pixel_shape"]
271
+ return self.pixels["pixel_shape"]
233
272
 
234
273
  def get_lightguide_efficiency_angle_file_name(self):
235
274
  """
@@ -240,8 +279,7 @@ class Camera:
240
279
  str
241
280
  File name of the light guide efficiency as a function of incidence angle.
242
281
  """
243
-
244
- return self._pixels["lightguide_efficiency_angle_file"]
282
+ return self.pixels["lightguide_efficiency_angle_file"]
245
283
 
246
284
  def get_lightguide_efficiency_wavelength_file_name(self):
247
285
  """
@@ -252,24 +290,26 @@ class Camera:
252
290
  str
253
291
  File name of the light guide efficiency as a function of wavelength.
254
292
  """
255
- return self._pixels["lightguide_efficiency_wavelength_file"]
293
+ return self.pixels["lightguide_efficiency_wavelength_file"]
256
294
 
257
295
  def get_camera_fill_factor(self):
258
296
  """
259
- Calculate the fill factor of the camera, defined as (pixel_diameter/pixel_spacing)**2
297
+ Calculate the fill factor of the camera, defined as (pixel_diameter/pixel_spacing)**2.
260
298
 
261
299
  Returns
262
300
  -------
263
301
  float
264
302
  The camera fill factor.
265
303
  """
266
-
267
- if self._pixels["pixel_spacing"] == 9999:
268
- points = np.array([self._pixels["x"], self._pixels["y"]]).T
304
+ if self.pixels["pixel_spacing"] == 9999:
305
+ points = np.array([self.pixels["x"], self.pixels["y"]]).T
269
306
  pixel_distances = distance.cdist(points, points, "euclidean")
270
- self._pixels["pixel_spacing"] = np.min(pixel_distances[pixel_distances > 0])
307
+ # pylint: disable=unsubscriptable-object
308
+ pixel_distances = pixel_distances[pixel_distances > 0]
309
+ pixel_spacing = np.min(pixel_distances)
310
+ self.pixels["pixel_spacing"] = pixel_spacing
271
311
 
272
- return (self._pixels["pixel_diameter"] / self._pixels["pixel_spacing"]) ** 2
312
+ return (self.pixels["pixel_diameter"] / self.pixels["pixel_spacing"]) ** 2
273
313
 
274
314
  def calc_fov(self):
275
315
  """
@@ -286,14 +326,13 @@ class Camera:
286
326
  -----
287
327
  The x,y pixel positions and focal length are assumed to have the same unit (usually cm)
288
328
  """
289
-
290
329
  self._logger.debug("Calculating the FoV")
291
330
 
292
331
  return self._calc_fov(
293
- self._pixels["x"],
294
- self._pixels["y"],
332
+ self.pixels["x"],
333
+ self.pixels["y"],
295
334
  self.get_edge_pixels(),
296
- self._focal_length,
335
+ self.focal_length,
297
336
  )
298
337
 
299
338
  def _calc_fov(self, x_pixel, y_pixel, edge_pixel_indices, focal_length):
@@ -323,7 +362,6 @@ class Camera:
323
362
  -----
324
363
  The x,y pixel positions and focal length are assumed to have the same unit (usually cm)
325
364
  """
326
-
327
365
  self._logger.debug("Calculating the FoV")
328
366
 
329
367
  average_edge_distance = 0
@@ -336,10 +374,11 @@ class Camera:
336
374
  return fov, average_edge_distance
337
375
 
338
376
  @staticmethod
339
- def _find_neighbours(x_pos, y_pos, radius):
377
+ def _find_neighbors(x_pos, y_pos, radius):
340
378
  """
341
- use a KD-Tree to quickly find nearest neighbours (e.g., of the pixels in a camera or mirror\
342
- facets)
379
+ Use a KD-Tree to quickly find nearest neighbors.
380
+
381
+ This applies to e.g., of the pixels in a camera or mirror facets.
343
382
 
344
383
  Parameters
345
384
  ----------
@@ -348,80 +387,101 @@ class Camera:
348
387
  y_pos : numpy.array_like
349
388
  y position of each e.g., pixel
350
389
  radius : float
351
- radius to consider neighbour it should be slightly larger than the pixel diameter or \
390
+ radius to consider neighbor it should be slightly larger than the pixel diameter or \
352
391
  mirror facet.
353
392
 
354
393
  Returns
355
394
  -------
356
- neighbours: numpy.array_like
357
- Array of neighbour indices in a list for each e.g., pixel.
395
+ neighbors: numpy.array_like
396
+ Array of neighbor indices in a list for each e.g., pixel.
358
397
  """
359
-
360
398
  points = np.array([x_pos, y_pos]).T
361
399
  indices = np.arange(len(x_pos))
362
400
  kdtree = KDTree(points)
363
- neighbours = [kdtree.query_ball_point(p, r=radius) for p in points]
401
+ neighbors = [kdtree.query_ball_point(p, r=radius) for p in points]
364
402
 
365
- for neighbour_now, index_now in zip(neighbours, indices):
366
- neighbour_now.remove(index_now) # get rid of the pixel or mirror itself
403
+ for neighbor_now, index_now in zip(neighbors, indices):
404
+ neighbor_now.remove(index_now) # get rid of the pixel or mirror itself
367
405
 
368
- return neighbours
406
+ return neighbors
369
407
 
370
- def _find_adjacent_neighbour_pixels(self, x_pos, y_pos, radius, row_coloumn_dist):
408
+ def _find_adjacent_neighbor_pixels(self, x_pos, y_pos, radius, row_column_dist):
371
409
  """
372
- Find adjacent neighbour pixels in cameras with square pixels. Only directly adjacent \
373
- neighbours are allowed, no diagonals.
410
+ Find adjacent neighbor pixels in cameras with square pixels.
411
+
412
+ Only directly adjacent neighbors are allowed, no diagonals.
374
413
 
375
414
  Parameters
376
415
  ----------
377
416
  x_pos : numpy.array_like
378
417
  x position of each pixel
379
418
  y_pos : numpy.array_like
380
- y position of each pixels
419
+ y position of each pixel
381
420
  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.
421
+ Radius within which to find neighbors
422
+ row_column_dist : float
423
+ Distance to consider for row/column adjacency.
424
+ Should be around 20% of the pixel diameter.
387
425
 
388
426
  Returns
389
427
  -------
390
- neighbours: numpy.array_like
391
- Array of neighbour indices in a list for each pixel
428
+ list of lists
429
+ Array of neighbor indices in a list for each pixel
392
430
  """
431
+ # First find the neighbors with the usual method and the original radius
432
+ # which does not allow for diagonal neighbors.
433
+ neighbors = self._find_neighbors(x_pos, y_pos, radius)
393
434
 
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):
435
+ for i_pix, nn in enumerate(neighbors):
398
436
  # Find pixels defined as edge pixels now
399
437
  if len(nn) < 4:
400
438
  # Go over all other pixels and search for ones which are adjacent
401
439
  # 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.
440
+ self._add_additional_neighbors(i_pix, nn, x_pos, y_pos, radius, row_column_dist)
441
+
442
+ return neighbors
443
+
444
+ def _add_additional_neighbors(self, i_pix, nn, x_pos, y_pos, radius, row_column_dist):
445
+ """
446
+ Add neighbors for a given pixel if they are not already neighbors and are adjacent.
447
+
448
+ Parameters
449
+ ----------
450
+ i_pix : int
451
+ Index of the pixel to find neighbors for
452
+ nn : list
453
+ Current list of neighbors for the pixel
454
+ x_pos : numpy.array_like
455
+ x position of each pixel
456
+ y_pos : numpy.array_like
457
+ y position of each pixel
458
+ radius : float
459
+ Radius within which to find neighbors
460
+ row_column_dist : float
461
+ Distance to consider for row/column adjacency
462
+ """
463
+ for j_pix, _ in enumerate(x_pos):
464
+ # No need to look at the pixel itself
465
+ # nor at any pixels already in the neighbors list
466
+ if j_pix != i_pix and j_pix not in nn:
467
+ dist = np.sqrt(
468
+ (x_pos[i_pix] - x_pos[j_pix]) ** 2 + (y_pos[i_pix] - y_pos[j_pix]) ** 2
469
+ )
470
+ # Check if this pixel is in the same row or column
471
+ # and allow it to be ~1.68*diameter away (1.4*1.2 = 1.68)
472
+ # Need to increase the distance because of the curvature
473
+ # of the CHEC camera
474
+ if (
475
+ abs(x_pos[i_pix] - x_pos[j_pix]) < row_column_dist
476
+ or abs(y_pos[i_pix] - y_pos[j_pix]) < row_column_dist
477
+ ) and dist < 1.2 * radius:
478
+ nn.append(j_pix)
479
+
480
+ def _calc_neighbor_pixels(self, pixels):
481
+ """
482
+ Find adjacent neighbor pixels in cameras with hexagonal or square pixels.
483
+
484
+ Only directly adjacent neighbors are searched for, no diagonals.
425
485
 
426
486
  Parameters
427
487
  ----------
@@ -430,14 +490,13 @@ class Camera:
430
490
 
431
491
  Returns
432
492
  -------
433
- neighbours: numpy.array_like
434
- Array of neighbour indices in a list for each pixel
493
+ neighbors: numpy.array_like
494
+ Array of neighbor indices in a list for each pixel
435
495
  """
436
-
437
- self._logger.debug("Searching for neighbour pixels")
496
+ self._logger.debug("Searching for neighbor pixels")
438
497
 
439
498
  if pixels["pixel_shape"] == 1 or pixels["pixel_shape"] == 3:
440
- self._neighbours = self._find_neighbours(
499
+ self._neighbors = self._find_neighbors(
441
500
  pixels["x"],
442
501
  pixels["y"],
443
502
  self.PMT_NEIGHBOR_RADIUS_FACTOR * pixels["pixel_diameter"],
@@ -445,21 +504,22 @@ class Camera:
445
504
  elif pixels["pixel_shape"] == 2:
446
505
  # Distance increased by 40% to take into account gaps in the SiPM cameras
447
506
  # Pixels in the same row/column can be 20% shifted from one another
448
- # Inside find_adjacent_neighbour_pixels the distance is increased
507
+ # Inside find_adjacent_neighbor_pixels the distance is increased
449
508
  # further for pixels in the same row/column to 1.68*diameter.
450
- self._neighbours = self._find_adjacent_neighbour_pixels(
509
+ self._neighbors = self._find_adjacent_neighbor_pixels(
451
510
  pixels["x"],
452
511
  pixels["y"],
453
512
  self.SIPM_NEIGHBOR_RADIUS_FACTOR * pixels["pixel_diameter"],
454
513
  self.SIPM_ROW_COLUMN_DIST_FACTOR * pixels["pixel_diameter"],
455
514
  )
456
515
 
457
- return self._neighbours
516
+ return self._neighbors
458
517
 
459
- def get_neighbour_pixels(self, pixels=None):
518
+ def get_neighbor_pixels(self, pixels=None):
460
519
  """
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 \
520
+ Get a list of neighbor pixels by calling calc_neighbor_pixels() when necessary.
521
+
522
+ The purpose of this function is to ensure the calculation occurs only once and only when
463
523
  necessary.
464
524
 
465
525
  Parameters
@@ -469,18 +529,17 @@ class Camera:
469
529
 
470
530
  Returns
471
531
  -------
472
- neighbours: numpy.array_like
473
- Array of neighbour indices in a list for each pixel.
532
+ neighbors: numpy.array_like
533
+ Array of neighbor indices in a list for each pixel.
474
534
  """
475
-
476
- if self._neighbours is None:
535
+ if self._neighbors is None:
477
536
  if pixels is None:
478
- pixels = self._pixels
479
- return self._calc_neighbour_pixels(pixels)
537
+ pixels = self.pixels
538
+ return self._calc_neighbor_pixels(pixels)
480
539
 
481
- return self._neighbours
540
+ return self._neighbors
482
541
 
483
- def _calc_edge_pixels(self, pixels, neighbours):
542
+ def _calc_edge_pixels(self, pixels, neighbors):
484
543
  """
485
544
  Find the edge pixels of the camera.
486
545
 
@@ -488,32 +547,34 @@ class Camera:
488
547
  ----------
489
548
  pixels: dictionary
490
549
  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.
550
+ neighbors: numpy.array_like
551
+ Array of neighbor indices in a list for each pixel.
493
552
 
494
553
  Returns
495
554
  -------
496
555
  edge_pixel_indices: numpy.array_like
497
556
  Array of edge pixel indices.
498
557
  """
499
-
500
558
  self._logger.debug("Searching for edge pixels")
501
-
502
559
  edge_pixel_indices = []
503
560
 
561
+ def is_edge_pixel(i_pix):
562
+ pixel_shape = pixels["pixel_shape"]
563
+ pix_on = pixels["pix_on"][i_pix]
564
+ num_neighbors = len(neighbors[i_pix])
565
+
566
+ shape_condition = (pixel_shape in [1, 3] and num_neighbors < 6) or (
567
+ pixel_shape == 2 and num_neighbors < 4
568
+ )
569
+ return pix_on and shape_condition
570
+
504
571
  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)
572
+ if is_edge_pixel(i_pix):
573
+ edge_pixel_indices.append(i_pix)
513
574
 
514
575
  return edge_pixel_indices
515
576
 
516
- def get_edge_pixels(self, pixels=None, neighbours=None):
577
+ def get_edge_pixels(self, pixels=None, neighbors=None):
517
578
  """
518
579
  Get the indices of the edge pixels of the camera.
519
580
 
@@ -521,340 +582,19 @@ class Camera:
521
582
  ----------
522
583
  pixels: dict
523
584
  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.
585
+ neighbors: numpy.array_like
586
+ Array of neighbor indices in a list for each pixel.
526
587
 
527
588
  Returns
528
589
  -------
529
590
  edge_pixel_indices: numpy.array_like
530
591
  Array of edge pixel indices.
531
592
  """
532
-
533
593
  if self._edge_pixel_indices is None:
534
594
  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)
595
+ pixels = self.pixels
596
+ if neighbors is None:
597
+ neighbors = self.get_neighbor_pixels()
598
+ return self._calc_edge_pixels(pixels, neighbors)
539
599
 
540
600
  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