gammasimtools 0.5.1__py3-none-any.whl → 0.6.1__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 (78) hide show
  1. {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/METADATA +80 -28
  2. gammasimtools-0.6.1.dist-info/RECORD +91 -0
  3. {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/entry_points.txt +4 -2
  5. simtools/_version.py +14 -2
  6. simtools/applications/add_file_to_db.py +2 -1
  7. simtools/applications/compare_cumulative_psf.py +10 -15
  8. simtools/applications/db_development_tools/add_new_parameter_to_db.py +12 -6
  9. simtools/applications/derive_mirror_rnda.py +95 -71
  10. simtools/applications/generate_corsika_histograms.py +216 -131
  11. simtools/applications/generate_default_metadata.py +110 -0
  12. simtools/applications/generate_simtel_array_histograms.py +192 -0
  13. simtools/applications/get_file_from_db.py +1 -1
  14. simtools/applications/get_parameter.py +3 -3
  15. simtools/applications/make_regular_arrays.py +89 -93
  16. simtools/applications/{plot_layout_array.py → plot_array_layout.py} +15 -14
  17. simtools/applications/print_array_elements.py +81 -34
  18. simtools/applications/produce_array_config.py +2 -2
  19. simtools/applications/production.py +39 -5
  20. simtools/applications/sim_showers_for_trigger_rates.py +26 -30
  21. simtools/applications/simulate_prod.py +49 -107
  22. simtools/applications/submit_data_from_external.py +8 -10
  23. simtools/applications/tune_psf.py +16 -18
  24. simtools/applications/validate_camera_efficiency.py +63 -9
  25. simtools/applications/validate_camera_fov.py +9 -13
  26. simtools/applications/validate_file_using_schema.py +127 -0
  27. simtools/applications/validate_optics.py +13 -15
  28. simtools/camera_efficiency.py +73 -80
  29. simtools/configuration/commandline_parser.py +52 -22
  30. simtools/configuration/configurator.py +98 -33
  31. simtools/constants.py +9 -0
  32. simtools/corsika/corsika_config.py +28 -22
  33. simtools/corsika/corsika_default_config.py +282 -0
  34. simtools/corsika/corsika_histograms.py +328 -282
  35. simtools/corsika/corsika_histograms_visualize.py +162 -163
  36. simtools/corsika/corsika_runner.py +8 -4
  37. simtools/corsika_simtel/corsika_simtel_runner.py +18 -23
  38. simtools/data_model/data_reader.py +129 -0
  39. simtools/data_model/metadata_collector.py +346 -118
  40. simtools/data_model/metadata_model.py +123 -218
  41. simtools/data_model/model_data_writer.py +79 -22
  42. simtools/data_model/validate_data.py +96 -46
  43. simtools/db_handler.py +67 -42
  44. simtools/io_operations/__init__.py +0 -0
  45. simtools/io_operations/hdf5_handler.py +112 -0
  46. simtools/{io_handler.py → io_operations/io_handler.py} +51 -22
  47. simtools/job_execution/job_manager.py +1 -1
  48. simtools/layout/{layout_array.py → array_layout.py} +168 -199
  49. simtools/layout/geo_coordinates.py +196 -0
  50. simtools/layout/telescope_position.py +12 -12
  51. simtools/model/array_model.py +16 -14
  52. simtools/model/camera.py +5 -8
  53. simtools/model/mirrors.py +136 -73
  54. simtools/model/model_utils.py +1 -69
  55. simtools/model/telescope_model.py +32 -25
  56. simtools/psf_analysis.py +26 -19
  57. simtools/ray_tracing.py +54 -26
  58. simtools/schemas/data.metaschema.yml +400 -0
  59. simtools/schemas/metadata.metaschema.yml +566 -0
  60. simtools/simtel/simtel_config_writer.py +14 -5
  61. simtools/simtel/simtel_histograms.py +266 -83
  62. simtools/simtel/simtel_runner.py +8 -7
  63. simtools/simtel/simtel_runner_array.py +7 -8
  64. simtools/simtel/simtel_runner_camera_efficiency.py +48 -2
  65. simtools/simtel/simtel_runner_ray_tracing.py +61 -25
  66. simtools/simulator.py +43 -50
  67. simtools/utils/general.py +232 -286
  68. simtools/utils/geometry.py +163 -0
  69. simtools/utils/names.py +294 -142
  70. simtools/visualization/legend_handlers.py +115 -9
  71. simtools/visualization/visualize.py +13 -13
  72. gammasimtools-0.5.1.dist-info/RECORD +0 -83
  73. simtools/applications/plot_simtel_histograms.py +0 -120
  74. simtools/applications/validate_schema_files.py +0 -135
  75. simtools/corsika/corsika_output_visualize.py +0 -345
  76. simtools/data_model/validate_schema.py +0 -285
  77. {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/LICENSE +0 -0
  78. {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/top_level.txt +0 -0
@@ -6,13 +6,12 @@
6
6
  This application produces a set of histograms of the distribution of Cherenkov photons on the
7
7
  ground (at observation level) read from the CORSIKA IACT output file provided as input.
8
8
 
9
- The histograms can be saved both in a png and in a ecsv file. By default, it saves in both
10
- formats.
9
+ The histograms can be saved both into pdfs and in a hdf5 file.
11
10
 
12
11
  The following 2D histograms are produced:
13
12
  - Number of Cherenkov photons on the ground;
14
13
  - Density of Cherenkov photons on the ground;
15
- - Incoming direction (directive cosinus) of the Cherenkov photons;
14
+ - Incoming direction (directive cosines) of the Cherenkov photons;
16
15
  - Time of arrival (ns) vs altitude of production (km);
17
16
  - Number of Cherenkov photons per event per telescope.
18
17
 
@@ -25,10 +24,13 @@
25
24
  - Number of photons per telescope;
26
25
  - Number of photons per event.
27
26
 
27
+ Histograms for the distribution of CORSIKA event header elements can also be generated by using
28
+ the `--event_1d_histograms` and `--event_2d_histograms` arguments. The accepted arguments (keys)
29
+ are to be found in the CORSIKA manual, e.g., "total_energy", "zenith", "azimuth".
28
30
 
29
31
  Command line arguments
30
32
  ----------------------
31
- IACT_file (str, required)
33
+ iact_file (str, required)
32
34
  The name of the CORSIKA IACT file resulted from the CORSIKA simulation.
33
35
 
34
36
  telescope_indices (list, optional)
@@ -40,88 +42,106 @@
40
42
  Indicates whether single histograms are generated for the individual telescopes, or if
41
43
  a master histogram is generated for all the telescopes together.
42
44
  If the argument is not given, the Cherenkov photons from the given telescopes are considered
43
- together in the same histograms.
45
+ together in the same histograms.
44
46
 
45
- hist_config (ecsv or dict, optional)
47
+ hist_config (hdf5 or dict, optional)
46
48
  The configuration used for generating the histograms.
47
49
  It includes information about the bin sizes, the ranges, scale of the plot and units.
48
50
  By construction, three major histograms are created to start with:
49
- - hist_direction (2D): Directive cosinus (x and y) for the incoming photohs;
50
- - hist_position (3D): position x, position y, and wavelength;
51
- - hist_time_altitude (2D): time of arrival and altitude of emission;
51
+ - hist_direction (2D): Directive cosines (x and y) for the incoming photons;
52
+ - hist_position (3D): position x, position y, and wavelength;
53
+ - hist_time_altitude (2D): time of arrival and altitude of emission;
52
54
 
53
- If the argument is not given, the default configuration is generated:
55
+ If the argument is not given, the default configuration is generated:
54
56
 
55
- .. code-block:: console
56
- hist_direction:
57
- x axis: {bins: 100, scale: linear, start: -1, stop: 1}
58
- y axis: {bins: 100, scale: linear, start: -1, stop: 1}
57
+ .. code-block:: console
58
+
59
+ hist_direction:
60
+ x axis: {bins: 100, scale: linear, start: -1, stop: 1}
61
+ y axis: {bins: 100, scale: linear, start: -1, stop: 1}
59
62
 
60
- hist_position:
61
- x axis:
63
+ hist_position:
64
+ x axis:
62
65
  bins: 100
63
66
  scale: linear
64
67
  start: !astropy.units.Quantity
65
- unit: &id001 !astropy.units.Unit {unit: m}
66
- value: -1000.0
68
+ unit: &id001 !astropy.units.Unit {unit: m}
69
+ value: -1000.0
67
70
  stop: &id002 !astropy.units.Quantity
68
- unit: *id001
69
- value: 1000.0
70
- y axis:
71
+ unit: *id001
72
+ value: 1000.0
73
+ y axis:
71
74
  bins: 100
72
75
  scale: linear
73
76
  start: !astropy.units.Quantity
74
- unit: *id001
75
- value: -1000.0
77
+ unit: *id001
78
+ value: -1000.0
76
79
  stop: *id002
77
- z axis:
80
+ z axis:
78
81
  bins: 80
79
82
  scale: linear
80
83
  start: !astropy.units.Quantity
81
- unit: &id003 !astropy.units.Unit {unit: nm}
82
- value: 200.0
84
+ unit: &id003 !astropy.units.Unit {unit: nm}
85
+ value: 200.0
83
86
  stop: !astropy.units.Quantity
84
- unit: *id003
85
- value: 1000.0
86
- hist_time_altitude:
87
- x axis:
87
+ unit: *id003
88
+ value: 1000.0
89
+ hist_time_altitude:
90
+ x axis:
88
91
  bins: 100
89
92
  scale: linear
90
93
  start: !astropy.units.Quantity
91
- unit: &id004 !astropy.units.Unit {unit: ns}
92
- value: -2000.0
94
+ unit: &id004 !astropy.units.Unit {unit: ns}
95
+ value: -2000.0
93
96
  stop: !astropy.units.Quantity
94
- unit: *id004
95
- value: 2000.0
96
- y axis:
97
+ unit: *id004
98
+ value: 2000.0
99
+ y axis:
97
100
  bins: 100
98
101
  scale: linear
99
102
  start: !astropy.units.Quantity
100
- unit: &id005 !astropy.units.Unit {unit: km}
101
- value: 120.0
103
+ unit: &id005 !astropy.units.Unit {unit: km}
104
+ value: 120.0
102
105
  stop: !astropy.units.Quantity
103
- unit: *id005
104
- value: 0.0
106
+ unit: *id005
107
+ value: 0.0
108
+
109
+
110
+ pdf (bool, optional)
111
+ If set, histograms are saved into pdf files.
112
+ One pdf file contains all the histograms for the Cherenkov photons.
113
+ The name of the file is controlled via `hdf5_file_name`.
114
+ If event_1d_histograms and event_2d_histograms are used, two separate pdf files might be
115
+ created to accommodate the histograms for the CORSIKA event header elements. The core names
116
+ of these output pdf files are also given by `hdf5_file_name` argument with the addition of
117
+ 'event_1d_histograms' and 'event_2d_histograms'.
118
+
119
+
120
+ hdf5 (bool, optional)
121
+ If set, histograms are saved into hdf5 files.
105
122
 
106
- png (bool, optional)
107
- If true, histograms are saved into png files.
123
+ hdf5_file_name (str, optional)
124
+ The name of the output hdf5 data (without the path).
125
+ It requires the `--hdf5` flag.
126
+ If not given, `hdf5_file_name` takes the name from the input IACT file (`input_file`).
127
+ If the output `hdf5_file_name` file already exists, the tables associated to the chosen
128
+ flags (e.g. `hdf5`, `event_1d_histograms`, `event_2d_histograms`) will be overwritten. The
129
+ remaining tables, if any, will stay untouched.
108
130
 
109
- ecsv (bool, optional)
110
- If true, histograms are saved into ecsv files.
111
131
 
112
- event_1D_histograms (str, optional)
113
- Produce 1D histograms for elements given in `--event_1D_histograms` from the CORSIKA event
114
- header and save into ecsv/png files.
132
+ event_1d_histograms (str, optional)
133
+ Produce 1D histograms for elements given in `--event_1d_histograms` from the CORSIKA event
134
+ header and save into hdf5/pdf files.
115
135
  It allows more than one argument, separated by simple spaces.
116
- Usage: `--event_1D_histograms first_interaction_height total_energy`.
136
+ Usage: `--event_1d_histograms first_interaction_height total_energy`.
117
137
 
118
- event_2D_histograms (str, optional)
119
- Produce 2D histograms for elements given in `--event_2D_histograms` from the CORSIKA event
120
- header and save into ecsv/png files.
138
+ event_2d_histograms (str, optional)
139
+ Produce 2D histograms for elements given in `--event_2d_histograms` from the CORSIKA event
140
+ header and save into hdf5/pdf files.
121
141
  It allows more than one argument, separated by simple spaces.
122
142
  The elements are grouped into pairs and the 2D histograms are produced always for two
123
143
  subsequent elements.
124
- For example, `--event_2D_histograms first_interaction_height total_energy zenith azimuth`
144
+ For example, `--event_2d_histograms first_interaction_height total_energy zenith azimuth`
125
145
  will produce one 2D histogram for `first_interaction_height` `total_energy` and another 2D
126
146
  histogram for `zenith` and `azimuth`.
127
147
 
@@ -131,29 +151,31 @@
131
151
 
132
152
  .. code-block:: console
133
153
 
134
- simtools-generate-corsika-histograms --IACT_file /workdir/external/simtools/tests/\
135
- resources/tel_output_10GeV-2-gamma-20deg-CTAO-South.corsikaio --png --ecsv
136
- --event_2D_histograms zenith azimuth --event_1D_histograms total_energy
154
+ simtools-generate-corsika-histograms --iact_file /workdir/external/simtools/tests/\
155
+ resources/tel_output_10GeV-2-gamma-20deg-CTAO-South.corsikaio --pdf --hdf5
156
+ --event_2d_histograms zenith azimuth --event_1d_histograms total_energy
137
157
 
138
158
 
139
159
  Expected final print-out message:
140
160
 
141
161
  .. code-block:: console
162
+
142
163
  INFO::generate_corsika_histograms(l358)::main::Finalizing the application.
143
164
  Total time needed: 8s.
144
165
  """
145
166
 
146
167
  import logging
168
+ import re
147
169
  import time
148
170
  from pathlib import Path
149
171
 
150
172
  import numpy as np
151
173
 
152
174
  import simtools.utils.general as gen
153
- from simtools import io_handler
154
175
  from simtools.configuration import configurator
155
176
  from simtools.corsika import corsika_histograms_visualize
156
177
  from simtools.corsika.corsika_histograms import CorsikaHistograms
178
+ from simtools.io_operations import io_handler
157
179
 
158
180
  logger = logging.getLogger()
159
181
 
@@ -178,7 +200,7 @@ def _parse(label, description):
178
200
  config = configurator.Configurator(label=label, description=description)
179
201
 
180
202
  config.parser.add_argument(
181
- "--IACT_file",
203
+ "--iact_file",
182
204
  help="Name of the CORSIKA IACT file from which to generate the histograms.",
183
205
  type=str,
184
206
  required=True,
@@ -204,35 +226,43 @@ def _parse(label, description):
204
226
 
205
227
  config.parser.add_argument(
206
228
  "--hist_config",
207
- help="ecsv file with the configuration parameters to create the histograms.",
229
+ help="hdf5 file with the configuration parameters to create the histograms.",
208
230
  type=str,
209
231
  required=False,
210
232
  default=None,
211
233
  )
212
234
 
213
235
  config.parser.add_argument(
214
- "--png", help="Save histograms into png files.", action="store_true", required=False
236
+ "--pdf", help="Save histograms into a pdf file.", action="store_true", required=False
215
237
  )
216
238
 
217
239
  config.parser.add_argument(
218
- "--ecsv", help="Save histograms into ecsv files.", action="store_true", required=False
240
+ "--hdf5", help="Save histograms into hdf5 files.", action="store_true", required=False
219
241
  )
220
242
 
221
243
  config.parser.add_argument(
222
- "--event_1D_histograms",
244
+ "--hdf5_file_name",
245
+ help="Name of the hdf5 file where to save the histograms.",
246
+ type=str,
247
+ required=False,
248
+ default=None,
249
+ )
250
+
251
+ config.parser.add_argument(
252
+ "--event_1d_histograms",
223
253
  help="The keys from the CORSIKA event header to be used for the generation of 1D "
224
- "histograms. The available choices can been found in the `all_event_keys` attribute of"
225
- "the CorsikaHistograms.",
254
+ "histograms. The available choices can been found in the `all_event_keys` attribute of"
255
+ "the CorsikaHistograms.",
226
256
  required=False,
227
257
  default=None,
228
258
  nargs="*",
229
259
  )
230
260
 
231
261
  config.parser.add_argument(
232
- "--event_2D_histograms",
262
+ "--event_2d_histograms",
233
263
  help="The keys from the CORSIKA event header to be used for the generation of 2D "
234
- "histograms. The available choices can been found in the `all_event_keys` attribute of"
235
- "the CorsikaHistograms.",
264
+ "histograms. The available choices can been found in the `all_event_keys` attribute of"
265
+ "the CorsikaHistograms.",
236
266
  required=False,
237
267
  default=None,
238
268
  nargs="*",
@@ -240,13 +270,21 @@ def _parse(label, description):
240
270
 
241
271
  config_parser, _ = config.initialize(db_config=False, paths=True)
242
272
 
243
- if not config_parser["png"] and not config_parser["ecsv"]:
244
- config.parser.error("At least one argument between `--png` and `--ecsv` is required.")
273
+ if not config_parser["pdf"]:
274
+ if (
275
+ not config_parser["hdf5"]
276
+ and not config_parser["event_1d_histograms"]
277
+ and not config_parser["event_2d_histograms"]
278
+ ):
279
+ config.parser.error(
280
+ "At least one argument is required: `--pdf`, `--hdf5`, `--event_1d_histograms`, or "
281
+ "`--event_2d_histograms`."
282
+ )
245
283
 
246
284
  return config_parser, _
247
285
 
248
286
 
249
- def _plot_figures(corsika_histograms_instance):
287
+ def _plot_figures(corsika_histograms_instance, test=False):
250
288
  """
251
289
  Auxiliary function to centralize the plotting functions.
252
290
 
@@ -254,6 +292,8 @@ def _plot_figures(corsika_histograms_instance):
254
292
  ----------
255
293
  corsika_histograms_instance: `CorsikaHistograms` instance.
256
294
  The CorsikaHistograms instance created in main.
295
+ test: bool
296
+ If true plots the figures for the first two functions only.
257
297
  """
258
298
 
259
299
  plot_function_names = [
@@ -262,87 +302,114 @@ def _plot_figures(corsika_histograms_instance):
262
302
  if plotting_method.startswith("plot_")
263
303
  and "event_header_distribution" not in plotting_method
264
304
  ]
305
+ if test:
306
+ plot_function_names = plot_function_names[:2]
265
307
 
308
+ figure_list = []
266
309
  for function_name in plot_function_names:
267
310
  function = getattr(corsika_histograms_visualize, function_name)
268
- figures, figure_names = function(corsika_histograms_instance)
269
- for figure, figure_name in zip(figures, figure_names):
270
- output_file_name = Path(corsika_histograms_instance.output_path).joinpath(figure_name)
271
- logger.info(f"Saving histogram to {output_file_name}")
272
- figure.savefig(output_file_name, bbox_inches="tight")
311
+ figures = function(corsika_histograms_instance)
312
+ for fig in figures:
313
+ figure_list.append(fig)
314
+
315
+ figure_list = np.array(figure_list).flatten()
316
+ core_name = re.sub(r"\.hdf5$", "", corsika_histograms_instance.hdf5_file_name)
317
+ output_file_name = Path(corsika_histograms_instance.output_path).joinpath(f"{core_name}.pdf")
318
+ corsika_histograms_visualize.save_figs_to_pdf(figure_list, output_file_name)
273
319
 
274
320
 
275
- def _derive_event_1D_histograms(corsika_histograms_instance, event_1D_header_keys, png, ecsv):
321
+ def _derive_event_1d_histograms(
322
+ corsika_histograms_instance, event_1d_header_keys, pdf, hdf5, overwrite=False
323
+ ):
276
324
  """
277
- Auxiliary function to derive the histograms for the arguments given by event_1D_histograms.
325
+ Auxiliary function to derive the histograms for the arguments given by event_1d_histograms.
278
326
 
279
327
  Parameters
280
328
  ----------
281
329
  corsika_histograms_instance: `CorsikaHistograms` instance.
282
330
  The CorsikaHistograms instance created in main.
283
- event_1D_header_keys: str
284
- Produce 1D histograms for elements given in `event_1D_header_keys` from the CORSIKA event
285
- header and save into ecsv/png files.
286
- png: bool
287
- If true, histograms are saved into png files.
288
- ecsv: bool
289
- If true, histograms are saved into ecsv files.
331
+ event_1d_header_keys: str
332
+ Produce 1D histograms for elements given in `event_1d_header_keys` from the CORSIKA event
333
+ header and save into hdf5/pdf files.
334
+ pdf: bool
335
+ If true, histograms are saved into a pdf file.
336
+ hdf5: bool
337
+ If true, histograms are saved into hdf5 files.
338
+ overwrite: bool
339
+ If true, overwrites the current output hdf5 file.
290
340
  """
291
- for event_header_element in event_1D_header_keys:
292
- if png:
293
- figure, figure_name = corsika_histograms_visualize.plot_1D_event_header_distribution(
341
+ figure_list = []
342
+ for event_header_element in event_1d_header_keys:
343
+ if pdf:
344
+ figure = corsika_histograms_visualize.plot_1d_event_header_distribution(
294
345
  corsika_histograms_instance, event_header_element
295
346
  )
296
- output_file_name = Path(corsika_histograms_instance.output_path).joinpath(figure_name)
297
- logger.info(f"Saving histogram to {output_file_name}")
298
- figure.savefig(output_file_name, bbox_inches="tight")
299
- if ecsv:
300
- corsika_histograms_instance.export_event_header_1D_histogram(
301
- event_header_element, bins=50, hist_range=None
347
+ figure_list.append(figure)
348
+ if hdf5:
349
+ corsika_histograms_instance.export_event_header_1d_histogram(
350
+ event_header_element, bins=50, hist_range=None, overwrite=overwrite
302
351
  )
352
+ if pdf:
353
+ figures_list = np.array(figure_list).flatten()
354
+ output_file_name = Path(corsika_histograms_instance.output_path).joinpath(
355
+ f"{corsika_histograms_instance.hdf5_file_name}_event_1d_histograms.pdf"
356
+ )
357
+ corsika_histograms_visualize.save_figs_to_pdf(figures_list, output_file_name)
303
358
 
304
359
 
305
- def _derive_event_2D_histograms(corsika_histograms_instance, event_2D_header_keys, png, ecsv):
360
+ def _derive_event_2d_histograms(
361
+ corsika_histograms_instance, event_2d_header_keys, pdf, hdf5, overwrite=False
362
+ ):
306
363
  """
307
- Auxiliary function to derive the histograms for the arguments given by event_1D_histograms.
364
+ Auxiliary function to derive the histograms for the arguments given by event_1d_histograms.
308
365
  If an odd number of event header keys are given, the last one is discarded.
309
366
 
310
367
  Parameters
311
368
  ----------
312
369
  corsika_histograms_instance: `CorsikaHistograms` instance.
313
370
  The CorsikaHistograms instance created in main.
314
- event_2D_header_keys: str
315
- Produce 1D histograms for elements given in `event_1D_header_keys` from the CORSIKA event
316
- header and save into ecsv/png files.
317
- png: bool
318
- If true, histograms are saved into png files.
319
- ecsv: bool
320
- If true, histograms are saved into ecsv files.
371
+ event_2d_header_keys: str
372
+ Produce 1D histograms for elements given in `event_1d_header_keys` from the CORSIKA event
373
+ header and save into hdf5/pdf files.
374
+ pdf: bool
375
+ If true, histograms are saved into a pdf file.
376
+ hdf5: bool
377
+ If true, histograms are saved into hdf5 files.
378
+ overwrite: bool
379
+ If true, overwrites the current output hdf5 file.
321
380
  """
322
- for i_event_header_element, _ in enumerate(event_2D_header_keys[::2]):
381
+ figure_list = []
382
+ for i_event_header_element, _ in enumerate(event_2d_header_keys[::2]):
323
383
  # [::2] to discard the last one in case an odd number of keys are passed
324
384
 
325
- if len(event_2D_header_keys) % 2 == 1: # if odd number of keys
326
- msg = "An odd number of keys was passed to produce 2D histograms." \
327
- "The last key is being ignored."
385
+ if len(event_2d_header_keys) % 2 == 1: # if odd number of keys
386
+ msg = (
387
+ "An odd number of keys was passed to produce 2D histograms."
388
+ "The last key is being ignored."
389
+ )
328
390
  logger.warning(msg)
329
391
 
330
- if png:
331
- figure, figure_name = corsika_histograms_visualize.plot_2D_event_header_distribution(
392
+ if pdf:
393
+ figure = corsika_histograms_visualize.plot_2d_event_header_distribution(
332
394
  corsika_histograms_instance,
333
- event_2D_header_keys[i_event_header_element],
334
- event_2D_header_keys[i_event_header_element + 1],
395
+ event_2d_header_keys[i_event_header_element],
396
+ event_2d_header_keys[i_event_header_element + 1],
335
397
  )
336
- output_file_name = Path(corsika_histograms_instance.output_path).joinpath(figure_name)
337
- logger.info(f"Saving histogram to {output_file_name}")
338
- figure.savefig(output_file_name, bbox_inches="tight")
339
- if ecsv:
340
- corsika_histograms_instance.export_event_header_2D_histogram(
341
- event_2D_header_keys[i_event_header_element],
342
- event_2D_header_keys[i_event_header_element + 1],
398
+ figure_list.append(figure)
399
+ if hdf5:
400
+ corsika_histograms_instance.export_event_header_2d_histogram(
401
+ event_2d_header_keys[i_event_header_element],
402
+ event_2d_header_keys[i_event_header_element + 1],
343
403
  bins=50,
344
404
  hist_range=None,
405
+ overwrite=overwrite,
345
406
  )
407
+ if pdf:
408
+ figures_list = np.array(figure_list).flatten()
409
+ output_file_name = Path(corsika_histograms_instance.output_path).joinpath(
410
+ f"{corsika_histograms_instance.hdf5_file_name}_event_2d_histograms.pdf"
411
+ )
412
+ corsika_histograms_visualize.save_figs_to_pdf(figures_list, output_file_name)
346
413
 
347
414
 
348
415
  def main():
@@ -351,13 +418,15 @@ def main():
351
418
  io_handler_instance = io_handler.IOHandler()
352
419
  args_dict, _ = _parse(label, description)
353
420
 
354
- output_path = io_handler_instance.get_output_directory(label, dir_type="application-plots")
421
+ output_path = io_handler_instance.get_output_directory(label, sub_dir="application-plots")
355
422
 
356
423
  logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
357
424
  initial_time = time.time()
358
425
  logger.info("Starting the application.")
359
426
 
360
- corsika_histograms_instance = CorsikaHistograms(args_dict["IACT_file"], output_path=output_path)
427
+ corsika_histograms_instance = CorsikaHistograms(
428
+ args_dict["iact_file"], output_path=output_path, hdf5_file_name=args_dict["hdf5_file_name"]
429
+ )
361
430
  if args_dict["telescope_indices"] is not None:
362
431
  try:
363
432
  indices = np.array(args_dict["telescope_indices"]).astype(int)
@@ -370,6 +439,18 @@ def main():
370
439
  raise
371
440
  else:
372
441
  indices = None
442
+ # If the hdf5 output file already exists, the results are appended to it.
443
+ if (Path(corsika_histograms_instance.hdf5_file_name).exists()) and (
444
+ args_dict["hdf5"] or args_dict["event_1d_histograms"] or args_dict["event_2d_histograms"]
445
+ ):
446
+ msg = (
447
+ f"Output hdf5 file {corsika_histograms_instance.hdf5_file_name} already exists. "
448
+ f"Overwriting it."
449
+ )
450
+ logger.warning(msg)
451
+ overwrite = True
452
+ else:
453
+ overwrite = False
373
454
  corsika_histograms_instance.set_histograms(
374
455
  telescope_indices=indices,
375
456
  individual_telescopes=args_dict["individual_telescopes"],
@@ -377,25 +458,29 @@ def main():
377
458
  )
378
459
 
379
460
  # Cherenkov photons
380
- if args_dict["png"]:
381
- _plot_figures(corsika_histograms_instance=corsika_histograms_instance)
382
- if args_dict["ecsv"]:
383
- corsika_histograms_instance.export_histograms()
461
+ if args_dict["pdf"]:
462
+ _plot_figures(
463
+ corsika_histograms_instance=corsika_histograms_instance, test=args_dict["test"]
464
+ )
465
+ if args_dict["hdf5"]:
466
+ corsika_histograms_instance.export_histograms(overwrite=overwrite)
384
467
 
385
468
  # Event information
386
- if args_dict["event_1D_histograms"] is not None:
387
- _derive_event_1D_histograms(
469
+ if args_dict["event_1d_histograms"] is not None:
470
+ _derive_event_1d_histograms(
388
471
  corsika_histograms_instance,
389
- args_dict["event_1D_histograms"],
390
- args_dict["png"],
391
- args_dict["ecsv"],
472
+ args_dict["event_1d_histograms"],
473
+ args_dict["pdf"],
474
+ args_dict["hdf5"],
475
+ overwrite=not args_dict["hdf5"],
392
476
  )
393
- if args_dict["event_2D_histograms"] is not None:
394
- _derive_event_2D_histograms(
477
+ if args_dict["event_2d_histograms"] is not None:
478
+ _derive_event_2d_histograms(
395
479
  corsika_histograms_instance,
396
- args_dict["event_2D_histograms"],
397
- args_dict["png"],
398
- args_dict["ecsv"],
480
+ args_dict["event_2d_histograms"],
481
+ args_dict["pdf"],
482
+ args_dict["hdf5"],
483
+ overwrite=not (args_dict["hdf5"] or args_dict["event_1d_histograms"]),
399
484
  )
400
485
 
401
486
  final_time = time.time()
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/python3
2
+ """
3
+ Summary
4
+ -------
5
+ Generate a default simtools metadata file from a json schema.
6
+
7
+ Command line arguments
8
+ ----------------------
9
+ schema (str, optional)
10
+ Schema file describing the input data
11
+ (default: simtools/schemas/metadata.metaschema.yml)
12
+ output_file (str, optional)
13
+ Output file name.
14
+
15
+ Example
16
+ -------
17
+ .. code-block:: console
18
+
19
+ simtools-generate-default-metadata
20
+ --schema simtools/schemas/metadata.metaschema.yml
21
+ --output_file default_metadata.yml
22
+
23
+
24
+ """
25
+
26
+ import json
27
+ import logging
28
+ from pathlib import Path
29
+
30
+ import yaml
31
+
32
+ import simtools.utils.general as gen
33
+ from simtools.configuration import configurator
34
+ from simtools.data_model import metadata_model
35
+ from simtools.io_operations import io_handler
36
+
37
+
38
+ def _parse(label, description):
39
+ """
40
+ Parse command line configuration
41
+
42
+ Parameters
43
+ ----------
44
+ label: str
45
+ Label describing application.
46
+ description: str
47
+ Description of application.
48
+
49
+ Returns
50
+ -------
51
+ CommandLineParser
52
+ Command line parser object
53
+
54
+ """
55
+
56
+ config = configurator.Configurator(label=label, description=description)
57
+
58
+ config.parser.add_argument(
59
+ "--schema",
60
+ help="schema file describing input data",
61
+ type=str,
62
+ required=True,
63
+ )
64
+ config.parser.add_argument(
65
+ "--output_file",
66
+ help="output file name (if not given: print to stdout)",
67
+ type=str,
68
+ required=False,
69
+ )
70
+
71
+ return config.initialize(output=False, require_command_line=True)
72
+
73
+
74
+ def main():
75
+ label = Path(__file__).stem
76
+ args_dict, _ = _parse(
77
+ label, description="Generate a default simtools metadata file from a json schema."
78
+ )
79
+
80
+ _logger = logging.getLogger()
81
+ _logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
82
+
83
+ default_values = metadata_model.get_default_metadata_dict(args_dict["schema"])
84
+
85
+ if args_dict["output_file"] is None:
86
+ print(default_values)
87
+ else:
88
+ _io_handler = io_handler.IOHandler()
89
+ _out_file = _io_handler.get_output_file(args_dict["output_file"])
90
+ _logger.info(f"Writing default values to {_out_file}")
91
+ if args_dict["output_file"].endswith((".yml", ".yaml")):
92
+ with open(_out_file, "w", encoding="utf-8") as file:
93
+ yaml.dump(
94
+ default_values,
95
+ file,
96
+ default_flow_style=False,
97
+ sort_keys=False,
98
+ )
99
+ if args_dict["output_file"].endswith(".json"):
100
+ with open(_out_file, "w", encoding="utf-8") as file:
101
+ json.dump(
102
+ default_values,
103
+ file,
104
+ indent=4,
105
+ sort_keys=False,
106
+ )
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()