Source code for furst.feed_optics._feed_optics

from typing import Generic
import dataclasses
import numpy as np
import astropy.units as u
import named_arrays as na
import optika
import furst

__all__ = [
    "FeedOptic",
]


[docs] @dataclasses.dataclass(eq=False, repr=False) class FeedOptic( optika.mixins.Rollable, optika.mixins.Yawable, optika.mixins.Pitchable, optika.mixins.Translatable, furst.abc.AbstractRowlandComponent, Generic[furst.typevars.MaterialT], ): """ Model of the FURST feed optics. These are tall narrow cylinders which are analogs of the slit used in a traditional spectrograph. They are necesseary to achieve the demagnification necessary to fit the entire Sun onto one pixel on the detector. Examples -------- Plot an exaggerated feed optic array on top of a Rowland circle. .. jupyter-execute:: import numpy as np import matplotlib.pyplot as plt import astropy.units as u import astropy.visualization import named_arrays as na import furst # Define the Rowland circle rowland_radius = 1000 * u.mm a = na.linspace(0, 360, axis="angle", num=1001) * u.deg rowland_circle = rowland_radius * na.Cartesian3dVectorArray( x=np.sin(a), z=np.cos(a), ) # Define the exaggerated feed optic array feed_optic = furst.feed_optics.FeedOptic( radius=25 * u.mm, aperture_subtent=30 * u.deg, aperture_height=10 * u.mm, rowland_radius=rowland_radius, rowland_azimuth=na.linspace( start=5 * u.deg, stop=45 * u.deg, axis="az", num=7, ), ) # Plot the feed optic array and the # Rowland circle. with astropy.visualization.quantity_support(): fig, ax = plt.subplots() feed_optic.surface.plot( ax=ax, components=("z", "x"), color="tab:blue", ) xlim = ax.get_xlim() ylim = ax.get_ylim() na.plt.plot( rowland_circle, ax=ax, components=("z", "x"), color="black", linestyle="dashed", zorder=-10, ) ax.set_xlim(xlim) ax.set_ylim(ylim) ax.set_aspect("equal") """ name: str = "feed optic" """ The human-readable name of this optic. """ radius: u.Quantity | na.AbstractScalar = 0 * u.deg """ The radius of curvature of the optical surface. """ aperture_subtent: u.Quantity | na.AbstractScalar = 0 * u.deg """ The angular width of the clear aperture. """ aperture_height: u.Quantity | na.AbstractScalar = 0 * u.mm """ The physical height of the clear aperture. """ margin_polishing: u.Quantity | na.AbstractScalar = 0 * u.mm """ The height above and below the clear aperture needed to hold the optic for polishing. """ margin_mounting: u.Quantity | na.AbstractScalar = 0 * u.mm """ The length of the optic used to hold it in its mount. """ material: furst.typevars.MaterialT = None """ The coating material used to make the optic reflective in the target spectral range. """ rowland_radius: u.Quantity | na.AbstractScalar = 0 * u.mm """ The distance from the center of the Rowland circle to the virtual image of the Sun within the feed optic. """ rowland_azimuth: u.Quantity | na.AbstractScalar = 0 * u.deg """ The azimuth of the virtual image of the Sun on the Rowland circle, relative to the optic axis of the instrument. """ translation: u.Quantity | na.AbstractCartesian3dVectorArray = 0 * u.mm """ Physical offset from the optic's nominal position on the Rowland circle. """ pitch: u.Quantity | na.AbstractScalar = 0 * u.deg """ The angle of rotation about the vector tangent to the Rowland circle. """ yaw: u.Quantity | na.AbstractScalar = 0 * u.deg """ The angle of rotation about the axis of symmetry of the feed optic. """ roll: u.Quantity | na.AbstractScalar = 0 * u.deg """ The angle of rotation about the vector normal to the Rowland circle. """ @property def transformation(self) -> na.transformations.AbstractTransformation: t_center = na.transformations.Cartesian3dTranslation( x=0 * u.mm, y=0 * u.mm, z=-self.radius, ) t_yaw = na.transformations.Cartesian3dRotationY( angle=-self.rowland_azimuth, ) t_img = na.transformations.Cartesian3dTranslation( x=0 * u.mm, y=0 * u.mm, z=self.radius / 2, ) return t_img @ super().transformation @ t_yaw @ t_center @property def transformation_image(self): """ Coordinate transformation from the global coordinate system and the virtual image of the Sun inside the feed optic. """ t_img = na.transformations.Cartesian3dTranslation( x=0 * u.mm, y=0 * u.mm, z=self.radius / 2, ) return t_img @ self.transformation @property def surface(self) -> optika.surfaces.Surface: return optika.surfaces.Surface( name=self.name, sag=optika.sags.CylindricalSag( radius=self.radius, ), material=self.material, aperture=optika.apertures.RectangularAperture( half_width=na.Cartesian2dVectorArray( x=self.radius * np.sin(self.aperture_subtent / 2), y=self.aperture_height / 2, ) ), aperture_mechanical=optika.apertures.RectangularAperture( half_width=na.Cartesian2dVectorArray( x=0.99 * self.radius, y=( self.aperture_height / 2 + self.margin_mounting + self.margin_polishing ), ), samples_wire=1001, transformation=na.transformations.Cartesian3dTranslation( y=self.margin_polishing - self.margin_mounting, ), ), transformation=self.transformation, )