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
@@ -0,0 +1,196 @@
1
+ import logging
2
+
3
+ import astropy.units as u
4
+ import numpy as np
5
+ import pyproj
6
+
7
+
8
+ class GeoCoordinates:
9
+ """
10
+ Geospatial Coordinate systems.
11
+ Defines UTM, WGS84 and ground (sim_telarray) coordinate systems.
12
+
13
+ """
14
+
15
+ def __init__(self):
16
+ """
17
+ Initialize GeoCoordinates
18
+
19
+ """
20
+
21
+ self._logger = logging.getLogger(__name__)
22
+
23
+ def crs_utm(self, epsg):
24
+ """
25
+ UTM coordinate system definition.
26
+
27
+ Parameters
28
+ ----------
29
+ epsg: int
30
+ EPSG code for UTM zone.
31
+
32
+ Returns
33
+ -------
34
+ pyproj.CRS
35
+ UTM coordinate system.
36
+
37
+ """
38
+ if epsg:
39
+ crs_utm = pyproj.CRS.from_user_input(epsg)
40
+ self._logger.debug(f"UTM coordinate system: {crs_utm}")
41
+ return crs_utm
42
+
43
+ return None
44
+
45
+ @staticmethod
46
+ def crs_wgs84():
47
+ """
48
+ WGS84 coordinate system definition.
49
+
50
+ Returns
51
+ -------
52
+ pyproj.CRS
53
+ WGS84 coordinate system.
54
+
55
+ """
56
+ return pyproj.CRS("EPSG:4326")
57
+
58
+ def crs_local(self, reference_point):
59
+ """
60
+ Local coordinate system definition.
61
+ This is a cartesian coordinate system with the origin at the array center.
62
+ X-axis points towards geographic North, y-axis towards geographic West.
63
+
64
+ Parameters
65
+ ----------
66
+ reference_point: simtools.layout.telescope_position
67
+ Reference coordinate.
68
+
69
+ Returns
70
+ -------
71
+ pyproj.CRS
72
+ local coordinate system.
73
+
74
+ """
75
+ try:
76
+ if self._valid_reference_point(reference_point):
77
+ _center_lat, _center_lon, _ = reference_point.get_coordinates("mercator")
78
+ _scale_factor_k_0 = self._coordinate_scale_factor(reference_point)
79
+ proj4_string = (
80
+ "+proj=tmerc +ellps=WGS84 +datum=WGS84"
81
+ f" +lon_0={_center_lon} +lat_0={_center_lat}"
82
+ f" +axis=nwu +units=m +k_0={_scale_factor_k_0}"
83
+ )
84
+ crs_local = pyproj.CRS.from_proj4(proj4_string)
85
+ self._logger.debug(f"Ground (sim_telarray) coordinate system: {crs_local}")
86
+ return crs_local
87
+ except AttributeError:
88
+ self._logger.error("Failed to derive local coordinate system. Missing reference point")
89
+ raise
90
+
91
+ return None
92
+
93
+ def _valid_reference_point(self, reference_point):
94
+ """
95
+ Check if reference point has valid long/lat coordinates (including altitude).
96
+ This is required to derive the local coordinate system.
97
+ Try if a conversion from UTM coordinates to long/lat is possible.
98
+
99
+ Parameters
100
+ ----------
101
+ reference_point: simtools.layout.telescope_position
102
+ Reference coordinate.
103
+
104
+ Returns
105
+ -------
106
+ bool
107
+ True if reference point has valid coordinates.
108
+
109
+ """
110
+ _center_lat, _center_lon, _center_alt = reference_point.get_coordinates("mercator")
111
+ if np.isnan(_center_alt.value):
112
+ self._logger.debug("Missing array center altitude")
113
+ return False
114
+
115
+ if np.isnan(_center_lat.value) or np.isnan(_center_lon.value):
116
+ self._logger.debug(
117
+ "Invalid array center coordinates "
118
+ f"(lat={_center_lat}, lon={_center_lon}, alt={_center_alt})"
119
+ )
120
+ return False
121
+
122
+ return True
123
+
124
+ def _coordinate_scale_factor(self, reference_point):
125
+ """
126
+ Derive coordinate scale factor for transformation into
127
+ local coordinate system
128
+
129
+ Depends on latitude and altitude of array center.
130
+ Ignores transformation of geodetic height to geocentric height
131
+ (this introduces an error on the sub-mm scale).
132
+
133
+ Parameters
134
+ ----------
135
+ reference_point: simtools.layout.telescope_position
136
+ Reference coordinate.
137
+
138
+ Returns
139
+ -------
140
+ k_0: float
141
+ Scale factor for local coordinate system.
142
+
143
+ Raises
144
+ ------
145
+ AttributeError
146
+ If reference_point does not have a valid center or UTM system is not defined.
147
+
148
+ """
149
+
150
+ try:
151
+ _center_lat, _, _centre_altitude = reference_point.get_coordinates("mercator")
152
+ except AttributeError:
153
+ self._logger.debug("Missing array center, cannot derive coordinate scale factor")
154
+ raise
155
+
156
+ crs_utm = self.crs_wgs84()
157
+ _semi_major_axis = crs_utm.geodetic_crs.ellipsoid.semi_major_metre * u.m
158
+ _semi_minor_axis = crs_utm.geodetic_crs.ellipsoid.semi_minor_metre * u.m
159
+
160
+ _local_geo_radius = self._geocentric_radius(_center_lat, _semi_major_axis, _semi_minor_axis)
161
+
162
+ return (_local_geo_radius.value + _centre_altitude.value) / _local_geo_radius.value
163
+
164
+ def _geocentric_radius(self, latitude, semi_major_axis, semi_minor_axis):
165
+ """
166
+ Calculate geocentric radius from WGS84 ellipsoid parameters.
167
+
168
+ derivation:
169
+ https://gis.stackexchange.com/questions/20200/how-do-you-compute-the-earths-radius-at-a-given-geodetic-latitude
170
+
171
+ Parameters
172
+ ----------
173
+ latitude: astropy.Quantity
174
+ Latitude in degrees.
175
+ semi_major_axis: astropy.Quantity
176
+ Semi-major axis of ellipsoid in meters.
177
+ semi_minor_axis: astropy.Quantity
178
+ Semi-minor axis of ellipsoid in meters.
179
+
180
+ Returns
181
+ -------
182
+ float
183
+ Ellipsoid radius at given latitude.
184
+
185
+ """
186
+
187
+ _lat_rad = np.deg2rad(latitude)
188
+ _numerator = (semi_major_axis**2 * np.cos(_lat_rad)) ** 2 + (
189
+ semi_minor_axis**2 * np.sin(_lat_rad)
190
+ ) ** 2
191
+
192
+ _denominator = (
193
+ semi_major_axis**2 * np.cos(_lat_rad) ** 2
194
+ + semi_minor_axis**2 * np.sin(_lat_rad) ** 2
195
+ )
196
+ return np.sqrt(_numerator / _denominator)
@@ -45,10 +45,10 @@ class TelescopePosition:
45
45
 
