Source code for optiland.wavefront.zernike_opd

"""
This module defines the ZernikeOPD class.

Kramer Harrison, 2024
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from optiland.zernike import ZernikeFit

from .opd import OPD

if TYPE_CHECKING:
    from optiland._types import ZernikeType
    from optiland.optic.optic import Optic
    from optiland.wavefront.strategy import WavefrontStrategyType


[docs] class ZernikeOPD(ZernikeFit, OPD): """Represents a Zernike Optical Path Difference (OPD) calculation. This class inherits from both the ZernikeFit and OPD classes. It first generates the OPD map(s), then fits Zernike polynomials to the map(s). Args: optic (object): The optic object representing the optical system. field (tuple): The field used for the calculation. wavelength (str | float): The wavelength of light used in the calculation. Can be 'primary' or a float value. num_rings (int, optional): The number of rings used in the Zernike calculation. Default is 15. zernike_type (str, optional): The type of Zernike polynomials used. Default is 'fringe'. See zernike module for more information. num_terms (int, optional): The number of Zernike terms used in the calculation. Default is 37. strategy (str): The calculation strategy to use. Supported options are "chief_ray", "centroid_sphere", and "best_fit_sphere". Defaults to "chief_ray". remove_tilt (bool): If True, removes tilt and piston from the OPD data. Defaults to False. **kwargs: Additional keyword arguments passed to the strategy, including the `afocal` boolean flag to indicate if the system is afocal. """ def __init__( self, optic: Optic, field: tuple[float, float], wavelength: str | float, num_rings: int = 15, zernike_type: ZernikeType = "fringe", num_terms: int = 37, strategy: WavefrontStrategyType = "chief_ray", remove_tilt: bool = False, **kwargs, ): OPD.__init__( self, optic=optic, field=field, wavelength=wavelength, num_rays=num_rings, distribution="hexapolar", strategy=strategy, remove_tilt=remove_tilt, **kwargs, ) x = self.distribution.x y = self.distribution.y data = self.get_data(self.fields[0], self.wavelengths[0]) z = data.opd mask = data.intensity > 0 x = x[mask] y = y[mask] z = z[mask] ZernikeFit.__init__(self, x, y, z, zernike_type, num_terms)