Files
fourierOptics/scripts/SPIE/generate_strehl_relation.py
T

160 lines
6.0 KiB
Python

import numpy as np
import os
import sys
sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics')
from fourierOptics import *
'''
Author: Joost van den Born, PhD student at University of Groningen
Contact: born@astro.rug.nl
Description: Script to compare the Strehl ratio of 50 different instances
of a generated WFE. Higher spatial frequency perturbations
should follow the Marechal approximation more closely than
lower order shape errors only. We verify this using Power
Spectral Density and Zernike modes (66 coefficients).
Script made for the SPIE manuscript.
'''
def expected_strehl2(rms, l):
return np.exp( -(2*np.pi*rms/l)**2 )
def append_to_existing_data(A, filename):
if os.path.exists(filename):
A_o = load_variable(filename)
A2 = np.append(A, A_o)
else:
A2 = A
return A2
if __name__ == '__main__':
# 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 = np.linspace(1e-6, 1e-3, 25) # 1 to 1000 nm WFE RMS in mm
number_of_iterations = 50
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))
# Propagate unperturbed field
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)
# Initiate psf fitter object to calculate the strehl ratios
psf = PSFFitter(x, y, E)
# Create some empty lists for later
strehl_zern_complete, strehl_psd_complete = [], []
rms_zern_list_complete, rms_psd_list_complete = [], []
# Repeat the analysis 50 times
for i in range(number_of_iterations):
print('Iteration {}/{}'.format(i+1,number_of_iterations))
# 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 = [], []
# Compare results at different RMS values
for rms in rmsList:
########################################
# 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)
Strehl_zern = np.abs(psf.get_coupling(E, Exyz_zern))**2
StrehlList_zern.append(Strehl_zern)
########################################
########################################
# 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)
Strehl_psd = np.abs(psf.get_coupling(E, Exyz_psd))**2
StrehlList_psd.append(Strehl_psd)
########################################
# 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 results
print('RMS: {:.3f}, S_zern: {:.3f}, S_psd: {:.3f}, \
RMS_zern: {:.3f}, RMS_psd: {:.3f}'.format(
rms*1e6, Strehl_zern, Strehl_psd, rms_zern*1e6, rms_psd*1e6))
# Put the results in the correct list
strehl_zern_complete.append(StrehlList_zern)
strehl_psd_complete.append(StrehlList_psd)
rms_zern_list_complete.append(rms_zern_list)
rms_psd_list_complete.append(rms_psd_list)
rms_zern_list_complete = np.asarray(rms_zern_list_complete)
rms_psd_list_complete = np.asarray(rms_psd_list_complete)
# Import old results if they are available and append them to the results.
rmsList_present = rmsList.copy()
rmsList = append_to_existing_data(rmsList, './data/SR_RMS_values_obscured.pkl')
strehl_zern_complete = append_to_existing_data(strehl_zern_complete, './data/SR_strehl_zern_obscured.pkl')
strehl_psd_complete = append_to_existing_data(strehl_psd_complete, './data/SR_strehl_psd_obscured.pkl')
rms_zern_list_complete = append_to_existing_data(rms_zern_list_complete, './data/SR_rms_zern_complete_obscured.pkl')
rms_psd_list_complete = append_to_existing_data(rms_psd_list_complete, './data/SR_rms_psd_complete_obscured.pkl')
mean_strehl_zern = np.mean(strehl_zern_complete, axis=0)
err_strehl_zern = np.std(strehl_zern_complete, axis=0)
mean_strehl_psd = np.mean(strehl_psd_complete, axis=0)
err_strehl_psd = np.std(strehl_psd_complete, axis=0)
save_variable('./data/SR_RMS_values_obscured.pkl', rmsList)
save_variable('./data/SR_strehl_zern_obscured.pkl', strehl_zern_complete)
save_variable('./data/SR_strehl_psd_obscured.pkl', strehl_psd_complete)
save_variable('./data/SR_rms_zern_complete_obscured.pkl', rms_zern_list_complete)
save_variable('./data/SR_rms_psd_complete_obscured.pkl', rms_psd_list_complete)