gammasimtools 0.15.0__py3-none-any.whl → 0.17.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/METADATA +5 -33
  2. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/RECORD +243 -229
  3. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/entry_points.txt +8 -3
  5. simtools/_version.py +2 -2
  6. simtools/applications/calculate_trigger_rate.py +10 -10
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +16 -16
  8. simtools/applications/convert_model_parameter_from_simtel.py +1 -1
  9. simtools/applications/derive_ctao_array_layouts.py +5 -5
  10. simtools/applications/derive_psf_parameters.py +12 -9
  11. simtools/applications/docs_produce_array_element_report.py +3 -3
  12. simtools/applications/docs_produce_calibration_reports.py +49 -0
  13. simtools/applications/docs_produce_simulation_configuration_report.py +50 -0
  14. simtools/applications/{generate_simtel_array_histograms.py → generate_sim_telarray_histograms.py} +2 -2
  15. simtools/applications/generate_simtel_event_data.py +36 -46
  16. simtools/applications/merge_tables.py +104 -0
  17. simtools/applications/plot_array_layout.py +145 -258
  18. simtools/applications/production_derive_corsika_limits.py +35 -167
  19. simtools/applications/production_derive_statistics.py +159 -0
  20. simtools/applications/production_generate_grid.py +197 -0
  21. simtools/applications/simulate_light_emission.py +6 -13
  22. simtools/applications/simulate_prod.py +45 -21
  23. simtools/applications/simulate_prod_htcondor_generator.py +0 -1
  24. simtools/applications/submit_array_layouts.py +93 -0
  25. simtools/applications/validate_cumulative_psf.py +6 -4
  26. simtools/applications/validate_file_using_schema.py +7 -3
  27. simtools/applications/validate_optics.py +5 -4
  28. simtools/applications/verify_simulation_model_production_tables.py +52 -0
  29. simtools/camera/camera_efficiency.py +17 -42
  30. simtools/configuration/commandline_parser.py +32 -37
  31. simtools/configuration/configurator.py +10 -4
  32. simtools/corsika/corsika_config.py +120 -17
  33. simtools/corsika/primary_particle.py +46 -13
  34. simtools/data_model/format_checkers.py +9 -0
  35. simtools/data_model/metadata_collector.py +7 -3
  36. simtools/data_model/model_data_writer.py +3 -0
  37. simtools/data_model/schema.py +27 -16
  38. simtools/data_model/validate_data.py +27 -7
  39. simtools/db/db_handler.py +21 -15
  40. simtools/db/db_model_upload.py +2 -2
  41. simtools/io_operations/io_handler.py +2 -2
  42. simtools/io_operations/io_table_handler.py +345 -0
  43. simtools/job_execution/htcondor_script_generator.py +2 -2
  44. simtools/job_execution/job_manager.py +7 -121
  45. simtools/layout/array_layout.py +1 -0
  46. simtools/layout/array_layout_utils.py +385 -0
  47. simtools/model/array_model.py +68 -29
  48. simtools/model/model_parameter.py +76 -51
  49. simtools/model/model_repository.py +134 -0
  50. simtools/model/model_utils.py +43 -1
  51. simtools/model/site_model.py +3 -2
  52. simtools/model/telescope_model.py +4 -4
  53. simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -116
  54. simtools/production_configuration/derive_corsika_limits.py +239 -111
  55. simtools/production_configuration/derive_corsika_limits_grid.py +189 -0
  56. simtools/production_configuration/derive_production_statistics.py +155 -0
  57. simtools/production_configuration/derive_production_statistics_handler.py +152 -0
  58. simtools/production_configuration/generate_production_grid.py +364 -0
  59. simtools/production_configuration/interpolation_handler.py +303 -96
  60. simtools/ray_tracing/mirror_panel_psf.py +16 -20
  61. simtools/ray_tracing/psf_analysis.py +2 -2
  62. simtools/ray_tracing/ray_tracing.py +12 -7
  63. simtools/reporting/docs_read_parameters.py +426 -81
  64. simtools/runners/corsika_runner.py +11 -1
  65. simtools/runners/corsika_simtel_runner.py +84 -90
  66. simtools/runners/runner_services.py +22 -8
  67. simtools/runners/simtel_runner.py +27 -10
  68. simtools/schemas/model_parameter.metaschema.yml +4 -0
  69. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +1 -0
  70. simtools/schemas/model_parameters/adjust_gain.schema.yml +2 -2
  71. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +2 -2
  72. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +2 -2
  73. simtools/schemas/model_parameters/array_window.schema.yml +2 -2
  74. simtools/schemas/model_parameters/asum_offset.schema.yml +2 -2
  75. simtools/schemas/model_parameters/asum_shaping.schema.yml +2 -2
  76. simtools/schemas/model_parameters/asum_threshold.schema.yml +2 -2
  77. simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
  78. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +2 -2
  79. simtools/schemas/model_parameters/camera_body_shape.schema.yml +2 -2
  80. simtools/schemas/model_parameters/camera_config_file.schema.yml +2 -2
  81. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +2 -2
  82. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +2 -2
  83. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +2 -2
  84. simtools/schemas/model_parameters/camera_depth.schema.yml +2 -2
  85. simtools/schemas/model_parameters/camera_filter.schema.yml +2 -2
  86. simtools/schemas/model_parameters/camera_pixels.schema.yml +2 -2
  87. simtools/schemas/model_parameters/camera_transmission.schema.yml +2 -2
  88. simtools/schemas/model_parameters/channels_per_chip.schema.yml +2 -2
  89. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +2 -2
  90. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +90 -1
  91. simtools/schemas/model_parameters/default_trigger.schema.yml +2 -2
  92. simtools/schemas/model_parameters/design_model.schema.yml +2 -2
  93. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +2 -2
  94. simtools/schemas/model_parameters/disc_bins.schema.yml +2 -2
  95. simtools/schemas/model_parameters/disc_start.schema.yml +2 -2
  96. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +2 -2
  97. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +2 -2
  98. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +2 -2
  99. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +2 -2
  100. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +2 -2
  101. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +2 -2
  102. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +2 -2
  103. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +2 -2
  104. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +2 -2
  105. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +2 -2
  106. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +2 -2
  107. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +2 -2
  108. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +2 -2
  109. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +2 -2
  110. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +2 -2
  111. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +2 -2
  112. simtools/schemas/model_parameters/dish_shape_length.schema.yml +2 -2
  113. simtools/schemas/model_parameters/dsum_clipping.schema.yml +2 -2
  114. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +2 -2
  115. simtools/schemas/model_parameters/dsum_offset.schema.yml +2 -2
  116. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +2 -2
  117. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +2 -2
  118. simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
  119. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +2 -2
  120. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +2 -2
  121. simtools/schemas/model_parameters/dsum_shaping.schema.yml +2 -2
  122. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +2 -2
  123. simtools/schemas/model_parameters/dsum_threshold.schema.yml +44 -3
  124. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +2 -2
  125. simtools/schemas/model_parameters/effective_focal_length.schema.yml +2 -2
  126. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +2 -2
  127. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -2
  128. simtools/schemas/model_parameters/fadc_bins.schema.yml +2 -2
  129. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +2 -2
  130. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +2 -2
  131. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +2 -2
  132. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -2
  133. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +2 -2
  134. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +2 -2
  135. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +2 -2
  136. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +2 -2
  137. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +2 -2
  138. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +2 -2
  139. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +2 -2
  140. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +2 -2
  141. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +2 -2
  142. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +2 -2
  143. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +2 -2
  144. simtools/schemas/model_parameters/fadc_mhz.schema.yml +2 -2
  145. simtools/schemas/model_parameters/fadc_noise.schema.yml +2 -2
  146. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +2 -2
  147. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +2 -2
  148. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +2 -2
  149. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +2 -2
  150. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +2 -2
  151. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +2 -2
  152. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +2 -2
  153. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +2 -2
  154. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  155. simtools/schemas/model_parameters/flatfielding.schema.yml +2 -2
  156. simtools/schemas/model_parameters/focal_length.schema.yml +2 -2
  157. simtools/schemas/model_parameters/focus_offset.schema.yml +2 -2
  158. simtools/schemas/model_parameters/gain_variation.schema.yml +2 -2
  159. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +2 -2
  160. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +2 -2
  161. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +2 -2
  162. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  163. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +2 -2
  164. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +2 -2
  165. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +2 -2
  166. simtools/schemas/model_parameters/min_photons.schema.yml +2 -2
  167. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +2 -2
  168. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +2 -2
  169. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +2 -2
  170. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -2
  171. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +2 -2
  172. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +2 -2
  173. simtools/schemas/model_parameters/mirror_list.schema.yml +2 -2
  174. simtools/schemas/model_parameters/mirror_offset.schema.yml +2 -2
  175. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +2 -2
  176. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +2 -2
  177. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +2 -2
  178. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +2 -2
  179. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
  180. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +2 -2
  181. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +2 -2
  182. simtools/schemas/model_parameters/num_gains.schema.yml +2 -2
  183. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +2 -2
  184. simtools/schemas/model_parameters/optics_properties.schema.yml +2 -2
  185. simtools/schemas/model_parameters/pedestal_events.schema.yml +7 -3
  186. simtools/schemas/model_parameters/photon_delay.schema.yml +2 -2
  187. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +2 -2
  188. simtools/schemas/model_parameters/pm_average_gain.schema.yml +2 -2
  189. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +2 -2
  190. simtools/schemas/model_parameters/pm_gain_index.schema.yml +2 -2
  191. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +2 -2
  192. simtools/schemas/model_parameters/pm_transit_time.schema.yml +2 -2
  193. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +2 -2
  194. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +2 -2
  195. simtools/schemas/model_parameters/qe_variation.schema.yml +2 -2
  196. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +2 -2
  197. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
  198. simtools/schemas/model_parameters/random_generator.schema.yml +2 -2
  199. simtools/schemas/model_parameters/random_mono_probability.schema.yml +2 -2
  200. simtools/schemas/model_parameters/sampled_output.schema.yml +2 -2
  201. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +2 -2
  202. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +2 -2
  203. simtools/schemas/model_parameters/tailcut_scale.schema.yml +2 -2
  204. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +2 -2
  205. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +2 -2
  206. simtools/schemas/model_parameters/telescope_random_error.schema.yml +2 -2
  207. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +2 -2
  208. simtools/schemas/model_parameters/telescope_transmission.schema.yml +2 -2
  209. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +2 -2
  210. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +2 -2
  211. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +2 -2
  212. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +2 -2
  213. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +2 -2
  214. simtools/schemas/model_parameters/transit_time_error.schema.yml +2 -2
  215. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +2 -2
  216. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +2 -2
  217. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +2 -2
  218. simtools/schemas/model_parameters/trigger_pixels.schema.yml +2 -2
  219. simtools/schemas/production_configuration_metrics.schema.yml +2 -2
  220. simtools/simtel/simtel_config_reader.py +21 -17
  221. simtools/simtel/simtel_config_writer.py +258 -66
  222. simtools/simtel/simtel_io_event_reader.py +301 -194
  223. simtools/simtel/simtel_io_event_writer.py +207 -227
  224. simtools/simtel/simtel_io_file_info.py +62 -0
  225. simtools/simtel/simtel_io_histogram.py +10 -14
  226. simtools/simtel/simtel_io_histograms.py +2 -2
  227. simtools/simtel/simtel_io_metadata.py +106 -0
  228. simtools/simtel/simulator_array.py +28 -14
  229. simtools/simtel/simulator_camera_efficiency.py +12 -6
  230. simtools/simtel/simulator_light_emission.py +85 -45
  231. simtools/simtel/simulator_ray_tracing.py +16 -6
  232. simtools/simulator.py +286 -89
  233. simtools/testing/configuration.py +5 -0
  234. simtools/testing/helpers.py +18 -0
  235. simtools/testing/sim_telarray_metadata.py +212 -0
  236. simtools/testing/validate_output.py +16 -6
  237. simtools/utils/general.py +18 -27
  238. simtools/utils/names.py +32 -10
  239. simtools/visualization/plot_array_layout.py +242 -0
  240. simtools/visualization/plot_pixels.py +681 -0
  241. simtools/visualization/visualize.py +5 -221
  242. simtools/applications/production_generate_simulation_config.py +0 -162
  243. simtools/applications/production_scale_events.py +0 -185
  244. simtools/layout/ctao_array_layouts.py +0 -172
  245. simtools/production_configuration/event_scaler.py +0 -120
  246. simtools/production_configuration/generate_simulation_config.py +0 -158
  247. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/licenses/LICENSE +0 -0
  248. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/top_level.txt +0 -0
