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
@@ -1,24 +1,25 @@
1
1
  import functools
2
2
  import logging
3
3
  import operator
4
+ import re
4
5
  import time
5
6
  from pathlib import Path
6
7
 
7
8
  import boost_histogram as bh
8
9
  import numpy as np
9
10
  from astropy import units as u
10
- from astropy.table import QTable
11
+ from astropy.io.misc import yaml
11
12
  from astropy.units import cds
12
13
  from corsikaio.subblocks import event_header, get_units_from_fields, run_header
14
+ from ctapipe.io import write_table
13
15
  from eventio import IACTFile
14
16
 
15
- from simtools import io_handler, version
16
- from simtools.utils.general import (
17
- collect_data_from_yaml_or_dict,
18
- convert_2D_to_radial_distr,
19
- rotate,
20
- save_dict_to_file,
21
- )
17
+ from simtools import version
18
+ from simtools.io_operations import io_handler
19
+ from simtools.io_operations.hdf5_handler import fill_hdf5_table
20
+ from simtools.utils.general import collect_data_from_file_or_dict
21
+ from simtools.utils.geometry import convert_2d_to_radial_distr, rotate
22
+ from simtools.utils.names import sanitize_name
22
23
 
23
24
 
24
25
  class HistogramNotCreated(Exception):
@@ -37,6 +38,8 @@ class CorsikaHistograms:
37
38
  Instance label.
38
39
  output_path: str
39
40
  Path where to save the output of the class methods.
41
+ hdf5_file_name: str
42
+ HDF5 file name for histogram storage.
40
43
 
41
44
  Raises
42
45
  ------
@@ -44,7 +47,7 @@ class CorsikaHistograms:
44
47
  if the input file given does not exist.
45
48
  """
46
49
 
47
- def __init__(self, input_file, label=None, output_path=None):
50
+ def __init__(self, input_file, label=None, output_path=None, hdf5_file_name=None):
48
51
  self.label = label
49
52
  self._logger = logging.getLogger(__name__)
50
53
  self._logger.debug("Init CorsikaHistograms")
@@ -58,18 +61,17 @@ class CorsikaHistograms:
58
61
 
59
62
  self.io_handler = io_handler.IOHandler()
60
63
  _default_output_path = self.io_handler.get_output_directory(self.label, "corsika")
64
+
61
65
  if output_path is None:
62
66
  self.output_path = _default_output_path
63
67
  else:
64
68
  self.output_path = Path(output_path)
65
- self._initialize_attributes()
66
- self.read_event_information()
67
- self._initialize_header()
68
69
 
69
- def _initialize_attributes(self):
70
- """
71
- Initializes the class attributes.
72
- """
70
+ if hdf5_file_name is None:
71
+ self.hdf5_file_name = re.split(r"\.", self.input_file.name)[0] + ".hdf5"
72
+ else:
73
+ self.hdf5_file_name = hdf5_file_name
74
+
73
75
  self._telescope_indices = None
74
76
  self._telescope_positions = None
75
77
  self.num_events = None
@@ -79,8 +81,8 @@ class CorsikaHistograms:
79
81
  self._num_photons_per_event = None
80
82
  self._num_photons_per_telescope = None
81
83
  self.__meta_dict = None
82
- self.__dict_2D_distributions = None
83
- self.__dict_1D_distributions = None
84
+ self._dict_2d_distributions = None
85
+ self._dict_1d_distributions = None
84
86
  self._event_azimuth_angles = None
85
87
  self._event_zenith_angles = None
86
88
  self._hist_config = None
@@ -93,9 +95,36 @@ class CorsikaHistograms:
93
95
  self.event_information = None
94
96
  self._individual_telescopes = None
95
97
  self._allowed_histograms = {"hist_position", "hist_direction", "hist_time_altitude"}
96
- self._allowed_1D_labels = {"wavelength", "time", "altitude"}
97
- self._allowed_2D_labels = {"counts", "density", "direction", "time_altitude"}
98
+ self._allowed_1d_labels = {"wavelength", "time", "altitude"}
99
+ self._allowed_2d_labels = {"counts", "density", "direction", "time_altitude"}
98
100
  self._header = None
101
+ self.hist_position = None
102
+ self.hist_direction = None
103
+ self.hist_time_altitude = None
104
+
105
+ self.read_event_information()
106
+ self._initialize_header()
107
+
108
+ @property
109
+ def hdf5_file_name(self):
110
+ """
111
+ Property for the hdf5 file name.
112
+ The idea of this property is to allow setting (or changing) the name of the hdf5 file
113
+ even after creating the class instance.
114
+ """
115
+ return self._hdf5_file_name
116
+
117
+ @hdf5_file_name.setter
118
+ def hdf5_file_name(self, hdf5_file_name):
119
+ """
120
+ Sets the hdf5_file_name to the argument passed.
121
+
122
+ Parameters
123
+ ----------
124
+ hdf5_file_name: str
125
+ The name of hdf5 file to be set.
126
+ """
127
+ self._hdf5_file_name = Path(self.output_path).joinpath(hdf5_file_name).absolute().as_posix()
99
128
 
100
129
  @property
101
130
  def corsika_version(self):
@@ -193,7 +222,7 @@ class CorsikaHistograms:
193
222
  self.all_event_keys, all_event_units
194
223
  )
195
224
 
196
- # Add the unities to dictionary with the parameters and turn it into
225
+ # Add the units to dictionary with the parameters and turn it into
197
226
  # astropy.Quantities.
198
227
  for i_key, key in enumerate(self.all_event_keys[1:]): # starting at the second
199
228
  # element to avoid the non-numeric (e.g. 'EVTH') key.
@@ -315,7 +344,7 @@ class CorsikaHistograms:
315
344
  input_dict = input_config
316
345
  else:
317
346
  input_yaml = input_config
318
- self._hist_config = collect_data_from_yaml_or_dict(input_yaml, input_dict, allow_empty=True)
347
+ self._hist_config = collect_data_from_file_or_dict(input_yaml, input_dict, allow_empty=True)
319
348
 
320
349
  def hist_config_to_yaml(self, file_name=None):
321
350
  """
@@ -331,8 +360,9 @@ class CorsikaHistograms:
331
360
  if file_name is None:
332
361
  file_name = "hist_config"
333
362
  file_name = Path(file_name).with_suffix(".yml")
334
- output_config_file = Path(self.output_path).joinpath(file_name)
335
- save_dict_to_file(self.hist_config, output_config_file)
363
+ output_config_file_name = Path(self.output_path).joinpath(file_name)
364
+ with open(output_config_file_name, "w", encoding="utf-8") as file:
365
+ yaml.dump(self.hist_config, file)
336
366
 
