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.
- {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/METADATA +80 -28
- gammasimtools-0.6.1.dist-info/RECORD +91 -0
- {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/WHEEL +1 -1
- {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/entry_points.txt +4 -2
- simtools/_version.py +14 -2
- simtools/applications/add_file_to_db.py +2 -1
- simtools/applications/compare_cumulative_psf.py +10 -15
- simtools/applications/db_development_tools/add_new_parameter_to_db.py +12 -6
- simtools/applications/derive_mirror_rnda.py +95 -71
- simtools/applications/generate_corsika_histograms.py +216 -131
- simtools/applications/generate_default_metadata.py +110 -0
- simtools/applications/generate_simtel_array_histograms.py +192 -0
- simtools/applications/get_file_from_db.py +1 -1
- simtools/applications/get_parameter.py +3 -3
- simtools/applications/make_regular_arrays.py +89 -93
- simtools/applications/{plot_layout_array.py → plot_array_layout.py} +15 -14
- simtools/applications/print_array_elements.py +81 -34
- simtools/applications/produce_array_config.py +2 -2
- simtools/applications/production.py +39 -5
- simtools/applications/sim_showers_for_trigger_rates.py +26 -30
- simtools/applications/simulate_prod.py +49 -107
- simtools/applications/submit_data_from_external.py +8 -10
- simtools/applications/tune_psf.py +16 -18
- simtools/applications/validate_camera_efficiency.py +63 -9
- simtools/applications/validate_camera_fov.py +9 -13
- simtools/applications/validate_file_using_schema.py +127 -0
- simtools/applications/validate_optics.py +13 -15
- simtools/camera_efficiency.py +73 -80
- simtools/configuration/commandline_parser.py +52 -22
- simtools/configuration/configurator.py +98 -33
- simtools/constants.py +9 -0
- simtools/corsika/corsika_config.py +28 -22
- simtools/corsika/corsika_default_config.py +282 -0
- simtools/corsika/corsika_histograms.py +328 -282
- simtools/corsika/corsika_histograms_visualize.py +162 -163
- simtools/corsika/corsika_runner.py +8 -4
- simtools/corsika_simtel/corsika_simtel_runner.py +18 -23
- simtools/data_model/data_reader.py +129 -0
- simtools/data_model/metadata_collector.py +346 -118
- simtools/data_model/metadata_model.py +123 -218
- simtools/data_model/model_data_writer.py +79 -22
- simtools/data_model/validate_data.py +96 -46
- simtools/db_handler.py +67 -42
- simtools/io_operations/__init__.py +0 -0
- simtools/io_operations/hdf5_handler.py +112 -0
- simtools/{io_handler.py → io_operations/io_handler.py} +51 -22
- simtools/job_execution/job_manager.py +1 -1
- simtools/layout/{layout_array.py → array_layout.py} +168 -199
- simtools/layout/geo_coordinates.py +196 -0
- simtools/layout/telescope_position.py +12 -12
- simtools/model/array_model.py +16 -14
- simtools/model/camera.py +5 -8
- simtools/model/mirrors.py +136 -73
- simtools/model/model_utils.py +1 -69
- simtools/model/telescope_model.py +32 -25
- simtools/psf_analysis.py +26 -19
- simtools/ray_tracing.py +54 -26
- simtools/schemas/data.metaschema.yml +400 -0
- simtools/schemas/metadata.metaschema.yml +566 -0
- simtools/simtel/simtel_config_writer.py +14 -5
- simtools/simtel/simtel_histograms.py +266 -83
- simtools/simtel/simtel_runner.py +8 -7
- simtools/simtel/simtel_runner_array.py +7 -8
- simtools/simtel/simtel_runner_camera_efficiency.py +48 -2
- simtools/simtel/simtel_runner_ray_tracing.py +61 -25
- simtools/simulator.py +43 -50
- simtools/utils/general.py +232 -286
- simtools/utils/geometry.py +163 -0
- simtools/utils/names.py +294 -142
- simtools/visualization/legend_handlers.py +115 -9
- simtools/visualization/visualize.py +13 -13
- gammasimtools-0.5.1.dist-info/RECORD +0 -83
- simtools/applications/plot_simtel_histograms.py +0 -120
- simtools/applications/validate_schema_files.py +0 -135
- simtools/corsika/corsika_output_visualize.py +0 -345
- simtools/data_model/validate_schema.py +0 -285
- {gammasimtools-0.5.1.dist-info → gammasimtools-0.6.1.dist-info}/LICENSE +0 -0
- {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
|
|
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
|
|
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", "
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
Instance of the
|
|
105
|
+
ArrayLayout
|
|
106
|
+
Instance of the ArrayLayout.
|
|
94
107
|
"""
|
|
95
108
|
|
|
96
|
-
split_name =
|
|
109
|
+
split_name = array_layout_name.split("-")
|
|
97
110
|
site_name = names.validate_site_name(split_name[0])
|
|
98
|
-
array_name = names.
|
|
99
|
-
|
|
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=
|
|
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-{
|
|
122
|
+
"layout", f"telescope_positions-{valid_array_layout_name}.ecsv"
|
|
110
123
|
)
|
|
111
|
-
layout.
|
|
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
|
-
|
|
172
|
-
|
|
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 =
|
|
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
|
|
188
|
-
corsika_dict[simtools_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 =
|
|
194
|
-
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, "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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("
|
|
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
|
-
|
|
417
|
+
try:
|
|
385
418
|
return self._corsika_telescope["corsika_sphere_center"][
|
|
386
|
-
names.
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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["
|
|
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, "
|
|
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", "
|
|
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
|
-
|
|
539
|
-
|
|
586
|
+
def initialize_array_layout_from_telescope_file(
|
|
587
|
+
self, telescope_list_file, telescope_list_metadata_file=None, validate=False
|
|
588
|
+
):
|
|
540
589
|
"""
|
|
541
|
-
|
|
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
|
-
|
|
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
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
|
615
|
-
CORSIKA telescope parameters, and EPSG
|
|
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
|
|
686
|
+
def export_telescope_list_table(self, crs_name, corsika_z=False):
|
|
653
687
|
"""
|
|
654
|
-
|
|
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
|
-
|
|
664
|
-
|
|
699
|
+
astropy.table.QTable
|
|
700
|
+
Astropy table with the telescope layout information.
|
|
665
701
|
|
|
666
702
|
"""
|
|
667
703
|
|
|
668
|
-
|
|
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["
|
|
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
|
-
|
|
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("
|
|
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.
|
|
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
|
-
|
|
777
|
-
|
|
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"
|
|
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
|
|
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
|
-
|
|
850
|
-
crs_local = self.
|
|
851
|
-
crs_utm = self.
|
|
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=
|
|
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
|
-
|
|
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):
|