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
|
@@ -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("
|
|
48
|
+
if self.has_coordinates("ground"):
|
|
49
49
|
telstr += (
|
|
50
|
-
f"\t
|
|
51
|
-
f"y(->West): {self.crs['
|
|
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 == "
|
|
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 = "
|
|
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
|
|
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=
|
|
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("
|
|
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
|
|
515
|
+
coordinate system definition
|
|
516
516
|
|
|
517
517
|
"""
|
|
518
518
|
|
|
519
519
|
return {
|
|
520
|
-
"
|
|
520
|
+
"ground": {
|
|
521
521
|
"crs": None,
|
|
522
|
-
"xx": {"name": "
|
|
523
|
-
"yy": {"name": "
|
|
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": {
|
simtools/model/array_model.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from copy import copy
|
|
3
3
|
|
|
4
|
-
from simtools import db_handler
|
|
5
|
-
from simtools.
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
85
|
-
self.layout_name = names.
|
|
86
|
-
self.layout =
|
|
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
|
-
|
|
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
|
|
98
|
-
self.model_version = "
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
16
|
-
|
|
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 =
|
|
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.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
self.
|
|
123
|
-
|
|
124
|
-
|
|
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,
|
|
140
|
-
X, Y positions,
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
"""
|