@@ -5,38 +5,167 @@ import logging
5
5
  import astropy.units as u
6
6
  import matplotlib.pyplot as plt
7
7
  import numpy as np
8
+ from matplotlib.colors import LogNorm
8
9
 
9
10
  from simtools.simtel.simtel_io_event_reader import SimtelIOEventDataReader
10
11
 
11
12
 
12
13
  class LimitCalculator:
13
14
  """
14
- Compute thresholds for CORSIKA configuration for energy, radial distance, and viewcone.
15
+ Compute limits for CORSIKA configuration for energy, radial distance, and viewcone.
15
16
 
16
17
  Event data is read from the reduced MC event data file.
17
18
 
18
19
  Parameters
19
20
  ----------
20
21
  event_data_file : str
21
- Path to the HDF5 file containing the event data.
22
+ Path to the event-data file.
23
+ array_name : str, optional
24
+ Name of the telescope array configuration (default is None).
22
25
  telescope_list : list, optional
23
26
  List of telescope IDs to filter the events (default is None).
24
27
  """
25
28
 
26
- def __init__(self, event_data_file, telescope_list=None):
29
+ def __init__(self, event_data_file, array_name=None, telescope_list=None):
27
30
  """Initialize the LimitCalculator with the given event data file."""
28
31
  self._logger = logging.getLogger(__name__)
