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
@@ -3,23 +3,28 @@ from pathlib import Path
3
3
 
4
4
  import astropy.units as u
5
5
  import numpy as np
6
- import pyproj
7
6
  from astropy.table import QTable
8
7
 
9
- from simtools import db_handler, io_handler
8
+ from simtools import db_handler
9
+ from simtools.data_model import data_reader
10
+ from simtools.io_operations import io_handler
11
+ from simtools.layout.geo_coordinates import GeoCoordinates
10
12
  from simtools.layout.telescope_position import TelescopePosition
11
13
  from simtools.utils import names
12
- from simtools.utils.general import collect_data_from_yaml_or_dict
13
- from simtools.utils.names import all_telescope_class_names
14
+ from simtools.utils.general import collect_data_from_file_or_dict
14
15
 
15
- __all__ = ["InvalidTelescopeListFile", "LayoutArray"]
16
+ __all__ = ["InvalidTelescopeListFile", "ArrayLayout"]
16
17
 
17
18
 
18
19
  class InvalidTelescopeListFile(Exception):
19
20
  """Exception for invalid telescope list file."""
20
21
 
21
22
 
22
- class LayoutArray:
23
+ class InvalidCoordinateDataType(Exception):
24
+ """Exception for low-precision coordinate data type."""
25
+
26
+
27
+ class ArrayLayout:
23
28
  """
24
29
  Manage telescope positions at the array layout level.
25
30
 
@@ -50,9 +55,11 @@ class LayoutArray:
50
55
  layout_center_data=None,
51
56
  corsika_telescope_data=None,
52
57
  telescope_list_file=None,
58
+ telescope_list_metadata_file=None,
59
+ validate=False,
53
60
  ):
54
61
  """
55
- Initialize LayoutArray.
62
+ Initialize ArrayLayout.
56
63
  """
57
64
 
58
65
  self._logger = logging.getLogger(__name__)
@@ -62,18 +69,24 @@ class LayoutArray:
62
69
  self.name = name
63
70
  self.site = None if site is None else names.validate_site_name(site)
64
71
  self.io_handler = io_handler.IOHandler()
72
+ self.geo_coordinates = GeoCoordinates()
65
73
 
66
74
  self.telescope_list_file = None
67
75
  self._telescope_list = []
68
76
  self._epsg = None
77
+
69
78
  if telescope_list_file is None:
70
79
  self._initialize_coordinate_systems(layout_center_data)
71
80
  self._initialize_corsika_telescope(corsika_telescope_data)
72
81
  else:
73
- self.initialize_layout_array_from_telescope_file(telescope_list_file)
82
+ self.initialize_array_layout_from_telescope_file(
83
+ telescope_list_file=telescope_list_file,
84
+ telescope_list_metadata_file=telescope_list_metadata_file,
85
+ validate=validate,
86
+ )
74
87
 
75
88
  @classmethod
76
- def from_layout_array_name(cls, mongo_db_config, layout_array_name, label=None):
89
+ def from_array_layout_name(cls, mongo_db_config, array_layout_name, label=None):
77
90
  """
78
91
  Read telescope list from file for given layout name (e.g. South-4LST, North-Prod5, ...).
79
92
  Layout definitions are given in the `data/layout` path.
@@ -82,33 +95,33 @@ class LayoutArray:
82
95
  ----------
83
96
  mongo_db_config: dict
84
97
  MongoDB configuration.
85
- layout_array_name: str
98
+ array_layout_name: str
86
99
  e.g. South-4LST, North-Prod5 ...
87
100
  label: str
88
101
  Instance label. Important for output file naming.
89
102
 
90
103
  Returns
91
104
  -------
