gammasimtools 0.19.0__py3-none-any.whl → 0.20.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 (47) hide show
  1. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/METADATA +1 -3
  2. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/RECORD +43 -41
  3. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/entry_points.txt +2 -2
  4. simtools/_version.py +2 -2
  5. simtools/applications/calculate_incident_angles.py +182 -0
  6. simtools/applications/db_add_simulation_model_from_repository_to_db.py +17 -14
  7. simtools/applications/db_add_value_from_json_to_db.py +6 -9
  8. simtools/applications/db_generate_compound_indexes.py +7 -3
  9. simtools/applications/db_get_file_from_db.py +11 -23
  10. simtools/applications/derive_trigger_rates.py +91 -0
  11. simtools/applications/plot_simtel_events.py +73 -31
  12. simtools/applications/validate_file_using_schema.py +7 -4
  13. simtools/configuration/commandline_parser.py +17 -11
  14. simtools/data_model/validate_data.py +8 -3
  15. simtools/db/db_handler.py +83 -26
  16. simtools/db/db_model_upload.py +11 -16
  17. simtools/dependencies.py +10 -5
  18. simtools/layout/array_layout_utils.py +37 -5
  19. simtools/model/array_model.py +18 -1
  20. simtools/model/site_model.py +25 -0
  21. simtools/production_configuration/derive_corsika_limits.py +9 -34
  22. simtools/ray_tracing/incident_angles.py +706 -0
  23. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +2 -2
  24. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +1 -1
  25. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +22 -29
  26. simtools/schemas/model_parameters/stars.schema.yml +1 -1
  27. simtools/schemas/production_tables.schema.yml +5 -0
  28. simtools/simtel/simtel_config_writer.py +17 -19
  29. simtools/simtel/simtel_io_event_histograms.py +253 -516
  30. simtools/simtel/simtel_io_event_reader.py +51 -2
  31. simtools/simtel/simtel_io_event_writer.py +31 -11
  32. simtools/simtel/simtel_io_metadata.py +1 -1
  33. simtools/simtel/simtel_table_reader.py +3 -3
  34. simtools/telescope_trigger_rates.py +119 -0
  35. simtools/testing/log_inspector.py +13 -11
  36. simtools/utils/geometry.py +20 -0
  37. simtools/visualization/plot_incident_angles.py +431 -0
  38. simtools/visualization/plot_simtel_event_histograms.py +376 -0
  39. simtools/visualization/visualize.py +1 -3
  40. simtools/applications/calculate_trigger_rate.py +0 -187
  41. simtools/applications/generate_sim_telarray_histograms.py +0 -196
  42. simtools/simtel/simtel_io_histogram.py +0 -623
  43. simtools/simtel/simtel_io_histograms.py +0 -556
  44. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/WHEEL +0 -0
  45. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/licenses/LICENSE +0 -0
  46. {gammasimtools-0.19.0.dist-info → gammasimtools-0.20.0.dist-info}/top_level.txt +0 -0
  47. /simtools/visualization/{simtel_event_plots.py → plot_simtel_events.py} +0 -0
