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
@@ -1,37 +1,88 @@
1
- """Interpolates between instances of StatisticalErrorEvaluator using EventScaler."""
1
+ """Handle interpolation between multiple StatisticalUncertaintyEvaluator instances."""
2
+
3
+ import logging
2
4
 
3
5
  import astropy.units as u
4
6
  import numpy as np
5
7
  from scipy.interpolate import griddata
6
8
 
7
- from simtools.production_configuration.event_scaler import EventScaler
9
+ from simtools.production_configuration.derive_production_statistics import (
10
+ ProductionStatisticsDerivator,
11
+ )
8
12
 
9
13
  __all__ = ["InterpolationHandler"]
10
14
 
11
15
 
12
16
  class InterpolationHandler:
13
- """Handle interpolation between multiple StatisticalErrorEvaluator instances."""
17
+ """
18
+ Calculate the required events for production via interpolation from a grid.
19
+
20
+ This class provides methods to interpolate production statistics across a grid of
21
+ parameter values (azimuth, zenith, NSB, offset) and energy.
22
+ """
23
+
24
+ def __init__(self, evaluators, metrics: dict, grid_points_production: list):
25
+ """
26
+ Initialize the InterpolationHandler.
14
27
 
15
- def __init__(self, evaluators, metrics: dict):
28
+ Parameters
29
+ ----------
30
+ evaluators : list
31
+ List of StatisticalUncertaintyEvaluator instances.
32
+ metrics : dict
33
+ Dictionary of metrics to use for production statistics.
34
+ grid_points_production : list
35
+ List of grid points for interpolation, each being a dictionary with keys
36
+ 'azimuth', 'zenith_angle', 'nsb', 'offset' etc.
37
+ """
38
+ self._logger = logging.getLogger(__name__)
16
39
  self.evaluators = evaluators
17
40
  self.metrics = metrics
18
- self.event_scalers = [EventScaler(e, self.metrics) for e in self.evaluators]
41
+ self.grid_points_production = grid_points_production
19
42
 
43
+ self._initialize_derivators()
44
+ self._extract_grid_properties()
45
+
46
+ self.data, self.grid_points = self._build_data_array()
47
+ self.interpolated_production_statistics = None
48
+ self.interpolated_production_statistics_with_energy = None
49
+ self._non_flat_mask = None
50
+
51
+ def _initialize_derivators(self):
52
+ """Initialize production statistics derivators for all evaluators."""
53
+ self.derive_production_statistics = [
54
+ ProductionStatisticsDerivator(e, self.metrics) for e in self.evaluators
55
+ ]
56
+
57
+ self.production_statistics = [
58
+ derivator.derive_statistics(return_sum=False)
59
+ for derivator in self.derive_production_statistics
60
+ ]
61
+ self.production_statistics_sum = [
62
+ derivator.derive_statistics(return_sum=True)
63
+ for derivator in self.derive_production_statistics
64
+ ]
65
+
66
+ def _extract_grid_properties(self):
67
+ """Extract grid properties from evaluators."""
20
68
  self.azimuths = [e.grid_point[1].to(u.deg).value for e in self.evaluators]
21
69
  self.zeniths = [e.grid_point[2].to(u.deg).value for e in self.evaluators]
22
70
  self.nsbs = [e.grid_point[3] for e in self.evaluators]
23
71
  self.offsets = [e.grid_point[4].to(u.deg).value for e in self.evaluators]
24
-
25
72
  self.energy_grids = [
26
73
  (e.data["bin_edges_low"][:-1] + e.data["bin_edges_high"][:-1]) / 2
27
74
  for e in self.evaluators
28
75
  ]
29
- self.scaled_events = [
30
- scaler.scale_events(return_sum=False) for scaler in self.event_scalers
31
- ]
32
76
  self.energy_thresholds = np.array([e.energy_threshold for e in self.evaluators])
33
77
 
34
- self.data, self.grid_points = self._build_data_array()
78
+ # Check if energy grids are consistent
79
+ if self.energy_grids and not all(
80
+ np.array_equal(self.energy_grids[0], grid) for grid in self.energy_grids
81
+ ):
82
+ self._logger.warning(
83
+ "Energy grids are not identical across evaluators. "
84
+ "Using the first evaluator's energy grid for interpolation."
85
+ )
35
86
 