92
- LayoutArray
93
- Instance of the LayoutArray.
105
+ ArrayLayout
106
+ Instance of the ArrayLayout.
94
107
  """
95
108
 
96
- split_name = layout_array_name.split("-")
109
+ split_name = array_layout_name.split("-")
97
110
  site_name = names.validate_site_name(split_name[0])
98
- array_name = names.validate_layout_array_name(split_name[1])
99
- valid_layout_array_name = site_name + "-" + array_name
111
+ array_name = names.validate_array_layout_name(split_name[1])
112
+ valid_array_layout_name = site_name + "-" + array_name
100
113
 
101
114
  layout = cls(
102
115
  site=site_name,
103
116
  mongo_db_config=mongo_db_config,
104
- name=valid_layout_array_name,
117
+ name=valid_array_layout_name,
105
118
  label=label,
106
119
  )
107
120
 
108
121
  telescope_list_file = layout.io_handler.get_input_data_file(
109
- "layout", f"telescope_positions-{valid_layout_array_name}.ecsv"
122
+ "layout", f"telescope_positions-{valid_array_layout_name}.ecsv"
110
123
  )
111
- layout.initialize_layout_array_from_telescope_file(telescope_list_file)
124
+ layout.initialize_array_layout_from_telescope_file(telescope_list_file)
112
125
 
113
126
  return layout
114
127
 
@@ -168,14 +181,19 @@ class LayoutArray:
168
181
  If file_name does not exist.
169
182
  """
170
183
  if file_name is None:
171
- corsika_parameters_dict = collect_data_from_yaml_or_dict(
172
- self.io_handler.get_input_data_file("parameters", "corsika_parameters.yml"), None
173
- )
184
+ try:
185
+ corsika_parameters_dict = collect_data_from_file_or_dict(
186
+ self.io_handler.get_input_data_file("parameters", "corsika_parameters.yml"),
187
+ None,
188
+ )
189
+ except io_handler.IncompleteIOHandlerInit:
190
+ self._logger.info("Error reading CORSIKA parameters from file")
191
+ return {}
174
192
  else:
175
193
  if not isinstance(file_name, Path):
176
194
  file_name = Path(file_name)
177
195
  if file_name.exists():
178
- corsika_parameters_dict = collect_data_from_yaml_or_dict(file_name, None)
196
+ corsika_parameters_dict = collect_data_from_file_or_dict(file_name, None)
179
197
  else:
180
198
  raise FileNotFoundError
181
199
 
@@ -184,22 +202,16 @@ class LayoutArray:
184
202
  for simtools_par in corsika_pars:
185
203
  corsika_par = names.translate_simtools_to_corsika(simtools_par)
186
204
  corsika_dict[simtools_par] = {}
187
- for tel_type in all_telescope_class_names:
188
- corsika_dict[simtools_par][tel_type] = corsika_parameters_dict[corsika_par][
189
- tel_type
190
- ]["value"]
191
-
205
+ for key, value in corsika_parameters_dict[corsika_par].items():
206
+ corsika_dict[simtools_par][key] = value["value"]
192
207
  try:
193
- unit = corsika_parameters_dict[corsika_par][tel_type]["unit"]
194
- corsika_dict[simtools_par][tel_type] = corsika_dict[simtools_par][
195
- tel_type
196
- ] * u.Unit(unit)
208
+ unit = value["unit"]
209
+ corsika_dict[simtools_par][key] = corsika_dict[simtools_par][key] * u.Unit(unit)
197
210
  except KeyError:
198
211
  self._logger.warning(
199
212
  "Key not valid. Dictionary does not have a key 'unit'. Continuing without "
200
213
  "the unit."
201
214
  )
202
- corsika_dict[simtools_par][tel_type] = corsika_dict[simtools_par][tel_type]
203
215
 
204
216
  if self.mongo_db_config is None:
205
217
  self._logger.error("DB connection info was not provided, cannot set site altitude")
@@ -210,19 +222,20 @@ class LayoutArray:
210
222
 
211
223
  db = db_handler.DatabaseHandler(mongo_db_config=self.mongo_db_config)
212
224
  self._logger.debug("Reading site parameters from DB")
213
- _site_pars = db.get_site_parameters(self.site, "Current", only_applicable=True)
225
+ _site_pars = db.get_site_parameters(self.site, "Released", only_applicable=True)
214
226
  corsika_dict["corsika_obs_level"] = _site_pars["altitude"]["Value"] * u.Unit(
215
227
  _site_pars["altitude"]["units"]
216
228
  )