337
367
  def _create_histogram_default_config(self):
338
368
  """
@@ -510,9 +540,9 @@ class CorsikaHistograms:
510
540
  of the Cherenkov photons on the ground are saved:
511
541
  x: x position on the ground (CORSIKA coordinate system),
512
542
  y: y position on the ground (CORSIKA coordinate system),
513
- cx: direction cosinus in the x direction, i.e., the cosinus of the angle between the
543
+ cx: direction cosine in the x direction, i.e., the cosine of the angle between the
514
544
  incoming direction and the x axis,
515
- cy: direction cosinus in the y direction, i.e., the cosinus of the angle between the
545
+ cy: direction cosine in the y direction, i.e., the cosine of the angle between the
516
546
  incoming direction and the y axis,
517
547
  time: time of arrival of the photon in ns. The clock starts when the particle crosses
518
548
  the top of the atmosphere (CORSIKA-defined) if `self.event_first_interaction_heights`
@@ -605,7 +635,7 @@ class CorsikaHistograms:
605
635
 
606
636
  num_photons_per_event_per_telescope_to_set = []
607
637
  start_time = time.time()
608
- self._logger.debug("Starting reading the file at {}.".format(start_time))
638
+ self._logger.debug(f"Starting reading the file at {start_time}.")
609
639
  with IACTFile(self.input_file) as f:
610
640
  event_counter = 0
611
641
  for event in f:
@@ -667,7 +697,7 @@ class CorsikaHistograms:
667
697
  """
668
698
 
669
699
  for histogram in self._allowed_histograms:
670
- if not hasattr(self, histogram):
700
+ if not hasattr(self, histogram) or getattr(self, histogram) is None:
671
701
  msg = (
672
702
  "The histograms were not created. Please, use `create_histograms` to create "
673
703
  "histograms from the CORSIKA output file."
@@ -675,7 +705,7 @@ class CorsikaHistograms:
675
705
  self._logger.error(msg)
676
706
  raise HistogramNotCreated
677
707
 
678
- def _get_hist_2D_projection(self, label):
708
+ def _get_hist_2d_projection(self, label):
679
709
  """
680
710
  Helper function to get 2D distributions.
681
711
 
@@ -689,9 +719,9 @@ class CorsikaHistograms:
689
719
  numpy.ndarray
690
720
  The counts of the histogram.
691
721
  numpy.array
692
- The x edges of the histograms.
722
+ The x bin edges of the histograms.
693
723
  numpy.array
694
- The y edges of the histograms.
724
+ The y bin edges of the histograms.
695
725
 
696
726
  Raises
697
727
  ------
@@ -699,8 +729,8 @@ class CorsikaHistograms:
699
729
  if label is not valid.
700
730
  """
701
731
 
702
- if label not in self._allowed_2D_labels:
703
- msg = f"label is not valid. Valid entries are {self._allowed_2D_labels}"
732
+ if label not in self._allowed_2d_labels:
733
+ msg = f"label is not valid. Valid entries are {self._allowed_2d_labels}"
704
734
  self._logger.error(msg)
705
735
  raise ValueError
706
736
  self._raise_if_no_histogram()
@@ -709,7 +739,7 @@ class CorsikaHistograms:
709
739
  len(self.telescope_indices) if self.individual_telescopes is True else 1
710
740
  )
711
741
 
712
- x_edges, y_edges, hist_values = [], [], []
742
+ x_bin_edges, y_bin_edges, hist_values = [], [], []
713
743
  for i_telescope in range(num_telescopes_to_fill):
714
744
  if label == "counts":
715
745
  mini_hist = self.hist_position[i_telescope][:, :, sum]
@@ -724,12 +754,12 @@ class CorsikaHistograms:
724
754
  elif label == "time_altitude":
725
755
  mini_hist = self.hist_time_altitude[i_telescope]
726
756
  hist_values.append(self.hist_time_altitude[i_telescope].view().T)
727
- x_edges.append(mini_hist.axes.edges[0].flatten())
728
- y_edges.append(mini_hist.axes.edges[1].flatten())
757
+ x_bin_edges.append(mini_hist.axes.edges[0].flatten())
758
+ y_bin_edges.append(mini_hist.axes.edges[1].flatten())
729
759
 
730
- return np.array(hist_values), np.array(x_edges), np.array(y_edges)
760
+ return np.array(hist_values), np.array(x_bin_edges), np.array(y_bin_edges)
731
761
 
732
- def get_2D_photon_position_distr(self):
762
+ def get_2d_photon_position_distr(self):
733
763
  """
734
764
  Get 2D histograms of position of the Cherenkov photons on the ground.
735
765
 
@@ -738,13 +768,13 @@ class CorsikaHistograms:
738
768
  numpy.ndarray
739
769
  The counts of the histogram.
740
770
  numpy.array
741
- The x edges of the count histograms in x, usually in meters.
771
+ The x bin edges of the count histograms in x, usually in meters.
742
772
  numpy.array
743
- The y edges of the count histograms in y, usually in meters.
773
+ The y bin edges of the count histograms in y, usually in meters.
744
774
  """
745
- return self._get_hist_2D_projection("counts")
775
+ return self._get_hist_2d_projection("counts")
746
776
 
747
- def get_2D_photon_density_distr(self):
777
+ def get_2d_photon_density_distr(self):
748
778
  """
749
779
  Get 2D histograms of position of the Cherenkov photons on the ground. It returns the photon
750
780
  density per square meter.
@@ -754,13 +784,13 @@ class CorsikaHistograms:
754
784
  numpy.ndarray
755
785
  The values of the histogram, usually in $m^{-2}$
756
786
  numpy.array
757
- The x edges of the density/count histograms in x, usually in meters.
787
+ The x bin edges of the density/count histograms in x, usually in meters.
758
788
  numpy.array
759
- The y edges of the density/count histograms in y, usually in meters.
789
+ The y bin edges of the density/count histograms in y, usually in meters.
760
790
  """
761
- return self._get_hist_2D_projection("density")
791
+ return self._get_hist_2d_projection("density")
762
792
 
763
- def get_2D_photon_direction_distr(self):
793
+ def get_2d_photon_direction_distr(self):
764
794
  """
765
795
  Get 2D histograms of incoming direction of the Cherenkov photons on the ground.
766
796
 
@@ -769,13 +799,13 @@ class CorsikaHistograms:
769
799
  numpy.ndarray
770
800
  The counts of the histogram.
771
801
  numpy.array
772
- The x edges of the direction histograms in cos(x).
802
+ The x bin edges of the direction histograms in cos(x).
773
803
  numpy.array
774
- The y edges of the direction histograms in cos(y)
804
+ The y bin edges of the direction histograms in cos(y)
775
805
  """
