Source code for phise.classes.telescope
"""Utilities et représentation d'un télescope simulé.
La classe `Telescope` encapsule la surface collectrice (``a``), la position
relative sur le plan (``r``) et un nom. Les setters effectuent des
validations d'unités via ``astropy.units``.
"""
import astropy.units as u
import numpy as np
[docs]
class Telescope:
"""Représentation d'un télescope utilisé par l'interféromètre.
Attributs principaux
- a : surface collectrice (astropy.Quantity en m**2)
- r : position relative sur le plan (astropy.Quantity, shape (2,), unité m)
- name : nom lisible
"""
__slots__ = ('_parent_interferometer', '_a', '_r', '_name')
[docs]
def __init__(self, a: u.Quantity, r: u.Quantity, name: str = 'Unnamed Telescope'):
"""Initialise un télescope.
Exceptions levées par les setters si les unités sont incorrectes.
"""
self._parent_interferometer = None
self.a = a
self.r = r
self.name = name
def __str__(self) -> str:
res = f'Telescope "{self.name}"\n'
res += f' Area: {self.a:.2e}\n'
res += f" Relative position: [{', '.join([f'{i:.2e}' for i in self.r.value])}] {self.r.unit}"
return res.replace('e+00', '')
def __repr__(self) -> str:
return self.__str__()
@property
def a(self) -> u.Quantity:
"""Surface collectrice du télescope (astropy.Quantity en m**2)."""
return self._a
@a.setter
def a(self, a: u.Quantity):
if not isinstance(a, u.Quantity):
raise TypeError('a must be an astropy Quantity')
try:
a = a.to(u.m ** 2)
except u.UnitConversionError:
raise ValueError('a must be in a surface area unit')
self._a = a
if self.parent_interferometer is not None:
self.parent_interferometer.parent_ctx.update_photon_flux()
@property
def r(self) -> u.Quantity:
"""Position relative du télescope sur le plan (astropy.Quantity en m, shape (2,))."""
return self._r
@r.setter
def r(self, r: u.Quantity):
if not isinstance(r, u.Quantity):
raise TypeError('r must be an astropy Quantity')
try:
r = r.to(u.m)
except u.UnitConversionError:
raise ValueError('r must be in a length unit')
if r.shape != (2,):
raise ValueError('r must have a shape of (2,)')
self._r = r
if self.parent_interferometer is not None:
self.parent_interferometer.parent_ctx.project_telescopes_position()
@property
def name(self) -> str:
"""Nom du télescope."""
return self._name
@name.setter
def name(self, name: str):
if not isinstance(name, str):
raise TypeError('name must be a string')
self._name = name
@property
def parent_interferometer(self):
"""Référence vers l'interféromètre parent (lecture seule)."""
return self._parent_interferometer
@parent_interferometer.setter
def parent_interferometer(self, parent_interferometer):
raise ValueError('parent_interferometer is read-only')
def get_VLTI_UTs() -> list[Telescope]:
"""Retourne la géométrie relative des UTs du VLTI.
Retourne une liste de 4 objets `Telescope` positionnés selon la
configuration standard des UTs.
"""
r = np.array([[-70.4048732988764, -24.627602893919807], [-70.40465753243652, -24.627118902835786], [-70.40439460074228, -24.62681028261176], [-70.40384287956437, -24.627033500373024]])
r -= r[0]
earth_radius = 6378137 * u.m
UTs_elevation = 2635 * u.m
r = np.tan((r * u.deg).to(u.rad)) * (earth_radius + UTs_elevation)
a = 4 * np.pi * (4 * u.m) ** 2
return [Telescope(a=a, r=pos, name=f'UT {i + 1}') for (i, pos) in enumerate(r)]
def get_LIFE_telescopes() -> list[Telescope]:
"""Génère une configuration de télescopes pour le concept LIFE.
Renvoie une liste de 4 objets `Telescope`.
"""
r = np.array([[0, 0], [1, 0], [0, 6], [1, 6]]) * 100 * u.m
a = np.pi * (2 * u.m) ** 2
return [Telescope(a=a, r=pos, name=f'LIFE telescope {i + 1}') for (i, pos) in enumerate(r)]