217
229
 
218
230
  return corsika_dict
219
231
 
220
- @staticmethod
221
- def _initialize_sphere_parameters(sphere_dict):
232
+ def _initialize_sphere_parameters(self, sphere_dict):
222
233
  """
223
234
  Set CORSIKA sphere parameters from dictionary. Type of input varies and depend on data \
224
235
  source for these parameters.
225
236
 
237
+ Example for sphere_dict: {LST: 12.5 m, MST: 9.15 m, SST: 3 m}
238
+
226
239
  Parameters
227
240
  ----------
228
241
  sphere_dict: dict
@@ -242,8 +255,9 @@ class LayoutArray:
242
255
  _sphere_dict_cleaned[key] = u.Quantity(value)
243
256
  else:
244
257
  _sphere_dict_cleaned[key] = value["value"] * u.Unit(value["unit"])
245
- except (TypeError, KeyError):
246
- pass
258
+ except (TypeError, KeyError) as exc:
259
+ self._logger.error(f"Error setting CORSIKA sphere parameters from {sphere_dict}")
260
+ raise exc
247
261
 
248
262
  return _sphere_dict_cleaned
249
263
 
@@ -257,28 +271,25 @@ class LayoutArray:
257
271
  dictionary with CORSIKA telescope parameters
258
272
 
259
273
  """
274
+
260
275
  try:
261
276
  self._corsika_telescope["corsika_obs_level"] = u.Quantity(
262
277
  corsika_dict["corsika_obs_level"]
263
278
  )
264
279
  except (TypeError, KeyError):
265
280
  self._corsika_telescope["corsika_obs_level"] = np.nan * u.m
266
- try:
267
- self._corsika_telescope["corsika_sphere_center"] = self._initialize_sphere_parameters(
268
- corsika_dict["corsika_sphere_center"]
269
- )
270
- except (TypeError, KeyError):
271
- pass
272
- try:
273
- self._corsika_telescope["corsika_sphere_radius"] = self._initialize_sphere_parameters(
274
- corsika_dict["corsika_sphere_radius"]
275
- )
276
- except (TypeError, KeyError):
277
- pass
281
+
282
+ for key in ["corsika_sphere_center", "corsika_sphere_radius"]:
283
+ try:
284
+ self._corsika_telescope[key] = self._initialize_sphere_parameters(corsika_dict[key])
285
+ except (TypeError, KeyError):
286
+ pass
278
287
 
279
288
  def _initialize_coordinate_systems(self, center_dict=None):
280
289
  """
281
290
  Initialize array center and coordinate systems.
291
+ By definition, the array center is at (0,0) in
292
+ the ground coordinate system.
282
293
 
283
294
  Parameters
284
295
  ----------
@@ -292,11 +303,29 @@ class LayoutArray:
292
303
 
293
304
  """
294
305
 
306
+ center_dict = {} if center_dict is None else center_dict
307
+
295
308
  self._array_center = TelescopePosition()
296
309
  self._array_center.name = "array_center"
297
- self._array_center.set_coordinates("corsika", 0.0 * u.m, 0.0 * u.m, 0.0 * u.m)
310
+ self._array_center.set_coordinates("ground", 0.0 * u.m, 0.0 * u.m, 0.0 * u.m)
311
+ self._set_array_center_mercator(center_dict)
312
+ self._set_array_center_utm(center_dict)
313
+ self._array_center.set_altitude(u.Quantity(center_dict.get("center_alt", np.nan * u.m)))
314
+ _name = center_dict.get("array_name")
315
+ self.name = _name if _name is not None else self.name
316
+
317
+ self._array_center.convert_all(
318
+ crs_local=self.geo_coordinates.crs_local(self._array_center),
319
+ crs_wgs84=self.geo_coordinates.crs_wgs84(),
320
+ crs_utm=self.geo_coordinates.crs_utm(self._epsg),
321
+ )
322
+
323
+ def _set_array_center_mercator(self, center_dict):
324
+ """
325
+ Set array center coordinates in mercator system.
326
+
327
+ """
298
328
 
