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,189 @@
1
+ """Derive CORSIKA limits for a grid of parameters."""
2
+
3
+ import datetime
4
+ import logging
5
+
6
+ import numpy as np
7
+ from astropy.table import Column, Table
8
+
9
+ import simtools.utils.general as gen
10
+ from simtools.data_model.metadata_collector import MetadataCollector
11
+ from simtools.io_operations import io_handler
12
+ from simtools.production_configuration.derive_corsika_limits import LimitCalculator
13
+
14
+ _logger = logging.getLogger(__name__)
15
+
16
+
17
+ def generate_corsika_limits_grid(args_dict):
18
+ """
19
+ Generate CORSIKA limits for a grid of parameters.
20
+
21
+ Requires at least one event data file per parameter set.
22
+
23
+ Parameters
24
+ ----------
25
+ args_dict : dict
26
+ Dictionary containing command line arguments.
27
+ """
28
+ event_data_files = gen.collect_data_from_file(args_dict["event_data_files"])["files"]
29
+ telescope_configs = gen.collect_data_from_file(args_dict["telescope_ids"])["telescope_configs"]
30
+
31
+ results = []
32
+ for file_path in event_data_files:
33
+ for array_name, telescope_ids in telescope_configs.items():
34
+ _logger.info(f"Processing file: {file_path} with telescope config: {array_name}")
35
+ result = _process_file(
36
+ file_path,
37
+ array_name,
38
+ telescope_ids,
39
+ args_dict["loss_fraction"],
40
+ args_dict["plot_histograms"],
41
+ )
42
+ result["layout"] = array_name
43
+ results.append(result)
44
+
45
+ write_results(results, args_dict)
46
+
47
+
48
+ def _process_file(file_path, array_name, telescope_ids, loss_fraction, plot_histograms):
49
+ """
50
+ Compute limits for a single file.
51
+
52
+ Parameters
53
+ ----------
54
+ file_path : str
55
+ Path to the event data file.
56
+ array_name : str
57
+ Name of the telescope array configuration.
58
+ telescope_ids : list[int]
59
+ List of telescope IDs to filter the events.
60
+ loss_fraction : float
61
+ Fraction of events to be lost.
62
+ plot_histograms : bool
63
+ Whether to plot histograms.
64
+
65
+ Returns
66
+ -------
67
+ dict
68
+ Dictionary containing the computed limits and metadata.
69
+ """
70
+ calculator = LimitCalculator(file_path, array_name=array_name, telescope_list=telescope_ids)
71
+ limits = calculator.compute_limits(loss_fraction)
72
+
73
+ if plot_histograms:
74
+ calculator.plot_data(io_handler.IOHandler().get_output_directory())
75
+
76
+ return limits
77
+
78
+
79
+ def write_results(results, args_dict):
80
+ """
81
+ Write the computed limits as astropy table to file.
82
+
83
+ Parameters
84
+ ----------
85
+ results : list[dict]
86
+ List of computed limits.
87
+ args_dict : dict
88
+ Dictionary containing command line arguments.
89
+ """
90
+ table = _create_results_table(results, args_dict["loss_fraction"])
91
+
92
+ output_dir = io_handler.IOHandler().get_output_directory("corsika_limits")
93
+ output_file = output_dir / args_dict["output_file"]
94
+
95
+ table.write(output_file, format="ascii.ecsv", overwrite=True)
96
+ _logger.info(f"Results saved to {output_file}")
97
+
98
+ MetadataCollector.dump(args_dict, output_file)
99
+
100
+
101
+ def _create_results_table(results, loss_fraction):
102
+ """
103
+ Convert list of simulation results to an astropy Table with metadata.
104
+
105
+ Round values to appropriate precision and add metadata.
106
+
107
+ Parameters
108
+ ----------
109
+ results : list[dict]
110
+ Computed limits per file and telescope configuration.
111
+ loss_fraction : float
112
+ Fraction of lost events (added to metadata).
113
+
114
+ Returns
115
+ -------
116
+ astropy.table.Table
117
+ Table with computed limits.
118
+ """
119
+ cols = [
120
+ "primary_particle",
121
+ "array_name",
122
+ "telescope_ids",
123
+ "zenith",
124
+ "azimuth",
125
+ "nsb_level",
126
+ "lower_energy_limit",
127
+ "upper_radius_limit",
128
+ "viewcone_radius",
129
+ ]
130
+
131
+ columns = {name: [] for name in cols}
132
+ units = {}
133
+
134
+ for res in results:
135
+ _process_result_row(res, cols, columns, units)
136
+
137
+ table_cols = _create_table_columns(cols, columns, units)
138
+ table = Table(table_cols)
139
+
140
+ table.meta.update(
141
+ {
142
+ "created": datetime.datetime.now().isoformat(),
143
+ "description": "Lookup table for CORSIKA limits computed from simulations.",
144
+ "loss_fraction": loss_fraction,
145
+ }
146
+ )
147
+
148
+ return table
149
+
150
+
151
+ def _process_result_row(res, cols, columns, units):
152
+ """Process a single result row and add values to columns."""
153
+ for k in cols:
154
+ val = res.get(k, None)
155
+ if val is not None:
156
+ val = _round_value(k, val)
157
+ _logger.debug(f"Adding {k}: {val} to column data")
158
+
159
+ if hasattr(val, "unit"):
160
+ columns[k].append(val.value)
161
+ units[k] = val.unit
162
+ else:
163
+ columns[k].append(val)
164
+ if k not in units:
165
+ units[k] = None
166
+
167
+
168
+ def _round_value(key, val):
169
+ """Round value based on key type."""
170
+ if key == "lower_energy_limit":
171
+ return np.floor(val * 1e3) / 1e3
172
+ if key == "upper_radius_limit":
173
+ return np.ceil(val / 25) * 25
174
+ if key == "viewcone_radius":
175
+ return np.ceil(val / 0.25) * 0.25
176
+ return val
177
+
178
+
179
+ def _create_table_columns(cols, columns, units):
180
+ """Create table columns with appropriate data types."""
181
+ table_cols = []
182
+ for k in cols:
183
+ col_data = columns[k]
184
+ if any(isinstance(v, list | tuple) for v in col_data):
185
+ col = Column(data=col_data, name=k, unit=units.get(k), dtype=object)
186
+ else:
187
+ col = Column(data=col_data, name=k, unit=units.get(k))
188
+ table_cols.append(col)
189
+ return table_cols
@@ -0,0 +1,155 @@
1
+ """
2
+ Calculate the event production statistics based on metrics.
3
+
4
+ Module for calculating the production event statistics based on statistical error metrics.
5
+ Contains the `ProductionStatisticsDerivator` class, which derives the number of events for
6
+ both the entire dataset and specific grid points. Event statistic is calculated using error
7
+ metrics and the evaluator's results.
8
+ """
9
+
10
+ import astropy.units as u
11
+ import numpy as np
12
+
13
+ __all__ = ["ProductionStatisticsDerivator"]
14
+
15
+
16
+ class ProductionStatisticsDerivator:
17
+ """
18
+ Derives the production statistics based on statistical error metrics.
19
+
20
+ Supports deriving statistics for both the entire dataset and
21
+ specific grid points like energy values.
22
+ """
23
+
24
+ def __init__(self, evaluator, metrics: dict):
25
+ """
26
+ Initialize the ProductionStatisticsDerivator with the evaluator and metrics.
27
+
28
+ Parameters
29
+ ----------
30
+ evaluator : StatisticalUncertaintyEvaluator
31
+ The evaluator responsible for calculating metrics and handling event data.
32
+ metrics : dict
33
+ Dictionary containing metrics, including target error for effective area.
34
+ """
35
+ self.evaluator = evaluator
36
+ self.metrics = metrics
37
+
38
+ def _compute_scaling_factor(self) -> np.ndarray:
39
+ """
40
+ Compute bin-wise scaling factors based on error metrics.
41
+
42
+ Takes into account the energy range specified in the metrics and
43
+ calculates a separate scaling factor for each energy bin.
44
+
45
+ Returns
46
+ -------
47
+ np.ndarray
48
+ Array of scaling factors for each energy bin.
49
+ """
50
+ uncertainty_effective_area = self.evaluator.metric_results.get("uncertainty_effective_area")
51
+ relative_uncertainties = uncertainty_effective_area.get("relative_uncertainties")
52
+ energy_range = (
53
+ self.metrics.get("uncertainty_effective_area").get("energy_range").get("value")
54
+ )
55
+ energy_unit = u.Unit(
56
+ self.metrics.get("uncertainty_effective_area").get("energy_range").get("unit")
57
+ )
58
+
59
+ energy_range_converted = np.array(energy_range) * energy_unit
60
+
61
+ bin_edges = self.evaluator.energy_bin_edges
62
+ bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
63
+
64
+ # Mask for bins within the metric specified energy range
65
+ mask = (bin_centers >= energy_range_converted[0]) & (
66
+ bin_centers <= energy_range_converted[1]
67
+ )
68
+
69
+ scaling_factors = np.zeros_like(relative_uncertainties)
70
+
71
+ target_uncertainty = (
72
+ self.metrics.get("uncertainty_effective_area").get("target_uncertainty").get("value")
73
+ )
74
+
75
+ # Calculate scaling factor only for bins within the energy range
76
+ # For bins with zero events/uncertainty, use a scaling factor of 0
77
+ valid_bins = mask & (relative_uncertainties > 0)
78
+ scaling_factors[valid_bins] = (relative_uncertainties[valid_bins] / target_uncertainty) ** 2
79
+
80
+ return scaling_factors
81
+
82
+ def derive_statistics(self, return_sum: bool = True) -> u.Quantity:
83
+ """
84
+ Derive the production statistics based on statistical error metrics.
85
+
86
+ Parameters
87
+ ----------
88
+ return_sum : bool, optional
89
+ If True, returns the sum of production statistics for the entire set of MC events.
90
+ If False, returns the production statistics for each grid point along the energy axis.
91
+ Default is True.
92
+
93
+ Returns
94
+ -------
95
+ u.Quantity
96
+ If 'return_sum' is True, returns the total
97
+ derived production statistics as a u.Quantity.
98
+ If 'return_sum' is False, returns an array of production statistics along the energy
99
+ axis as a u.Quantity.
100
+ """
101
+ scaling_factors = self._compute_scaling_factor()
102
+ base_events = self._number_of_simulated_events()
103
+ # currently we use the maximum of the scaling factors to scale the events. This is a soft
104
+ # requirement if we want to keep the power law shape of the production statistics.
105
+ scaled_events = base_events * np.max(scaling_factors)
106
+
107
+ if return_sum:
108
+ return np.sum(scaled_events)
109
+ return scaled_events
110
+
111
+ def _number_of_simulated_events(self) -> u.Quantity:
112
+ """
113
+ Fetch the number of simulated events from the evaluator's data.
114
+
115
+ Returns
116
+ -------
117
+ u.Quantity
118
+ The number of simulated events.
119
+ """
120
+ return self.evaluator.data.get("simulated_event_histogram")
121
+
122
+ def calculate_production_statistics_at_grid_point(
123
+ self,
124
+ grid_point: tuple,
125
+ ) -> u.Quantity:
126
+ """
127
+ Derive the production statistics for a specific energy grid point.
128
+
129
+ Parameters
130
+ ----------
131
+ grid_point : tuple
132
+ The grid point specifying energy, azimuth, zenith, NSB, and offset.
133
+
134
+ Returns
135
+ -------
136
+ float
137
+ The derived production statistics at the specified grid point (energy).
138
+ """
139
+ energy = grid_point[0]
140
+ bin_edges = self.evaluator.create_bin_edges()
141
+ bin_idx = np.digitize(energy, bin_edges) - 1
142
+
143
+ # Get scaling factors for all bins
144
+ scaling_factors = self._compute_scaling_factor()
145
+
146
+ simulated_event_histogram = self.evaluator.data.get("simulated_event_histogram", [])
147
+
148
+ if bin_idx < 0 or bin_idx >= len(simulated_event_histogram):
149
+ raise ValueError(f"Energy {energy} is outside the range of the simulated events data.")
150
+
151
+ base_events = self._number_of_simulated_events()
152
+ base_event_at_energy = base_events[bin_idx]
153
+ scaling_factor_at_energy = scaling_factors[bin_idx]
154
+
155
+ return base_event_at_energy * scaling_factor_at_energy
@@ -0,0 +1,152 @@
1
+ """
2
+ Derives the required statistics for a requested set of production parameters through interpolation.
3
+
4
+ This module provides the `ProductionStatisticsHandler` class, which manages the workflow for
5
+ derivation of required number of events for a simulation production using pre-defined metrics.
6
+
7
+ The module includes functionality to:
8
+ - Initialize evaluators for statistical uncertainty calculations based on input parameters.
9
+ - Perform interpolation using the initialized evaluators to estimate production statistics at a
10
+ query point.
11
+ - Write the results of the interpolation to an output file.
12
+ """
13
+
14
+ import itertools
15
+ import json
16
+ import logging
17
+ from pathlib import Path
18
+
19
+ import astropy.units as u
20
+
21
+ from simtools.production_configuration.calculate_statistical_uncertainties_grid_point import (
22
+ StatisticalUncertaintyEvaluator,
23
+ )
24
+ from simtools.production_configuration.interpolation_handler import InterpolationHandler
25
+ from simtools.utils.general import collect_data_from_file
26
+
27
+
28
+ class ProductionStatisticsHandler:
29
+ """
30
+ Handles the workflow for deriving production statistics.
31
+
32
+ This class manages the evaluation of statistical uncertainties from DL2 MC event files
33
+ and performs interpolation to estimate the required number of events for a simulation
34
+ production at a specified query point.
35
+ """
36
+
37
+ def __init__(self, args_dict, output_path):
38
+ """
39
+ Initialize the manager with the provided arguments.
40
+
41
+ Parameters
42
+ ----------
43
+ args_dict : dict
44
+ Dictionary of command-line arguments.
45
+ output_path : Path
46
+ Path to the directory where the event statistics output file will be saved.
47
+ """
48
+ self.args = args_dict
49
+ self.logger = logging.getLogger(__name__)
50
+ self.output_path = output_path
51
+ self.metrics = collect_data_from_file(self.args["metrics_file"])
52
+ self.evaluator_instances = []
53
+ self.interpolation_handler = None
54
+ self.grid_points_production = self._load_grid_points_production()
55
+
56
+ def _load_grid_points_production(self):
57
+ """Load grid points from the JSON file."""
58
+ grid_points_production_file = self.args["grid_points_production_file"]
59
+ return collect_data_from_file(grid_points_production_file)
60
+
61
+ def initialize_evaluators(self):
62
+ """Initialize StatisticalUncertaintyEvaluator instances for the given grid point."""
63
+ if not (
64
+ self.args["base_path"]
65
+ and self.args["zeniths"]
66
+ and self.args["azimuths"]
67
+ and self.args["nsb"]
68
+ and self.args["offsets"]
69
+ ):
70
+ self.logger.warning("No files read")
71
+ self.logger.warning(f"Base Path: {self.args['base_path']}")
72
+ self.logger.warning(f"Zeniths: {self.args['zeniths']}")
73
+ self.logger.warning(f"Camera offsets: {self.args['offsets']}")
74
+ return
75
+
76
+ for zenith, azimuth, nsb, offset in itertools.product(
77
+ self.args["zeniths"], self.args["azimuths"], self.args["nsb"], self.args["offsets"]
78
+ ):
79
+ file_name = self.args["file_name_template"].format(
80
+ zenith=int(zenith),
81
+ azimuth=azimuth,
82
+ nsb=nsb,
83
+ offset=offset,
84
+ )
85
+ file_path = Path(self.args["base_path"]).joinpath(file_name)
86
+
87
+ if not file_path.exists():
88
+ self.logger.warning(f"File not found: {file_path}. Skipping.")
89
+ continue
90
+
91
+ evaluator = StatisticalUncertaintyEvaluator(
92
+ file_path,
93
+ metrics=self.metrics,
94
+ grid_point=(None, azimuth, zenith, nsb, offset * u.deg),
95
+ )
96
+ evaluator.calculate_metrics()
97
+ self.evaluator_instances.append(evaluator)
98
+
99
+ def perform_interpolation(self):
100
+ """Perform interpolation for the query point."""
101
+ if not self.evaluator_instances:
102
+ self.logger.error("No evaluators initialized. Cannot perform interpolation.")
103
+ return None
104
+
105
+ self.interpolation_handler = InterpolationHandler(
106
+ self.evaluator_instances,
107
+ metrics=self.metrics,
108
+ grid_points_production=self.grid_points_production,
109
+ )
110
+ qrid_points_with_statistics = []
111
+
112
+ interpolated_production_statistics = self.interpolation_handler.interpolate()
113
+ for grid_point, statistics in zip(
114
+ self.grid_points_production, interpolated_production_statistics
115
+ ):
116
+ qrid_points_with_statistics.append(
117
+ {
118
+ "grid_point": grid_point,
119
+ "interpolated_production_statistics": float(statistics),
120
+ }
121
+ )
122
+ return qrid_points_with_statistics
123
+
124
+ def write_output(self, production_statistics):
125
+ """Write the derived event statistics to a file."""
126
+ output_data = (production_statistics,)
127
+ output_filename = self.args["output_file"]
128
+ self.output_path.mkdir(parents=True, exist_ok=True)
129
+ output_file_path = self.output_path.joinpath(output_filename)
130
+ with open(output_file_path, "w", encoding="utf-8") as f:
131
+ json.dump(output_data, f, indent=4)
132
+ self.logger.info(f"Output saved to {self.output_path}")
133
+
134
+ def plot_production_statistics_comparison(self):
135
+ """Plot the derived event statistics."""
136
+ ax = self.interpolation_handler.plot_comparison()
137
+ plot_path = self.output_path.joinpath("production_statistics_comparison.png")
138
+ plot_path.parent.mkdir(parents=True, exist_ok=True)
139
+ ax.figure.savefig(plot_path)
140
+ self.logger.info(f"Plot saved to {plot_path}")
141
+
142
+ def run(self):
143
+ """Run the scaling and interpolation workflow."""
144
+ self.logger.info(f"Grid Points File: {self.args['grid_points_production_file']}")
145
+ self.logger.info(f"Metrics File: {self.args['metrics_file']}")
146
+
147
+ self.initialize_evaluators()
148
+ production_statistics = self.perform_interpolation()
149
+ if self.args.get("plot_production_statistics"):
150
+ self.plot_production_statistics_comparison()
151
+
152
+ self.write_output(production_statistics)