from enum import Enum, StrEnum, unique
import astropy.units as u
import numpy as np
[docs]
@unique
class SizeType(StrEnum):
"""
Enumeration of different telescope sizes (LST, MST, SST)
"""
#: Unkown
UNKNOWN = "UNKNOWN"
#: A telescope with a mirror diameter larger than 16m
LST = "LST"
#: A telescope with a mirror diameter larger than 8m
MST = "MST"
#: Telescopes with a mirror diameter smaller than 8m
SST = "SST"
[docs]
@unique
class ReflectorShape(Enum):
"""
Enumeration of the different reflector shapes
"""
#: Unkown
UNKNOWN = "UNKNOWN"
#: A telescope with a parabolic dish
PARABOLIC = "PARABOLIC"
#: A telescope with a Davies--Cotton dish
DAVIES_COTTON = "DAVIES_COTTON"
#: A telescope with a hybrid between parabolic and Davies--Cotton dish
HYBRID = "HYBRID"
#: A dual mirror Schwarzschild-Couder reflector
SCHWARZSCHILD_COUDER = "SCHWARZSCHILD_COUDER"
[docs]
class OpticsDescription:
"""
Describes the optics of a Cherenkov Telescope mirror
The string representation of an `OpticsDescription` will be a combination
of the telescope-type and sub-type as follows: "type-subtype". You can
also get each individually.
Parameters
----------
name : str
Name of this optical system
n_mirrors : int
Number of mirrors, i. e. 2 for Schwarzschild-Couder else 1
equivalent_focal_length : astropy.units.Quantity[length]
Equivalent focal-length of telescope, independent of which type of
optics (as in the Monte-Carlo). This is the nominal focal length
for single mirror telescopes and the equivalent focal length for dual
mirror telescopes.
effective_focal_length : astropy.units.Quantity[length]
The effective_focal_length is the focal length estimated from
ray tracing to correct for coma aberration. It is thus not automatically
available for all simulations, but only if it was set beforehand
in the simtel configuration. This is the focal length that should be
used for transforming from camera frame to telescope frame for all
reconstruction tasks to correct for the mean aberration.
mirror_area : astropy.units.Quantity[area]
total reflective surface area of the optical system (in m^2)
n_mirror_tiles : int
number of mirror facets
Raises
------
ValueError:
if tel_type or mirror_type are not one of the accepted values
TypeError, astropy.units.UnitsError:
if the units of one of the inputs are missing or incompatible
"""
CURRENT_TAB_VERSION = "4.0"
COMPATIBLE_VERSIONS = {"4.0"}
__slots__ = (
"name",
"size_type",
"effective_focal_length",
"equivalent_focal_length",
"mirror_area",
"n_mirrors",
"n_mirror_tiles",
"reflector_shape",
)
[docs]
@u.quantity_input(
mirror_area=u.m**2,
equivalent_focal_length=u.m,
effective_focal_length=u.m,
)
def __init__(
self,
name,
size_type,
n_mirrors,
equivalent_focal_length,
effective_focal_length,
mirror_area,
n_mirror_tiles,
reflector_shape,
):
self.name = name
self.size_type = SizeType(size_type)
self.reflector_shape = ReflectorShape(reflector_shape)
self.equivalent_focal_length = equivalent_focal_length.to(u.m)
self.effective_focal_length = effective_focal_length.to(u.m)
self.mirror_area = mirror_area
self.n_mirrors = n_mirrors
self.n_mirror_tiles = n_mirror_tiles
def __hash__(self):
"""Make this hashable, so it can be used as dict keys or in sets"""
# From python >= 3.10, hash of nan is random, we want a fixed hash also for
# unknown effective focal length:
if np.isnan(self.effective_focal_length.value):
effective_focal_length = -1
else:
effective_focal_length = self.effective_focal_length.to_value(u.m)
return hash(
(
round(self.equivalent_focal_length.to_value(u.m), 4),
round(effective_focal_length, 4),
round(self.mirror_area.to_value(u.m**2)),
self.size_type.value,
self.reflector_shape.value,
self.n_mirrors,
self.n_mirror_tiles,
)
)
def __eq__(self, other):
"""For eq, we just compare equal hash"""
return hash(self) == hash(other)
def __str__(self):
return self.name