299
- center_dict = {} if center_dict is None else center_dict
300
329
  try:
301
330
  self._array_center.set_coordinates(
302
331
  "mercator",
@@ -305,6 +334,15 @@ class LayoutArray:
305
334
  )
306
335
  except TypeError:
307
336
  pass
337
+
338
+ def _set_array_center_utm(self, center_dict):
339
+ """
340
+ Set array center coordinates in UTM system.
341
+ Convert array center position to WGS84 system
342
+ (as latitudes are required for the definition
343
+ for the definition of the ground coordinate system)
344
+
345
+ """
308
346
  try:
309
347
  self._epsg = center_dict.get("EPSG", None)
310
348
  self._array_center.set_coordinates(
@@ -312,23 +350,13 @@ class LayoutArray:
312
350
  u.Quantity(center_dict.get("center_easting", np.nan * u.m)),
313
351
  u.Quantity(center_dict.get("center_northing", np.nan * u.m)),
314
352
  )
353
+ self._array_center.convert_all(
354
+ crs_local=None,
355
+ crs_wgs84=self.geo_coordinates.crs_wgs84(),
356
+ crs_utm=self.geo_coordinates.crs_utm(self._epsg),
357
+ )
315
358
  except TypeError:
316
359
  pass
317
- try:
318
- self._array_center.set_altitude(u.Quantity(center_dict.get("center_alt", 0.0 * u.m)))
319
- except TypeError:
320
- pass
321
- try:
322
- _name = center_dict.get("array_name")
323
- self.name = _name if _name is not None else self.name
324
- except KeyError:
325
- pass
326
-
327
- self._array_center.convert_all(
328
- crs_local=self._get_crs_local(),
329
- crs_wgs84=self._get_crs_wgs84(),
330
- crs_utm=self._get_crs_utm(),
331
- )
332
360
 
333
361
  def _altitude_from_corsika_z(self, pos_z=None, altitude=None, tel_name=None):
334
362
  """
@@ -379,12 +407,26 @@ class LayoutArray:
379
407
  astropy.Quantity
380
408
  Telescope sphere center value (0.0*u.m if sphere center is not defined).
381
409
 
410
+ Raises
411
+ ------
412
+ KeyError
413
+ if Missing definition of CORSIKA sphere center for this telescope type.
414
+
382
415
  """
383
416
 
384
- if len(names.get_telescope_type(tel_name)) > 0:
417
+ try:
385
418
  return self._corsika_telescope["corsika_sphere_center"][
386
- names.get_telescope_type(tel_name)
419
+ names.get_telescope_class(tel_name)
387
420
  ]
421
+ except KeyError:
422
+ self._logger.warning(
423
+ "Missing definition of CORSIKA sphere center for telescope "
424
+ f"{tel_name} of type {names.get_telescope_class(tel_name)}"
425
+ )
426
+ except ValueError:
427
+ self._logger.warning(
428
+ f"Missing definition of CORSIKA sphere center for telescope {tel_name}"
429
+ )
388
430
 
389
431
  return 0.0 * u.m
390
432
 
@@ -413,7 +455,11 @@ class LayoutArray:
413
455
  try:
414
456
  tel.name = row["telescope_name"]
415
457
  if "asset_code" not in row:
416
- tel.asset_code = names.get_telescope_type(tel.name)
458
+ try:
459
+ tel.asset_code = names.get_telescope_class(tel.name)
460
+ # asset code is not a valid telescope name; possibly a calibration device
461
+ except ValueError:
462
+ tel.asset_code = tel.name.split("-")[0]
417
463
  except KeyError:
418
464
  pass
419
465
  try:
@@ -486,7 +532,7 @@ class LayoutArray:
486
532
  self._assign_unit_to_quantity(row[key2], table[key2].unit),
487
533
  )
488
534
  except KeyError:
489
- self._logger.debug(f"{key1} and {key2} are not given. Coordinates not set.")
535
+ pass
490
536
 
491
537
  def _try_set_altitude(self, row, tel, table):