29
32
  self.event_data_file = event_data_file
33
+ self.array_name = array_name
30
34
  self.telescope_list = telescope_list
31
35
 
36
+ self.limits = None
37
+ self.histograms = {}
38
+ self.file_info = {}
39
+
32
40
  self.reader = SimtelIOEventDataReader(event_data_file, telescope_list=telescope_list)
33
- self.event_data = self.reader.triggered_shower_data
34
- self.triggered_data = self.reader.triggered_data
41
+
42
+ def _prepare_limit_data(self, file_info_table):
43
+ """
44
+ Prepare result data structure for limit calculation.
45
+
46
+ Contains both the point in parameter space and the limits derived for that point.
47
+
48
+ Parameters
49
+ ----------
50
+ file_info_table : astropy.table.Table
51
+ Table containing file information.
52
+
53
+ Returns
54
+ -------
55
+ dict
56
+ Dictionary containing limits (not yet calculated) and parameter space information.
57
+ """
58
+ self.file_info = self.reader.get_reduced_simulation_file_info(file_info_table)
59
+ return {
60
+ "primary_particle": self.file_info["primary_particle"],
61
+ "zenith": self.file_info["zenith"],
62
+ "azimuth": self.file_info["azimuth"],
63
+ "nsb_level": self.file_info["nsb_level"],
64
+ "array_name": self.array_name,
65
+ "telescope_ids": self.telescope_list,
66
+ "lower_energy_limit": None,
67
+ "upper_radius_limit": None,
68
+ "viewcone_radius": None,
69
+ }
70
+
71
+ def compute_limits(self, loss_fraction):
72
+ """
73
+ Compute the limits for energy, radial distance, and viewcone.
74
+
75
+ Parameters
76
+ ----------
77
+ loss_fraction : float
78
+ Fraction of events to be lost.
79
+
80
+ Returns
81
+ -------
82
+ dict
83
+ Dictionary containing the computed limits.
84
+ """
85
+ self._fill_histograms()
86
+
87
+ self.limits["lower_energy_limit"] = self.compute_lower_energy_limit(loss_fraction)
88
+ self.limits["upper_radius_limit"] = self.compute_upper_radius_limit(loss_fraction)
89
+ self.limits["viewcone_radius"] = self.compute_viewcone(loss_fraction)
90
+
91
+ return self.limits
92
+
93
+ def _fill_histogram_and_bin_edges(self, name, data, bins, hist1d=True):
94
+ """
95
+ Fill histogram and bin edges and it both to histogram dictionary.
96
+
97
+ Adds histogram to existing histogram if it exists, otherwise initializes it.
98
+
99
+ """
100
+ if name in self.histograms:
101
+ if hist1d:
102
+ bins = self.histograms[f"{name}_bin_edges"]
103
+ hist, _ = np.histogram(data, bins=bins)
104
+ self.histograms[name] += hist
105
+ else:
106
+ x_bins = self.histograms[f"{name}_bin_x_edges"]
107
+ y_bins = self.histograms[f"{name}_bin_y_edges"]
108
+ hist, _, _ = np.histogram2d(data[0], data[1], bins=[x_bins, y_bins])
109
+ self.histograms[name] += hist
110
+ else:
111
+ if hist1d:
112
+ hist, bin_edges = np.histogram(data, bins=bins)
113
+ self.histograms[name] = hist
114
+ self.histograms[f"{name}_bin_edges"] = bin_edges
115
+ else:
116
+ hist, x_edges, y_edges = np.histogram2d(data[0], data[1], bins=bins)
117
+ self.histograms[name] = hist
118
+ self.histograms[f"{name}_bin_x_edges"] = x_edges
119
+ self.histograms[f"{name}_bin_y_edges"] = y_edges
120
+
121
+ def _fill_histograms(self):
122
+ """
123
+ Fill histograms with event data.
124
+
125
+ Involves looping over all event data, and therefore is the slowest part of the
126
+ limit calculation. Adds the histograms to the histogram dictionary.
127
+ """
128
+ for data_set in self.reader.data_sets:
129
+ self._logger.info(f"Reading event data from {self.event_data_file} for {data_set}")
130
+ file_info, _, event_data, triggered_data = self.reader.read_event_data(
131
+ self.event_data_file, table_name_map=data_set
132
+ )
133
+ self.limits = self.limits if self.limits else self._prepare_limit_data(file_info)
134
+
135
+ self._fill_histogram_and_bin_edges(
136
+ "energy", event_data.simulated_energy, self.energy_bins
137
+ )
138
+ self._fill_histogram_and_bin_edges(
139
+ "core_distance", event_data.core_distance_shower, self.core_distance_bins
140
+ )
141
+ self._fill_histogram_and_bin_edges(
142
+ "angular_distance", triggered_data.angular_distance, self.view_cone_bins
143
+ )
144
+
145
+ xy_bins = np.linspace(
146
+ -1.0 * self.core_distance_bins.max(),
147
+ self.core_distance_bins.max(),
148
+ len(self.core_distance_bins),
149
+ )
150
+ self._fill_histogram_and_bin_edges(
151
+ "shower_cores",
152
+ (event_data.x_core_shower, event_data.y_core_shower),
153
+ [xy_bins, xy_bins],
154
+ hist1d=False,
155
+ )
156
+ self._fill_histogram_and_bin_edges(
157
+ "core_vs_energy",
158
+ (event_data.core_distance_shower, event_data.simulated_energy),
159
+ [self.core_distance_bins, self.energy_bins],
160
+ hist1d=False,
161
+ )
35
162
 
