import numpy as np import sys # sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics') # sys.path.insert(0, '/home/joost/Dropbox/Werk/PhD/Analyses/Python/Analytical/Fourier Optics') sys.path.insert(0, '/Users/joost/Dropbox/Werk/PhD/Documents/Published/FourierOptics GitHub release/') from fourierOptics import * from pdb import set_trace ''' Author: Joost van den Born, PhD student at University of Groningen Contact: born@astro.rug.nl Description: Script to generate the geometric distortions due to WFEs off the ADC prisms. The distortions are produced by measuring the offset of the PSF centroid with and without WFE as a function of field position. Script made for the SPIE manuscript. ''' def plot_distortions(xe, ye, xf, yf): # Coordinates without WFEs # xe = load_variable('../data/GD_IFP_zoom93_obscured_PSF_xcentroid_expected_zern_psd.pkl') # ye = load_variable('../data/GD_IFP_zoom93_obscured_PSF_ycentroid_expected_zern_psd.pkl') # # Coordinates with WFEs # xf = load_variable('../data/GD_IFP_zoom93_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl') # yf = load_variable('../data/GD_IFP_zoom93_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl') mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, l=1.49e-3) n = xe.shape[0] X = xe.flatten() Y = ye.flatten() dX = xf - xe dY = yf - ye dx = dX.flatten() dy = dY.flatten() # Magnitude of the distortions R = np.sqrt(dx**2 + dy**2) * mcd.platescale * 1e6 # Dummy figure, used for the colorbar colors in the quiver plot im = plt.imshow(np.reshape(R, (n,n))) plt.close() fig = plt.figure() ax = fig.add_subplot() for i in range(len(xe)): ax.plot(xe[:,i], ye[:,i], 'k-', alpha=0.2, linewidth=.5) ax.plot(xe[i,:], ye[i,:], 'k-', alpha=0.2, linewidth=.5) ax.quiver(X,Y,dx,dy, angles='xy', edgecolor='k', facecolor='None', linewidth=.5, units='dots') ax.quiver(X,Y,dx,dy,R, alpha=.5, units='dots') ax.set_aspect('equal', adjustable='box') ax.set_xlabel('x (mm)') ax.set_ylabel('y (mm)') fig.colorbar(im, ax=ax, label='PSF centroid shift ($\\mu$as)') # plt.savefig('../figures/UPDATED_GD_zoom93_focal_plane_zern.pdf', bbox_inches='tight', transparent=True) # plt.savefig('../figures/UPDATED_GD_zoom93_focal_plane_zern.png', bbox_inches='tight', transparent=True) plt.tight_layout() plt.show() if __name__ == '__main__': # plt.rc('text', usetex=True) # plt.rc('font', family='serif') # Setup of simulation l = 1.49e-3 mcd = ParaxialMICADO(n=3001, m=3001, dpix=50) print(mcd.platescale) wfe = WavefrontError(mcd.n, mcd.m) # Get x3 and y3 (the coordinates to apply the ADC WFE to): # The focal plane area is about 200x200 mm, so we apply the wavefront error to an area # of 200x200 mm. xfp, yfp = np.meshgrid(np.linspace(-150, 150, mcd.n), np.linspace(-150, 150, mcd.m)) # _, xfp, yfp = mcd.propagate(l=l,surface=1, zoom=False, show=False, # print_progress=False) # Generate a wavefront error for the intermediate focal plane. print('\nSetting the wavefront errors...') wfe_fp = WavefrontError(mcd.n,mcd.m) wfe_rms = 50e-6 psdWFE = True zernikeWFE = False if psdWFE: #PSD Wavefront error # surface_map_fp = wfe_fp.generate_height_map(wfe_rms, 2, 1) # plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, logscale=False, fig_size=(8,6.4), grid=False, show=True, # colormap='jet', labeltext='Surface height (nm)') # save_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl', wfe_fp) # wfe_fp = load_variable('../data/MCD_IFP_wfe_object_PSD_50nm.pkl') wfe_fp = load_variable('../data/GD Focal Plane WFEs/MCD_IFP_wfe_object_PSD_50nm.pkl') surface_map_fp = wfe_fp.data # from copy import deepcopy # xy_img = deepcopy((xfp, yfp)) # x, y = np.meshgrid(np.linspace(-10, 10, mcd.n), np.linspace(-10, 10, mcd.m)) # xy = deepcopy((x,y)) # zoom_map = wfe_fp.generate_height_map_window( # xy, xy_img, ip_type='phase') # plt.figure() # plt.grid() # plt.plot(1e3*x[1500,:], 1e6*zoom_map[1500,:], '-') # # plt.imshow(1e6*zoom_map, cmap='jet', extent=[x.min(), x.max(), y.min(), y.max()], # # origin='lower', vmin=-150, vmax=150) # plt.xlabel('x (μm)') # plt.ylabel('surface heigth (nm)') # plt.show() if zernikeWFE: # Zernike wavefront error # We reuse the ADC_surface classes because it can be applicable to any surface, if used properly ifp_wfe = ADC_surfaces(xfp, yfp, clearAperture=300) ifp_wfe.generate_ADC_wfe(zernRMS=wfe_rms, psdRMS=0.) mask = np.sqrt(ifp_wfe.xz**2 + ifp_wfe.yz**2) > 1 # Plot the wfe # surf_ifp = ifp_wfe.latest_surface.copy() # surf_ifp[mask]=np.nan # surface_map_fp = surf_ifp # wfe_fp.data = surf_ifp # wfe_fp = load_variable('../data/GD Focal Plane WFEs/MCD_IFP_wfe_object_zern_50nm.pkl') surface_map_fp = wfe_fp.data # save_variable('../data/MCD_IFP_wfe_object_zern_50nm.pkl', wfe_fp) # figp1, axp1 = plot_real_valued_results(surface_map_fp*1e6, xfp, yfp, # colormap='jet', labeltext='Surface height (nm)', fig_size=None, show=True) # plt.show() # plt.close() # List of performed simulations # Defined by PSD ## GD_IFP : -1:1 ## GD_IFP_zoom : -1/11 :1/11 ## GD_IFP_zoom2 : -.5/11:.5/11 ## GD_IFP_zoom3 : -.1/11:.1/11 ## GD_IFP_zoom7 : -.02/11:.02/11, surface interpolation ## GD_IFP_zoom8 : -.05/11:.05/11, surface interpolation ## GD_IFP_zoom9 : -.01/11:.01/11, surface interpolation ## GD_IFP_zoom91 : -.01/11:.01/11 but hx and hy shifted to an area near the corner of the image, where distortion is supposed to be large ## GD_IFP_zoom92 : -.01/11:.01/11, phase interpolation ## GD_IFP_zoom93 : -.004/11:.004/11, phase interpolation # (implemented)IDEA: Automate it, let it run all simulations for the lower left corner. Take multiples of 11 for the field points # i.e. 1/11, 1/11**2, 1/11**3, 1/11**4 # GD_IFP_zoom94 : - 1/11:1/11, phase interpolation, with shift to left bottom corner # GD_IFP_zoom95 : - 1/11**2:1/11**2, phase interpolation, with shift to left bottom corner # GD_IFP_zoom96 : - 1/11**3:1/11**3, phase interpolation, with shift to left bottom corner # GD_IFP_zoom97 : - 1/11:1/11, phase interpolation, center of the field # GD_IFP_zoom98 : - 1/11**2:1/11**2, phase interpolation, center of the field # GD_IFP_zoom99 : - 1/11**3:1/11**3, phase interpolation, center of the field # GD_IFP_zoom100 : - 1/11**4:1/11**4, phase interpolation, center of the field # GD_IFP_zoom101 : - 1/11**4:1/11**4, phase interpolation, center of the field, with shift to left bottom corner # Defined by zernikes ## GD_IFP_obscured_PSF_xcentroid_expected_zern : 50 nm rms 19 zernike modes : -1 : 1 ## To do; check PSF shape at center field with zern wfe ## To do; check interpolated WFE at any zoomed in field point. ## GD_IFP_zoom4 : -.02/11:.02/11 (actually zernike WFE!), surface interpolation --> CONTINUOUS :D ## GD_IFP_zoom5 : -.05/11:.05/11 (actually zernike WFE!), surface interpolation --> CONTINUOUS :D ## GD_IFP_zoom6 : -.05/11:.05/11, surface interpolation, but hx and hy shifted to an area near the corner of the image (actually zern!) Elist, Eplist = [], [] xlist, ylist = [], [] xplist, yplist = [], [] x_expected_list, y_expected_list = [], [] x_centroid_list, y_centroid_list = [], [] # Set field points hxList, hyList = np.mgrid[-1:1:11j,-1:1:11j]-(1/300) # hxList, hyList = np.mgrid[-.1/11:.1/11:11j,-.1/11:.1/11:11j]-(1/300) # Zoomed in to central area # hxList, hyList = np.mgrid[-.004/11:.004/11:11j,-.004/11:.004/11:11j]-(1/300) # Zoomed in to central area hxList = hxList.flatten() hyList = hyList.flatten() # Zooming in around -84mm by -84mm on the image plane ihx = hxList[108] ihy = hyList[108] for p in [4]: x_expected_list, y_expected_list = [], [] x_centroid_list, y_centroid_list = [], [] hxList, hyList = np.mgrid[ihx-1/11**p:ihx+1/11**p:11j, ihy-1/11**p:ihy+1/11**p:11j]-(1/300) hxList = hxList.flatten() hyList = hyList.flatten() # Propagate through the field positions without any WFEs for i in range(len(hxList)): print('Field point #: {}/{}'.format(i+1, len(hxList))) hx = hxList[i] hy = hyList[i] print('hx: {} - hy: {}'.format(hx,hy) ) # Initializing MICADO paraxial model mcd = ParaxialMICADO(n=3001, m=3001, dpix=50, entrance_pupil='obscured') # Propagate through the MICADO system E, x, y = mcd.propagate(l=l,surface=-1, zoom=False, show=False, print_progress=False, hx=hx, hy=hy) # Find approximation for the centroid and amplitude as a first # guess for the Obscured Airy pattern fit. amplitude_guess = np.max(np.abs(E))**2 xbc, ybc, _, _ = mcd.centroid_shift(method='Barycenter') print('Barycenter guess: ', xbc, ybc) # Do the Obscured Airy Pattern Fit for the unperturbed PSF xe, ye, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xbc, ybc, 0.0, 0.03)) print('Airy result: ', xe, ye) x_expected_list.append(xe) y_expected_list.append(ye) # Load in the WFEs and set them to the corresponding ADC surfaces print('\nSetting the wavefront errors...') # null_wfe = np.zeros((3001,3001)) mcd.ELT_FP.add_wavefront_error(surface_map_fp, xfp, yfp, wfe=wfe_fp) # Propagate through the MICADO system print('Propagating with Wavefront Errors applied...') Ep, xp, yp = mcd.propagate(l=l,surface=-1, zoom=False, show=False, print_progress=False, hx=hx, hy=hy) # Do the Obscured Airy Pattern Fit for the perturbed PSF xf, yf, _, _ = mcd.centroid_shift(method='Obscured Airy', initialGuess=(amplitude_guess, xe, ye, 0.0, 0.03)) # Resulting centroid shift due to the WFE x_centroid_list.append(xf) y_centroid_list.append(yf) R = np.sqrt( (xf-xe)**2 + (yf-ye)**2 ) * mcd.platescale * 1e6 print('xf: {}, yf: {}, shift (uas): {}'.format(xf,yf, R)) # Clear the memory by emptying the large arrays mcd = None wfe = None E, x, y = None, None, None Ep, xp, yp = None, None, None # Reshaping the arrays shape = (int(np.sqrt(len(x_expected_list))), int(np.sqrt(len(x_expected_list)))) x_expected = np.asarray(x_expected_list).reshape(shape) y_expected = np.asarray(y_expected_list).reshape(shape)#.T # Saving the arrays zoom_value = 97 + p save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_xcentroid_expected_zern_psd.pkl'.format(zoom_value), x_expected) save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_ycentroid_expected_zern_psd.pkl'.format(zoom_value), y_expected) # Reshaping the arrays shape = (int(np.sqrt(len(x_centroid_list))), int(np.sqrt(len(x_centroid_list)))) x_centroid = np.asarray(x_centroid_list).reshape(shape) y_centroid = np.asarray(y_centroid_list).reshape(shape) # Saving the arrays save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_xcentroid_found_with_wfe_airy_zern_psd.pkl'.format(zoom_value), x_centroid) save_variable('../data/GD_IFP_zoom{:.0f}_obscured_PSF_ycentroid_found_with_wfe_airy_zern_psd.pkl'.format(zoom_value), y_centroid) plot_distortions(x_expected, y_expected, x_centroid, y_centroid)