@@ -0,0 +1,376 @@
1
+ """Plot simtel event histograms filled with SimtelIOEventHistograms."""
2
+
3
+ import logging
4
+
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ from matplotlib.colors import LogNorm
8
+
9
+ from simtools.simtel.simtel_io_event_histograms import SimtelIOEventHistograms
10
+
11
+ _logger = logging.getLogger(__name__)
12
+
13
+
14
+ def plot(histograms, output_path=None, limits=None, rebin_factor=2, array_name=None):
15
+ """
16
+ Plot simtel event histograms.
17
+
18
+ Parameters
19
+ ----------
20
+ histograms: SimtelIOEventHistograms
21
+ Instance containing the histograms to plot.
22
+ output_path: Path or str, optional
23
+ Directory to save plots. If None, plots will be displayed.
24
+ limits: dict, optional
25
+ Dictionary containing limits for plotting. Keys can include:
26
+ - "upper_radius_limit": Upper limit for core distance
27
+ - "lower_energy_limit": Lower limit for energy
28
+ - "viewcone_radius": Radius for the viewcone
29
+ rebin_factor: int, optional
30
+ Factor by which to reduce the number of bins in 2D histograms for re-binned plots.
31
+ Default is 2 (merge every 2 bins). Set to 0 or 1 to disable re-binning.
32
+ array_name: str, optional
33
+ Name of the telescope array configuration.
34
+ """
35
+ _logger.info(f"Plotting histograms written to {output_path}")
36
+
37
+ plots = _generate_plot_configurations(histograms, limits)
38
+ _execute_plotting_loop(plots, output_path, rebin_factor, array_name)
39
+
40
+
41
+ def _get_limits(name, limits):
42
+ """
43
+ Extract limits from the provided dictionary for plotting.
44
+
45
+ Fine tuned to expected histograms to be plotted.
46
+ """
47
+
48
+ def _safe_value(limits, key):
49
+ val = limits.get(key)
50
+ return getattr(val, "value", None)
51
+
52
+ mapping = {
53
+ "energy": {"x": _safe_value(limits, "lower_energy_limit")},
54
+ "core_distance": {"x": _safe_value(limits, "upper_radius_limit")},
55
+ "angular_distance": {"x": _safe_value(limits, "viewcone_radius")},
56
+ "core_vs_energy": {
57
+ "x": _safe_value(limits, "upper_radius_limit"),
58
+ "y": _safe_value(limits, "lower_energy_limit"),
59
+ },
60
+ "angular_distance_vs_energy": {
61
+ "x": _safe_value(limits, "viewcone_radius"),
62
+ "y": _safe_value(limits, "lower_energy_limit"),
63
+ },
64
+ "x_core_shower_vs_y_core_shower": {"r": _safe_value(limits, "upper_radius_limit")},
65
+ }
66
+ return mapping.get(name)
67
+
68
+
69
+ def _generate_plot_configurations(histograms, limits):
70
+ """Generate plot configurations for all histogram types."""
71
+ hist_1d_params = {"color": "tab:green", "edgecolor": "tab:green", "lw": 1}
72
+ hist_2d_params = {"norm": "log", "cmap": "viridis", "show_contour": False}
73
+ hist_2d_normalized_params = {"norm": "linear", "cmap": "viridis", "show_contour": True}
74
+ plots = {}
75
+ for name, hist in histograms.items():
76
+ if hist["histogram"] is None:
77
+ continue
78
+ if hist["1d"]:
79
+ plots[name] = _create_1d_plot_config(
80
+ hist, name=name, plot_params=hist_1d_params, limits=limits
81
+ )
82
+ else:
83
+ if "cumulative" in name or "efficiency" in name:
84
+ plot_params = hist_2d_normalized_params
85
+ else:
86
+ plot_params = hist_2d_params
87
+
88
+ plots[name] = _create_2d_plot_config(
89
+ hist, name=name, plot_params=plot_params, limits=limits
90
+ )
91
+ return plots
92
+
93
+
94
+ def _get_axis_title(axis_titles, axis):
95
+ """Return axis title for given axis."""
96
+ if axis_titles is None:
97
+ return None
98
+ if axis == "x" and len(axis_titles) > 0:
99
+ return axis_titles[0]
100
+ if axis == "y" and len(axis_titles) > 1:
101
+ return axis_titles[1]
102
+ if axis == "z" and len(axis_titles) > 2:
103
+ return axis_titles[2]
104
+ return None
105
+
106
+
107
+ def _create_1d_plot_config(histogram, name, plot_params, limits):
108
+ """Create a 1D plot configuration."""
109
+ _logger.debug(f"Creating plot config for {name} with params: {plot_params}")
110
+ return {
111
+ "data": histogram["histogram"],
112
+ "bins": histogram["bin_edges"],
113
+ "plot_type": "histogram",
114
+ "plot_params": plot_params,
115
+ "labels": {
116
+ "x": _get_axis_title(histogram.get("axis_titles"), "x"),
117
+ "y": _get_axis_title(histogram.get("axis_titles"), "y"),
118
+ "title": f"{histogram['title']}: {name.replace('_', ' ')}",
119
+ },
120
+ "scales": histogram["plot_scales"],
121
+ "lines": _get_limits(name, limits) if limits else {},
122
+ "filename": name,
123
+ }
124
+
125
+
126
+ def _create_2d_plot_config(histogram, name, plot_params, limits):
127
+ """Create a 2D plot configuration."""
128
+ _logger.debug(f"Creating plot config for {name} with params: {plot_params}")
129
+ return {
130
+ "data": histogram["histogram"],
131
+ "bins": [histogram["bin_edges"][0], histogram["bin_edges"][1]],
132
+ "plot_type": "histogram2d",
133
+ "plot_params": plot_params,
134
+ "labels": {
135
+ "x": _get_axis_title(histogram.get("axis_titles"), "x"),
136
+ "y": _get_axis_title(histogram.get("axis_titles"), "y"),
137
+ "title": f"{histogram['title']}: {name.replace('_', ' ')}",
138
+ },
139
+ "lines": _get_limits(name, limits) if limits else {},
140
+ "scales": histogram["plot_scales"],
141
+ "colorbar_label": _get_axis_title(histogram.get("axis_titles"), "z"),
142
+ "filename": name,
143
+ }
144
+
145
+
146
+ def _execute_plotting_loop(plots, output_path, rebin_factor, array_name):
147
+ """Execute the main plotting loop for all plot configurations."""
148
+ for plot_key, plot_args in plots.items():
149
+ plot_filename = plot_args.pop("filename")
150
+
151
+ if plot_args.get("data") is None:
152
+ _logger.warning(f"Skipping plot {plot_key} - no data available")
153
+ continue
154
+
155
+ if array_name and plot_args.get("labels", {}).get("title"):
156
+ plot_args["labels"]["title"] += f" ({array_name} array)"
157
+
158
+ filename = _build_plot_filename(plot_filename, array_name)
159
+ output_file = output_path / filename if output_path else None
160
+ result = _create_plot(**plot_args, output_file=output_file)
161
+
162
+ # Skip re-binned plot if main plot failed
163
+ if result is None:
164
+ continue
165
+
166
+ if _should_create_rebinned_plot(rebin_factor, plot_args, plot_key):
167
+ _create_rebinned_plot(plot_args, filename, output_path, rebin_factor)
168
+
169
+
170
+ def _build_plot_filename(base_filename, array_name=None):
171
+ """
172
+ Build the full plot filename with appropriate extensions.
173
+
174
+ Parameters
175
+ ----------
176
+ base_filename : str
177
+ The base filename without extension
178
+ array_name : str, optional
179
+ Name of the array to append to filename
180
+
181
+ Returns
182
+ -------
183
+ str
184
+ Complete filename with extension
185
+ """
186
+ return f"{base_filename}_{array_name}.png" if array_name else f"{base_filename}.png"
187
+
188
+
189
+ def _should_create_rebinned_plot(rebin_factor, plot_args, plot_key):
190
+ """
191
+ Check if a re-binned version of the plot should be created.
192
+
193
+ Parameters
194
+ ----------
195
+ rebin_factor : int
196
+ Factor by which to rebin the energy axis
197
+ plot_args : dict
198
+ Plot arguments
199
+ plot_key : str
200
+ Key identifying the plot type
201
+
202
+ Returns
203
+ -------
204
+ bool
205
+ True if a re-binned plot should be created, False otherwise
206
+ """
207
+ return (
208
+ rebin_factor > 1
209
+ and plot_args["plot_type"] == "histogram2d"
210
+ and plot_key.endswith("_cumulative")
211
+ and plot_args.get("plot_params", {}).get("norm") == "linear"
212
+ )
213
+
214
+
215
+ def _create_rebinned_plot(plot_args, filename, output_path, rebin_factor):
216
+ """
217
+ Create a re-binned version of a 2D histogram plot.
218
+
219
+ Parameters
220
+ ----------
221
+ plot_args : dict
222
+ Plot arguments for the original plot
223
+ filename : str
224
+ Filename of the original plot
225
+ output_path : Path or None
226
+ Path to save the plot to, or None
227
+ rebin_factor : int
228
+ Factor by which to rebin the energy axis
229
+ """
230
+ data = plot_args["data"]
231
+ bins = plot_args["bins"]
232
+
233
+ rebinned_data, rebinned_x_bins, rebinned_y_bins = SimtelIOEventHistograms.rebin_2d_histogram(
234
+ data, bins[0], bins[1], rebin_factor
235
+ )
236
+
237
+ rebinned_plot_args = plot_args.copy()
238
+ rebinned_plot_args["data"] = rebinned_data
239
+ rebinned_plot_args["bins"] = [rebinned_x_bins, rebinned_y_bins]
240
+
241
+ if rebinned_plot_args.get("labels", {}).get("title"):
242
+ rebinned_plot_args["labels"]["title"] += f" (Energy rebinned {rebin_factor}x)"
243
+
244
+ rebinned_filename = f"{filename.replace('.png', '')}_rebinned.png"
245
+ rebinned_output_file = output_path / rebinned_filename if output_path else None
246
+ _create_plot(**rebinned_plot_args, output_file=rebinned_output_file)
247
+
248
+
249
+ def _create_plot(
250
+ data,
251
+ bins=None,
252
+ plot_type="histogram",
253
+ plot_params=None,
254
+ labels=None,
255
+ scales=None,
256
+ colorbar_label=None,
257
+ output_file=None,
258
+ lines=None,
259
+ ):
260
+ """Create and save a plot with the given parameters."""
261
+ plot_params = plot_params or {}
262
+ labels = labels or {}
263
+ scales = scales or {}
264
+ lines = lines or {}
265
+
266
+ if not _has_data(data):
267
+ return None
268
+
269
+ fig, ax = plt.subplots(figsize=(8, 6))
270
+ _plot_data(ax, data, bins, plot_type, plot_params, colorbar_label)
271
+ _add_lines(ax, lines)
272
+ ax.set(
273
+ xlabel=labels.get("x", ""),
274
+ ylabel=labels.get("y", ""),
275
+ title=labels.get("title", ""),
276
+ xscale=scales.get("x", "linear"),
277
+ yscale=scales.get("y", "linear"),
278
+ )
279
+ if output_file:
280
+ _logger.info(f"Saving plot to {output_file}")
281
+ fig.savefig(output_file, dpi=300, bbox_inches="tight")
282
+ plt.close(fig)
283
+ else:
284
+ plt.tight_layout()
285
+ plt.show()
286
+
287
+ return fig
288
+
289
+
290
+ def _has_data(data):
291
+ """Check that the data for plotting is not None or empty."""
292
+ if data is None or (isinstance(data, np.ndarray) and data.size == 0):
293
+ _logger.warning("No data available for plotting")
294
+ return False
295
+ return True
296
+
297
+
298
+ def _plot_data(ax, data, bins, plot_type, plot_params, colorbar_label):
299
+ """Plot the data on the given axes."""
300
+ if plot_type == "histogram":
301
+ ax.bar(bins[:-1], data, width=np.diff(bins), **plot_params)
302
+ elif plot_type == "histogram2d":
303
+ pcm = _create_2d_histogram_plot(data, bins, plot_params)
304
+ plt.colorbar(pcm, label=colorbar_label)
305
+
306
+
307
+ def _add_lines(ax, lines):
308
+ """Add reference lines to the plot."""
309
+ if lines.get("x") is not None:
310
+ ax.axvline(lines["x"], color="r", linestyle="--", linewidth=0.5)
311
+ if lines.get("y") is not None:
312
+ ax.axhline(lines["y"], color="r", linestyle="--", linewidth=0.5)
313
+ if lines.get("r") is not None:
314
+ ax.add_artist(
315
+ plt.Circle((0, 0), lines["r"], color="r", fill=False, linestyle="--", linewidth=0.5)
316
+ )
317
+
318
+
319
+ def _create_2d_histogram_plot(data, bins, plot_params):
320
+ """
321
+ Create a 2D histogram plot with the given parameters.
322
+
323
+ Parameters
324
+ ----------
325
+ data : np.ndarray
326
+ 2D histogram data
327
+ bins : tuple of np.ndarray
328
+ Bin edges for x and y axes
329
+ plot_params : dict
330
+ Plot parameters including norm, cmap, and show_contour
331
+
332
+ Returns
333
+ -------
334
+ matplotlib.collections.QuadMesh
335
+ The created pcolormesh object for colorbar attachment
336
+ """
337
+ if plot_params.get("norm") == "linear":
338
+ pcm = plt.pcolormesh(
339
+ bins[0],
340
+ bins[1],
341
+ data.T,
342
+ vmin=0,
343
+ vmax=1,
344
+ cmap=plot_params.get("cmap", "viridis"),
345
+ )
346
+ # Add contour line at value=1.0 for normalized histograms
347
+ if plot_params.get("show_contour", True):
348
+ x_centers = (bins[0][1:] + bins[0][:-1]) / 2
349
+ y_centers = (bins[1][1:] + bins[1][:-1]) / 2
350
+ x_mesh, y_mesh = np.meshgrid(x_centers, y_centers)
351
+ plt.contour(
352
+ x_mesh,
353
+ y_mesh,
354
+ data.T,
355
+ levels=[0.999999], # very close to 1 for floating point precision
356
+ colors=["tab:red"],
357
+ linestyles=["--"],
358
+ linewidths=[0.5],
359
+ )
360
+ else:
361
+ # Handle empty or invalid data for logarithmic scaling
362
+ data_max = data.max()
363
+ if data_max <= 0:
364
+ _logger.warning("No positive data found for logarithmic scaling, using linear scale")
365
+ pcm = plt.pcolormesh(
366
+ bins[0], bins[1], data.T, vmin=0, vmax=max(1, data_max), cmap="viridis"
367
+ )
368
+ else:
369
+ # Ensure vmin is less than vmax for LogNorm
370
+ vmin = max(1, data[data > 0].min()) if np.any(data > 0) else 1
371
+ vmax = max(vmin + 1, data_max)
372
+ pcm = plt.pcolormesh(
373
+ bins[0], bins[1], data.T, norm=LogNorm(vmin=vmin, vmax=vmax), cmap="viridis"
374
+ )
375
+
376
+ return pcm
@@ -4,6 +4,7 @@
4
4
  import logging
