mirror of
https://github.com/yosata/fourierOptics.git
synced 2026-10-30 05:01:04 +00:00
190 lines
7.2 KiB
Python
190 lines
7.2 KiB
Python
import numpy as np
|
|
import sys
|
|
sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Documents/Published/FourierOptics GitHub release/')
|
|
from fourierOptics import *
|
|
|
|
|
|
'''
|
|
Author: Joost van den Born, PhD student at University of Groningen
|
|
Contact: born@astro.rug.nl
|
|
Description: Script to analyse the shift of the PSF centroid as a function
|
|
of the RMS value of the WFE. Higher wavefront errors will
|
|
result in a larger shift of the centroid.
|
|
Script made for the SPIE manuscript.
|
|
'''
|
|
|
|
|
|
if __name__ == '__main__':
|
|
plt.rc('font', family='serif')
|
|
plt.rc('text', usetex=True)
|
|
plt.rc('axes', labelsize='large')
|
|
|
|
# Setting some input parameters
|
|
dpix = 160
|
|
n, m = 1001, 1001
|
|
l = 1.49e-3 # wavelength
|
|
zern_rms = 25e-5 # RMS of the zernike component of the WFE
|
|
midspat_rms = 25e-5 # RMS of the PSD component of the WFE
|
|
rmsList = 10**np.linspace(-6, -3, 25) # 1 to 1000 nm WFE RMS in mm, equidistant in log-space
|
|
input_field_location = './data/ADC_input_field_1001x1001_1.49_onaxis_obscured.pkl'
|
|
|
|
# Initiate ELT-MICADO and wavefront error objects
|
|
mcd = ParaxialMICADO(l=l, n=n, m=m, dpix=dpix, entrance_pupil='open')
|
|
wfe = WavefrontError(n,m)
|
|
|
|
print ('Your resolution is {}x{} with a pixel size of {}'.format(n,m,dpix))
|
|
|
|
# Do the unperturbed propagation for a reference PSF
|
|
E, x, y = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
|
|
print_progress=False, hx=0, hy=0,
|
|
load_up_to_adc=input_field_location)
|
|
|
|
# Generate a PSD surface
|
|
psd_surface = wfe.generate_height_map(midspat_rms, 2, 1)
|
|
|
|
# Fit the PSD surface using zernike terms
|
|
# Generating a normalized grid for the Zernike evaluation
|
|
yz,xz = np.mgrid[-1:1:wfe.n*1j, -1:1:wfe.m*1j]
|
|
_r = np.sqrt(xz**2 + yz**2)
|
|
_mask = _r > 1
|
|
coeffic = wfe.fit_zernike_coefficients(psd_surface, modes=66)
|
|
|
|
# StrehlList_zern, StrehlList_psd = [], []
|
|
rms_zern_list, rms_psd_list = [], []
|
|
|
|
# psd lists
|
|
popt_airy_list_psd, pcov_airy_list_psd = [], []
|
|
popt_cust_list_psd, custom_var_list_psd, strehl_list_psd = [], [], []
|
|
Exyzlist_psd = []
|
|
|
|
# zern lists
|
|
popt_airy_list_zern, pcov_airy_list_zern = [], []
|
|
popt_cust_list_zern, custom_var_list_zern, strehl_list_zern = [], [], []
|
|
Exyzlist_zern = []
|
|
|
|
# Now iterate over the wavefront rms values.
|
|
for rms in rmsList:
|
|
# Print results
|
|
print('RMS: {:.3f} nm'.format(rms*1e6))
|
|
|
|
|
|
########################################
|
|
# WFE described by Zernike polynomials #
|
|
########################################
|
|
# Make a new perturbed wavefront error with the required RMS
|
|
coeffic = wfe.scale_zernike_coefficients(coeffic, rms)
|
|
hzern = wfe.zernike_cartesian(coeffic, xz, yz)
|
|
|
|
# Add the new wavefront error
|
|
mcd.ELT_ADC.p1.add_wavefront_error(hzern)
|
|
|
|
# Performing the propagation using Fresnel approximation to the focal plane.
|
|
Exyz_zern,xzern,yzern = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
|
|
print_progress=False, hx=0, hy=0,
|
|
load_up_to_adc=input_field_location)
|
|
|
|
psfFit_zern = PSFFitter(xzern.real,yzern.real,np.abs(Exyz_zern)**2)
|
|
Exyzlist_zern.append(Exyz_zern)
|
|
|
|
# Fit using airy pattern
|
|
amplitude_guess = np.max(np.abs(Exyz_zern))**2
|
|
airyFitted, popt_airy, pcov_airy = psfFit_zern.fit_fixed_obscuration_airy_PSF(initialGuess=(amplitude_guess, 0.0, 0.0, 0.0, 0.03))
|
|
popt_airy_list_zern.append(popt_airy)
|
|
pcov_airy_list_zern.append([ np.sqrt(pcov_airy[i][i]) for i in range(len(pcov_airy)) ])
|
|
strehl_list_zern.append( np.abs(psfFit_zern.get_coupling(E, Exyz_zern))**2 )
|
|
########################################
|
|
|
|
|
|
|
|
########################################
|
|
# WFE described by PSD profile #
|
|
########################################
|
|
factor = rms / midspat_rms
|
|
h_psd = wfe.scale_rms(factor, data=psd_surface)
|
|
|
|
# Add the new wavefront error
|
|
mcd.ELT_ADC.p1.add_wavefront_error(h_psd)
|
|
|
|
# Performing the propagation using Fresnel approximation to the focal plane.
|
|
Exyz_psd,xpsd,ypsd = mcd.propagate(l=l,surface=-1, zoom=False, show=False,
|
|
print_progress=False, hx=0, hy=0,
|
|
load_up_to_adc=input_field_location)
|
|
|
|
psfFit_psd = PSFFitter(xpsd.real,ypsd.real,np.abs(Exyz_psd)**2)
|
|
|
|
# Fit using coupling integral
|
|
Exyzlist_psd.append(Exyz_psd)
|
|
|
|
# Fit using airy pattern
|
|
amplitude_guess = np.max(np.abs(Exyz_psd))**2
|
|
airyFitted, popt_airy, pcov_airy = psfFit_psd.fit_fixed_obscuration_airy_PSF(initialGuess=(amplitude_guess, 0.0, 0.0, 0.0, 0.03))
|
|
popt_airy_list_psd.append(popt_airy)
|
|
pcov_airy_list_psd.append([ np.sqrt(pcov_airy[i][i]) for i in range(len(pcov_airy)) ])
|
|
strehl_list_psd.append( np.abs(psfFit_psd.get_coupling(E, Exyz_psd))**2 )
|
|
########################################
|
|
|
|
|
|
# Find the RMS value of the wavefront errros
|
|
rms_zern = np.sqrt(np.mean(np.asarray(hzern[~_mask])**2))
|
|
rms_psd = np.sqrt(np.mean(np.asarray(h_psd)**2))
|
|
rms_zern_list.append(rms_zern)
|
|
rms_psd_list.append(rms_psd)
|
|
|
|
print('Strehl: {:.3f} ... {:.3f}'.format(strehl_list_psd[-1], strehl_list_zern[-1]))
|
|
|
|
|
|
|
|
# PSD
|
|
# Optimum fit parameters for all scaled surface errors
|
|
_, x0_airy_psd, y0_airy_psd, _, _ = np.asarray(popt_airy_list_psd).T
|
|
|
|
# Errors in the optimum fit parameters for all scaled surface errors
|
|
_, x0_airy_psd_err, y0_airy_psd_err, _, _ = np.asarray(pcov_airy_list_psd).T
|
|
|
|
# Distance from original centroid at (0,0)
|
|
r_airy_psd = np.sqrt(x0_airy_psd**2+y0_airy_psd**2)
|
|
r_err_psd_airy = np.sqrt( (x0_airy_psd**2*x0_airy_psd_err**2+y0_airy_psd**2*y0_airy_psd_err**2) / (x0_airy_psd**2+y0_airy_psd**2))
|
|
|
|
rms_psd_nm = np.asarray(rms_psd_list)*1e6
|
|
r_airy_psd_mas = r_airy_psd * mcd.platescale * 1000
|
|
r_err_psd_airy_mas = r_err_psd_airy * mcd.platescale * 1000
|
|
|
|
|
|
# Zernikes
|
|
# Optimum fit parameters for all scaled surface errors
|
|
_, x0_airy_zern, y0_airy_zern, _, _ = np.asarray(popt_airy_list_zern).T
|
|
|
|
# Errors in the optimum fit parameters for all scaled surface errors
|
|
_, x0_airy_zern_err, y0_airy_zern_err, _, _ = np.asarray(pcov_airy_list_zern).T
|
|
|
|
# Distance from original centroid at (0,0)
|
|
r_airy_zern = np.sqrt(x0_airy_zern**2+y0_airy_zern**2)
|
|
r_err_zern_airy = np.sqrt( (x0_airy_zern**2*x0_airy_zern_err**2+y0_airy_zern**2*y0_airy_zern_err**2) / (x0_airy_zern**2+y0_airy_zern**2))
|
|
|
|
rms_zern_nm = np.asarray(rms_zern_list)*1e6
|
|
r_airy_zern_mas = r_airy_zern * mcd.platescale * 1000
|
|
r_err_zern_airy_mas = r_err_zern_airy * mcd.platescale * 1000
|
|
|
|
save_variable('./data/centroid_shift_zernikes.pkl', (rms_zern_nm, r_airy_zern_mas, r_err_zern_airy_mas, strehl_list_zern))
|
|
save_variable('./data/centroid_shift_psd.pkl', (rms_psd_nm, r_airy_psd_mas, r_err_psd_airy_mas, strehl_list_psd))
|
|
|
|
|
|
# Plot some of the point spread functions
|
|
fig = plt.figure(figsize=(9,3))
|
|
ax1 = fig.add_subplot(131)
|
|
ax1.imshow(np.abs(Exyzlist_zern[0][450:550,450:550]), cmap='hot', interpolation='bicubic')
|
|
ax1.set_xticks([])
|
|
ax1.set_yticks([])
|
|
ax2 = fig.add_subplot(132)
|
|
ax2.imshow(np.abs(Exyzlist_zern[-5][450:550,450:550]), cmap='hot', interpolation='bicubic')
|
|
ax2.set_xticks([])
|
|
ax2.set_yticks([])
|
|
ax4 = fig.add_subplot(133)
|
|
ax4.imshow(np.abs(Exyzlist_psd[-5][450:550,450:550]), cmap='hot', interpolation='bicubic')
|
|
ax4.set_xticks([])
|
|
ax4.set_yticks([])
|
|
plt.tight_layout()
|
|
plt.savefig('./figures/PSF_shape.png', bbox_inches='tight', transparent=True)
|
|
plt.savefig('./figures/PSF_shape.pdf', bbox_inches='tight', transparent=True)
|
|
plt.show()
|
|
|