492
538
  """
@@ -505,14 +551,16 @@ class LayoutArray:
505
551
  try:
506
552
  tel.set_altitude(
507
553
  self._altitude_from_corsika_z(
508
- pos_z=self._assign_unit_to_quantity(row["pos_z"], table["pos_z"].unit),
554
+ pos_z=self._assign_unit_to_quantity(
555
+ row["position_z"], table["position_z"].unit
556
+ ),
509
557
  tel_name=tel.name,
510
558
  )
511
559
  )
512
560
  except KeyError:
513
561
  pass
514
562
  try:
515
- tel.set_altitude(self._assign_unit_to_quantity(row["alt"], table["alt"].unit))
563
+ tel.set_altitude(self._assign_unit_to_quantity(row["altitude"], table["altitude"].unit))
516
564
  except KeyError:
517
565
  pass
518
566
 
@@ -528,58 +576,44 @@ class LayoutArray:
528
576
  """
529
577
  for row in table:
530
578
  tel = self._load_telescope_names(row)
531
- self._try_set_coordinate(row, tel, table, "corsika", "pos_x", "pos_y")
579
+ self._try_set_coordinate(row, tel, table, "ground", "position_x", "position_y")
532
580
  self._try_set_coordinate(row, tel, table, "utm", "utm_east", "utm_north")
533
- self._try_set_coordinate(row, tel, table, "mercator", "mercator", "lon")
581
+ self._try_set_coordinate(row, tel, table, "mercator", "latitude", "longitude")
534
582
  self._try_set_altitude(row, tel, table)
535
583
 
536
584
  self._telescope_list.append(tel)
537
585
 
538
- @staticmethod
539
- def read_telescope_list_file(telescope_list_file):
586
+ def initialize_array_layout_from_telescope_file(
587
+ self, telescope_list_file, telescope_list_metadata_file=None, validate=False
588
+ ):
540
589
  """
541
- Read list of telescopes from a ecsv file.
590
+ Initialize the Layout array from a telescope list file.
542
591
 
543
592
  Parameters
544
593
  ----------
545
594
  telescope_list_file: str or Path
546
595
  Path to the telescope list file.
596
+ telescope_list_metadata_file: str or Path
597
+ Path to the telescope list metadata file.
598
+ validate: bool
599
+ Validate the telescope list file.
547
600
 
548
601
  Returns
549
602
  -------
550
- astropy.QTable
551
- Astropy table with the telescope layout information.
552
-
553
- Raises
554
- ------
555
- FileNotFoundError
556
- If file cannot be opened.
557
-
558
- """
559
- _logger = logging.getLogger(__name__)
560
- try:
561
- table = QTable.read(telescope_list_file, format="ascii.ecsv")
562
- except FileNotFoundError:
563
- _logger.error(f"Error reading list of array elements from {telescope_list_file}")
564
- raise
565
- _logger.info(f"Reading array elements from {telescope_list_file}")
566
-
567
- return table
568
-
569
- def initialize_layout_array_from_telescope_file(self, telescope_list_file):
603
+ astropy.table.QTable
604
+ Table with the telescope layout information.
570
605
  """
571
- Initialize the Layout array from a telescope list file.
572
-
573
- Parameters
574
- ----------
575
- telescope_list_file: str or Path
576
- Path to the telescope list file.
577
- """
578
- table = self.read_telescope_list_file(telescope_list_file=telescope_list_file)
606
+ table = data_reader.read_table_from_file(
607
+ file_name=telescope_list_file,
608
+ validate=validate,
609
+ metadata_file=telescope_list_metadata_file,
610
+ )
579
611
  self._initialize_corsika_telescope(table.meta)
580
612
  self._initialize_coordinate_systems(table.meta)
581
613
  self._load_telescope_list(table)
582
614
 
615
+ return table
616
+
583
617
  def add_telescope(self, telescope_name, crs_name, xx, yy, altitude=None, tel_corsika_z=None):
584
618
  """
585
619
  Add an individual telescope to the telescope list.
@@ -611,8 +645,8 @@ class LayoutArray:
611
645
 
612
646
  def _get_export_metadata(self, export_corsika_meta=False):
613
647
  """
