Source code for lasy.optical_elements.zernike_aberrations

import numpy as np

from lasy.utils.zernike import zernike

from .optical_element import OpticalElement


[docs] class ZernikeAberrations(OpticalElement): r""" Class for an optic applying a set of Zernike aberrations. More precisely, the amplitude multiplier corresponds to: .. math:: T(\boldsymbol{x}_\perp,\omega) = \exp( i \sum_j a_j Z_j(\boldsymbol{x}_\perp)) where :math:`\boldsymbol{x}_\perp` is the transverse coordinate (orthogonal to the propagation direction). :math:`Z_j` is the j-th Zernike polynomial, ordered according the OSA/ANSI indexing. The Zernike polynomials are normalized such that their integral over the unit disk is equal to :math:`\pi`. In the above formula, the total phase added to the pulse is a weighted sum of these Zernike polynomials with weights :math:`a_j`. For more information see: https://en.wikipedia.org/wiki/Zernike_polynomials Parameters ---------- pupil_coords : tuple of floats (meters) A tuple of floats (cgx,cgy,r) with the first two elements corresponding to the center point and third element the radius of the pupil on which the zernike polynomial is defined. zernike_amplitudes : dict A dictionary with integer keys representing the OSA/ANSI indexing of the individual Zernike Polynomials. The values corresponding to these keys are floats giving the amplitudes / weights of the relevant Zernike polynomials. """ def __init__(self, pupil_coords, zernike_amplitudes): self.pupil_coords = pupil_coords self.zernike_amplitudes = zernike_amplitudes
[docs] def amplitude_multiplier(self, x, y, omega): """ Return the amplitude multiplier. Parameters ---------- x, y, omega : ndarrays of floats Define points on which to evaluate the multiplier. These arrays need to all have the same shape. Returns ------- multiplier : ndarray of complex numbers Contains the value of the multiplier at the specified points. This array has the same shape as the array omega. """ rr = np.sqrt(x**2 + y**2) phase = np.zeros_like(rr) for j in list(self.zernike_amplitudes): # Create the zernike phase and ensure it has the same number of dimensions as phase zernike_phase = zernike(x[..., 0], y[..., 0], self.pupil_coords, j)[ ..., None ] # Expand last axis # Increase the length of the frequency dimension such that the shape is suitable to be added # to the phase array, then add it phase += self.zernike_amplitudes[j] * np.broadcast_to( zernike_phase, phase.shape ) return np.exp(1j * phase)