36
87
  def _build_data_array(self):
37
88
  """
@@ -44,148 +95,304 @@ class InterpolationHandler:
44
95
  np.ndarray
45
96
  The corresponding grid points.
46
97
  """
47
- # Flatten the energy grid and other dimensions into a combined array
98
+ if not self.evaluators:
99
+ return np.array([]), np.array([])
100
+
48
101
  flat_data_list = []
49
102
  flat_grid_points = []
50
103
 
51
- for e, energy_grid, scaled_events in zip(
52
- self.evaluators, self.energy_grids, self.scaled_events
104
+ for i, (energy_grid, production_statistics) in enumerate(
105
+ zip(self.energy_grids, self.production_statistics)
53
106
  ):
54
- az = np.full(len(energy_grid), e.grid_point[1].to(u.deg).value)
55
- zen = np.full(len(energy_grid), e.grid_point[2].to(u.deg).value)
56
- nsb = np.full(len(energy_grid), e.grid_point[3])
57
- offset = np.full(len(energy_grid), e.grid_point[4].to(u.deg).value)
107
+ az = self.azimuths[i]
108
+ zen = self.zeniths[i]
109
+ nsb = self.nsbs[i]
110
+ offset = self.offsets[i]
111
+
112
+ az_array = np.full(len(energy_grid), az)
113
+ zen_array = np.full(len(energy_grid), zen)
114
+ nsb_array = np.full(len(energy_grid), nsb)
115
+ offset_array = np.full(len(energy_grid), offset)
116
+
117
+ grid_points = np.column_stack(
118
+ [energy_grid.to(u.TeV).value, az_array, zen_array, nsb_array, offset_array]
119
+ )
58
120
 
59
- # Combine grid points and data
60
- grid_points = np.column_stack([energy_grid.to(u.TeV).value, az, zen, nsb, offset])
61
121
  flat_grid_points.append(grid_points)
62
- flat_data_list.append(scaled_events)
122
+ flat_data_list.append(production_statistics)
63
123
 
64
- # Flatten the list and convert to numpy arrays
65
124
  flat_grid_points = np.vstack(flat_grid_points)
66
125
  flat_data = np.hstack(flat_data_list)
67
126
 
68
- # Sort the grid points and corresponding data by energy
69
127
  sorted_indices = np.argsort(flat_grid_points[:, 0])
70
128
  sorted_grid_points = flat_grid_points[sorted_indices]
71
129
  sorted_data = flat_data[sorted_indices]
72
130
 
73
131
  return sorted_data, sorted_grid_points
74
132
 
75
- def _remove_flat_dimensions(self, grid_points):
76
- """Identify and remove flat dimensions (dimensions with no variance)."""
133
+ def _remove_flat_dimensions(self, grid_points, threshold=1e-6):
134
+ """
135
+ Identify and remove flat dimensions (dimensions with no variance).
136
+
137
+ Parameters
138
+ ----------
139
+ grid_points : np.ndarray
140
+ Grid points to analyze.
141
+ threshold : float, optional
142
+ Threshold for determining flatness, by default 1e-6
143
+
144
+ Returns
145
+ -------
146
+ tuple
147
+ (reduced_grid_points, non_flat_mask)
148
+ """
149
+ if grid_points.size == 0:
150
+ return grid_points, np.array([], dtype=bool)
151
+
77
152
  variance = np.var(grid_points, axis=0)
78
- non_flat_mask = variance > 1e-6 # Threshold for determining flatness
153
+ non_flat_mask = variance > threshold
154
+
155
+ if not np.any(non_flat_mask):
156
+ self._logger.warning(
157
+ "All dimensions are flat. Keeping all dimensions for interpolation."
158
+ )
159
+ return grid_points, np.ones_like(variance, dtype=bool)
160
+
79
161
  reduced_grid_points = grid_points[:, non_flat_mask]
80
162
  return reduced_grid_points, non_flat_mask
81
163
 
82
- def interpolate(self, query_points: np.ndarray) -> np.ndarray:
164
+ def build_grid_points_no_energy(self):
165
+ """
166
+ Build grid points without energy dimension.
167
+
168
+ Returns
169
+ -------
170
+ tuple
171
+ (production_statistics, grid_points_no_energy)
172
+ """
173
+ if not self.evaluators:
174
+ self._logger.error("No evaluators available for grid point building.")
175
+ return np.array([]), np.array([])
176
+
177
+ flat_data_list = []
178
+ flat_grid_points = []
179
+
180
+ for i, production_statistics_sum in enumerate(self.production_statistics_sum):
181
+ az = self.azimuths[i]
182
+ zen = self.zeniths[i]
183
+ nsb = self.nsbs[i]
184
+ offset = self.offsets[i]
185
+
186
+ flat_data_list.append(float(production_statistics_sum.value))
187
+
188
+ grid_point = np.array([[az, zen, nsb, offset]])
189
+ flat_grid_points.append(grid_point)
190
+
191
+ flat_grid_points = np.vstack(flat_grid_points)
192
+ return flat_data_list, flat_grid_points
193
+
194
+ def _prepare_energy_independent_data(self):
195
+ """
196
+ Prepare data for energy-independent interpolation.
197
+
198
+ Returns
199
+ -------
200
+ tuple
201
+ (production_statistic, grid_points_no_energy)
83
202
  """
84
- Interpolate the number of simulated events given query points.
203
+ production_statistic, grid_points_no_energy = self.build_grid_points_no_energy()
204
+ production_statistic = np.array(production_statistic, dtype=float)
205
+ grid_points_no_energy, non_flat_mask = self._remove_flat_dimensions(grid_points_no_energy)
206
+
207
+ self._non_flat_mask = non_flat_mask # Store for later use
208
+ return production_statistic, grid_points_no_energy
209
+
210
+ def _prepare_production_grid_points(self):
211
+ """
212
+ Convert grid_points_production to a format suitable for interpolation.
213
+
214
+ Returns
215
+ -------
216
+ np.ndarray
217
+ Reduced production grid points.
218
+ """
219
+ production_grid_points = []
220
+
221
+ for point in self.grid_points_production:
222
+ production_grid_points.append(
223
+ [
224
+ point["azimuth"]["value"],
225
+ point["zenith_angle"]["value"],
226
+ point["nsb"]["value"],
227
+ point["offset"]["value"],
228
+ ]
229
+ )
230
+
231
+ production_grid_points = np.array(production_grid_points)
232
+
233
+ return production_grid_points[:, self._non_flat_mask]
234
+
235
+ def _perform_interpolation(self, grid_points, values, query_points, method="linear"):
236
+ """
237
+ Perform interpolation using griddata.
85
238
 
86
239
  Parameters
87
240
  ----------
241
+ grid_points : np.ndarray
242
+ Grid points for interpolation.
243
+ values : np.ndarray
244
+ Values at the grid points.
88
245
  query_points : np.ndarray
89
- Array of query points with shape (n, 5), where n is the number of points,
90
- and 5 represents (energy, azimuth, zenith, nsb, offset).
246
+ Query points for interpolation.
247
+ method : str, optional
248
+ Interpolation method, by default "linear".
91
249
 
92
250
  Returns
93
251
  -------
94
252
  np.ndarray
95
- Interpolated values at the query points.
253
+ Interpolated values.
96
254
  """
97
- reduced_grid_points, non_flat_mask = self._remove_flat_dimensions(self.grid_points)
98
- reduced_query_points = query_points[:, non_flat_mask]
255
+ self._logger.debug(f"Grid points shape: {grid_points.shape}")
256
+ self._logger.debug(f"Values shape: {values.shape}")
257
+ self._logger.debug(f"Query points shape: {query_points.shape}")
99
258
 
100
- # Interpolate using the reduced dimensions
101
259
  return griddata(
102
- reduced_grid_points,
103
- self.data,
104
- reduced_query_points,
105
- method="linear",
260
+ grid_points,
261
+ values,
262
+ query_points,
263
+ method=method,
106
264
  fill_value=np.nan,
107
265
  rescale=True,
108
266
  )
109
267
 
110
- def interpolate_energy_threshold(self, query_point: np.ndarray) -> float:
268
+ def _perform_interpolation_with_energy(self):
111
269
  """
112
- Interpolate the energy threshold for a given grid point.
270
+ Perform energy-dependent interpolation.
113
271
 
114
- Parameters
115
- ----------
116
- query_point : np.ndarray
117
- Array specifying the grid point (energy, azimuth, zenith, NSB, offset).
272
+ Returns
273
+ -------
274
+ np.ndarray
275
+ Energy-dependent interpolated values.
276
+ """
277
+ # Get grid points with energy dimension
278
+ grid_points_energy = self.grid_points
279
+ grid_points_energy, _ = self._remove_flat_dimensions(grid_points_energy)
280
+
281
+ # Build energy query grid
282
+ reduced_production_grid_points = self._prepare_production_grid_points()
283
+ energy_grid = self.energy_grids[0] if self.energy_grids else []
284
+
285
+ energy_query_grid = []
286
+ for energy in energy_grid:
287
+ for grid_point in reduced_production_grid_points:
288
+ energy_query_grid.append(np.hstack([energy.to(u.TeV).value, grid_point]))
289
+
290
+ energy_query_grid = np.array(energy_query_grid)
291
+
292
+ self._logger.debug(f"Grid points with energy shape: {grid_points_energy.shape}")
293
+ self._logger.debug(f"Data shape: {self.data.shape}")
294
+ self._logger.debug(f"Energy query grid shape: {energy_query_grid.shape}")
295
+
296
+ interpolated_values = self._perform_interpolation(
297
+ grid_points_energy, self.data, energy_query_grid
298
+ )
299
+
300
+ reshaped = interpolated_values.reshape(
301
+ len(reduced_production_grid_points), len(energy_grid)
302
+ )
303
+ return np.array([reshaped])
304
+
305
+ def interpolate(self) -> np.ndarray:
306
+ """
307
+ Interpolate production statistics at the grid points specified in grid_points_production.
308
+
309
+ This method performs two types of interpolation:
310
+ 1. Energy-independent interpolation using the sum of production statistics
311
+ 2. Energy-dependent interpolation for each energy bin
118
312
 
119
313
  Returns
120
314
  -------
121
- float
122
- Interpolated energy threshold.
315
+ np.ndarray
316
+ Interpolated values at the query points.
123
317
  """
124
- flat_grid_points = []
125
- flat_energy_thresholds = []
126
-
127
- for e in self.evaluators:
128
- az = e.grid_point[1].to(u.deg).value
129
- zen = e.grid_point[2].to(u.deg).value
130
- nsb = e.grid_point[3]
131
- offset = e.grid_point[4].to(u.deg).value
132
- grid_point = np.array([az, zen, nsb, offset])
133
- flat_grid_points.append(grid_point)
134
- flat_energy_thresholds.append(e.energy_threshold)
318
+ if not self.evaluators:
319
+ self._logger.error("No evaluators available for interpolation.")
320
+ return np.array([])
135
321
 
136
- flat_grid_points = np.array(flat_grid_points)
137
- flat_energy_thresholds = np.array(flat_energy_thresholds)
322
+ # Energy-independent interpolation
323
+ production_statistic, grid_points_no_energy = self._prepare_energy_independent_data()
324
+ reduced_production_grid_points = self._prepare_production_grid_points()
138
325
 
139
- reduced_grid_points, non_flat_mask = self._remove_flat_dimensions(flat_grid_points)
140
- full_non_flat_mask = np.concatenate(([False], non_flat_mask))
141
- reduced_query_point = query_point[0][full_non_flat_mask]
326
+ self.interpolated_production_statistics = self._perform_interpolation(
327
+ grid_points_no_energy, production_statistic, reduced_production_grid_points
328
+ )
142
329
 
143
- interpolated_threshold = griddata(
144
- reduced_grid_points,
145
- flat_energy_thresholds,
146
- reduced_query_point,
147
- method="linear",
148
- fill_value=np.nan,
149
- rescale=False,
330
+ # Energy-dependent interpolation
331
+ self.interpolated_production_statistics_with_energy = (
332
+ self._perform_interpolation_with_energy()
150
333
  )
151
334
 
152
- return interpolated_threshold.item()
335
+ return self.interpolated_production_statistics
153
336
 
154
- def plot_comparison(self, evaluator):
337
+ def plot_comparison(self, grid_point_index=0):
155
338
  """
156
- Plot a comparison between the simulated, scaled, and reconstructed events.
339
+ Plot a comparison between interpolated production statistics and reconstructed events.
157
340
 
158
341
  Parameters
159
342
  ----------
160
- evaluator : StatisticalErrorEvaluator
161
- The evaluator for which to plot the comparison.
343
+ grid_point_index : int, optional
344
+ Index of the grid point to plot, by default 0
345
+
346
+ Returns
347
+ -------
348
+ matplotlib.axes.Axes
349
+ The Axes object containing the plot.
162
350
  """
163
- import matplotlib.pyplot as plt # pylint: disable=import-outside-toplevel
351
+ import matplotlib.pyplot as plt # pylint: disable=C0415
352
+
353
+ if not self.evaluators:
354
+ self._logger.error("No evaluators available for plotting.")
355
+ _, ax = plt.subplots()
356
+ ax.text(0.5, 0.5, "No data available", ha="center", va="center")
357
+ return ax
358
+
359
+ # Use first evaluator for energy bins
360
+ bin_edges_low = self.evaluators[0].data["bin_edges_low"][:-1]
361
+ bin_edges_high = self.evaluators[0].data["bin_edges_high"][:-1]
362
+ midpoints = (bin_edges_low + bin_edges_high) / 2
363
+
364
+ if (
365
+ self.interpolated_production_statistics_with_energy is None
366
+ or len(self.interpolated_production_statistics_with_energy) == 0
367
+ or len(self.interpolated_production_statistics_with_energy[0]) <= grid_point_index
368
+ ):
369
+ self._logger.warning(
370
+ f"Invalid grid point index {grid_point_index}. Using index 0 instead."
371
+ )
372
+ grid_point_index = 0
164
373
 
165
- midpoints = 0.5 * (evaluator.data["bin_edges_high"] + evaluator.data["bin_edges_low"])
374
+ _, ax = plt.subplots()
166
375
 
167
- self.grid_points = np.column_stack(
168
- [
169
- midpoints,
170
- np.full_like(midpoints, evaluator.grid_point[1]),
171
- np.full_like(midpoints, evaluator.grid_point[2]),
172
- np.full_like(midpoints, evaluator.grid_point[3]),
173
- np.full_like(midpoints, evaluator.grid_point[4]),
376
+ if (
377
+ self.interpolated_production_statistics_with_energy is not None
378
+ and len(self.interpolated_production_statistics_with_energy) > 0
379
+ ):
380
+ interpolated_stats = self.interpolated_production_statistics_with_energy[0][
381
+ grid_point_index
174
382
  ]
175
- )
176
-
177
- self.interpolate(self.grid_points)
178
-
179
- plt.plot(midpoints, evaluator.scaled_events, label="Scaled")
383
+ ax.plot(midpoints, interpolated_stats, label="Interpolated Production Statistics")
180
384
 
181
385
  reconstructed_event_histogram, _ = np.histogram(
182
- evaluator.data["event_energies_reco"], bins=evaluator.data["bin_edges_low"]
386
+ self.evaluators[0].data["event_energies_reco"],
387
+ bins=self.evaluators[0].data["bin_edges_low"],
183
388
  )
184
- plt.plot(midpoints[:-1], reconstructed_event_histogram, label="Reconstructed")
185
-
186
- plt.legend()
187
- plt.xscale("log")
188
- plt.xlabel("Energy (Midpoint of Bin Edges)")
189
- plt.ylabel("Event Count")
190
- plt.title("Comparison of Simulated, scaled, and reconstructed events")
191
- plt.show()
389
+ ax.plot(midpoints, reconstructed_event_histogram, label="Reconstructed Events")
390
+
391
+ ax.legend()
392
+ ax.set_xscale("log")
393
+ ax.set_yscale("log")
394
+ ax.set_xlabel("Energy (TeV)")
395
+ ax.set_ylabel("Event Count")
396
+ ax.set_title("Comparison of Interpolated and Reconstructed Events")
397
+
398
+ return ax
@@ -9,7 +9,7 @@ from astropy.table import QTable, Table
9
9
  import simtools.data_model.model_data_writer as writer
10
10
  import simtools.utils.general as gen
11
11
  from simtools.data_model.metadata_collector import MetadataCollector
12
- from simtools.model.telescope_model import TelescopeModel
12
+ from simtools.model.model_utils import initialize_simulation_models
13
13
  from simtools.ray_tracing.ray_tracing import RayTracing
14
14
 
15
15
 
@@ -38,7 +38,7 @@ class MirrorPanelPSF:
38
38
  self._logger.debug("Initializing MirrorPanelPSF")
39
39
 
40
40
  self.args_dict = args_dict
41
- self.telescope_model = self._define_telescope_model(label, db_config)
41
+ self.telescope_model, self.site_model = self._define_telescope_model(label, db_config)
42
42
 
43
43
  if self.args_dict["test"]:
44
44
  self.args_dict["number_of_mirrors_to_test"] = 2
@@ -63,37 +63,32 @@ class MirrorPanelPSF:
63
63
  This includes updating the configuration with mirror list and/or random focal length given
64
64
  as input.
65
65
 
66
- Attributes
67
- ----------
68
- label: str
69
- Application label.
70
- db_config:
71
- Dictionary with database configuration.
72
-
73
66
  Returns
74
67
  -------
75
- tel TelescopeModel
76
- telescope model
77
-
68
+ tel : TelescopeModel
69
+ The telescope model.
70
+ site_model : SiteModel
71
+ The site model.
78
72
  """
79
- tel = TelescopeModel(
73
+ tel_model, site_model = initialize_simulation_models(
74
+ label=label,
75
+ db_config=db_config,
80
76
  site=self.args_dict["site"],
81
77
  telescope_name=self.args_dict["telescope"],
82
78
  model_version=self.args_dict["model_version"],
83
- mongo_db_config=db_config,
84
- label=label,
85
79
  )
86
80
  if self.args_dict["mirror_list"] is not None:
87
81
  mirror_list_file = gen.find_file(
88
82
  name=self.args_dict["mirror_list"], loc=self.args_dict["model_path"]
89
83
  )
90
- tel.change_parameter("mirror_list", self.args_dict["mirror_list"])
91
- tel.export_parameter_file("mirror_list", mirror_list_file)
84
+ tel_model.change_parameter("mirror_list", self.args_dict["mirror_list"])
85
+ tel_model.export_parameter_file("mirror_list", mirror_list_file)
92
86
  if self.args_dict["random_focal_length"] is not None:
93
- tel.change_parameter("random_focal_length", str(self.args_dict["random_focal_length"]))
94
- tel.export_model_files()
87
+ tel_model.change_parameter(
88
+ "random_focal_length", str(self.args_dict["random_focal_length"])
89
+ )
95
90
 
96
- return tel
91
+ return tel_model, site_model
97
92
 
98
93
  def _get_psf_containment(self):
99
94
  """Read measured single-mirror point-spread function from file and return mean and sigma."""
@@ -229,6 +224,7 @@ class MirrorPanelPSF:
229
224
  self.telescope_model.change_parameter("mirror_reflection_random_angle", rnda)
230
225
  ray = RayTracing(
231
226
  telescope_model=self.telescope_model,
227
+ site_model=self.site_model,
232
228
  simtel_path=self.args_dict.get("simtel_path", None),
233
229
  single_mirror_mode=True,
234
230
  mirror_numbers=(
@@ -91,7 +91,7 @@ class PSFImage:
91
91
 
92
92
  def _process_simtel_file_using_rx(self, photon_file):
93
93
  """
94
- Process a simtel file with photon lists using the RX method.
94
+ Process a sim_telarray file with photon lists using the RX method.
95
95
 
96
96
  Parameters
97
97
  ----------
@@ -152,7 +152,7 @@ class PSFImage:
152
152
  self._process_simtel_line(line)
153
153
 
154
154
  if not self._is_photon_positions_ok():
155
- msg = "Problems reading Simtel file - invalid data"
155
+ msg = "Problems reading sim_telarray file - invalid data"
156
156
  self._logger.error(msg)
157
157
  raise RuntimeError(msg)
158
158
 
@@ -33,6 +33,8 @@ class RayTracing:
33
33
  ----------
34
34
  telescope_model: TelescopeModel
35
35
  telescope model
36
+ site_model: SiteModel
37
+ site model
36
38
  simtel_path: str (or Path)
37
39
  Location of sim_telarray installation.
38
40
  label: str
@@ -61,6 +63,7 @@ class RayTracing:
61
63
  def __init__(
62
64
  self,
63
65
  telescope_model,
66
+ site_model,
64
67
  simtel_path,
65
68
  label=None,
66
69
  zenith_angle=20.0 * u.deg,
@@ -77,7 +80,7 @@ class RayTracing:
77
80
  self.simtel_path = Path(simtel_path)
78
81
  self._io_handler = io_handler.IOHandler()
79
82
 
80
- self.telescope_model = telescope_model
83
+ self.telescope_model, self.site_model = telescope_model, site_model
81
84
  self.label = label if label is not None else self.telescope_model.label
82
85
 
83
86
  self.zenith_angle = zenith_angle.to("deg").value
@@ -86,11 +89,11 @@ class RayTracing:
86
89
  self.use_random_focal_length = use_random_focal_length
87
90
  self.mirrors = self._initialize_mirror_configuration(source_distance, mirror_numbers)
88
91
  self.output_directory = self._io_handler.get_output_directory(
89
- label=self.label, sub_dir="ray-tracing"
92
+ label=self.label, sub_dir="ray_tracing"
90
93
  )
91
94
  self.output_directory.joinpath("results").mkdir(parents=True, exist_ok=True)
92
95
  self._file_results = self.output_directory.joinpath("results").joinpath(
93
- self._generate_file_name(file_type="ray-tracing", suffix=".ecsv")
96
+ self._generate_file_name(file_type="ray_tracing", suffix=".ecsv")
94
97
  )
95
98
  self._psf_images = {}
96
99
  self._results = None
@@ -211,6 +214,7 @@ class RayTracing:
211
214
  simtel = SimulatorRayTracing(
212
215
  simtel_path=self.simtel_path,
213
216
  telescope_model=self.telescope_model,
217
+ site_model=self.site_model,
214
218
  test=test,
215
219
  config_data={
216
220
  "zenith_angle": self.zenith_angle,
@@ -509,7 +513,7 @@ class RayTracing:
509
513
 
510
514
  if save:
511
515
  plot_file_name = self._generate_file_name(
512
- file_type="ray-tracing",
516
+ file_type="ray_tracing",
513
517
  suffix=".pdf",
514
518
  extra_label=key,
515
519
  )
@@ -520,7 +524,7 @@ class RayTracing:
520
524
 
521
525
  for off_axis_key, image in self._psf_images.items():
522
526
  image_file_name = self._generate_file_name(
523
- file_type="ray-tracing",
527
+ file_type="ray_tracing",
524
528
  off_axis_angle=off_axis_key,
525
529
  suffix=".pdf",
526
530
  extra_label=f"image_{key}",
@@ -530,7 +534,7 @@ class RayTracing:
530
534
  image.plot_image(file_name=image_file)
531
535
 
532
536
  image_cumulative_file_name = self._generate_file_name(
533
- file_type="ray-tracing",
537
+ file_type="ray_tracing",
534
538
  off_axis_angle=off_axis_key,
535
539
  suffix=".pdf",
536
540
  extra_label=f"cumulative_psf_{key}",
@@ -632,8 +636,9 @@ class RayTracing:
632
636
  self, file_type, suffix, off_axis_angle=None, mirror_number=None, extra_label=None
633
637
  ):
634
638
  """Generate file name for output files."""
639
+ file_type_prefix = file_type if file_type == "ray_tracing" else f"ray_tracing_{file_type}"
635
640
  return names.generate_file_name(
636
- file_type=file_type,
641
+ file_type=file_type_prefix,
637
642
  suffix=suffix,
638
643
  site=self.telescope_model.site,
639
644
  telescope_model_name=self.telescope_model.name,