614
- File metadata for export of array element list to file. Included array center definiton,\
615
- CORSIKA telescope parameters, and EPSG centre
648
+ File metadata for export of array element list to file. Included array center definition,\
649
+ CORSIKA telescope parameters, and EPSG center
616
650
 
617
651
  Parameters
618
652
  ----------
@@ -649,42 +683,25 @@ class LayoutArray:
649
683
 
650
684
  return _meta
651
685
 
652
- def _set_telescope_list_file(self, crs_name):
686
+ def export_telescope_list_table(self, crs_name, corsika_z=False):
653
687
  """
654
- Set file location for writing of telescope list
688
+ Export array elements positions to astropy table.
655
689
 
656
690
  Parameters
657
691
  ----------
658
692
  crs_name: str
659
693
  Name of coordinate system to be used for export.
694
+ corsika_z: bool
695
+ Write telescope height in CORSIKA coordinates (for CORSIKA system).
660
696
 
661
697
  Returns
662
698
  -------
663
- Path
664
- Output file
699
+ astropy.table.QTable
700
+ Astropy table with the telescope layout information.
665
701
 
666
702
  """
667
703
 
668
- _output_directory = self.io_handler.get_output_directory(self.label, "layout")
669
-
670
- _name = crs_name if self.name is None else self.name + "-" + crs_name
671
- self.telescope_list_file = _output_directory.joinpath(
672
- names.layout_telescope_list_file_name(_name, None)
673
- )
674
-
675
- def export_telescope_list(self, crs_name, corsika_z=False):
676
- """
677
- Export array elements positions to ECSV file
678
-
679
- Parameters
680
- ----------
681
- crs_name: str
682
- Name of coordinate system to be used for export.
683
- corsika_z: bool
684
- Write telescope height in CORSIKA coordinates (for CORSIKA system).
685
- """
686
-
687
- table = QTable(meta=self._get_export_metadata(crs_name == "corsika"))
704
+ table = QTable(meta=self._get_export_metadata(crs_name == "ground"))
688
705
 
689
706
  tel_names, asset_code, sequence_number, geo_code = [], [], [], []
690
707
  pos_x, pos_y, pos_z = [], [], []
@@ -716,15 +733,13 @@ class LayoutArray:
716
733
  table[_name_x] = pos_x
717
734
  table[_name_y] = pos_y
718
735
  if corsika_z:
719
- table["pos_z"] = pos_z
736
+ table["position_z"] = pos_z
720
737
  else:
721
738
  table[_name_z] = pos_z
722
739
  except IndexError:
723
740
  pass
724
741
 
725
- self._set_telescope_list_file(crs_name)
726
- self._logger.info(f"Exporting telescope list to {self.telescope_list_file}")
727
- table.write(self.telescope_list_file, format="ascii.ecsv", overwrite=True)
742
+ return table
728
743
 
729
744
  def get_number_of_telescopes(self):
730
745
  """
@@ -754,10 +769,10 @@ class LayoutArray:
754
769
 
755
770
  corsika_list = ""
756
771
  for tel in self._telescope_list:
757
- pos_x, pos_y, pos_z = tel.get_coordinates("corsika")
772
+ pos_x, pos_y, pos_z = tel.get_coordinates("ground")
758
773
  try:
759
774
  sphere_radius = self._corsika_telescope["corsika_sphere_radius"][
760
- names.get_telescope_type(tel.name)
775
+ names.get_telescope_class(tel.name)
761
776
  ]
762
777
  except KeyError:
763
778
  self._logger.error("Missing definition of CORSIKA sphere radius")
@@ -773,9 +788,8 @@ class LayoutArray:
773
788
  raise
774
789
 
775
790
  corsika_list += "TELESCOPE"
776
- corsika_list += f"\t {pos_x.value:.3f}E2"
777
- corsika_list += f"\t {pos_y.value:.3f}E2"
778
- corsika_list += f"\t {pos_z.value:.3f}E2"
791
+ for pos in [pos_x, pos_y, pos_z]:
792
+ corsika_list += f"\t {pos.value:.3f}E2"
779
793
  corsika_list += f"\t {sphere_radius.value:.3f}E2"
780
794
  corsika_list += f"\t # {tel.name}\n"
781
795
 
@@ -787,7 +801,7 @@ class LayoutArray:
787
801
 
788
802
  """
