gammasimtools 0.9.0__py3-none-any.whl → 0.11.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 (135) hide show
  1. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/METADATA +4 -2
  2. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/RECORD +133 -117
  3. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/entry_points.txt +6 -1
  5. simtools/_version.py +9 -4
  6. simtools/applications/calculate_trigger_rate.py +15 -38
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +9 -29
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +47 -45
  9. simtools/applications/convert_model_parameter_from_simtel.py +2 -3
  10. simtools/applications/db_add_file_to_db.py +1 -3
  11. simtools/applications/db_add_simulation_model_from_repository_to_db.py +110 -0
  12. simtools/applications/db_add_value_from_json_to_db.py +1 -2
  13. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +6 -6
  14. simtools/applications/db_get_file_from_db.py +11 -12
  15. simtools/applications/db_get_parameter_from_db.py +26 -35
  16. simtools/applications/derive_mirror_rnda.py +1 -2
  17. simtools/applications/derive_photon_electron_spectrum.py +99 -0
  18. simtools/applications/derive_psf_parameters.py +1 -0
  19. simtools/applications/docs_produce_array_element_report.py +71 -0
  20. simtools/applications/docs_produce_model_parameter_reports.py +63 -0
  21. simtools/applications/generate_array_config.py +17 -17
  22. simtools/applications/generate_corsika_histograms.py +2 -2
  23. simtools/applications/generate_regular_arrays.py +19 -17
  24. simtools/applications/generate_simtel_array_histograms.py +11 -48
  25. simtools/applications/production_derive_limits.py +95 -0
  26. simtools/applications/production_generate_simulation_config.py +37 -33
  27. simtools/applications/production_scale_events.py +4 -9
  28. simtools/applications/run_application.py +165 -0
  29. simtools/applications/simulate_light_emission.py +0 -4
  30. simtools/applications/simulate_prod.py +1 -1
  31. simtools/applications/simulate_prod_htcondor_generator.py +26 -26
  32. simtools/applications/submit_data_from_external.py +12 -4
  33. simtools/applications/submit_model_parameter_from_external.py +18 -11
  34. simtools/applications/validate_camera_efficiency.py +2 -2
  35. simtools/applications/validate_file_using_schema.py +26 -22
  36. simtools/camera/single_photon_electron_spectrum.py +168 -0
  37. simtools/configuration/commandline_parser.py +37 -1
  38. simtools/configuration/configurator.py +8 -10
  39. simtools/constants.py +10 -3
  40. simtools/corsika/corsika_config.py +19 -17
  41. simtools/corsika/corsika_histograms.py +5 -7
  42. simtools/corsika/corsika_histograms_visualize.py +2 -4
  43. simtools/data_model/data_reader.py +0 -3
  44. simtools/data_model/metadata_collector.py +20 -12
  45. simtools/data_model/metadata_model.py +8 -124
  46. simtools/data_model/model_data_writer.py +81 -75
  47. simtools/data_model/schema.py +220 -0
  48. simtools/data_model/validate_data.py +79 -68
  49. simtools/db/db_handler.py +350 -492
  50. simtools/db/db_model_upload.py +139 -0
  51. simtools/dependencies.py +112 -0
  52. simtools/io_operations/hdf5_handler.py +54 -24
  53. simtools/layout/array_layout.py +38 -32
  54. simtools/model/array_model.py +13 -7
  55. simtools/model/model_parameter.py +55 -54
  56. simtools/model/site_model.py +2 -2
  57. simtools/production_configuration/calculate_statistical_errors_grid_point.py +119 -145
  58. simtools/production_configuration/event_scaler.py +9 -35
  59. simtools/production_configuration/generate_simulation_config.py +9 -44
  60. simtools/production_configuration/interpolation_handler.py +9 -15
  61. simtools/production_configuration/limits_calculation.py +202 -0
  62. simtools/reporting/docs_read_parameters.py +310 -0
  63. simtools/runners/corsika_simtel_runner.py +4 -4
  64. simtools/schemas/{integration_tests_config.metaschema.yml → application_workflow.metaschema.yml} +61 -27
  65. simtools/schemas/array_elements.yml +8 -0
  66. simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +39 -0
  67. simtools/schemas/input/single_pe_spectrum.schema.yml +38 -0
  68. simtools/schemas/model_parameter.metaschema.yml +103 -2
  69. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +4 -1
  70. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
  71. simtools/schemas/model_parameters/array_window.schema.yml +37 -0
  72. simtools/schemas/model_parameters/asum_clipping.schema.yml +0 -4
  73. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  74. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  75. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +2 -0
  76. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +2 -0
  77. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +2 -0
  78. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +4 -2
  79. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +2 -0
  80. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +2 -0
  81. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +2 -0
  82. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +2 -0
  83. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +2 -0
  84. simtools/schemas/model_parameters/dsum_clipping.schema.yml +0 -2
  85. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +0 -2
  86. simtools/schemas/model_parameters/dsum_offset.schema.yml +0 -2
  87. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +0 -2
  88. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +0 -2
  89. simtools/schemas/model_parameters/dsum_prescale.schema.yml +0 -2
  90. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +0 -2
  91. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +0 -2
  92. simtools/schemas/model_parameters/dsum_shaping.schema.yml +0 -2
  93. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +0 -2
  94. simtools/schemas/model_parameters/dsum_threshold.schema.yml +0 -2
  95. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +0 -2
  96. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  97. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  98. simtools/schemas/model_parameters/fadc_noise.schema.yml +3 -3
  99. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +33 -0
  100. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  101. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  102. simtools/schemas/model_parameters/laser_photons.schema.yml +2 -2
  103. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  104. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +1 -1
  105. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  106. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  107. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  108. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  109. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  110. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  111. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  112. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  113. simtools/schemas/production_configuration_metrics.schema.yml +68 -0
  114. simtools/schemas/production_tables.schema.yml +41 -0
  115. simtools/simtel/simtel_config_reader.py +1 -2
  116. simtools/simtel/simtel_config_writer.py +6 -8
  117. simtools/simtel/simtel_io_histogram.py +32 -68
  118. simtools/simtel/simtel_io_histograms.py +17 -34
  119. simtools/simtel/simulator_array.py +2 -1
  120. simtools/simtel/simulator_camera_efficiency.py +6 -3
  121. simtools/simtel/simulator_light_emission.py +5 -6
  122. simtools/simtel/simulator_ray_tracing.py +3 -4
  123. simtools/testing/configuration.py +2 -1
  124. simtools/testing/helpers.py +6 -13
  125. simtools/testing/validate_output.py +141 -47
  126. simtools/utils/general.py +114 -14
  127. simtools/utils/names.py +299 -157
  128. simtools/utils/value_conversion.py +17 -13
  129. simtools/version.py +2 -2
  130. simtools/visualization/legend_handlers.py +2 -0
  131. simtools/applications/db_add_model_parameters_from_repository_to_db.py +0 -176
  132. simtools/db/db_array_elements.py +0 -130
  133. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/LICENSE +0 -0
  134. {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/top_level.txt +0 -0
  135. /simtools/{camera_efficiency.py → camera/camera_efficiency.py} +0 -0
@@ -6,16 +6,10 @@ which scales the number of events for both the entire dataset and specific grid
6
6
  Scaling factors are calculated using error metrics and the evaluator's results.
7
7
  """
8
8
 
9
- import logging
10
-
11
9
  import astropy.units as u
12
10
  import numpy as np
13
11
 
14
- from simtools.production_configuration.calculate_statistical_errors_grid_point import (
15
- StatisticalErrorEvaluator,
16
- )
17
-
18
- _logger = logging.getLogger(__name__)
12
+ __all__ = ["EventScaler"]
19
13
 
20
14
 
21
15
  class EventScaler:
@@ -25,43 +19,36 @@ class EventScaler:
25
19
  Supports scaling both the entire dataset and specific grid points like energy values.
26
20
  """
27
21
 
28
- def __init__(self, evaluator: StatisticalErrorEvaluator, science_case: str, metrics: dict):
22
+ def __init__(self, evaluator, metrics: dict):
29
23
  """
30
- Initialize the EventScaler with the evaluator, science case, and metrics.
24
+ Initialize the EventScaler with the evaluator and metrics.
31
25
 
32
26
  Parameters
33
27
  ----------
34
28
  evaluator : StatisticalErrorEvaluator
35
29
  The evaluator responsible for calculating metrics and handling event data.
36
- science_case : str
37
- The science case used to adjust the uncertainty factor.
38
30
  metrics : dict
39
31
  Dictionary containing metrics, including target error for effective area.
40
32
  """
41
33
  self.evaluator = evaluator
42
- self.science_case = science_case
43
34
  self.metrics = metrics
44
35
 
45
36
  def scale_events(self, return_sum: bool = True) -> u.Quantity:
46
37
  """
47
38
  Calculate the scaled number of events based on statistical error metrics.
48
39
 
49
- If `return_sum` is `True`, the method returns the sum of scaled events for the entire
50
- dataset. If `return_sum` is `False`, it returns the scaled number of events for each
51
- grid point (e.g., along the energy axis).
52
-
53
40
  Parameters
54
41
  ----------
55
42
  return_sum : bool, optional
56
- If `True`, returns the sum of scaled events for the entire dataset. If `False`,
57
- returns the scaled events for each grid point along the energy axis. Default is `True`.
43
+ If True, returns the sum of scaled events for the entire set of MC events. If False,
44
+ returns the scaled events for each grid point along the energy axis. Default is True.
58
45
 
59
46
  Returns
60
47
  -------
61
48
  u.Quantity
62
- If `return_sum` is `True`, returns the total scaled number of events as a `u.Quantity`.
63
- If `return_sum` is `False`, returns an array of scaled events along the energy axis as
64
- a `u.Quantity`.
49
+ If 'return_sum' is True, returns the total scaled number of events as a u.Quantity.
50
+ If 'return_sum' is False, returns an array of scaled events along the energy axis as
51
+ a u.Quantity.
65
52
  """
66
53
  scaling_factor = self._compute_scaling_factor()
67
54
 
@@ -87,20 +74,7 @@ class EventScaler:
87
74
  "value"
88
75
  ]