776
- return self._get_hist_2D_projection("direction")
806
+ return self._get_hist_2d_projection("direction")
777
807
 
778
- def get_2D_photon_time_altitude_distr(self):
808
+ def get_2d_photon_time_altitude_distr(self):
779
809
  """
780
810
  Get 2D histograms of the time and altitude of the photon production.
781
811
 
@@ -784,13 +814,13 @@ class CorsikaHistograms:
784
814
  numpy.ndarray
785
815
  The counts of the histogram.
786
816
  numpy.array
787
- The x edges of the time_altitude histograms, usually in ns.
817
+ The x bin edges of the time_altitude histograms, usually in ns.
788
818
  numpy.array
789
- The y edges of the time_altitude histograms, usually in km.
819
+ The y bin edges of the time_altitude histograms, usually in km.
790
820
  """
791
- return self._get_hist_2D_projection("time_altitude")
821
+ return self._get_hist_2d_projection("time_altitude")
792
822
 
793
- def get_2D_num_photons_distr(self):
823
+ def get_2d_num_photons_distr(self):
794
824
  """
795
825
  Get the distribution of Cherenkov photons per event per telescope. It returns the 2D array
796
826
  accounting for the events from the telescopes given by `self.telescope_indices`.
@@ -810,11 +840,11 @@ class CorsikaHistograms:
810
840
  telescope_counter = np.arange(len(self.telescope_indices) + 1).reshape(
811
841
  1, len(self.telescope_indices) + 1
812
842
  )
813
- hist_2D = np.array(self.num_photons_per_event_per_telescope)
814
- hist_2D = hist_2D.reshape((1, len(self.telescope_indices), self.num_events))
815
- return (hist_2D, num_events_array, telescope_counter)
843
+ hist_2d = np.array(self.num_photons_per_event_per_telescope)
844
+ hist_2d = hist_2d.reshape((1, len(self.telescope_indices), self.num_events))
845
+ return (hist_2d, num_events_array, telescope_counter)
816
846
 
817
- def _get_hist_1D_projection(self, label):
847
+ def _get_hist_1d_projection(self, label):
818
848
  """
819
849
  Helper function to get 1D distributions.
820
850
 
@@ -828,7 +858,7 @@ class CorsikaHistograms:
828
858
  numpy.ndarray
829
859
  The counts of the histogram.
830
860
  numpy.array
831
- The edges of the histogram.
861
+ The bin edges of the histogram.
832
862
 
833
863
  Raises
834
864
  ------
@@ -836,13 +866,13 @@ class CorsikaHistograms:
836
866
  if label is not valid.
837
867
  """
838
868
 
839
- if label not in self._allowed_1D_labels:
840
- msg = f"{label} is not valid. Valid entries are {self._allowed_1D_labels}"
869
+ if label not in self._allowed_1d_labels:
870
+ msg = f"{label} is not valid. Valid entries are {self._allowed_1d_labels}"
841
871
  self._logger.error(msg)
842
872
  raise ValueError
843
873
  self._raise_if_no_histogram()
844
874
 
845
- x_edges_list, hist_1D_list = [], []
875
+ x_bin_edges_list, hist_1d_list = [], []
846
876
  for i_hist, _ in enumerate(self.hist_position):
847
877
  if label == "wavelength":
848
878
  mini_hist = self.hist_position[i_hist][sum, sum, :]
@@ -851,9 +881,9 @@ class CorsikaHistograms:
851
881
  elif label == "altitude":
852
882
  mini_hist = self.hist_time_altitude[i_hist][sum, :]
853
883
 
854
- x_edges_list.append(mini_hist.axes.edges.T.flatten()[0])
855
- hist_1D_list.append(mini_hist.view().T)
856
- return np.array(hist_1D_list), np.array(x_edges_list)
884
+ x_bin_edges_list.append(mini_hist.axes.edges.T.flatten()[0])
885
+ hist_1d_list.append(mini_hist.view().T)
886
+ return np.array(hist_1d_list), np.array(x_bin_edges_list)
857
887
 
858
888
  def _get_bins_max_dist(self, bins=None, max_dist=None):
859
889
  """Auxiliary function to get the number of bins and the max distance to generate the
@@ -905,26 +935,26 @@ class CorsikaHistograms:
905
935
  np.array
906
936
  The counts of the 1D histogram with size = int(max_dist/bin_size).
907
937
  np.array
908
- The edges of the 1D histogram in meters with size = int(max_dist/bin_size) + 1,
938
+ The bin edges of the 1D histogram in meters with size = int(max_dist/bin_size) + 1,
909
939
  usually in meter.
910
940
  """
911
941
 
912
942
  bins, max_dist = self._get_bins_max_dist(bins=bins, max_dist=max_dist)
913
- edges_1D_list, hist1D_list = [], []
943
+ bin_edges_1d_list, hist_1d_list = [], []
914
944
 
915
- hist2D_values_list, x_position_list, y_position_list = self.get_2D_photon_position_distr()
945
+ hist_2d_values_list, x_position_list, y_position_list = self.get_2d_photon_position_distr()
916
946
 
917
947
  for i_hist, _ in enumerate(x_position_list):
918
- hist1D, edges_1D = convert_2D_to_radial_distr(
919
- hist2D_values_list[i_hist],
920
- x_position_list[i_hist],
948
+ hist_1d, bin_edges_1d = convert_2d_to_radial_distr(
949
+ hist_2d_values_list[i_hist],
950
+ x_position_list[i_hist], # pylint: disable=unnecessary-list-index-lookup
921
951
  y_position_list[i_hist],
922
952
  bins=bins,
923
953
  max_dist=max_dist,
924
954
  )
925
- edges_1D_list.append(edges_1D)
926
- hist1D_list.append(hist1D)
927
- return np.array(hist1D_list), np.array(edges_1D_list)
955
+ bin_edges_1d_list.append(bin_edges_1d)
956
+ hist_1d_list.append(hist_1d)
957
+ return np.array(hist_1d_list), np.array(bin_edges_1d_list)
928
958
 
929
959
  def get_photon_density_distr(self, bins=None, max_dist=None):
930
960
  """
@@ -944,25 +974,25 @@ class CorsikaHistograms:
944
974
  The density distribution of the 1D histogram with size = int(max_dist/bin_size),
945
975
  usually in $m^{-2}$.
946
976
  np.array
947
- The edges of the 1D histogram in meters with size = int(max_dist/bin_size) + 1,
977
+ The bin edges of the 1D histogram in meters with size = int(max_dist/bin_size) + 1,
948
978
  usually in meter.