789
803
 
790
- print(f"LayoutArray: {self.name}")
804
+ print(f"ArrayLayout: {self.name}")
791
805
  print("ArrayCenter")
792
806
  print(self._array_center)
793
807
  print("Telescopes")
@@ -826,7 +840,7 @@ class LayoutArray:
826
840
 
827
841
  def print_telescope_list(self, compact_printing="", corsika_z=False):
828
842
  """
829
- Print list of telescopes in current layout.
843
+ Print list of telescopes in latest released layout.
830
844
 
831
845
  Parameters
832
846
  ----------
@@ -846,69 +860,17 @@ class LayoutArray:
846
860
 
847
861
  self._logger.info("Converting telescope coordinates")
848
862
 
849
- wgs84 = self._get_crs_wgs84()
850
- crs_local = self._get_crs_local()
851
- crs_utm = self._get_crs_utm()
863
+ crs_wgs84 = self.geo_coordinates.crs_wgs84()
864
+ crs_local = self.geo_coordinates.crs_local(self._array_center)
865
+ crs_utm = self.geo_coordinates.crs_utm(self._epsg)
852
866
 
853
867
  for tel in self._telescope_list:
854
868
  tel.convert_all(
855
869
  crs_local=crs_local,
856
- crs_wgs84=wgs84,
870
+ crs_wgs84=crs_wgs84,
857
871
  crs_utm=crs_utm,
858
872
  )
859
873
 
860
- def _get_crs_local(self):
861
- """
862
- Local coordinate system definition.
863
-
864
- Returns
865
- -------
866
- pyproj.CRS
867
- local coordinate system.
868
-
869
- """
870
- if self._array_center:
871
- _center_lat, _center_lon, _ = self._array_center.get_coordinates("mercator")
872
- if not np.isnan(_center_lat.value) and not np.isnan(_center_lon.value):
873
- proj4_string = (
874
- "+proj=tmerc +ellps=WGS84 +datum=WGS84"
875
- + f" +lon_0={_center_lon} +lat_0={_center_lat}"
876
- + " +axis=nwu +units=m +k_0=1.0"
877
- )
878
- crs_local = pyproj.CRS.from_proj4(proj4_string)
879
- return crs_local
880
-
881
- return None
882
-
883
- def _get_crs_utm(self):
884
- """
885
- UTM coordinate system definition.
886
-
887
- Returns
888
- -------
889
- pyproj.CRS
890
- UTM coordinate system.
891
-
892
- """
893
- if self._epsg:
894
- crs_utm = pyproj.CRS.from_user_input(self._epsg)
895
- return crs_utm
896
-
897
- return None
898
-
899
- @staticmethod
900
- def _get_crs_wgs84():
901
- """
902
- WGS coordinate system definition.
903
-
904
- Returns
905
- -------
906
- pyproj.CRS
907
- WGS coordinate system.
908
-
909
- """
910
- return pyproj.CRS("EPSG:4326")
911
-
912
874
  @staticmethod
913
875
  def include_radius_into_telescope_table(telescope_table):
914
876
  """
@@ -926,9 +888,16 @@ class LayoutArray:
926
888
  """
927
889
 
928
890
  telescope_table["radius"] = [
929
- telescope_table.meta["corsika_sphere_radius"][names.get_telescope_type(tel_name_now)]
891
+ u.Quantity(
892
+ telescope_table.meta["corsika_sphere_radius"][
893
+ names.get_telescope_class(tel_name_now)
894
+ ]
895
+ )
896
+ .to("m")
897
+ .value
930
898
  for tel_name_now in telescope_table["telescope_name"]
931
899
  ]
900
+ telescope_table["radius"] = telescope_table["radius"].quantity * u.m
932
901
  return telescope_table
933
902
 
934
903
  def select_assets(self, asset_list=None):