46
46
  """
47
47
  telstr = self.name
48
- if self.has_coordinates("corsika"):
48
+ if self.has_coordinates("ground"):
49
49
  telstr += (
50
- f"\t CORSIKA x(->North): {self.crs['corsika']['xx']['value']:0.2f} "
51
- f"y(->West): {self.crs['corsika']['yy']['value']:0.2f}"
50
+ f"\t Ground x(->North): {self.crs['ground']['xx']['value']:0.2f} "
51
+ f"y(->West): {self.crs['ground']['yy']['value']:0.2f}"
52
52
  )
53
53
  if self.has_coordinates("utm"):
54
54
  telstr += (
@@ -94,7 +94,7 @@ class TelescopePosition:
94
94
  _zz = self.crs[crs_name]["zz"]["value"]
95
95
  _zz_header = self.crs[crs_name]["zz"]["name"]
96
96
  if (
97
- crs_name == "corsika"
97
+ crs_name == "ground"
98
98
  and corsika_obs_level is not None
99
99
  and corsika_sphere_center is not None
100
100
  ):
@@ -105,7 +105,7 @@ class TelescopePosition:
105
105
  corsika_sphere_center,
106
106
  )
107
107
  ).value
108
- _zz_header = "pos_z"
108
+ _zz_header = "position_z"
109
109
 
110
110
  if crs_name == "mercator":
111
111
  telstr = (
@@ -304,7 +304,7 @@ class TelescopePosition:
304
304
 
305
305
  def _get_reference_system_from(self):
306
306
  """
