Source code for esis.optics._gratings._gratings
import abc
import dataclasses
import numpy as np
import astropy.units as u
import named_arrays as na
import optika.mixins
from .. import mixins
__all__ = [
"AbstractGrating",
"Grating",
]
[docs]
@dataclasses.dataclass(eq=False, repr=False)
class AbstractGrating(
optika.mixins.Printable,
optika.mixins.Rollable,
optika.mixins.Yawable,
optika.mixins.Pitchable,
optika.mixins.Translatable,
mixins.CylindricallyTransformable,
):
"""An interface describing the diffraction gratings of the instrument."""
@property
@abc.abstractmethod
def serial_number(self) -> str:
"""The serial number of this diffraction grating."""
@property
@abc.abstractmethod
def manufacturing_number(self) -> str:
"""An additional number describing this diffraction grating."""
@property
@abc.abstractmethod
def angle_input(self) -> u.Quantity | na.AbstractScalar:
"""The nominal angle of the incident light from the field stop."""
@property
@abc.abstractmethod
def angle_output(self) -> u.Quantity | na.AbstractScalar:
"""The nominal angle of reflected light to the detectors."""
@property
@abc.abstractmethod
def sag(self) -> None | optika.sags.AbstractSag:
"""The sag function of this grating."""
@property
@abc.abstractmethod
def material(self) -> None | optika.materials.AbstractMaterial:
"""The optical material composing this grating."""
@property
@abc.abstractmethod
def rulings(self) -> None | optika.rulings.AbstractRulings:
"""The ruling pattern of this grating."""
@property
@abc.abstractmethod
def num_folds(self) -> int:
"""
The order of the rotational symmetry of the optical system.
This determines the aperture wedge angle of this grating.
"""
@property
def angle_aperture(self) -> u.Quantity | na.AbstractScalar:
r"""
The angle of the grating's aperture.
This is equal to :math:`2 \pi / n` radians, where :math:`n` is the
order of the rotational symmetry of the optical system.
"""
return (360 * u.deg) / self.num_folds
@property
@abc.abstractmethod
def halfwidth_inner(self) -> u.Quantity | na.AbstractScalar:
"""The distance from the apex to the inner edge of the clear aperture."""
@property
@abc.abstractmethod
def halfwidth_outer(self) -> u.Quantity | na.AbstractScalar:
"""The distance from the apex to the outer edge of the clear aperture."""
@property
@abc.abstractmethod
def width_border(self) -> u.Quantity | na.AbstractScalar:
"""The nominal width of the border around the clear aperture."""
@property
@abc.abstractmethod
def width_border_inner(self) -> u.Quantity | na.AbstractScalar:
"""The width of the border on the narrow edge of the grating."""
@property
@abc.abstractmethod
def clearance(self) -> u.Quantity | na.AbstractScalar:
"""The minimum distance between adjacent physical gratings."""
@property
def transformation(self) -> na.transformations.AbstractTransformation:
rotation = na.transformations.Cartesian3dRotationX(180 * u.deg)
return super().transformation @ rotation
@property
def surface(self) -> optika.surfaces.Surface:
"""Represent this object as an :mod:`optika` surface."""
angle_aperture = self.angle_aperture
halfwidth_inner = self.halfwidth_inner
halfwidth_outer = self.halfwidth_outer
width_border = self.width_border
width_border_inner = self.width_border_inner
clearance = self.clearance / np.sin(angle_aperture / 2)
distance_radial = self.distance_radial
side_border_x = width_border / np.sin(angle_aperture / 2) + clearance
offset_clear = distance_radial - side_border_x
offset_mechanical = distance_radial - clearance
return optika.surfaces.Surface(
name="grating",
sag=self.sag,
material=self.material,
aperture=optika.apertures.IsoscelesTrapezoidalAperture(
x_left=offset_clear - halfwidth_inner,
x_right=offset_clear + halfwidth_outer,
angle=angle_aperture,
transformation=na.transformations.Cartesian3dTranslation(
x=-offset_clear,
),
),
aperture_mechanical=optika.apertures.IsoscelesTrapezoidalAperture(
x_left=offset_mechanical - (halfwidth_inner + width_border_inner),
x_right=offset_mechanical + halfwidth_outer + width_border,
angle=angle_aperture,
transformation=na.transformations.Cartesian3dTranslation(
x=-offset_mechanical,
),
),
rulings=self.rulings,
is_pupil_stop=True,
transformation=self.transformation,
)
[docs]
@dataclasses.dataclass(eq=False, repr=False)
class Grating(
AbstractGrating,
):
"""A model of the diffraction gratings of this instrument."""
serial_number: str = ""
"""The serial number of this diffraction grating."""
manufacturing_number: str = ""
"""An additional number describing this diffraction grating."""
angle_input: u.Quantity = 0 * u.deg
"""The nominal angle of the incident light from the field stop."""
angle_output: u.Quantity = 0 * u.deg
"""The nominal angle of reflected light to the detectors."""
sag: None | optika.sags.AbstractSag = None
"""The sag function of this grating."""
material: None | optika.materials.AbstractMaterial = None
"""The optical material composing this grating."""
rulings: None | optika.rulings.AbstractRulings = None
"""The ruling pattern of this grating."""
num_folds: int = 0
"""
The order of the rotational symmetry of the optical system.
This determines the aperture wedge angle of this grating.
"""
halfwidth_inner: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The distance from the apex to the inner edge of the clear aperture."""
halfwidth_outer: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The distance from the apex to the outer edge of the clear aperture."""
width_border: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The nominal width of the border around the clear aperture."""
width_border_inner: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The width of the border on the narrow edge of the grating."""
clearance: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The minimum distance between adjacent physical gratings."""
distance_radial: u.Quantity | na.AbstractScalar = 0 * u.mm
"""The distance of this object from the axis of symmetry."""
azimuth: u.Quantity | na.AbstractScalar = 0 * u.deg
"""The angle of rotation about the axis of symmetry."""
translation: u.Quantity | na.AbstractCartesian3dVectorArray = 0 * u.mm
"""A transformation which can arbitrarily translate this object."""
pitch: u.Quantity | na.AbstractScalar = 0 * u.deg
"""The pitch angle of this object."""
yaw: u.Quantity | na.AbstractScalar = 0 * u.deg
"""The yaw angle of this object."""
roll: u.Quantity | na.AbstractScalar = 0 * u.deg
"""The roll angle of this object"""