89
76
 
90
- return (
91
- current_max_error / target_max_error
92
- ) ** 2 * self._apply_science_case_scaling_factor()
93
-
94
- def _apply_science_case_scaling_factor(self) -> float:
95
- """
96
- Apply the uncertainty factor based on the science case.
97
-
98
- Returns
99
- -------
100
- float
101
- The final scaling factor after applying uncertainty.
102
- """
103
- return 1 if self.science_case == "science case 1" else 1.0
77
+ return (current_max_error / target_max_error) ** 2
104
78
 
105
79
  def _number_of_simulated_events(self) -> u.Quantity:
106
80
  """
@@ -1,33 +1,11 @@
1
- """
2
- Configures and generates simulation parameters for a specific grid point.
3
-
4
- Used to configure and generate simulation parameters for a specific grid point
5
- based on statistical uncertainties.
6
- The class considers parameters, such as azimuth, elevation, and night sky background,
7
- to compute core scatter area, viewcone, and the required number of simulated events.
8
-
9
- Key Components:
10
- ---------------
11
- - `SimulationConfig`: Main class to handle simulation configuration for a grid point.
12
- - Attributes:
13
- - `grid_point` (dict): Contains azimuth, elevation, and night sky background.
14
- - `ctao_data_level` (str): The data level for the simulation (e.g., 'A', 'B', 'C').
15
- - `science_case` (str): The science case for the simulation.
16
- - `file_path` (str): Path to the DL2 MC event file
17
- used for statistical error evaluation.
18
- - `file_type` (str): Type of the DL2 MC event file ('point-like' or 'cone').
19
- - `metrics` (dict, optional): Dictionary of metrics to evaluate.
20
-
21
- """
22
-
23
- import logging
1
+ """Derives simulation configuration parameters for a grid point based on several metrics."""
24
2
 
25
3
  from simtools.production_configuration.calculate_statistical_errors_grid_point import (
26
4
  StatisticalErrorEvaluator,
27
5
  )
28
6
  from simtools.production_configuration.event_scaler import EventScaler
29
7
 
30
- _logger = logging.getLogger(__name__)
8
+ __all__ = ["SimulationConfig"]
31
9
 
32
10
 
33
11
  class SimulationConfig:
@@ -38,10 +16,6 @@ class SimulationConfig:
38
16
  ----------
39
17
  grid_point : dict
40
18
  Dictionary representing a grid point with azimuth, elevation, and night sky background.
41
- ctao_data_level : str
42
- The data level (e.g., 'A', 'B', 'C') for the simulation configuration.
43
- science_case : str
44
- The science case for the simulation configuration.
45
19
  file_path : str
46
20
  Path to the DL2 MC event file for statistical uncertainty evaluation.
47
21
  file_type : str
@@ -53,26 +27,22 @@ class SimulationConfig:
53
27
  def __init__(
54
28
  self,
55
29
  grid_point: dict[str, float],
56
- ctao_data_level: str,
57
- science_case: str,
58
30
  file_path: str,
59
31
  file_type: str,
60
32
  metrics: dict[str, float] | None = None,
61
33
  ):
62
34
  """Initialize the simulation configuration for a grid point."""
63
35
  self.grid_point = grid_point
64
- self.ctao_data_level = ctao_data_level
65
- self.science_case = science_case
66
36
  self.file_path = file_path
67
37
  self.file_type = file_type
68
38
  self.metrics = metrics or {}
69
39
  self.evaluator = StatisticalErrorEvaluator(file_path, file_type, metrics)
70
- self.event_scaler = EventScaler(self.evaluator, science_case, self.metrics)
40
+ self.event_scaler = EventScaler(self.evaluator, self.metrics)
71
41
  self.simulation_params = {}
72
42
 
73
43
  def configure_simulation(self) -> dict[str, float]:
74
44
  """
