def ptf(array_shape=(256, 256), dx=1., lam_over_diam=2., defocus=0., astig1=0., astig2=0., coma1=0., coma2=0., trefoil1=0., trefoil2=0., spher=0., circular_pupil=True, obscuration=0., nstruts=0, strut_thick=0.05, strut_angle=0.*galsim.degrees): """Return NumPy array containing the PTF [radians] of a circular (default) or square pupil with low-order aberrations. PTF array element ordering follows the DFT standard of kxky(array_shape), and has ptf[0, 0] = 0. by default. To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx. Output double NumPy array is C-contiguous. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param defocus defocus in units of incident light wavelength. @param astig1 astigmatism (like e2) in units of incident light wavelength. @param astig2 astigmatism (like e1) in units of incident light wavelength. @param coma1 coma along y in units of incident light wavelength. @param coma2 coma along x in units of incident light wavelength. @param trefoil1 trefoil (one of the arrows along y) in units of incident light wavelength. @param trefoil2 trefoil (one of the arrows along x) in units of incident light wavelength. @param spher spherical aberration in units of incident light wavelength. @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.) @param nstruts Number of radial support struts to add to the central obscuration [default `nstruts = 0`]. @param strut_thick Thickness of support struts as a fraction of pupil diameter [default `strut_thick = 0.05`]. @param strut_angle Angle made between the vertical and the strut starting closest to it, defined to be positive in the counter-clockwise direction; must be a galsim.Angle instance [default `strut_angle = 0. * galsim.degrees`]. """ kx, ky = utilities.kxky(array_shape) k2 = (kx**2 + ky**2) ptf = np.zeros(array_shape) kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Try to handle where both real and imag tend to zero... ptf[k2 < kmax_internal**2] = np.angle(otf( array_shape=array_shape, dx=dx, lam_over_diam=lam_over_diam, defocus=defocus, astig1=astig1, astig2=astig2, coma1=coma1, coma2=coma2, trefoil1=trefoil1, trefoil2=trefoil2, spher=spher, circular_pupil=circular_pupil, obscuration=obscuration, nstruts=nstruts, strut_thick=strut_thick, strut_angle=strut_angle)[k2 < kmax_internal**2]) return ptf
def generate_pupil_plane(array_shape=(256, 256), dx=1., lam_over_diam=2., circular_pupil=True, obscuration=0.): """Generate a pupil plane, including a central obscuration such as caused by a secondary mirror. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units. @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.) Returns a tuple (rho, theta, in_pupil), the first two of which are the coordinates of the pupil in unit disc-scaled coordinates for use by Zernike polynomials for describing the wavefront across the pupil plane. The array in_pupil is a vector of Bools used to specify where in the pupil plane described by rho, theta is illuminated. See also optics.wavefront. """ kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Build kx, ky coords kx, ky = utilities.kxky(array_shape) # Then define unit disc rho and theta pupil coords for Zernike polynomials rho = np.sqrt((kx**2 + ky**2) / (.5 * kmax_internal)**2) theta = np.arctan2(ky, kx) # Cut out circular pupil if desired (default, square pupil optionally supported) and include # central obscuration if obscuration >= 1.: raise ValueError("Pupil fully obscured! obscuration =" + str(obscuration) + " (>= 1)") if circular_pupil: in_pupil = (rho < 1.) if obscuration > 0.: in_pupil = in_pupil * (rho >= obscuration ) # * acts like "and" for boolean arrays else: in_pupil = (np.abs(kx) < .5 * kmax_internal) * (np.abs(ky) < .5 * kmax_internal) if obscuration > 0.: in_pupil = in_pupil * ( (np.abs(kx) >= .5 * obscuration * kmax_internal) * (np.abs(ky) >= .5 * obscuration * kmax_internal)) return rho, theta, in_pupil
def ptf(array_shape=(256, 256), dx=1., lam_over_diam=2., defocus=0., astig1=0., astig2=0., coma1=0., coma2=0., trefoil1=0., trefoil2=0., spher=0., circular_pupil=True, obscuration=0.): """Return NumPy array containing the PTF [radians] of a circular (default) or square pupil with low-order aberrations. PTF array element ordering follows the DFT standard of kxky(array_shape), and has ptf[0, 0] = 0. by default. To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx. Output double NumPy array is C-contiguous. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param defocus defocus in units of incident light wavelength. @param astig1 astigmatism (like e2) in units of incident light wavelength. @param astig2 astigmatism (like e1) in units of incident light wavelength. @param coma1 coma along y in units of incident light wavelength. @param coma2 coma along x in units of incident light wavelength. @param trefoil1 trefoil (one of the arrows along y) in units of incident light wavelength. @param trefoil2 trefoil (one of the arrows along x) in units of incident light wavelength. @param spher spherical aberration in units of incident light wavelength. @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.) """ kx, ky = utilities.kxky(array_shape) k2 = (kx**2 + ky**2) ptf = np.zeros(array_shape) kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Try to handle where both real and imag tend to zero... ptf[k2 < kmax_internal**2] = np.angle(otf( array_shape=array_shape, dx=dx, lam_over_diam=lam_over_diam, defocus=defocus, astig1=astig1, astig2=astig2, coma1=coma1, coma2=coma2, trefoil1=trefoil1, trefoil2=trefoil2, spher=spher, circular_pupil=circular_pupil, obscuration=obscuration)[k2 < kmax_internal**2]) return ptf
def generate_pupil_plane(array_shape=(256, 256), dx=1., lam_over_diam=2., circular_pupil=True, obscuration=0.): """Generate a pupil plane, including a central obscuration such as caused by a secondary mirror. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units. @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.) Returns a tuple (rho, theta, in_pupil), the first two of which are the coordinates of the pupil in unit disc-scaled coordinates for use by Zernike polynomials for describing the wavefront across the pupil plane. The array in_pupil is a vector of Bools used to specify where in the pupil plane described by rho, theta is illuminated. See also optics.wavefront. """ kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Build kx, ky coords kx, ky = utilities.kxky(array_shape) # Then define unit disc rho and theta pupil coords for Zernike polynomials rho = np.sqrt((kx**2 + ky**2) / (.5 * kmax_internal)**2) theta = np.arctan2(ky, kx) # Cut out circular pupil if desired (default, square pupil optionally supported) and include # central obscuration if obscuration >= 1.: raise ValueError("Pupil fully obscured! obscuration ="+str(obscuration)+" (>= 1)") if circular_pupil: in_pupil = (rho < 1.) if obscuration > 0.: in_pupil = in_pupil * (rho >= obscuration) # * acts like "and" for boolean arrays else: in_pupil = (np.abs(kx) < .5 * kmax_internal) * (np.abs(ky) < .5 * kmax_internal) if obscuration > 0.: in_pupil = in_pupil * ( (np.abs(kx) >= .5 * obscuration * kmax_internal) * (np.abs(ky) >= .5 * obscuration * kmax_internal)) return rho, theta, in_pupil
def generate_pupil_plane(array_shape=(256, 256), dx=1., lam_over_diam=2., circular_pupil=True, obscuration=0., nstruts=0, strut_thick=0.05, strut_angle=0.*galsim.degrees): """Generate a pupil plane, including a central obscuration such as caused by a secondary mirror. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units. @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.). @param nstruts Number of radial support struts to add to the central obscuration [default `nstruts = 0`]. @param strut_thick Thickness of support struts as a fraction of pupil diameter [default `strut_thick = 0.05`]. @param strut_angle Angle made between the vertical and the strut starting closest to it, defined to be positive in the counter-clockwise direction; must be a galsim.Angle instance [default `strut_angle = 0. * galsim.degrees`]. Returns a tuple (rho, in_pupil), the first of which is the coordinates of the pupil in unit disc-scaled coordinates for use by Zernike polynomials (as a complex number) for describing the wavefront across the pupil plane. The array in_pupil is a vector of Bools used to specify where in the pupil plane described by rho is illuminated. See also optics.wavefront. """ kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Build kx, ky coords kx, ky = utilities.kxky(array_shape) # Then define unit disc rho pupil coords for Zernike polynomials rho = (kx + 1j * ky) / (.5 * kmax_internal) rhosq = np.abs(rho)**2 # Amazingly, the above line is faster than the following. (~ 35% faster) # See the longer comment about this in psf function. #rhosq = rho.real**2 + rho.imag**2 # Cut out circular pupil if desired (default, square pupil optionally supported) and include # central obscuration if obscuration >= 1.: raise ValueError("Pupil fully obscured! obscuration ="+str(obscuration)+" (>= 1)") if circular_pupil: in_pupil = (rhosq < 1.) if obscuration > 0.: in_pupil *= rhosq >= obscuration**2 # * acts like "and" for boolean arrays else: in_pupil = (np.abs(kx) < .5 * kmax_internal) * (np.abs(ky) < .5 * kmax_internal) if obscuration > 0.: in_pupil *= ( (np.abs(kx) >= .5 * obscuration * kmax_internal) * (np.abs(ky) >= .5 * obscuration * kmax_internal) ) if nstruts > 0: if not isinstance(strut_angle, galsim.Angle): raise TypeError("Input kwarg strut_angle must be a galsim.Angle instance.") # Add the initial rotation if requested, converting to radians if strut_angle.rad != 0.: kxs, kys = utilities.rotate_xy(kx, ky, -strut_angle) # strut rotation +=ve, so coords # rotation -ve! else: kxs, kys = kx, ky # Define the angle between struts for successive use below rotang = 360. * galsim.degrees / float(nstruts) # Then loop through struts setting to zero in the pupil regions which lie under the strut in_pupil *= ( (np.abs(kxs) >= .5 * strut_thick * kmax_internal) + ((kys < 0.) * (np.abs(kxs) < .5 * strut_thick * kmax_internal))) for istrut in range(nstruts)[1:]: kxs, kys = utilities.rotate_xy(kxs, kys, -rotang) in_pupil *= ( (np.abs(kxs) >= .5 * strut_thick * kmax_internal) + ((kys < 0.) * (np.abs(kxs) < .5 * strut_thick * kmax_internal))) return rho, in_pupil
def ptf(array_shape=(256, 256), dx=1., lam_over_diam=2., defocus=0., astig1=0., astig2=0., coma1=0., coma2=0., trefoil1=0., trefoil2=0., spher=0., circular_pupil=True, obscuration=0.): """Return NumPy array containing the PTF [radians] of a circular (default) or square pupil with low-order aberrations. PTF array element ordering follows the DFT standard of kxky(array_shape), and has ptf[0, 0] = 0. by default. To ensure properly Nyquist sampled output any user should set lam_over_diam >= 2. * dx. Output double NumPy array is C-contiguous. @param array_shape the NumPy array shape desired for the output array. @param dx grid spacing of PSF in real space units @param lam_over_diam lambda / telescope diameter in the physical units adopted for dx (user responsible for consistency). @param defocus defocus in units of incident light wavelength. @param astig1 astigmatism (like e2) in units of incident light wavelength. @param astig2 astigmatism (like e1) in units of incident light wavelength. @param coma1 coma along y in units of incident light wavelength. @param coma2 coma along x in units of incident light wavelength. @param trefoil1 trefoil (one of the arrows along y) in units of incident light wavelength. @param trefoil2 trefoil (one of the arrows along x) in units of incident light wavelength. @param spher spherical aberration in units of incident light wavelength. @param circular_pupil adopt a circular pupil? @param obscuration linear dimension of central obscuration as fraction of pupil linear dimension, [0., 1.) """ kx, ky = utilities.kxky(array_shape) k2 = (kx**2 + ky**2) ptf = np.zeros(array_shape) kmax_internal = dx * 2. * np.pi / lam_over_diam # INTERNAL kmax in units of array grid spacing # Try to handle where both real and imag tend to zero... ptf[k2 < kmax_internal**2] = np.angle( otf(array_shape=array_shape, dx=dx, lam_over_diam=lam_over_diam, defocus=defocus, astig1=astig1, astig2=astig2, coma1=coma1, coma2=coma2, trefoil1=trefoil1, trefoil2=trefoil2, spher=spher, circular_pupil=circular_pupil, obscuration=obscuration)[k2 < kmax_internal**2]) return ptf