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
@@ -0,0 +1,364 @@
1
+ """
2
+ Module defines the `GridGeneration` class.
3
+
4
+ Used to generate a grid of simulation points based on flexible axes definitions such
5
+ azimuth, zenith angle, night-sky background, and camera offset.
6
+ The module handles axis binning, scaling and interpolation of energy thresholds, viewcone,
7
+ and radius limits from a lookup table.
8
+ Additionally, it allows for converting between Altitude/Azimuth and Right Ascension
9
+ Declination coordinates. The resulting grid points are saved to a file.
10
+ """
11
+
12
+ import json
13
+ import logging
14
+
15
+ import numpy as np
16
+ from astropy import units as u
17
+ from astropy.coordinates import AltAz, EarthLocation, SkyCoord
18
+ from astropy.table import Table
19
+ from astropy.units import Quantity
20
+ from scipy.interpolate import griddata
21
+
22
+
23
+ class GridGeneration:
24
+ """
25
+ Defines and generates a grid of simulation points based on flexible axes definitions.
26
+
27
+ This class generates a grid of points for a simulation based on parameters such as
28
+ azimuth, zenith angle, night-sky background, and camera offset,
29
+ taking into account axis definitions, scaling, and units and interpolating values
30
+ for simulations from a lookup table.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ axes: dict,
36
+ coordinate_system: str = "zenith_azimuth",
37
+ observing_location=None,
38
+ observing_time=None,
39
+ lookup_table: str | None = None,
40
+ telescope_ids: list | None = None,
41
+ ):
42
+ """
43
+ Initialize the grid with the given axes and coordinate system.
44
+
45
+ Parameters
46
+ ----------
47
+ axes : dict
48
+ Dictionary where each key is the axis name and the value is a dictionary
49
+ defining the axis properties (range, binning, scaling, etc.).
50
+ coordinate_system : str, optional
51
+ The coordinate system for the grid generation (default is 'zenith_azimuth').
52
+ observing_location : EarthLocation, optional
53
+ The location of the observation (latitude, longitude, height).
54
+ observing_time : Time, optional
55
+ The time of the observation. If None, coordinate conversion to RA/Dec not working.
56
+ lookup_table : str, optional
57
+ Path to the lookup table file (ECSV format).
58
+ telescope_ids : list of int, optional
59
+ List of telescope IDs to get the limits for.
60
+ """
61
+ self._logger = logging.getLogger(__name__)
62
+
63
+ self.axes = axes["axes"] if "axes" in axes else axes
64
+ self.coordinate_system = coordinate_system
65
+ self.observing_location = (
66
+ observing_location
67
+ if observing_location is not None
68
+ else EarthLocation(lat=0.0 * u.deg, lon=0.0 * u.deg, height=0 * u.m)
69
+ )
70
+ self.observing_time = observing_time
71
+ self.lookup_table = lookup_table
72
+ self.telescope_ids = telescope_ids
73
+
74
+ # Store target values for each axis
75
+ self.target_values = self._generate_target_values()
76
+
77
+ if self.lookup_table:
78
+ self._apply_lookup_table_limits()
79
+
80
+ def _generate_target_values(self):
81
+ """
82
+ Generate target axis values and store them as Quantities.
83
+
84
+ Returns
85
+ -------
86
+ dict
87
+ Dictionary of target values for each axis, stored as Quantity objects.
88
+ """
89
+ target_values = {}
90
+ for axis_name, axis in self.axes.items():
91
+ axis_range = axis["range"]
92
+ binning = axis["binning"]
93
+ scaling = axis.get("scaling", "linear")
94
+ units = axis.get("units", None)
95
+
96
+ if axis_name == "azimuth":
97
+ # Use circular binning for azimuth
98
+ values = self.create_circular_binning(axis_range, binning)
99
+ elif scaling == "log":
100
+ # Log scaling
101
+ values = np.logspace(np.log10(axis_range[0]), np.log10(axis_range[1]), binning)
102
+ elif scaling == "1/cos":
103
+ # 1/cos scaling
104
+ cos_min = np.cos(np.radians(axis_range[0]))
105
+ cos_max = np.cos(np.radians(axis_range[1]))
106
+ inv_cos_values = np.linspace(1 / cos_min, 1 / cos_max, binning)
107
+ values = np.degrees(np.arccos(1 / inv_cos_values))
108
+ else:
109
+ # Linear scaling
110
+ values = np.linspace(axis_range[0], axis_range[1], binning)
111
+
112
+ if units:
113
+ values = values * u.Unit(units)
114
+
115
+ target_values[axis_name] = values
116
+
117
+ return target_values
118
+
119
+ def _apply_lookup_table_limits(self):
120
+ """Apply limits from the lookup table and interpolate values."""
121
+ lookup_table = Table.read(self.lookup_table, format="ascii.ecsv")
122
+
123
+ matching_rows = [
124
+ row for row in lookup_table if set(self.telescope_ids) == set(row["telescope_ids"])
125
+ ]
126
+
127
+ if not matching_rows:
128
+ raise ValueError(
129
+ f"No matching rows in the lookup table for telescope_ids: {self.telescope_ids}"
130
+ )
131
+
132
+ def extract_array(field, transform=lambda x: x):
133
+ return np.array([transform(row[field]) for row in matching_rows])
134
+
135
+ zeniths = extract_array("zenith")
136
+ azimuths = extract_array("azimuth", lambda x: x % 360)
137
+ nsb_values = extract_array("nsb", lambda x: 1 if x == "dark" else 5)
138
+ lower_energy_thresholds = extract_array("lower_energy_threshold")
139
+ upper_radius_thresholds = extract_array("upper_radius_threshold")
140
+ viewcone_radii = extract_array("viewcone_radius")
141
+
142
+ # Wrap azimuths and repeat others
143
+ azimuths_wrapped = np.concatenate([azimuths + shift for shift in (0, 360, -360)])
144
+
145
+ def repeat_3(arr):
146
+ """Repeat an array three times."""
147
+ return np.tile(arr, 3)
148
+
149
+ points = np.column_stack(
150
+ (
151
+ repeat_3(zeniths),
152
+ azimuths_wrapped,
153
+ repeat_3(nsb_values),
154
+ )
155
+ )
156
+
157
+ target_grid = (
158
+ np.array(
159
+ np.meshgrid(
160
+ self.target_values["zenith_angle"].value,
161
+ self.target_values["azimuth"].value,
162
+ self.target_values["nsb"].value,
163
+ indexing="ij",
164
+ )
165
+ )
166
+ .reshape(3, -1)
167
+ .T
168
+ )
169
+
170
+ def interpolate(values):
171
+ return griddata(
172
+ points, repeat_3(values), target_grid, method="linear", fill_value=np.nan
173
+ ).reshape(
174
+ len(self.target_values["zenith_angle"]),
175
+ len(self.target_values["azimuth"]),
176
+ len(self.target_values["nsb"]),
177
+ )
178
+
179
+ self.interpolated_limits = {
180
+ "energy": interpolate(lower_energy_thresholds),
181
+ "radius": interpolate(upper_radius_thresholds),
182
+ "viewcone": interpolate(viewcone_radii),
183
+ }
184
+
185
+ def create_circular_binning(self, azimuth_range, num_bins):
186
+ """
187
+ Create bin centers for azimuth angles, handling circular wraparound (0 deg to 360 deg).
188
+
189
+ Parameters
190
+ ----------
191
+ azimuth_range : tuple
192
+ (min_azimuth, max_azimuth), can wrap around 0 deg.
193
+ num_bins : int
194
+ Number of bins.
195
+
196
+ Returns
197
+ -------
198
+ np.ndarray
199
+ Array of bin centers.
200
+ """
201
+ azimuth_min, azimuth_max = azimuth_range
202
+ azimuth_min %= 360 # Normalize to [0, 360)
203
+ azimuth_max %= 360
204
+
205
+ clockwise_distance = (azimuth_max - azimuth_min) % 360
206
+ counterclockwise_distance = (azimuth_min - azimuth_max) % 360
207
+
208
+ if clockwise_distance <= counterclockwise_distance:
209
+ bin_centers = (
210
+ np.linspace(azimuth_min, azimuth_min + clockwise_distance, num_bins, endpoint=True)
211
+ % 360
212
+ )
213
+ else:
214
+ bin_centers = (
215
+ np.linspace(
216
+ azimuth_min, azimuth_min - counterclockwise_distance, num_bins, endpoint=True
217
+ )
218
+ % 360
219
+ )
220
+
221
+ return bin_centers
222
+
223
+ def generate_grid(self) -> list[dict]:
224
+ """
225
+ Generate the grid based on the required axes and include interpolated limits.
226
+
227
+ Takes energy threshold, viewcone, and radius from the interpolated lookup table.
228
+
229
+ Returns
230
+ -------
231
+ list of dict
232
+ A list of grid points, each represented as a dictionary with axis names
233
+ as keys and axis values as values. Axis values may include units where defined.
234
+ """
235
+ value_arrays = [value.value for value in self.target_values.values()]
236
+ units = [value.unit for value in self.target_values.values()]
237
+ grid = np.meshgrid(*value_arrays, indexing="ij")
238
+ combinations = np.vstack(list(map(np.ravel, grid))).T
239
+ grid_points = []
240
+ for combination in combinations:
241
+ grid_point = {
242
+ key: Quantity(combination[i], units[i])
243
+ for i, key in enumerate(self.target_values.keys())
244
+ }
245
+
246
+ if "energy" in self.interpolated_limits:
247
+ zenith_idx = np.searchsorted(
248
+ self.target_values["zenith_angle"].value, grid_point["zenith_angle"].value
249
+ )
250
+ azimuth_idx = np.searchsorted(
251
+ self.target_values["azimuth"].value, grid_point["azimuth"].value
252
+ )
253
+ nsb_idx = np.searchsorted(self.target_values["nsb"].value, grid_point["nsb"].value)
254
+ energy_lower = self.interpolated_limits["energy"][zenith_idx, azimuth_idx, nsb_idx]
255
+ grid_point["energy_threshold"] = {"lower": energy_lower * u.TeV}
256
+
257
+ if "radius" in self.interpolated_limits:
258
+ radius_value = self.interpolated_limits["radius"][zenith_idx, azimuth_idx, nsb_idx]
259
+ grid_point["radius"] = radius_value * u.m
260
+
261
+ if "viewcone" in self.interpolated_limits:
262
+ viewcone_value = self.interpolated_limits["viewcone"][
263
+ zenith_idx, azimuth_idx, nsb_idx
264
+ ]
265
+ grid_point["viewcone"] = viewcone_value * u.deg
266
+
267
+ grid_points.append(grid_point)
268
+
269
+ return grid_points
270
+
271
+ def convert_altaz_to_radec(self, alt, az):
272
+ """
273
+ Convert Altitude/Azimuth (AltAz) coordinates to Right Ascension/Declination (RA/Dec).
274
+
275
+ Parameters
276
+ ----------
277
+ alt : float
278
+ Altitude angle in degrees.
279
+ az : float
280
+ Azimuth angle in degrees.
281
+
282
+ Returns
283
+ -------
284
+ SkyCoord
285
+ SkyCoord object containing the RA/Dec coordinates.
286
+
287
+ Raises
288
+ ------
289
+ ValueError
290
+ If observing_time is not set.
291
+ """
292
+ if self.observing_time is None:
293
+ raise ValueError(
294
+ "Observing time is not set. "
295
+ "Please provide an observing_time to convert coordinates."
296
+ )
297
+
298
+ alt_rad = alt.to(u.rad)
299
+ az_rad = az.to(u.rad)
300
+ aa = AltAz(
301
+ alt=alt_rad,
302
+ az=az_rad,
303
+ location=self.observing_location,
304
+ obstime=self.observing_time,
305
+ )
306
+ skycoord = SkyCoord(aa)
307
+ return skycoord.icrs # Return RA/Dec in ICRS frame
308
+
309
+ def convert_coordinates(self, grid_points: list[dict]) -> list[dict]:
310
+ """
311
+ Convert the grid points to RA/Dec coordinates if necessary.
312
+
313
+ Parameters
314
+ ----------
315
+ grid_points : list of dict
316
+ List of grid points, each represented as a dictionary with axis
317
+ names as keys and values.
318
+
319
+ Returns
320
+ -------
321
+ list of dict
322
+ The grid points with converted RA/Dec coordinates.
323
+ """
324
+ if self.coordinate_system == "ra_dec":
325
+ for point in grid_points:
326
+ if "zenith_angle" in point and "azimuth" in point:
327
+ alt = (90.0 * u.deg) - point.pop("zenith_angle")
328
+ az = point.pop("azimuth")
329
+ radec = self.convert_altaz_to_radec(alt, az)
330
+ point["ra"] = radec.ra.deg * u.deg
331
+ point["dec"] = radec.dec.deg * u.deg
332
+ return grid_points
333
+
334
+ def serialize_grid_points(self, grid_points, output_file=None):
335
+ """Serialize the grid output and save to a file or print to the console."""
336
+ cleaned_points = []
337
+
338
+ for point in grid_points:
339
+ cleaned_point = {}
340
+ for key, value in point.items():
341
+ if isinstance(value, dict):
342
+ # Nested dictionaries
343
+ cleaned_point[key] = {k: self.serialize_quantity(v) for k, v in value.items()}
344
+ else:
345
+ cleaned_point[key] = self.serialize_quantity(value)
346
+
347
+ cleaned_points.append(cleaned_point)
348
+
349
+ output_data = json.dumps(cleaned_points, indent=4)
350
+
351
+ if output_file:
352
+ with open(output_file, "w", encoding="utf-8") as f:
353
+ f.write(output_data)
354
+ self._logger.info(f"Output saved to {output_file}")
355
+ else:
356
+ self._logger.info(output_data)
357
+ return output_data
358
+
359
+ def serialize_quantity(self, value):
360
+ """Serialize Quantity."""
361
+ if isinstance(value, u.Quantity):
362
+ return {"value": value.value, "unit": str(value.unit)}
363
+ self._logger.warning(f"Unsupported type {type(value)} for serialization. Returning as is.")
364
+ return value