75
- Configure the simulation parameters for the grid point, data level, and science case.
45
+ Configure the simulation parameters for the grid point.
76
46
 
77
47
  Returns
78
48
  -------
@@ -80,16 +50,11 @@ class SimulationConfig:
80
50
  A dictionary with simulation parameters such as core scatter area,
81
51
  viewcone, and number of simulated events.
82
52
  """
83
- core_scatter_area = self._calculate_core_scatter_area()
84
- viewcone = self._calculate_viewcone()
85
- number_of_events = self.calculate_required_events()
86
-
87
53
  self.simulation_params = {
88
- "core_scatter_area": core_scatter_area,
89
- "viewcone": viewcone,
90
- "number_of_events": number_of_events,
54
+ "core_scatter_area": self._calculate_core_scatter_area(),
55
+ "viewcone": self._calculate_viewcone(),
56
+ "number_of_events": self.calculate_required_events(),
91
57
  }
92
-
93
58
  return self.simulation_params
94
59
 
95
60
  def calculate_required_events(self) -> int:
@@ -107,7 +72,7 @@ class SimulationConfig:
107
72
 
108
73
  def _calculate_core_scatter_area(self) -> float:
109
74
  """
110
- Calculate the core scatter area based on the grid point and data level.
75
+ Calculate the core scatter area based on the grid point.
111
76
 