949
979
  """
950
980
  bins, max_dist = self._get_bins_max_dist(bins=bins, max_dist=max_dist)
951
- edges_1D_list, hist1D_list = [], []
981
+ bin_edges_1d_list, hist_1d_list = [], []
952
982
 
953
- hist2D_values_list, x_position_list, y_position_list = self.get_2D_photon_density_distr()
983
+ hist_2d_values_list, x_position_list, y_position_list = self.get_2d_photon_density_distr()
954
984
 
955
985
  for i_hist, _ in enumerate(x_position_list):
956
- hist1D, edges_1D = convert_2D_to_radial_distr(
957
- hist2D_values_list[i_hist],
958
- x_position_list[i_hist],
986
+ hist_1d, bin_edges_1d = convert_2d_to_radial_distr(
987
+ hist_2d_values_list[i_hist],
988
+ x_position_list[i_hist], # pylint: disable=unnecessary-list-index-lookup
959
989
  y_position_list[i_hist],
960
990
  bins=bins,
961
991
  max_dist=max_dist,
962
992
  )
963
- edges_1D_list.append(edges_1D)
964
- hist1D_list.append(hist1D)
965
- return np.array(hist1D_list), np.array(edges_1D_list)
993
+ bin_edges_1d_list.append(bin_edges_1d)
994
+ hist_1d_list.append(hist_1d)
995
+ return np.array(hist_1d_list), np.array(bin_edges_1d_list)
966
996
 
967
997
  def get_photon_wavelength_distr(self):
968
998
  """
@@ -973,10 +1003,10 @@ class CorsikaHistograms:
973
1003
  np.array
974
1004
  The counts of the wavelength histogram.
975
1005
  np.array
976
- The edges of the wavelength histogram in nanometers.
1006
+ The bin edges of the wavelength histogram in nanometers.
977
1007
 
978
1008
  """
979
- return self._get_hist_1D_projection("wavelength")
1009
+ return self._get_hist_1d_projection("wavelength")
980
1010
 
981
1011
  def get_photon_time_of_emission_distr(self):
982
1012
  """
@@ -989,10 +1019,10 @@ class CorsikaHistograms:
989
1019
  numpy.ndarray
990
1020
  The counts of the histogram.
991
1021
  numpy.array
992
- The edges of the time histograms in ns.
1022
+ The bin edges of the time histograms in ns.
993
1023
 
994
1024
  """
995
- return self._get_hist_1D_projection("time")
1025
+ return self._get_hist_1d_projection("time")
996
1026
 
997
1027
  def get_photon_altitude_distr(self):
998
1028
  """
@@ -1003,10 +1033,10 @@ class CorsikaHistograms:
1003
1033
  numpy.ndarray
1004
1034
  The counts of the histogram.
1005
1035
  numpy.array
1006
- The edges of the photon altitude histograms in km.
1036
+ The bin edges of the photon altitude histograms in km.
1007
1037
 
1008
1038
  """
1009
- return self._get_hist_1D_projection("altitude")
1039
+ return self._get_hist_1d_projection("altitude")
1010
1040
 
1011
1041
  @property
1012
1042
  def num_photons_per_event_per_telescope(self):
@@ -1058,8 +1088,8 @@ class CorsikaHistograms:
1058
1088
  numpy.array
1059
1089
  Number of photons per event.
1060
1090
  """
1061
- hist, edges = np.histogram(self.num_photons_per_event, bins=bins, range=hist_range)
1062
- return hist.reshape(1, bins), edges.reshape(1, bins + 1)
1091
+ hist, bin_edges = np.histogram(self.num_photons_per_event, bins=bins, range=hist_range)
1092
+ return hist.reshape(1, bins), bin_edges.reshape(1, bins + 1)
1063
1093
 
1064
1094
  def get_num_photons_per_telescope_distr(self, bins=50, hist_range=None):
1065
1095
  """
@@ -1080,15 +1110,20 @@ class CorsikaHistograms:
1080
1110
  Number of photons per telescope.
1081
1111
  """
1082
1112
 
1083
- hist, edges = np.histogram(self.num_photons_per_telescope, bins=bins, range=hist_range)
1084
- return hist.reshape(1, bins), edges.reshape(1, bins + 1)
1113
+ hist, bin_edges = np.histogram(self.num_photons_per_telescope, bins=bins, range=hist_range)
1114
+ return hist.reshape(1, bins), bin_edges.reshape(1, bins + 1)
1085
1115
 
1086
- def export_histograms(self):
1116
+ def export_histograms(self, overwrite=False):
1087
1117
  """
1088
- Export the histograms to ecsv files.
1118
+ Export the histograms to hdf5 files.
1119
+
1120
+ Parameters
1121
+ ----------
1122
+ overwrite: bool
1123
+ If True overwrites the histograms already saved in the hdf5 file.
1089
1124
  """
1090
- self._export_1D_histograms()
1091
- self._export_2D_histograms()
1125
+ self._export_1d_histograms(overwrite=overwrite)
1126
+ self._export_2d_histograms(overwrite=False)
1092
1127
 
1093
1128
  @property
1094
1129
  def _meta_dict(self):
@@ -1098,22 +1133,22 @@ class CorsikaHistograms:
1098
1133
  Returns
1099
1134
  -------
1100
1135
  dict
1101
- Meta dictionary for the ECSV files with the histograms.
1136
+ Meta dictionary for the hdf5 files with the histograms.
1102
1137
  """
1103
1138
 
1104
1139
  if self.__meta_dict is None:
1105
1140
  self.__meta_dict = {
1106
- "CORSIKA version": self.corsika_version,
1107
- "simtools version": version.__version__,
1108
- "Original IACT file": self.input_file.name,
1141
+ "corsika_version": self.corsika_version,
1142
+ "simtools_version": version.__version__,
1143
+ "iact_file": self.input_file.name,
1109
1144
  "telescope_indices": list(self.telescope_indices),
1110
1145
  "individual_telescopes": self.individual_telescopes,
1111
- "Note": "Only lower bin edges are given.",
1146
+ "note": "Only lower bin edges are given.",
1112
1147
  }
1113
1148
  return self.__meta_dict
1114
1149
 
1115
1150
  @property
1116
- def _dict_1D_distributions(self):
1151
+ def dict_1d_distributions(self):
1117
1152
  """
1118
1153
  Dictionary to label the 1D distributions according to the class methods.
1119
1154
 
@@ -1122,96 +1157,111 @@ class CorsikaHistograms:
1122
1157
  dict:
1123
1158
  The dictionary with information about the 1D distributions.