36
163
  def _compute_limits(self, hist, bin_edges, loss_fraction, limit_type="lower"):
37
164
  """
38
165
  Compute the limits based on the loss fraction.
39
166
 
167
+ Add or subtract one bin to be on the safe side of the limit.
168
+
40
169
  Parameters
41
170
  ----------
42
171
  hist : np.ndarray
@@ -53,20 +182,25 @@ class LimitCalculator:
53
182
  float
54
183
  Bin edge value corresponding to the threshold.
55
184
  """
56
- cumulative_sum = np.cumsum(hist) if limit_type == "upper" else np.cumsum(hist[::-1])
57
185
  total_events = np.sum(hist)
58
186
  threshold = (1 - loss_fraction) * total_events
59
- bin_index = np.searchsorted(cumulative_sum, threshold)
60
-
61
- return bin_edges[bin_index] if limit_type == "upper" else bin_edges[-bin_index]
187
+ if limit_type == "upper":
188
+ cum = np.cumsum(hist)
189
+ idx = np.searchsorted(cum, threshold) + 1
190
+ return bin_edges[min(idx, len(bin_edges) - 1)]
191
+ if limit_type == "lower":
192
+ cum = np.cumsum(hist[::-1])
193
+ idx = np.searchsorted(cum, threshold) + 1
194
+ return bin_edges[max(len(bin_edges) - 1 - idx, 0)]
195
+ raise ValueError("limit_type must be 'lower' or 'upper'")
62
196
 