112
77
  Returns
113
78
  -------
@@ -120,7 +85,7 @@ class SimulationConfig:
120
85
 
121
86
  def _calculate_viewcone(self) -> float:
122
87
  """
123
- Calculate the viewcone based on the grid point conditions and data level.
88
+ Calculate the viewcone based on the grid point conditions.
124
89
 
125
90
  Returns
126
91
  -------
@@ -4,24 +4,18 @@ import astropy.units as u
4
4
  import numpy as np
5
5
  from scipy.interpolate import griddata
6
6
 
7
- from simtools.production_configuration.calculate_statistical_errors_grid_point import (
8
- StatisticalErrorEvaluator,
9
- )
10
7
  from simtools.production_configuration.event_scaler import EventScaler
11
8
 
9
+ __all__ = ["InterpolationHandler"]
10
+
12
11
 
13
12
  class InterpolationHandler:
14
13
  """Handle interpolation between multiple StatisticalErrorEvaluator instances."""
15
14
 
16
- def __init__(
17
- self, evaluators: list["StatisticalErrorEvaluator"], science_case: str, metrics: dict
18
- ):
15
+ def __init__(self, evaluators, metrics: dict):
19
16
  self.evaluators = evaluators
20
- self.science_case = science_case
21
17
  self.metrics = metrics
22
- self.event_scalers = [
23
- EventScaler(e, self.science_case, self.metrics) for e in self.evaluators
24
- ]
18
+ self.event_scalers = [EventScaler(e, self.metrics) for e in self.evaluators]
25
19
 
26
20
  self.azimuths = [e.grid_point[1].to(u.deg).value for e in self.evaluators]
27
21
  self.zeniths = [e.grid_point[2].to(u.deg).value for e in self.evaluators]
@@ -157,9 +151,9 @@ class InterpolationHandler:
157
151
 
158
152
  return interpolated_threshold.item()
159
153
 
160
- def plot_comparison(self, evaluator: "StatisticalErrorEvaluator"):
154
+ def plot_comparison(self, evaluator):
161
155
  """
162
- Plot a comparison between the simulated, scaled, and triggered events.
156
+ Plot a comparison between the simulated, scaled, and reconstructed events.
163
157
 