1124
1159
  """
1125
- self.__dict_1D_distributions = {
1160
+ self._dict_1d_distributions = {
1126
1161
  "wavelength": {
1127
1162
  "function": "get_photon_wavelength_distr",
1128
- "file name": "hist_1D_photon_wavelength_distr",
1163
+ "file name": "hist_1d_photon_wavelength_distr",
1129
1164
  "title": "Photon wavelength distribution",
1130
- "edges": "wavelength",
1131
- "edges unit": self.hist_config["hist_position"]["z axis"]["start"].unit,
1165
+ "bin edges": "wavelength",
1166
+ "axis unit": self.hist_config["hist_position"]["z axis"]["start"].unit,
1132
1167
  },
1133
1168
  "counts": {
1134
1169
  "function": "get_photon_radial_distr",
1135
- "file name": "hist_1D_photon_radial_distr",
1170
+ "file name": "hist_1d_photon_radial_distr",
1136
1171
  "title": "Radial photon distribution on the ground",
1137
- "edges": "Distance to center",
1138
- "edges unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1172
+ "bin edges": "Distance to center",
1173
+ "axis unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1139
1174
  },
1140
1175
  "density": {
1141
1176
  "function": "get_photon_density_distr",
1142
- "file name": "hist_1D_photon_density_distr",
1177
+ "file name": "hist_1d_photon_density_distr",
1143
1178
  "title": "Photon density distribution on the ground",
1144
- "edges": "Distance to center",
1145
- "edges unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1179
+ "bin edges": "Distance to center",
1180
+ "axis unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1146
1181
  },
1147
1182
  "time": {
1148
1183
  "function": "get_photon_time_of_emission_distr",
1149
- "file name": "hist_1D_photon_time_distr",
1184
+ "file name": "hist_1d_photon_time_distr",
1150
1185
  "title": "Photon time of arrival distribution",
1151
- "edges": "Time of arrival",
1152
- "edges unit": self.hist_config["hist_time_altitude"]["x axis"]["start"].unit,
1186
+ "bin edges": "Time of arrival",
1187
+ "axis unit": self.hist_config["hist_time_altitude"]["x axis"]["start"].unit,
1153
1188
  },
1154
1189
  "altitude": {
1155
1190
  "function": "get_photon_altitude_distr",
1156
- "file name": "hist_1D_photon_time_distr",
1191
+ "file name": "hist_1d_photon_altitude_distr",
1157
1192
  "title": "Photon altitude of emission distribution",
1158
- "edges": "Altitude of emission",
1159
- "edges unit": self.hist_config["hist_time_altitude"]["y axis"]["start"].unit,
1193
+ "bin edges": "Altitude of emission",
1194
+ "axis unit": self.hist_config["hist_time_altitude"]["y axis"]["start"].unit,
1160
1195
  },
1161
1196
  "num_photons_per_event": {
1162
1197
  "function": "get_num_photons_per_event_distr",
1163
- "file name": "hist_1D_photon_per_event_distr",
1198
+ "file name": "hist_1d_photon_per_event_distr",
1164
1199
  "title": "Photons per event distribution",
1165
- "edges": "Event counter",
1166
- "edges unit": u.dimensionless_unscaled,
1200
+ "bin edges": "Event counter",
1201
+ "axis unit": u.dimensionless_unscaled,
1167
1202
  },
1168
1203
  "num_photons_per_telescope": {
1169
1204
  "function": "get_num_photons_per_telescope_distr",
1170
- "file name": "hist_1D_photon_per_telescope_distr",
1205
+ "file name": "hist_1d_photon_per_telescope_distr",
1171
1206
  "title": "Photons per telescope distribution",
1172
- "edges": "Telescope counter",
1173
- "edges unit": u.dimensionless_unscaled,
1207
+ "bin edges": "Telescope counter",
1208
+ "axis unit": u.dimensionless_unscaled,
1174
1209
  },
1175
1210
  }
1176
- return self.__dict_1D_distributions
1211
+ return self._dict_1d_distributions
1177
1212
 
1178
- def _export_1D_histograms(self):
1213
+ def _export_1d_histograms(self, overwrite=False):
1179
1214
  """
1180
1215
  Auxiliary function to export only the 1D histograms.
1216
+
1217
+ Parameters
1218
+ ----------
1219
+ overwrite: bool
1220
+ If True overwrites the histograms already saved in the hdf5 file.
1181
1221
  """
1182
1222
 
1183
- for _, function_dict in self._dict_1D_distributions.items():
1184
- self._meta_dict["Title"] = function_dict["title"]
1223
+ for _, function_dict in self.dict_1d_distributions.items():
1224
+ self._meta_dict["Title"] = sanitize_name(function_dict["title"])
1185
1225
  function = getattr(self, function_dict["function"])
1186
- hist_1D_list, x_edges_list = function()
1187
- x_edges_list = x_edges_list * function_dict["edges unit"]
1226
+ hist_1d_list, x_bin_edges_list = function()
1227
+ x_bin_edges_list = x_bin_edges_list * function_dict["axis unit"]
1188
1228
  if function_dict["function"] == "get_photon_density_distr":
1189
- histogram_value_unit = 1 / (function_dict["edges unit"] ** 2)
1229
+ histogram_value_unit = 1 / (function_dict["axis unit"] ** 2)
1190
1230
  else:
1191
1231
  histogram_value_unit = u.dimensionless_unscaled
1192
- hist_1D_list = hist_1D_list * histogram_value_unit
1193
- for i_histogram, _ in enumerate(x_edges_list):
1232
+ hist_1d_list = hist_1d_list * histogram_value_unit
1233
+ for i_histogram, _ in enumerate(x_bin_edges_list):
1194
1234
  if self.individual_telescopes:
1195
- ecsv_file = (
1196
- f"{function_dict['file name']}_"
1197
- f"tel_index_{self.telescope_indices[i_histogram]}.ecsv"
1235
+ hdf5_table_name = (
1236
+ f"/{function_dict['file name']}_"
1237
+ f"tel_index_{self.telescope_indices[i_histogram]}"
1198
1238
  )
1199
1239
  else:
1200
- ecsv_file = f"{function_dict['file name']}_all_tels.ecsv"
1201
-
1202
- table = self.fill_ecsv_table(
1203
- hist_1D_list[i_histogram],
1204
- x_edges_list[i_histogram],
1205
- None,
1206
- function_dict["edges"],
1207
- None,
1240
+ hdf5_table_name = f"/{function_dict['file name']}_all_tels"
1241
+
1242
+ table = fill_hdf5_table(
1243
+ hist=hist_1d_list[i_histogram],
1244
+ x_bin_edges=x_bin_edges_list[i_histogram],
1245
+ y_bin_edges=None,
1246
+ x_label=function_dict["bin edges"],
1247
+ y_label=None,
1248
+ meta_data=self._meta_dict,
1249
+ )
1250
+ self._logger.info(
1251
+ f"Writing 1D histogram with name {hdf5_table_name} to "
1252
+ f"{self.hdf5_file_name}."
1253
+ )
1254
+ # overwrite takes precedence over append
1255
+ if overwrite is True:
1256
+ append = False
1257
+ else:
1258
+ append = True
1259
+ write_table(
1260
+ table, self.hdf5_file_name, hdf5_table_name, append=append, overwrite=overwrite
1208
1261
  )
1209
- ecsv_file = Path(self.output_path).joinpath(ecsv_file)
1210
- self._logger.info(f"Exporting histogram to {ecsv_file}.")
1211
- table.write(ecsv_file, format="ascii.ecsv", overwrite=True)
1212
1262
 
1213
1263
  @property
1214
- def _dict_2D_distributions(self):
1264
+ def dict_2d_distributions(self):
1215
1265
  """