63
197
  @property
64
198
  def energy_bins(self):
65
199
  """Return bins for the energy histogram."""
66
200
  return np.logspace(
67
- np.log10(self.event_data.simulated_energy.min()),
68
- np.log10(self.event_data.simulated_energy.max()),
69
- 1000,
201
+ np.log10(self.file_info.get("energy_min", 1.0e-3 * u.TeV).to("TeV").value),
202
+ np.log10(self.file_info.get("energy_max", 1.0e3 * u.TeV).to("TeV").value),
203
+ 100,
70
204
  )
71
205
 
72
206
  def compute_lower_energy_limit(self, loss_fraction):
@@ -83,21 +217,23 @@ class LimitCalculator:
83
217
  astropy.units.Quantity
84
218
  Lower energy limit.
85
219
  """
86
- hist, _ = np.histogram(self.event_data.simulated_energy, bins=self.energy_bins)
87
220
  return (
88
- self._compute_limits(hist, self.energy_bins, loss_fraction, limit_type="lower") * u.TeV
221
+ self._compute_limits(
222
+ self.histograms.get("energy"), self.energy_bins, loss_fraction, limit_type="lower"
223
+ )
224
+ * u.TeV
89
225
  )
90
226
 
91
227
  @property
92
228
  def core_distance_bins(self):
93
229
  """Return bins for the core distance histogram."""
94
230
  return np.linspace(
95
- self.event_data.core_distance_shower.min(),
96
- self.event_data.core_distance_shower.max(),
97
- 1000,
231
+ self.file_info.get("core_scatter_min", 0.0 * u.m).to("m").value,
232
+ self.file_info.get("core_scatter_max", 1.0e5 * u.m).to("m").value,
233
+ 100,
98
234
  )
99
235
 
100
- def compute_upper_radial_distance(self, loss_fraction):
236
+ def compute_upper_radius_limit(self, loss_fraction):
101
237
  """