164
158
  Parameters
165
159
  ----------
@@ -184,14 +178,14 @@ class InterpolationHandler:
184
178
 
185
179
  plt.plot(midpoints, evaluator.scaled_events, label="Scaled")
186
180
 
187
- triggered_event_histogram, _ = np.histogram(
181
+ reconstructed_event_histogram, _ = np.histogram(
188
182
  evaluator.data["event_energies_reco"], bins=evaluator.data["bin_edges_low"]
189
183
  )
190
- plt.plot(midpoints[:-1], triggered_event_histogram, label="Triggered")
184
+ plt.plot(midpoints[:-1], reconstructed_event_histogram, label="Reconstructed")
191
185
 
192
186
  plt.legend()
193
187
  plt.xscale("log")
194
188
  plt.xlabel("Energy (Midpoint of Bin Edges)")
195
189
  plt.ylabel("Event Count")
196
- plt.title("Comparison of Simulated, Scaled, and Triggered Events")
190
+ plt.title("Comparison of Simulated, scaled, and reconstructed events")
197
191
  plt.show()
@@ -0,0 +1,202 @@
1
+ """Calculate the thresholds for energy, radial distance, and viewcone."""
2
+
3
+ import astropy.units as u
4
+ import numpy as np
5
+
6
+
7
+ class LimitCalculator:
8
+ """
9
+ Compute thresholds/limits for energy, radial distance, and viewcone.
10
+
11
+ Histograms are generated with simtools-generate-simtel-array-histograms with --hdf5 flag.
12
+
13
+ Event data is read from the generated HDF5 file from the following tables:
14
+ - angle_to_observing_position__triggered_showers_ for the viewcone limit.
15
+ - event_weight__ra3d__log10_e__ for the energy and radial distance limit.
16
+
17
+
18
+ Parameters
19
+ ----------
20
+ event_data_file : list of astropy.table.Table
21
+ The list of tables containing the event data.
22
+ """
23
+
24
+ def __init__(self, event_data_file_tables):
25
+ """
26
+ Initialize the LimitCalculator with the given event data file.
27
+
28
+ Parameters
29
+ ----------
30
+ event_data_file : list of astropy.table.Table
31
+ The list of tables containing the event data.
32
+ """
33
+ self.angle_to_observing_position__triggered_showers_ = None
34
+ self.event_weight__ra3d__log10_e__ = None
35
+
36
+ for table in event_data_file_tables:
37
+ if (
38
+ "Title" in table.meta
39
+ and table.meta["Title"] == "angle_to_observing_position__triggered_showers_"
40
+ ):
41
+ self.angle_to_observing_position__triggered_showers_ = table
42
+ elif "Title" in table.meta and table.meta["Title"] == "event_weight__ra3d__log10_e__":
43
+ self.event_weight__ra3d__log10_e__ = table
44
+
45
+ def _compute_limits(
46
+ self, event_weight_array, bin_edges, loss_fraction, axis=0, limit_type="lower"
47
+ ):
48
+ """
49
+ Compute the limits based on the loss fraction.
50
+
51
+ Parameters
52
+ ----------
53
+ event_weight_array : np.ndarray
54
+ Array of event weights.
55
+ bin_edges : np.ndarray
56
+ Array of bin edges.
57
+ loss_fraction : float
58
+ Fraction of events to be lost.
59
+ axis : int, optional
60
+ Axis along which to sum the event weights. Default is 0.
61
+ limit_type : str, optional
62
+ Type of limit ('lower' or 'upper'). Default is 'lower'.
63
+
64
+ Returns
65
+ -------
66
+ int
67
+ Bin index where the threshold is reached.
68
+ float
69
+ Bin edge value corresponding to the threshold.
70
+ """
71
+ projection = np.sum(event_weight_array, axis=axis)
72
+ bin_edge_value = None
73
+ cumulative_sum = None
74
+ if limit_type == "upper":
75
+ cumulative_sum = np.cumsum(projection)
76
+
77
+ elif limit_type == "lower":
78
+ cumulative_sum = np.cumsum(projection[::-1])
79
+
80
+ total_events = np.sum(projection)
81
+ threshold = (1 - loss_fraction) * total_events
82
+ bin_index = np.searchsorted(cumulative_sum, threshold)
83
+ if limit_type == "upper":
84
+ bin_edge_value = bin_edges[bin_index]
85
+ elif limit_type == "lower":
86
+ bin_edge_value = bin_edges[-bin_index]
87
+ return bin_index, bin_edge_value
88
+
89
+ def get_bin_edges_and_units(self, table, axis="x"):
90
+ """
91
+ Extract bin edges and units from the table metadata.
92
+
93
+ Parameters
94
+ ----------
95
+ table : astropy.table.Table
96
+ Table containing the event data.
97
+
98
+ Returns
99
+ -------
100
+ tuple
101
+ Tuple containing the bin edges and their units.
102
+ """
103
+ bin_edges = table.meta[f"{axis}_bin_edges"]
104
+ try:
105
+ bin_edges_unit = table.meta[f"{axis}_bin_edges_unit"]
106
+ except KeyError:
107
+ bin_edges_unit = ""
108
+ return bin_edges, bin_edges_unit
109
+
110
+ def compute_lower_energy_limit(self, loss_fraction):
111
+ """
112
+ Compute the lower energy limit in TeV based on the event loss fraction.
113
+
114
+ Parameters
115
+ ----------
116
+ loss_fraction : float
117
+ Fraction of events to be lost.
118
+
119
+ Returns
120
+ -------
121
+ astropy.units.Quantity
122
+ Lower energy limit.
123
+ """
124
+ event_weight_array = np.column_stack(
125
+ [
126
+ self.event_weight__ra3d__log10_e__[name]
127
+ for name in self.event_weight__ra3d__log10_e__.dtype.names
128
+ ]
129
+ )
130
+ bin_edges, bin_edges_unit = self.get_bin_edges_and_units(
131
+ self.event_weight__ra3d__log10_e__, axis="y"
132
+ )
133
+ if bin_edges_unit == "":
134
+ bin_edges_unit = "TeV"
135
+ _, lower_bin_edge_value = self._compute_limits(
136
+ event_weight_array, bin_edges, loss_fraction, axis=0, limit_type="lower"
137
+ )
138
+ return (10**lower_bin_edge_value) * u.Unit(bin_edges_unit)
139
+
140
+ def compute_upper_radial_distance(self, loss_fraction):
141
+ """
142
+ Compute the upper radial distance based on the event loss fraction.
143
+
144
+ Parameters
145
+ ----------
146
+ loss_fraction : float
147
+ Fraction of events to be lost.
148
+
149
+ Returns
150
+ -------
151
+ astropy.units.Quantity
152
+ Upper radial distance in m.
153
+ """
154
+ event_weight_array = np.column_stack(
155
+ [
156
+ self.event_weight__ra3d__log10_e__[name]
157
+ for name in self.event_weight__ra3d__log10_e__.dtype.names
158
+ ]
159
+ )
160
+ bin_edges, bin_edges_unit = self.get_bin_edges_and_units(
161
+ self.event_weight__ra3d__log10_e__, axis="x"
162
+ )
163
+ if bin_edges_unit == "":
164
+ bin_edges_unit = "m"
165
+ _, upper_bin_edge_value = self._compute_limits(
166
+ event_weight_array, bin_edges, loss_fraction, axis=1, limit_type="upper"
167
+ )
168
+ return upper_bin_edge_value * u.Unit(bin_edges_unit)
169
+
170
+ def compute_viewcone(self, loss_fraction):
171
+ """
172
+ Compute the viewcone based on the event loss fraction.
173
+
174
+ Parameters
175
+ ----------
176
+ loss_fraction : float
177
+ Fraction of events to be lost.
178
+
179
+ Returns
180
+ -------
181
+ astropy.units.Quantity
182
+ Viewcone radius in degrees.
183
+ """
184
+ angle_to_observing_position__triggered_showers = np.column_stack(
185
+ [
186
+ self.angle_to_observing_position__triggered_showers_[name]
187
+ for name in self.angle_to_observing_position__triggered_showers_.dtype.names
188
+ ]
189
+ )
190
+ bin_edges, bin_edges_unit = self.get_bin_edges_and_units(
191
+ self.angle_to_observing_position__triggered_showers_, axis="x"
192
+ )
193
+ if bin_edges_unit == "":
194
+ bin_edges_unit = "deg"
195
+ _, upper_bin_edge_value = self._compute_limits(
196
+ angle_to_observing_position__triggered_showers,
197
+ bin_edges,
198
+ loss_fraction,
199
+ axis=0,
200
+ limit_type="upper",
201
+ )
202
+ return upper_bin_edge_value * u.Unit(bin_edges_unit)