1216
1266
  Dictionary to label the 2D distributions according to the class methods.
1217
1267
 
@@ -1220,150 +1270,118 @@ class CorsikaHistograms:
1220
1270
  dict:
1221
1271
  The dictionary with information about the 2D distributions.
1222
1272
  """
1223
- if self.__dict_2D_distributions is None:
1224
- self.__dict_2D_distributions = {
1273
+ if self._dict_2d_distributions is None:
1274
+ self._dict_2d_distributions = {
1225
1275
  "counts": {
1226
- "function": "get_2D_photon_position_distr",
1227
- "file name": "hist_2D_photon_count_distr",
1276
+ "function": "get_2d_photon_position_distr",
1277
+ "file name": "hist_2d_photon_count_distr",
1228
1278
  "title": "Photon count distribution on the ground",
1229
- "x edges": "x position on the ground",
1230
- "x edges unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1231
- "y edges": "y position on the ground",
1232
- "y edges unit": self.hist_config["hist_position"]["y axis"]["start"].unit,
1279
+ "x bin edges": "x position on the ground",
1280
+ "x axis unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1281
+ "y bin edges": "y position on the ground",
1282
+ "y axis unit": self.hist_config["hist_position"]["y axis"]["start"].unit,
1233
1283
  },
1234
1284
  "density": {
1235
- "function": "get_2D_photon_density_distr",
1236
- "file name": "hist_2D_photon_density_distr",
1285
+ "function": "get_2d_photon_density_distr",
1286
+ "file name": "hist_2d_photon_density_distr",
1237
1287
  "title": "Photon density distribution on the ground",
1238
- "x edges": "x position on the ground",
1239
- "x edges unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1240
- "y edges": "y position on the ground",
1241
- "y edges unit": self.hist_config["hist_position"]["y axis"]["start"].unit,
1288
+ "x bin edges": "x position on the ground",
1289
+ "x axis unit": self.hist_config["hist_position"]["x axis"]["start"].unit,
1290
+ "y bin edges": "y position on the ground",
1291
+ "y axis unit": self.hist_config["hist_position"]["y axis"]["start"].unit,
1242
1292
  },
1243
1293
  "direction": {
1244
- "function": "get_2D_photon_direction_distr",
1245
- "file name": "hist_2D_photon_direction_distr",
1294
+ "function": "get_2d_photon_direction_distr",
1295
+ "file name": "hist_2d_photon_direction_distr",
1246
1296
  "title": "Photon arrival direction",
1247
- "x edges": "x direction cosine",
1248
- "x edges unit": u.dimensionless_unscaled,
1249
- "y edges": "y direction cosine",
1250
- "y edges unit": u.dimensionless_unscaled,
1297
+ "x bin edges": "x direction cosine",
1298
+ "x axis unit": u.dimensionless_unscaled,
1299
+ "y bin edges": "y direction cosine",
1300
+ "y axis unit": u.dimensionless_unscaled,
1251
1301
  },
1252
1302
  "time_altitude": {
1253
- "function": "get_2D_photon_time_altitude_distr",
1254
- "file name": "hist_2D_photon_time_altitude_distr",
1303
+ "function": "get_2d_photon_time_altitude_distr",
1304
+ "file name": "hist_2d_photon_time_altitude_distr",
1255
1305
  "title": "Time of arrival vs altitude of emission",
1256
- "x edges": "Time of arrival",
1257
- "x edges unit": self.hist_config["hist_time_altitude"]["x axis"]["start"].unit,
1258
- "y edges": "Altitude of emission",
1259
- "y edges unit": self.hist_config["hist_time_altitude"]["y axis"]["start"].unit,
1306
+ "x bin edges": "Time of arrival",
1307
+ "x axis unit": self.hist_config["hist_time_altitude"]["x axis"]["start"].unit,
1308
+ "y bin edges": "Altitude of emission",
1309
+ "y axis unit": self.hist_config["hist_time_altitude"]["y axis"]["start"].unit,
1260
1310
  },
1261
1311
  "num_photons_per_telescope": {
1262
- "function": "get_2D_num_photons_distr",
1263
- "file name": "hist_2D_photon_telescope_event_distr",
1312
+ "function": "get_2d_num_photons_distr",
1313
+ "file name": "hist_2d_photon_telescope_event_distr",
1264
1314
  "title": "Number of photons per telescope and per event",
1265
- "x edges": "Telescope counter",
1266
- "x edges unit": u.dimensionless_unscaled,
1267
- "y edges": "Event counter",
1268
- "y edges unit": u.dimensionless_unscaled,
1315
+ "x bin edges": "Telescope counter",
1316
+ "x axis unit": u.dimensionless_unscaled,
1317
+ "y bin edges": "Event counter",
1318
+ "y axis unit": u.dimensionless_unscaled,
1269
1319
  },
1270
1320
  }
1271
- return self.__dict_2D_distributions
1321
+ return self._dict_2d_distributions
1272
1322
 
1273
- def _export_2D_histograms(self):
1323
+ def _export_2d_histograms(self, overwrite):
1274
1324
  """
1275
1325
  Auxiliary function to export only the 2D histograms.
1276
- """
1277
1326
 
1278
- for property_name, function_dict in self._dict_2D_distributions.items():
1279
- self._meta_dict["Title"] = function_dict["title"]
1327
+ Parameters
1328
+ ----------
1329
+ overwrite: bool
1330
+ If True overwrites the histograms already saved in the hdf5 file.
1331
+ """
1332
+ for property_name, function_dict in self.dict_2d_distributions.items():
1333
+ self._meta_dict["Title"] = sanitize_name(function_dict["title"])
1280
1334
  function = getattr(self, function_dict["function"])
