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