102
238
  Compute the upper radial distance based on the event loss fraction.
103
239
 
@@ -111,9 +247,13 @@ class LimitCalculator:
111
247
  astropy.units.Quantity
112
248
  Upper radial distance in m.
113
249
  """
114
- hist, _ = np.histogram(self.event_data.core_distance_shower, bins=self.core_distance_bins)
115
250
  return (
116
- self._compute_limits(hist, self.core_distance_bins, loss_fraction, limit_type="upper")
251
+ self._compute_limits(
252
+ self.histograms.get("core_distance"),
253
+ self.core_distance_bins,
254
+ loss_fraction,
255
+ limit_type="upper",
256
+ )
117
257
  * u.m
118
258
  )
119
259
 
@@ -121,9 +261,9 @@ class LimitCalculator:
121
261
  def view_cone_bins(self):
122
262
  """Return bins for the viewcone histogram."""
123
263
  return np.linspace(
124
- self.triggered_data.angular_distance.min(),
125
- self.triggered_data.angular_distance.max(),
126
- 1000,
264
+ self.file_info.get("viewcone_min", 0.0 * u.deg).to("deg").value,
265
+ self.file_info.get("viewcone_max", 20.0 * u.deg).to("deg").value,
266
+ 100,
127
267
  )
128
268
 
129
269
  def compute_viewcone(self, loss_fraction):
@@ -144,33 +284,34 @@ class LimitCalculator:
144
284
  astropy.units.Quantity
145
285
  Viewcone radius in degrees.
146
286
  """
147
- hist, _ = np.histogram(self.triggered_data.angular_distance, bins=self.view_cone_bins)
148
287
  return (
149
- self._compute_limits(hist, self.view_cone_bins, loss_fraction, limit_type="upper")
288
+ self._compute_limits(
289
+ self.histograms.get("angular_distance"),
290
+ self.view_cone_bins,
291
+ loss_fraction,
292
+ limit_type="upper",
293
+ )
150
294
  * u.deg
151
295
  )
152
296
 
153
- def plot_data(self, lower_energy_limit, upper_radial_distance, viewcone, output_path=None):
297
+ def plot_data(self, output_path=None):
154
298
  """
155
- Plot the core distances and energies of triggered events.
299
+ Histogram plotting.
156
300
 
157
301
  Parameters
158
302
  ----------
159
- lower_energy_limit: astropy.units.Quantity
160
- Lower energy limit to display on plots.
161
- upper_radial_distance: astropy.units.Quantity
162
- Upper radial distance limit to display on plots.
163
- viewcone: astropy.units.Quantity
164
- Viewcone radius to display on plots.
165
303
  output_path: Path or str, optional
166
304
  Directory to save plots. If None, plots will be displayed.
167
305
  """
306
+ self._logger.info(f"Plotting histograms written to {output_path}")
168
307
  event_counts = "Event Count"