5
5
  import re
6
6
  from collections import OrderedDict
7
+ from pathlib import Path
7
8
 
8
9
  import astropy.units as u
9
10
  import matplotlib.pyplot as plt
@@ -641,9 +642,6 @@ def save_figure(fig, output_file, figure_format=None, log_title="", dpi="figure"
641
642
  title: str
642
643
  Title of the figure to be added to the log message.
643
644
  """
644
- # pylint: disable=import-outside-toplevel
645
- from pathlib import Path
646
-
647
645
  figure_format = figure_format or ["pdf", "png"]
648
646
  for fmt in figure_format:
649
647
  _file = Path(output_file).with_suffix(f".{fmt}")
@@ -1,187 +0,0 @@
1
- #!/usr/bin/python3
2
-
3
- r"""
4
- Calculates array or single-telescope trigger rates.
5
-
6
- The applications reads from a sim_telarray output file, a list of
7
- sim_telarray output files ou from a file containing a list of sim_telarray files.
8
-
9
-
10
- Command line arguments
11
- ----------------------
12
- simtel_file_names (str or list):
13
- Path to the sim_telarray file or a list of sim_telarray output files.
14
- Files can be generated in `simulate_prod` using the ``--save_file_lists`` option.
15
- save_tables (bool):
16
- If true, save the tables with the energy-dependent trigger rate to a ecsv file.
17
- area_from_distribution (bool):
18
- If true, the area thrown (the area in which the simulated events are distributed)
19
- in the trigger rate calculation is estimated based on the event distribution.
20
- The expected shape of the distribution of events as function of the core distance is triangular
21
- up to the maximum distance. The weighted mean radius of the triangular distribution is 2/3 times
22
- the upper edge. Therefore, when using the ``area_from_distribution`` flag, the mean distance
23
- times 3/2, returns just the position of the upper edge in the triangle distribution with little
24
- impact of the binning and little dependence on the scatter area defined in the simulation.
25
- This is special useful when calculating trigger rate for individual telescopes.
26
- If false, the area thrown is estimated based on the maximum distance as given in
27
- the simulation configuration.
28
-
29
- Example
30
- -------
31
- Calculate trigger rate from sim_telarray file
32
-
33
- .. code-block:: console
34
-
35
- simtools-calculate-trigger-rate --simtel_file_names tests/resources/ \\
36
- run201_proton_za20deg_azm0deg_North_test_layout_test-prod.simtel.zst
37
-
38
- Expected final print-out message:
39
-
40
- .. code-block:: console
41
-
42
- System trigger rate (Hz): 9.0064e+03 pm 9.0087e+03 Hz
43
-
44
- """
45
-
46
- import logging
47
- from pathlib import Path
48
-
49
- import simtools.utils.general as gen
50
- from simtools.configuration import configurator
51
- from simtools.io import io_handler
52
- from simtools.simtel.simtel_io_histograms import SimtelIOHistograms
53
-
54
-
55
- def _parse(label, description):
56
- """
57
- Parse command line configuration.
58
-
59
- Parameters
60
- ----------
61
- label: str
62
- Label describing the application.
63
- description: str
64
- Description of the application.
65
-
66
- Returns
67
- -------
68
- CommandLineParser
69
- Command line parser object
70
-
71
- """
72
- config = configurator.Configurator(label=label, description=description)
73
-
74
- config.parser.add_argument(
75
- "--simtel_file_names",
76
- help="Name of the sim_telarray output files to be calculate the trigger rate from or the "
77
- "text file containing the list of sim_telarray output files.",
78
- nargs="+",
79
- required=True,
80
- type=str,
81
- )
82
-
83
- config.parser.add_argument(
84
- "--save_tables",
85
- help="Save trigger rates per energy bin into ECSV files.",
86
- action="store_true",
87
- )
88
-
89
- config.parser.add_argument(
90
- "--area_from_distribution",
91
- help="Calculate trigger rates using the event distribution.",
92
- action="store_true",
93
- )
94
-
95
- config.parser.add_argument(
96
- "--stack_files",
97
- help="Stacks all histograms.",
98
- action="store_true",
99
- )
100
-
101
- config_parser, _ = config.initialize(
102
- db_config=False,
103
- paths=True,
104
- simulation_configuration={"corsika_configuration": ["energy_range", "view_cone"]},
105
- )
106
-
107
- return config_parser
108
-
109
-
110
- def _get_simulation_parameters(config_parser):
111
- """
112
- Get energy range and view cone in the correct form to use in the simtel classes.
113
-
114
- Parameters
115
- ----------
116
- CommandLineParser:
117
- Command line parser object as defined by the _parse function.
118
-
119
- Returns
120
- -------
121
- list:
122
- The energy range used in the simulation.
123
- list:
124
- The view cone used in the simulation.
125
-
126
- """
127
-
128
- def convert(param, unit):
129
- return [param[0].to(unit).value, param[1].to(unit).value] if param else None
130
-
131
- return convert(config_parser.get("energy_range"), "TeV"), convert(
132
- config_parser.get("view_cone"), "deg"
133
- )
134
-
135
-
136
- def main(): # noqa: D103
137
- label = Path(__file__).stem
138
- description = (
139
- "Calculates the simulated and triggered event rate based on sim_telarray output files."
140
- )
141
- config_parser = _parse(label, description)
142
-
143
- logger = logging.getLogger()
144
- logger.setLevel(gen.get_log_level_from_user(config_parser["log_level"]))
145
-
146
- sim_telarray_files = gen.get_list_of_files_from_command_line(
147
- config_parser["simtel_file_names"], [".zst", ".simtel", ".hdata"]
148
- )
149
- energy_range, view_cone = _get_simulation_parameters(config_parser)
150
-
151
- histograms = SimtelIOHistograms(
152
- sim_telarray_files,
153
- area_from_distribution=config_parser["area_from_distribution"],
154
- energy_range=energy_range,
155
- view_cone=view_cone,
156
- )
157
-
158
- logger.info("Calculating simulated and triggered event rate")
159
- (
160
- sim_event_rates,
161
- triggered_event_rates,
162
- triggered_event_rate_uncertainties,
163
- trigger_rate_in_tables,
164
- ) = histograms.calculate_trigger_rates(
165
- print_info=True, stack_files=config_parser["stack_files"]
166
- )
167
-
168
- # Print out results
169
- for i_hist, _ in enumerate(sim_event_rates):
170
- print(f"\nFile {histograms.histogram_files[i_hist]}\n")
171
- print(
172
- f"System trigger rate (Hz): {triggered_event_rates[i_hist].value:.4e} \u00b1 "
173
- f"{triggered_event_rate_uncertainties[i_hist].value:.4e} Hz"
174
- )
175
- if config_parser["save_tables"]:
176
- io_handler_instance = io_handler.IOHandler()
177
- output_path = io_handler_instance.get_output_directory(label, sub_dir="application-plots")
178
- for i_table, table in enumerate(trigger_rate_in_tables):
179
- output_file = (
180
- str(output_path.joinpath(Path(sim_telarray_files[i_table]).stem)) + ".ecsv"
181
- )
182
- logger.info(f"Writing table {i_table + 1} to {output_file}")
183
- table.write(output_file, overwrite=True)
184
-
185
-
186
- if __name__ == "__main__":
187
- main()