307
- Return coordinate system and coordinatee for a fully defined system. The first fully\
307
+ Return coordinate system and coordinates for a fully defined system. The first fully\
308
308
  defined system from self.crs is returned.
309
309
 
310
310
  Returns
@@ -353,7 +353,7 @@ class TelescopePosition:
353
353
  np.isfinite(
354
354
  np.array(
355
355
  [self.crs[crs_name]["xx"]["value"], self.crs[crs_name]["yy"]["value"]],
356
- dtype=float,
356
+ dtype=np.float64,
357
357
  )
358
358
  )
359
359
  )
@@ -481,7 +481,7 @@ class TelescopePosition:
481
481
 
482
482
  """
483
483
 
484
- self._set_coordinate_system("corsika", crs_local)
484
+ self._set_coordinate_system("ground", crs_local)
485
485
  self._set_coordinate_system("utm", crs_utm)
486
486
  self._set_coordinate_system("mercator", crs_wgs84)
487
487
 
@@ -512,15 +512,15 @@ class TelescopePosition:
512
512
  Returns
513
513
  -------
514
514
  dict
515
- coordinate system defintion
515
+ coordinate system definition
516
516
 
517
517
  """
518
518
 
519
519
  return {
520
- "corsika": {
520
+ "ground": {
521
521
  "crs": None,
522
- "xx": {"name": "pos_x", "value": np.nan, "unit": u.Unit("m")},
523
- "yy": {"name": "pos_y", "value": np.nan, "unit": u.Unit("m")},
522
+ "xx": {"name": "position_x", "value": np.nan, "unit": u.Unit("m")},
523
+ "yy": {"name": "position_y", "value": np.nan, "unit": u.Unit("m")},
524
524
  "zz": {"name": "altitude", "value": np.nan, "unit": u.Unit("m")},
525
525
  },
526
526
  "mercator": {
@@ -1,12 +1,13 @@
1
1
  import logging
2
2
  from copy import copy
3
3
 
4
- from simtools import db_handler, io_handler
5
- from simtools.layout.layout_array import LayoutArray
4
+ from simtools import db_handler
5
+ from simtools.io_operations import io_handler
6
+ from simtools.layout.array_layout import ArrayLayout
6
7
  from simtools.model.telescope_model import TelescopeModel
7
8
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
8
9
  from simtools.utils import names
9
- from simtools.utils.general import collect_data_from_yaml_or_dict
10
+ from simtools.utils.general import collect_data_from_file_or_dict
10
11
 
11
12
  __all__ = ["ArrayModel", "InvalidArrayConfigData"]
12
13
 
@@ -18,7 +19,7 @@ class InvalidArrayConfigData(Exception):
18
19
  class ArrayModel:
19
20
  """
20
21
  ArrayModel is an abstract representation of the MC model at the array level. It contains the\
21
- list of TelescopeModel's and a LayoutArray.
22
+ list of TelescopeModel's and a ArrayLayout.
22
23
 
23
24
  Parameters
24
25
  ----------
@@ -46,7 +47,7 @@ class ArrayModel:
46
47
  self.model_version = None
47
48
  self._config_file_path = None
48
49
  self.io_handler = io_handler.IOHandler()
49
- array_config_data = collect_data_from_yaml_or_dict(array_config_file, array_config_data)
50
+ array_config_data = collect_data_from_file_or_dict(array_config_file, array_config_data)
50
51
  self._load_array_data(array_config_data)
51
52
  self._set_config_file_directory()
52
53
  self._build_array_model()
@@ -81,11 +82,11 @@ class ArrayModel:
81
82
  # Site
82
83
  self.site = names.validate_site_name(array_config_data["site"])
83
84
 
84
- # Grabbing layout name and building LayoutArray
85
- self.layout_name = names.validate_layout_array_name(array_config_data["layout_name"])
86
- self.layout = LayoutArray.from_layout_array_name(
85
+ # Grabbing layout name and building ArrayLayout
86
+ self.layout_name = names.validate_array_layout_name(array_config_data["layout_name"])
87
+ self.layout = ArrayLayout.from_array_layout_name(
87
88
  mongo_db_config=self.mongo_db_config,
88
- layout_array_name=self.site + "-" + self.layout_name,
89
+ array_layout_name=self.site + "-" + self.layout_name,
89
90
  label=self.label,
90
91
  )
91
92
 
@@ -94,8 +95,8 @@ class ArrayModel:
94
95
  "model_version" not in array_config_data.keys()
95
96
  or array_config_data["model_version"] is None
96
97
  ):
97
- self._logger.warning("model_version not given in array_config_data - using current")
98
- self.model_version = "current"
98
+ self._logger.warning("model_version not given in array_config_data - using 'Released'")
99
+ self.model_version = "Released"
99
100
  else:
100
101
  self.model_version = names.validate_model_version_name(
101
102
  array_config_data["model_version"]
@@ -144,6 +145,7 @@ class ArrayModel:
144
145
  """
145
146
 
146
147
  # Getting site parameters from DB
148
+ self._logger.debug("Getting site parameters from DB")
147
149
  db = db_handler.DatabaseHandler(mongo_db_config=self.mongo_db_config)
148
150
  self._site_parameters = db.get_site_parameters(
149
151
  self.site, self.model_version, only_applicable=True
@@ -154,7 +156,7 @@ class ArrayModel:
154
156
  _all_telescope_model_names = [] # List of telescope names without repetition
155
157
  _all_pars_to_change = {}
156
158
  for tel in self.layout:
157
- tel_size = names.get_telescope_type(tel.name)
159
+ tel_size = names.get_telescope_class(tel.name)
158
160
 
159
161
  # Collecting telescope name and pars to change from array_config_data
160
162
  tel_model_name, pars_to_change = self._get_single_telescope_info_from_array_config(
@@ -174,7 +176,7 @@ class ArrayModel:
174
176
  telescope_model_name=tel_model_name,
175
177
  model_version=self.model_version,
176
178
  label=self.label,
177
- mongo_db_config=self.mongo_db_config,
179
+ db=db,
178
180
  )
179
181
  else:
180
182
  # Telescope name already exists.
@@ -192,7 +194,7 @@ class ArrayModel:
192
194
  if len(self._telescope_model) != len(self.layout):
193
195
  self._logger.warning(
194
196
  "Number of telescopes in the list of telescope models does "
195
- "not match the number of telescopes in the LayoutArray - something is wrong!"
197
+ "not match the number of telescopes in the ArrayLayout - something is wrong!"
196
198
  )
197
199
 
198
200
  # Changing parameters, if there are any in all_pars_to_change
simtools/model/camera.py CHANGED
@@ -11,12 +11,9 @@ from scipy.spatial import cKDTree as KDTree
11
11
  from scipy.spatial import distance
12
12
 
13
13
  import simtools.visualization.legend_handlers as leg_h
14
- from simtools.model.model_utils import (
15
- get_camera_name,
16
- get_telescope_class,
17
- is_two_mirror_telescope,
18
- )
19
- from simtools.utils.general import rotate
14
+ from simtools.model.model_utils import is_two_mirror_telescope
15
+ from simtools.utils import names
16
+ from simtools.utils.geometry import rotate
20
17
 
21
18
  __all__ = ["Camera"]
22
19
 
@@ -51,7 +48,7 @@ class Camera:
51
48
  self._logger = logging.getLogger(__name__)
52
49
 
53
50
  self._telescope_model_name = telescope_model_name
54
- self._camera_name = get_camera_name(self._telescope_model_name)
51
+ _, self._camera_name, _ = names.split_telescope_model_name(telescope_model_name)
55
52
  self._camera_config_file = camera_config_file
56
53
  self._focal_length = focal_length
57
54
  if self._focal_length <= 0:
@@ -743,7 +740,7 @@ class Camera:
743
740
 
744
741
  if self._pixels["pix_id"][i_pix] < pixels_id_to_print + 1:
745
742
  font_size = 4
746
- if get_telescope_class(self._telescope_model_name) == "SCT":
743
+ if names.get_telescope_class(self._telescope_model_name) == "SCT":
747
744
  font_size = 2
748
745
  plt.text(
749
746
  xy_pix_pos[0],
simtools/model/mirrors.py CHANGED
@@ -1,5 +1,7 @@
1
1
  import logging
2
2
 
3
+ import astropy.units as u
4
+ import numpy as np
3
5
  from astropy.table import Table
4
6
 
5
7
  __all__ = ["InvalidMirrorListFile", "Mirrors"]
@@ -19,24 +21,25 @@ class Mirrors:
19
21
  mirror list in sim_telarray or ecsv format (with panel focal length only).
20
22
  """
21
23
 
22
- def __init__(self, mirror_list_file):
24
+ def __init__(self, mirror_list_file, parameters=None):
23
25
  """
24
26
  Initialize Mirrors.
25
27
  """
26
28
  self._logger = logging.getLogger(__name__)
27
29
  self._logger.debug("Mirrors Init")
28
30
 
29
- self._mirrors = {}
30
- self.diameter = None
31
- self.shape = None
31
+ self.mirror_table = Table()
32
+ self.mirror_diameter = None
33
+ self.shape_type = None
32
34
  self.number_of_mirrors = 0
35
+ self.parameters = parameters
33
36
 
34
37
  self._mirror_list_file = mirror_list_file
35
38
  self._read_mirror_list()
36
39
 
37
40
  def _read_mirror_list(self):
38
41
  """
39
- Read the mirror lists from disk and store the data. Allow reading of mirro lists in \
42
+ Read the mirror lists from disk and store the data. Allow reading of mirror lists in \
40
43
  sim_telarray and ecsv format
41
44
  """
42
45
 
@@ -54,32 +57,66 @@ class Mirrors:
54
57
  InvalidMirrorListFile
55
58
  If number of mirrors is 0.
56
59
  """
60
+ # Getting mirror parameters from mirror list.
57
61
 
58
- # TODO - temporary hard wired geopars - should come from DB
59
- self.diameter = 120
60
- self.shape = 1
61
- self._logger.debug(f"Shape = {self.shape}")
62
- self._logger.debug(f"Diameter = {self.diameter}")
63
-
64
- _mirror_table = Table.read(self._mirror_list_file, format="ascii.ecsv")
62
+ self.mirror_table = Table.read(self._mirror_list_file, format="ascii.ecsv")
65
63
  self._logger.debug(f"Reading mirror properties from {self._mirror_list_file}")
66
- try:
67
- self._mirrors["flen"] = list(_mirror_table["mirror_panel_radius"].to("cm").value / 2.0)
68
- self.number_of_mirrors = len(self._mirrors["flen"])
69
- self._mirrors["number"] = list(range(self.number_of_mirrors))
70
- self._mirrors["pos_x"] = [0.0] * self.number_of_mirrors
71
- self._mirrors["pos_y"] = [0.0] * self.number_of_mirrors
72
- self._mirrors["diameter"] = [self.diameter] * self.number_of_mirrors
73
- self._mirrors["shape"] = [self.shape] * self.number_of_mirrors
74
- except KeyError:
75
- self._logger.debug(
76
- f"Missing column for mirror panel focal length (flen) in {self._mirror_list_file}"
77
- )
64
+
65
+ self.number_of_mirrors = np.shape(self.mirror_table)[0]
66
+ self._logger.debug(f"Number of Mirrors = {self.number_of_mirrors}")
78
67
 
79
68
  if self.number_of_mirrors == 0:
80
69
  msg = "Problem reading mirror list file"
81
70
  self._logger.error(msg)
82
- raise InvalidMirrorListFile()
71
+ raise InvalidMirrorListFile
72
+
73
+ try:
74
+ self.mirror_diameter = u.Quantity(self.mirror_table["mirror_diameter"])[0]
75
+ self._logger.debug(f"Mirror diameter = {self.mirror_diameter}")
76
+ except KeyError:
77
+ self._logger.debug("Mirror mirror_panel_diameter not in mirror file")
78
+ try:
79
+ self.mirror_diameter = u.Quantity(
80
+ self.parameters["mirror_panel_diameter"]["Value"],
81
+ self.parameters["mirror_panel_diameter"]["units"],
82
+ )
83
+ self._logger.debug("Take mirror_panel_diameter from parameters")
84
+ except TypeError as error:
85
+ msg = "Mirror mirror_panel_diameter not contained in DB"
86
+ self._logger.error(msg)
87
+ raise TypeError(msg) from error
88
+ if "focal_length" not in self.mirror_table.colnames:
89
+ try:
90
+ self.mirror_table["focal_length"] = (
91
+ self.mirror_table["mirror_curvature_radius"].to("cm") / 2
92
+ )
93
+ except KeyError:
94
+ self._logger.debug("mirror_curvature_radius not contained in mirror list")
95
+ try:
96
+ self.mirror_table["focal_length"] = self.number_of_mirrors * [
97
+ u.Quantity(
98
+ self.parameters["mirror_focal_length"]["Value"],
99
+ self.parameters["mirror_focal_length"]["units"],
100
+ )
101
+ ]
102
+ self._logger.debug("Take mirror_focal_length from parameters")
103
+ except TypeError as error:
104
+ msg = "mirror_focal_length not contained in DB"
105
+ self._logger.error(msg)
106
+ raise TypeError(msg) from error
107
+
108
+ try:
109
+ self.shape_type = u.Quantity(self.mirror_table["shape_type"])[0]
110
+ self._logger.debug(f"Mirror shape_type = {self.shape_type}")
111
+ except KeyError:
112
+ self._logger.debug("Mirror shape_type not in mirror file")
113
+ try:
114
+ self.shape_type = self.parameters["mirror_panel_shape"]["Value"]
115
+ self._logger.debug("Take shape_type from parameters")
116
+ except TypeError as error:
117
+ msg = "Mirror shape_type not contained in DB"
118
+ self._logger.error(msg)
119
+ raise TypeError(msg) from error
83
120
 
84
121
  def _read_mirror_list_from_sim_telarray(self):
85
122
  """
@@ -91,39 +128,37 @@ class Mirrors:
91
128
  If number of mirrors is 0.
92
129
  """
93
130
 
94
- self._mirrors["number"] = []
95
- self._mirrors["pos_x"] = []
96
- self._mirrors["pos_y"] = []
97
- self._mirrors["diameter"] = []
98
- self._mirrors["flen"] = []
99
- self._mirrors["shape"] = []
100
-
101
- mirror_counter = 0
102
- collect_geo_pars = True
103
- with open(self._mirror_list_file, "r", encoding="utf-8") as file:
104
- for line in file:
105
- line = line.split()
106
- if "#" in line[0] or "$" in line[0]:
107
- continue
108
- if collect_geo_pars:
109
- self.diameter = float(line[2])
110
- self.shape = int(line[4])
111
- collect_geo_pars = False
112
- self._logger.debug(f"Shape = {self.shape}")
113
- self._logger.debug(f"Diameter = {self.diameter}")
114
-
115
- self._mirrors["number"].append(mirror_counter)
116
- self._mirrors["pos_x"].append(float(line[0]))
117
- self._mirrors["pos_y"].append(float(line[1]))
118
- self._mirrors["diameter"].append(float(line[2]))
119
- self._mirrors["flen"].append(float(line[3]))
120
- self._mirrors["shape"].append(float(line[4]))
121
- mirror_counter += 1
122
- self.number_of_mirrors = mirror_counter
123
- if self.number_of_mirrors == 0:
124
- msg = "Problem reading mirror list file"
125
- self._logger.error(msg)
126
- raise InvalidMirrorListFile()
131
+ self.mirror_table = Table.read(
132
+ self._mirror_list_file,
133
+ format="ascii.no_header",
134
+ names=[
135
+ "mirror_x",
136
+ "mirror_y",
137
+ "mirror_diameter",
138
+ "focal_length",
139
+ "shape_type",
140
+ "mirror_z",
141
+ "sep",
142
+ "mirror_panel_id",
143
+ ],
144
+ units=["cm", "cm", "cm", "cm", None, "cm", None, None],
145
+ )
146
+ self.mirror_table["mirror_panel_id"] = np.array(
147
+ [
148
+ int("".join(filter(str.isdigit, string)))
149
+ for string in self.mirror_table["mirror_panel_id"]
150
+ ]
151
+ )
152
+
153
+ self.shape_type = self.mirror_table["shape_type"][0]
154
+ self.mirror_diameter = u.Quantity(
155
+ self.mirror_table["mirror_diameter"][0], self.mirror_table["mirror_diameter"].unit
156
+ )
157
+ self.number_of_mirrors = len(self.mirror_table["focal_length"])
158
+
159
+ self._logger.debug(f"Mirror shape_type = {self.shape_type}")
160
+ self._logger.debug(f"Mirror diameter = {self.mirror_diameter}")
161
+ self._logger.debug(f"Number of Mirrors = {self.number_of_mirrors}")
127
162
 
128
163
  def get_single_mirror_parameters(self, number):
129
164
  """
@@ -136,24 +171,52 @@ class Mirrors:
136
171
 
137
172
  Returns
138
173
  -------
139
- (pos_x, pos_y, diameter, flen, shape): tuple of float
140
- X, Y positions, diameter, focal length and shape.
174
+ (pos_x, pos_y, mirror_diameter, focal_length, shape_type): tuple of float
175
+ X, Y positions, mirror_diameter, focal length and shape_type.
141
176
  """
142
177
 
143
- if number > self.number_of_mirrors - 1:
144
- self._logger.error("Mirror number is out range")
145
- return None
146
- return (
147
- self._mirrors["pos_x"][number],
148
- self._mirrors["pos_y"][number],
149
- self._mirrors["diameter"][number],
150
- self._mirrors["flen"][number],
151
- self._mirrors["shape"][number],
152
- )
178
+ mask = self.mirror_table["mirror_panel_id"] == number
179
+ if not np.any(mask):
180
+ self._logger.debug(f"Mirror id{number} not in table, using first mirror instead")
181
+ mask[0] = True
182
+ try:
183
+ return_values = (
184
+ u.Quantity(
185
+ self.mirror_table[mask]["mirror_x"].value[0],
186
+ self.mirror_table[mask]["mirror_x"].unit,
187
+ ),
188
+ u.Quantity(
189
+ self.mirror_table[mask]["mirror_y"].value[0],
190
+ self.mirror_table[mask]["mirror_y"].unit,
191
+ ),
192
+ u.Quantity(
193
+ self.mirror_table[mask]["mirror_diameter"].value[0],
194
+ self.mirror_table[mask]["mirror_diameter"].unit,
195
+ ),
196
+ u.Quantity(
197
+ self.mirror_table[mask]["focal_length"].value[0],
198
+ self.mirror_table[mask]["focal_length"].unit,
199
+ ),
200
+ u.Quantity(
201
+ self.mirror_table[mask]["shape_type"].value[0],
202
+ self.mirror_table[mask]["shape_type"].unit,
203
+ ),
204
+ )
205
+ except KeyError:
206
+ self._logger.debug("Mirror list missing required column")
207
+ return_values = (
208
+ 0,
209
+ 0,
210
+ self.mirror_diameter,
211
+ u.Quantity(
212
+ self.mirror_table[mask]["focal_length"].value[0],
213
+ self.mirror_table[mask]["focal_length"].unit,
214
+ ),
215
+ self.shape_type,
216
+ )
217
+ return return_values
153
218
 
154
219
  def plot_mirror_layout(self):
155
220
  """
156
- Plot the mirror layout.
157
-
158
- TODO
221
+ Plot the mirror layout (not implemented yet).
159
222
  """