1281
1335
 
1282
- hist_2D_list, x_edges_list, y_edges_list = function()
1283
- if function_dict["function"] == "get_2D_photon_density_distr":
1336
+ hist_2d_list, x_bin_edges_list, y_bin_edges_list = function()
1337
+ if function_dict["function"] == "get_2d_photon_density_distr":
1284
1338
  histogram_value_unit = 1 / (
1285
- self._dict_2D_distributions[property_name]["x edges unit"]
1286
- * self._dict_2D_distributions[property_name]["y edges unit"]
1339
+ self.dict_2d_distributions[property_name]["x axis unit"]
1340
+ * self.dict_2d_distributions[property_name]["y axis unit"]
1287
1341
  )
1288
1342
  else:
1289
1343
  histogram_value_unit = u.dimensionless_unscaled
1290
1344
 
1291
- hist_2D_list, x_edges_list, y_edges_list = (
1292
- hist_2D_list * histogram_value_unit,
1293
- x_edges_list * self._dict_2D_distributions[property_name]["x edges unit"],
1294
- y_edges_list * self._dict_2D_distributions[property_name]["y edges unit"],
1345
+ hist_2d_list, x_bin_edges_list, y_bin_edges_list = (
1346
+ hist_2d_list * histogram_value_unit,
1347
+ x_bin_edges_list * self.dict_2d_distributions[property_name]["x axis unit"],
1348
+ y_bin_edges_list * self.dict_2d_distributions[property_name]["y axis unit"],
1295
1349
  )
1296
1350
 
1297
- for i_histogram, _ in enumerate(x_edges_list):
1351
+ for i_histogram, _ in enumerate(x_bin_edges_list):
1298
1352
  if self.individual_telescopes:
1299
- ecsv_file = (
1300
- f"{self._dict_2D_distributions[property_name]['file name']}"
1301
- f"_tel_index_{self.telescope_indices[i_histogram]}.ecsv"
1353
+ hdf5_table_name = (
1354
+ f"/{self.dict_2d_distributions[property_name]['file name']}"
1355
+ f"_tel_index_{self.telescope_indices[i_histogram]}"
1302
1356
  )
1303
1357
  else:
1304
- ecsv_file = (
1305
- f"{self._dict_2D_distributions[property_name]['file name']}_all_tels.ecsv"
1358
+ hdf5_table_name = (
1359
+ f"/{self.dict_2d_distributions[property_name]['file name']}" f"_all_tels"
1306
1360
  )
1307
-
1308
- table = self.fill_ecsv_table(
1309
- hist_2D_list[i_histogram],
1310
- x_edges_list[i_histogram],
1311
- y_edges_list[i_histogram],
1312
- function_dict["x edges"],
1313
- function_dict["y edges"],
1361
+ table = fill_hdf5_table(
1362
+ hist=hist_2d_list[i_histogram],
1363
+ x_bin_edges=x_bin_edges_list[i_histogram],
1364
+ y_bin_edges=y_bin_edges_list[i_histogram],
1365
+ x_label=function_dict["x bin edges"],
1366
+ y_label=function_dict["y bin edges"],
1367
+ meta_data=self._meta_dict,
1314
1368
  )
1315
- ecsv_file = Path(self.output_path).joinpath(ecsv_file)
1316
- self._logger.info(f"Exporting histogram to {ecsv_file}.")
1317
- table.write(ecsv_file, format="ascii.ecsv", overwrite=True)
1318
1369
 
1319
- def fill_ecsv_table(self, hist, x_edges, y_edges, x_label, y_label):
1320
- """
1321
- Create and fill an ecsv table with the histogram information.
1322
- It works for both 1D and 2D distributions.
1323
-
1324
- Parameters
1325
- ----------
1326
- hist: numpy.ndarray
1327
- The counts of the histogram.
1328
- x_edges: numpy.array
1329
- The x edges of the histograms.
1330
- y_edges: numpy.array
1331
- The y edges of the histograms.
1332
- x_label: str
1333
- X edges label.
1334
- y_label: str
1335
- Y edges label.
1336
- """
1337
- try:
1338
- x_edges_2D, y_edges_2D = np.meshgrid(x_edges[:-1], y_edges[:-1])
1339
- x_edges_2D_flattened, y_edges_2D_flattened, hist_2D_flattened = (
1340
- x_edges_2D.flatten(),
1341
- y_edges_2D.flatten(),
1342
- hist.flatten(),
1343
- )
1344
- table = QTable(
1345
- [
1346
- x_edges_2D_flattened,
1347
- y_edges_2D_flattened,
1348
- hist_2D_flattened,
1349
- ],
1350
- names=(x_label, y_label, "Values"),
1351
- meta=self._meta_dict,
1352
- )
1353
- except TypeError:
1354
- table = QTable(
1355
- [
1356
- x_edges[:-1],
1357
- hist,
1358
- ],
1359
- names=(x_label, "Values"),
1360
- meta=self._meta_dict,
1361
- )
1362
- return table
1370
+ self._logger.info(
1371
+ f"Writing 2D histogram with name {hdf5_table_name} to "
1372
+ f"{self.hdf5_file_name}."
1373
+ )
1374
+ # Always appending to table due to the file previously created
1375
+ # by self._export_1d_histograms.
1376
+ write_table(
1377
+ table, self.hdf5_file_name, hdf5_table_name, append=True, overwrite=overwrite
1378
+ )
1363
1379
 
1364
- def export_event_header_1D_histogram(self, event_header_element, bins=50, hist_range=None):
1380
+ def export_event_header_1d_histogram(
1381
+ self, event_header_element, bins=50, hist_range=None, overwrite=False
1382
+ ):
1365
1383
  """
1366
- Export to a ecsv file the 1D histogram for the key `event_header_element` from the CORSIKA
1384
+ Export to a hdf5 file the 1D histogram for the key `event_header_element` from the CORSIKA
1367
1385
  event header.
1368
1386
 
1369
1387
  Parameters
@@ -1375,28 +1393,44 @@ class CorsikaHistograms:
1375
1393
  Number of bins for the histogram.
1376
1394
  hist_range: 2-tuple
1377
1395
  Tuple to define the range of the histogram.
1396
+ overwrite: bool
1397
+ If True overwrites the histograms already saved in the hdf5 file.
1378
1398
  """
1379
1399
 
1380
- hist, edges = self.event_1D_histogram(
1400
+ hist, bin_edges = self.event_1d_histogram(
1381
1401
  event_header_element, bins=bins, hist_range=hist_range
1382
1402
  )
1383
- edges *= self.event_information[event_header_element].unit
1384
- table = self.fill_ecsv_table(hist, edges, None, event_header_element, None)
1385
- ecsv_file = Path(self.output_path).joinpath(
1386
- f"event_1D_histograms_{event_header_element}.ecsv"
1403
+ bin_edges *= self.event_information[event_header_element].unit
1404
+ table = fill_hdf5_table(
1405
+ hist=hist,
1406
+ x_bin_edges=bin_edges,
1407
+ y_bin_edges=None,
1408
+ x_label=event_header_element,
1409
+ y_label=None,
1410
+ meta_data=self._meta_dict,
1387
1411
  )
1388
- self._logger.info(f"Exporting histogram to {ecsv_file}.")
1389
- table.write(ecsv_file, format="ascii.ecsv", overwrite=True)
1412
+ hdf5_table_name = f"/event_2d_histograms_{event_header_element}"
1390
1413
 
1391
- def export_event_header_2D_histogram(
1414
+ self._logger.info(
1415
+ f"Exporting histogram with name {hdf5_table_name} to {self.hdf5_file_name}."
1416
+ )
1417
+ # overwrite takes precedence over append
1418
+ if overwrite is True:
1419
+ append = False
1420
+ else:
1421
+ append = True
1422
+ write_table(table, self.hdf5_file_name, hdf5_table_name, append=append, overwrite=overwrite)
1423
+
1424
+ def export_event_header_2d_histogram(
1392
1425
  self,
1393
1426
  event_header_element_1,
1394
1427
  event_header_element_2,
1395
1428
  bins=50,
1396
1429
  hist_range=None,
1430
+ overwrite=False,
1397
1431
  ):
1398
1432
  """
1399
- Export to a ecsv file the 2D histogram for the key `event_header_element_1` and
1433
+ Export to a hdf5 file the 2D histogram for the key `event_header_element_1` and
1400
1434
  `event_header_element_2`from the CORSIKA event header.
1401
1435
 
1402
1436
  Parameters
@@ -1411,23 +1445,35 @@ class CorsikaHistograms:
1411
1445
  Number of bins for the histogram.
1412
1446
  hist_range: 2-tuple
1413
1447
  Tuple to define the range of the histogram.
1448
+ overwrite: bool
1449
+ If True overwrites the histograms already saved in the hdf5 file.
1414
1450
  """
1415
- hist, x_edges, y_edges = self.event_2D_histogram(
1451
+ hist, x_bin_edges, y_bin_edges = self.event_2d_histogram(
1416
1452
  event_header_element_1, event_header_element_2, bins=bins, hist_range=hist_range
1417
1453
  )
1418
- x_edges *= self.event_information[event_header_element_1].unit
1419
- y_edges *= self.event_information[event_header_element_2].unit
1420
-
1421
- table = self.fill_ecsv_table(
1422
- hist, x_edges, y_edges, event_header_element_1, event_header_element_2
1454
+ x_bin_edges *= self.event_information[event_header_element_1].unit
1455
+ y_bin_edges *= self.event_information[event_header_element_2].unit
1456
+
1457
+ table = fill_hdf5_table(
1458
+ hist=hist,
1459
+ x_bin_edges=x_bin_edges,
1460
+ y_bin_edges=y_bin_edges,
1461
+ x_label=event_header_element_1,
1462
+ y_label=event_header_element_2,
1463
+ meta_data=self._meta_dict,
1423
1464
  )
1424
1465
 
1425
- ecsv_file = Path(self.output_path).joinpath(
1426
- f"event_2D_histograms_{event_header_element_1}" f"_{event_header_element_2}.ecsv"
1427
- )
1466
+ hdf5_table_name = f"/event_2d_histograms_{event_header_element_1}_{event_header_element_2}"
1428
1467
 
1429
- self._logger.info(f"Exporting histogram to {ecsv_file}.")
1430
- table.write(ecsv_file, format="ascii.ecsv", overwrite=True)
1468
+ self._logger.info(
1469
+ f"Exporting histogram with name {hdf5_table_name} to {self.hdf5_file_name}."
1470
+ )
1471
+ # overwrite takes precedence over append
1472
+ if overwrite is True:
1473
+ append = False
1474
+ else:
1475
+ append = True
1476
+ write_table(table, self.hdf5_file_name, hdf5_table_name, append=append, overwrite=overwrite)
1431
1477
 
1432
1478
  @property
1433
1479
  def num_photons_per_telescope(self):
@@ -1632,7 +1678,7 @@ class CorsikaHistograms:
1632
1678
  raise KeyError
1633
1679
  return self.header[parameter]
1634
1680
 
1635
- def event_1D_histogram(self, key, bins=50, hist_range=None):
1681
+ def event_1d_histogram(self, key, bins=50, hist_range=None):
1636
1682
  """
1637
1683
  Create a histogram for the all events using `key` as parameter.
1638
1684
  Valid keys are stored in `self.all_event_keys` (CORSIKA defined).
@@ -1664,14 +1710,14 @@ class CorsikaHistograms:
1664
1710
  msg = f"`key` is not valid. Valid entries are {self.all_event_keys}"
1665
1711
  self._logger.error(msg)
1666
1712
  raise KeyError
1667
- hist, edges = np.histogram(
1713
+ hist, bin_edges = np.histogram(
1668
1714
  self.event_information[key].value,
1669
1715
  bins=bins,
1670
1716
  range=hist_range,
1671
1717
  )
1672
- return hist, edges
1718
+ return hist, bin_edges
1673
1719
 
1674
- def event_2D_histogram(self, key_1, key_2, bins=50, hist_range=None):
1720
+ def event_2d_histogram(self, key_1, key_2, bins=50, hist_range=None):
1675
1721
  """
1676
1722
  Create a 2D histogram for the all events using `key_1` and `key_2` as parameters.
1677
1723
  Valid keys are stored in `self.all_event_keys` (CORSIKA defined).
@@ -1712,10 +1758,10 @@ class CorsikaHistograms:
1712
1758
  )
1713
1759
  self._logger.error(msg)
1714
1760
  raise KeyError
1715
- hist, x_edges, y_edges = np.histogram2d(
1761
+ hist, x_bin_edges, y_bin_edges = np.histogram2d(
1716
1762
  self.event_information[key_1].value,
1717
1763
  self.event_information[key_2].value,
1718
1764
  bins=bins,
1719
1765
  range=hist_range,
1720
1766
  )
1721
- return hist, x_edges, y_edges
1767
+ return hist, x_bin_edges, y_bin_edges