169
308
  plots = {
170
309
  "core_vs_energy": {
171
- "x_data": self.event_data.core_distance_shower,
172
- "y_data": self.event_data.simulated_energy,
173
- "bins": [self.core_distance_bins, self.energy_bins],
310
+ "data": self.histograms.get("core_vs_energy"),
311
+ "bins": [
312
+ self.histograms.get("core_vs_energy_bin_x_edges"),
313
+ self.histograms.get("core_vs_energy_bin_y_edges"),
314
+ ],
174
315
  "plot_type": "histogram2d",
175
316
  "plot_params": {"norm": "log", "cmap": "viridis"},
176
317
  "labels": {
@@ -178,76 +319,89 @@ class LimitCalculator:
178
319
  "y": "Energy [TeV]",
179
320
  "title": "Triggered events: core distance vs energy",
180
321
  },
322
+ "lines": {
323
+ "x": self.limits["upper_radius_limit"].value,
324
+ "y": self.limits["lower_energy_limit"].value,
325
+ },
181
326
  "scales": {"y": "log"},
182
327
  "colorbar_label": event_counts,
183
- "filename": "core_vs_energy_distribution.png",
328
+ "filename": "core_vs_energy_distribution",
184
329
  },
185
330
  "energy_distribution": {
186
- "x_data": self.event_data.simulated_energy,
187
- "bins": np.logspace(-3, 0.0, 100),
331
+ "data": self.histograms.get("energy"),
332
+ "bins": self.histograms.get("energy_bin_edges"),
188
333
  "plot_type": "histogram",
189
- "plot_params": {"histtype": "step", "color": "k", "lw": 2},
334
+ "plot_params": {"color": "g", "edgecolor": "g", "lw": 1},
190
335
  "labels": {
191
336
  "x": "Energy [TeV]",
192
337
  "y": event_counts,
193
338
  "title": "Triggered events: energy distribution",
194
339
  },
195
340
  "scales": {"x": "log", "y": "log"},
196
- "lines": {"x": lower_energy_limit.value},
197
- "filename": "energy_distribution.png",
341
+ "lines": {"x": self.limits["lower_energy_limit"].value},
342
+ "filename": "energy_distribution",
198
343
  },
199
344
  "core_distance": {
200
- "x_data": self.event_data.core_distance_shower,
201
- "bins": self.core_distance_bins,
345
+ "data": self.histograms.get("core_distance"),
346
+ "bins": self.histograms.get("core_distance_bin_edges"),
202
347
  "plot_type": "histogram",
203
- "plot_params": {"histtype": "step", "color": "k", "lw": 2},
348
+ "plot_params": {"color": "g", "edgecolor": "g", "lw": 1},
204
349
  "labels": {
205
350
  "x": "Core Distance [m]",
206
351
  "y": event_counts,
207
352
  "title": "Triggered events: core distance distribution",
208
353
  },
209
- "lines": {"x": upper_radial_distance.value},
210
- "filename": "core_distance_distribution.png",
354
+ "lines": {"x": self.limits["upper_radius_limit"].value},
355
+ "filename": "core_distance_distribution",
211
356
  },
212
357
  "core_xy": {
213
- "x_data": self.event_data.x_core_shower,
214
- "y_data": self.event_data.y_core_shower,
215
- "bins": 100,
358
+ "data": self.histograms.get("shower_cores"),
359
+ "bins": [
360
+ self.histograms.get("shower_cores_bin_x_edges"),
361
+ self.histograms.get("shower_cores_bin_y_edges"),
362
+ ],
216
363
  "plot_type": "histogram2d",
217
- "plot_params": {"norm": "log", "cmap": "viridis"},
364
+ "plot_params": {"norm": "log", "cmap": "viridis", "aspect": "equal"},
218
365
  "labels": {
219
366
  "x": "Core X [m]",
220
367
  "y": "Core Y [m]",
221
368
  "title": "Triggered events: core x vs core y",
222
369
  },
223
370
  "colorbar_label": event_counts,
224
- "lines": {"x": upper_radial_distance.value, "y": upper_radial_distance.value},
225
- "filename": "core_xy_distribution.png",
371
+ "lines": {
372
+ "r": self.limits["upper_radius_limit"].value,
373
+ },
374
+ "filename": "core_xy_distribution",
226
375
  },
227
- "view-cone": {
228
- "x_data": self.triggered_data.angular_distance,
229
- "bins": self.view_cone_bins,
376
+ "angular_distance": {
377
+ "data": self.histograms.get("angular_distance"),
378
+ "bins": self.histograms.get("angular_distance_bin_edges"),
230
379
  "plot_type": "histogram",
231
- "plot_params": {"histtype": "step", "color": "k", "lw": 2},
380
+ "plot_params": {"color": "g", "edgecolor": "g", "lw": 1},
232
381
  "labels": {
233
382
  "x": "Distance to pointing direction [deg]",
234
383
  "y": event_counts,
235
- "title": "Triggered events: viewcone distribution",
384
+ "title": "Triggered events: angular distance distribution",
236
385
  },
237
- "lines": {"x": viewcone.value},
238
- "filename": "viewcone_distribution.png",
386
+ "lines": {"x": self.limits["viewcone_radius"].value},
387
+ "filename": "angular_distance_distribution",
239
388
  },
240
389
  }
241
390
 
242
391
  for _, plot_args in plots.items():
243
392
  filename = plot_args.pop("filename")
393
+ if self.array_name:
394
+ if plot_args.get("labels", {}).get("title"):
395
+ plot_args["labels"]["title"] += f" ({self.array_name} array)"
396
+ filename = f"{filename}_{self.array_name}.png"
397
+ else:
398
+ filename = f"{filename}.png"
244
399
  output_file = output_path / filename if output_path else None
245
400
  self._create_plot(**plot_args, output_file=output_file)
246
401
 
247
402
  def _create_plot(
248
403
  self,
249
- x_data,
250
- y_data=None,
404
+ data,
251
405
  bins=None,
252
406
  plot_type="histogram",
253
407
  plot_params=None,
@@ -257,65 +411,39 @@ class LimitCalculator:
257
411
  output_file=None,
258
412
  lines=None,
259
413
  ):
260
- """
261
- Create and save a plot with the given parameters.
262
-
263
- Parameters
264
- ----------
265
- x_data : array-like
266
- Data for the x-axis or primary data for histograms.
267
- y_data : array-like, optional
268
- Data for the y-axis in scatter or 2D histograms.
269
- bins : int, array-like, or list, optional
270
- Bins specification for histograms.
271
- plot_type : str, optional
272
- Type of plot: 'histogram', 'histogram2d', or 'scatter'.
273
- plot_params : dict, optional
274
- Additional parameters to pass to the plotting function.
275
- labels : dict, optional
276
- Dictionary containing 'x', 'y', and 'title' labels.
277
- scales : dict, optional
278
- Dictionary containing 'x' and 'y' scale types ('log' or 'linear').
279
- colorbar_label : str, optional
280
- Label for the colorbar in 2D histograms.
281
- output_file : Path, optional
282
- File path to save the plot. If not provided, the plot will be displayed.
283
- lines : dict, optional
284
- Dictionary containing 'x' and 'y' values for reference lines.
285
-
286
- Returns
287
- -------
288
- matplotlib.figure.Figure
289
- The created figure object.
290
- """
291
- fig = plt.figure(figsize=(8, 6))
414
+ """Create and save a plot with the given parameters."""
292
415
  plot_params = plot_params or {}
293
416
  labels = labels or {}
294
417
  scales = scales or {}
295
418
  lines = lines or {}
296
419
 
420
+ fig, ax = plt.subplots(figsize=(8, 6))
421
+
297
422
  if plot_type == "histogram":
298
- plt.hist(x_data, bins=bins, **plot_params)
423
+ plt.bar(bins[:-1], data, width=np.diff(bins), **plot_params)
299
424
  elif plot_type == "histogram2d":
300
- plt.hist2d(x_data, y_data, bins=bins, **plot_params)
301
- if colorbar_label:
302
- plt.colorbar(label=colorbar_label)
303
- elif plot_type == "scatter":
304
- plt.scatter(x_data, y_data, **plot_params)
425
+ pcm = plt.pcolormesh(
426
+ bins[0], bins[1], data.T, norm=LogNorm(vmin=1, vmax=data.max()), cmap="viridis"
427
+ )
428
+ plt.colorbar(pcm, label=colorbar_label)
305
429
 
306
430
  if "x" in lines:
307
- plt.axvline(lines["x"], color="r", linestyle="--")
431
+ plt.axvline(lines["x"], color="r", linestyle="--", linewidth=0.5)
308
432
  if "y" in lines:
309
- plt.axhline(lines["y"], color="r", linestyle="--")
310
-
311
- plt.xlabel(labels.get("x", ""))
312
- plt.ylabel(labels.get("y", ""))
313
- plt.title(labels.get("title", ""))
314
-
315
- if "x" in scales:
316
- plt.xscale(scales["x"])
317
- if "y" in scales:
318
- plt.yscale(scales["y"])
433
+ plt.axhline(lines["y"], color="r", linestyle="--", linewidth=0.5)
434
+ if "r" in lines:
435
+ circle = plt.Circle(
436
+ (0, 0), lines["r"], color="r", fill=False, linestyle="--", linewidth=0.5
437
+ )
438
+ plt.gca().add_artist(circle)
439
+
440
+ ax.set(
441
+ xlabel=labels.get("x", ""),
442
+ ylabel=labels.get("y", ""),
443
+ title=labels.get("title", ""),
444
+ xscale=scales.get("x", "linear"),
445
+ yscale=scales.get("y", "linear"),
446
+ )
319
447
 
320
448
  if output_file:
321
449
  self._logger.info(f"Saving plot to {output_file}")