def NCPA_application(wf, npupil, NCPA, path, Debug_print, Debug): n = int(proper.prop_get_gridsize(wf)) lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda if (Debug_print == True): print ("NCPA", NCPA.shape) NCPA_pixels = (NCPA.shape)[0] # size of the phase screen scaling_factor_NCPA = float(npupil)/float(NCPA_pixels) # scaling factor the phase screen to the simulation pupil size if (Debug_print == True): print ("scaling_factor_NCPA: ", scaling_factor_NCPA) NCPA_scale = cv2.resize(NCPA.astype(np.float32), (0,0), fx=scaling_factor_NCPA, fy=scaling_factor_NCPA, interpolation=cv2.INTER_LINEAR) # scale the the phase screen to the simulation pupil size if (Debug_print == True): print ("NCPA_resample", NCPA_scale.shape) NCPA_large = np.zeros((n,n)) # define an array of n-0s, where to insert the screen if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) NCPA_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =NCPA_scale # insert the scaled screen into the 0s grid if (Debug == 1): fits.writeto(path + 'NCPA_large.fits', NCPA_large, overwrite=True) # fits file for the screen lambda2=lamda/(1e-6) # need to use lambda in microns screen_atm = np.exp(1j*NCPA_large/lambda2*2*math.pi) proper.prop_multiply(wf, screen_atm) # multiply the atm screen to teh wavefront #rms_error = NCPA[0]#30nm-- RMS wavefront error in meters #c_freq = NCPA[1]# 5 ;-- correlation frequency (cycles/meter) #high_power = NCPA[2]#3.0 ;-- high frequency falloff (r^-high_power) #RANDOM_MAP = readfits('RANDOM_MAP.fits') #proper.prop_psd_errormap_mod( wf, rms_error, c_freq, high_power, RMS=True) return
def quicklook_IQ(wfo, logAmp=False, show=True): I = np.real(wfo.wfarr) Q = np.imag(wfo.wfarr) print(np.sum(I), np.sum(Q)) I = proper.prop_shift_center(I) Q = proper.prop_shift_center(Q) fig = plt.figure(figsize=(14, 10)) ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2) ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2) ax3 = plt.subplot2grid((3, 2), (2, 0)) ax4 = plt.subplot2grid((3, 2), (2, 1)) if logAmp: ax1.imshow(I, origin='lower', cmap="YlGnBu_r", norm=LogNorm()) else: ax1.imshow(I, origin='lower', cmap="YlGnBu_r") ax2.imshow(Q, origin='lower', cmap="YlGnBu_r") # , vmin=-0.5, vmax=0.5) ax3.plot(I[int(tp.grid_size / 2)]) ax3.plot(np.sum(np.eye(tp.grid_size) * I, axis=1)) # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128]) ax4.plot(Q[int(tp.grid_size / 2)]) # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1)) plt.xlim([0, proper.prop_get_gridsize(wfo)]) fig.set_tight_layout(True) if show == True: plt.show()
def atmosphere(wf, npupil, atm_screen, Debug_print, Debug): n = int(proper.prop_get_gridsize(wf)) lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda screen = atm_screen # read the phase screen if (Debug_print == True): print ("screen", screen.shape) screen_pixels = (screen.shape)[0] # size of the phase screen scaling_factor_screen = float(npupil)/float(screen_pixels) # scaling factor the phase screen to the simulation pupil size if (Debug_print == True): print ("scaling_factor_screen: ", scaling_factor_screen) screen_scale = cv2.resize(screen.astype(np.float32), (0,0), fx=scaling_factor_screen, fy=scaling_factor_screen, interpolation=cv2.INTER_LINEAR) # scale the the phase screen to the simulation pupil size if (Debug_print == True): print ("screen_resample", screen_scale.shape) screen_large = np.zeros((n,n)) # define an array of n-0s, where to insert the screen if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) screen_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =screen_scale # insert the scaled screen into the 0s grid lambda2=lamda/(1e-6) # need to use lambda in microns screen_atm = np.exp(1j*screen_large/lambda2*2*math.pi) proper.prop_multiply(wf, screen_atm) # multiply the atm screen to teh wavefront return wf
def static_ncpa(wf, npupil, screen): n = int(proper.prop_get_gridsize(wf)) screen_pixels = (screen.shape)[0] # size of the phase screen sf = float(npupil) / float( screen_pixels ) # scaling factor the phase screen to the simulation pupil size screen_scale = cv2.resize( screen.astype(np.float32), (0, 0), fx=sf, fy=sf, interpolation=cv2.INTER_LINEAR ) # scale the the phase screen to the simulation pupil size screen_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the screen screen_large[ int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2), int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2)] = screen_scale # insert the scaled screen into the 0s grid proper.prop_add_phase(wf, screen_large) return wf
def quicklook_wf(wfo, logAmp=True, show=True): after_dm = proper.prop_get_amplitude(wfo) phase_afterdm = proper.prop_get_phase(wfo) fig = plt.figure(figsize=(14, 10)) ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2) ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2) ax3 = plt.subplot2grid((3, 2), (2, 0)) ax4 = plt.subplot2grid((3, 2), (2, 1)) if logAmp: ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r", norm=LogNorm()) else: ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r") ax2.imshow(phase_afterdm, origin='lower', cmap="YlGnBu_r") #, vmin=-0.5, vmax=0.5) ax3.plot(after_dm[int(tp.grid_size / 2)]) ax3.plot(np.sum(np.eye(tp.grid_size) * after_dm, axis=1)) # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128]) ax4.plot(phase_afterdm[int(tp.grid_size / 2)]) # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1)) plt.xlim([0, proper.prop_get_gridsize(wfo)]) fig.set_tight_layout(True) if show == True: plt.show()
def detector(wfo, conf): f_lens = conf['F_LENS'] nd = conf['N_D'] mode = conf['MODE'] prefix = conf['PREFIX'] Debug = conf['DEBUG'] n = proper.prop_get_gridsize(wfo) if (n >= nd): proper.prop_propagate(wfo, f_lens, "to reimaging lens") proper.prop_lens(wfo, f_lens, "apply reimaging lens") proper.prop_propagate(wfo, f_lens, "final focus") (wfo, sampling) = proper.prop_end( wfo, NOABS=False ) # conclude the simulation --> noabs= the wavefront array will be complex else: print('Error: final image is bigger than initial grid size') psf = wfo[int(n / 2 - nd / 2):int(n / 2 + nd / 2), int(n / 2 - nd / 2):int(n / 2 + nd / 2)] out_dir = str('./output_files/') if (Debug == True): fits.writeto(out_dir + prefix + '_' + mode + '_PSF' + '.fits', psf, overwrite=True) return psf
def gen_cached_name(label, wfo): prefix = 'cached' # Most of the cacheable steps depend upon wfo for: ngrid = proper.prop_get_gridsize(wfo) beamradius = proper.prop_get_beamradius(wfo) sampling = proper.prop_get_sampling(wfo) return '{}_{}_{}_{}_{}.npy'.format(prefix, label, ngrid, sampling, beamradius)
def prop_tilt(wf, tilt_x, tilt_y): """Tilt a wavefront in X and Y. based on tilt(self, Xangle, Yangle) from Poppy (poppy_core.py) From that function's docs: The sign convention is chosen such that positive Yangle tilts move the star upwards in the array at the focal plane. (This is sort of an inverse of what physically happens in the propagation to or through focus, but we're ignoring that here and trying to just work in sky coords) Parameters ---------- wf : obj WaveFront class object tilt_x : float Tilt angle along x in arc seconds tilt_y : float Tilt angle along y in arc seconds Returns ------- None Modifies wavefront array in wf object. """ if np.abs(tilt_x) > 0 or np.abs(tilt_y) > 0: sampling = proper.prop_get_sampling(wf) # m/pixel xangle_rad = tilt_x * np.pi / 648000. # rad. yangle_rad = tilt_y * np.pi / 648000. # rad. ngrid = proper.prop_get_gridsize(wf) # pixels U, V = np.indices(wf.wfarr.shape, dtype=float) U -= (ngrid - 1) / 2.0 # pixels X U *= sampling # m V -= (ngrid - 1) / 2.0 # pixels Y V *= sampling # m # Not totally comfortable that these are combined linearly # but go with Poppy for now phase = V * xangle_rad + U * yangle_rad # rad. m proper.prop_add_phase(wf, phase)
def island_effect_piston(wf, npupil, Island_Piston, path, Debug_print, Debug): n = int(proper.prop_get_gridsize(wf)) lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda PACKAGE_PATH = os.path.abspath(os.path.join(__file__, os.pardir)) petal1 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal1_243px.fits') petal2 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal2_243px.fits') petal3 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal3_243px.fits') petal4 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal4_243px.fits') petal5 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal5_243px.fits') petal6 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal6_243px.fits') piston_petal1 = Island_Piston[0]*petal1 piston_petal2 = Island_Piston[1]*petal2 piston_petal3 = Island_Piston[2]*petal3 piston_petal4 = Island_Piston[3]*petal4 piston_petal5 = Island_Piston[4]*petal5 piston_petal6 = Island_Piston[5]*petal6 piston = piston_petal1 + piston_petal2 + piston_petal3 + piston_petal4 + piston_petal5 + piston_petal6 piston_pixels = (piston.shape)[0]## fits file size scaling_factor = float(npupil)/float(piston_pixels) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print==True): print ("scaling_factor: ", scaling_factor) piston_scale = cv2.resize(piston.astype(np.float32), (0,0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR) # scale the pupil to the pupil size of the simualtions if (Debug_print==True): print ("piston_resample", piston_scale.shape) piston_large = np.zeros((n,n)) # define an array of n-0s, where to insert the pupuil if (Debug_print==True): print("n: ", n) print("npupil: ", npupil) piston_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =piston_scale # insert the scaled pupil into the 0s grid if (Debug == 1): fits.writeto(path+'piston_phase.fits', piston_large, overwrite=True) # fits file for the screen lambda2=lamda/(1e-6) # need to use lambda in microns piston_phase = np.exp(1j*piston_large/lambda2*2*math.pi) proper.prop_multiply(wf, piston_phase) # multiply the atm screen to teh wavefront return
def detector(wfo, f_lens, nd, coronagraph_type, prefix, Debug=False): n = proper.prop_get_gridsize(wfo) if (n >= nd): proper.prop_propagate(wfo, f_lens, "to reimaging lens") proper.prop_lens(wfo, f_lens, "apply reimaging lens") proper.prop_propagate(wfo, f_lens, "final focus") (wfo, sampling) = proper.prop_end( wfo, NOABS=False ) # conclude the simulation --> noabs= the wavefront array will be complex else: print('Error: final image is bigger than initial grid size') psf = wfo[int(n / 2 - nd / 2):int(n / 2 + nd / 2), int(n / 2 - nd / 2):int(n / 2 + nd / 2)] out_dir = str('./output_files/') if (Debug == True): fits.writeto(out_dir + prefix + '_' + coronagraph_type + '_PSF' + '.fits', psf, overwrite=True) return psf
def polmap( wavefront, polfile, pupil_diam_pix, condition, MUF=1.0 ): n = proper.prop_get_gridsize( wavefront ) lambda_m = proper.prop_get_wavelength(wavefront) if condition <= 2: (amp, pha) = polab( polfile, lambda_m, pupil_diam_pix, condition ) elif condition == 5: (amp_m45_x, pha_m45_x) = polab( polfile, lambda_m, pupil_diam_pix, -1 ) (amp_p45_x, pha_p45_x) = polab( polfile, lambda_m, pupil_diam_pix, +1 ) amp = (amp_m45_x + amp_p45_x) / 2 pha = (pha_m45_x + pha_p45_x) / 2 elif condition == 6: (amp_m45_y, pha_m45_y) = polab( polfile, lambda_m, pupil_diam_pix, -2 ) (amp_p45_y, pha_p45_y) = polab( polfile, lambda_m, pupil_diam_pix, +2 ) amp = (amp_m45_y + amp_p45_y) / 2 pha = (pha_m45_y + pha_p45_y) / 2 elif condition == 10: (amp_m45_x, pha_m45_x) = polab( polfile, lambda_m, pupil_diam_pix, -1 ) (amp_p45_x, pha_p45_x) = polab( polfile, lambda_m, pupil_diam_pix, +1 ) (amp_m45_y, pha_m45_y) = polab( polfile, lambda_m, pupil_diam_pix, -2 ) (amp_p45_y, pha_p45_y) = polab( polfile, lambda_m, pupil_diam_pix, +2 ) amp = (amp_m45_x + amp_p45_x + amp_m45_y + amp_p45_y) / 4 pha = (pha_m45_x + pha_p45_x + pha_m45_y + pha_p45_y) / 4 else: raise Exception( 'POLMAP: unmatched condition' ) proper.prop_multiply( wavefront, trim(amp,n) ) proper.prop_add_phase( wavefront, trim(MUF*pha,n) ) amp = 0 phase = 0 amp_p45x = 0 amp_m45x = 0 amp_p45y = 0 amp_m45y = 0 pha_p45x = 0 pha_m45x = 0 pha_p45y = 0 pha_m45y = 0 return
def vortex(wfo, charge, f_lens, diam, pixelsize, Debug_print=False): n = int(proper.prop_get_gridsize(wfo)) ofst = 0 # no offset ramp_sign = 1 #sign of charge is positive ramp_oversamp = 11. # vortex is oversampled for a better discretization if charge != 0: wavelength = proper.prop_get_wavelength(wfo) gridsize = proper.prop_get_gridsize(wfo) beam_ratio = pixelsize * 4.85e-9 / (wavelength / diam) calib = str(charge) + str('_') + str(int( beam_ratio * 100)) + str('_') + str(gridsize) my_file = str(tmp_dir + 'zz_perf_' + calib + '_r.fits') proper.prop_propagate(wfo, f_lens, 'inizio') # propagate wavefront proper.prop_lens(wfo, f_lens, 'focusing lens vortex') # propagate through a lens proper.prop_propagate(wfo, f_lens, 'VC') # propagate wavefront if (os.path.isfile(my_file) == True): if (Debug_print == True): print("Charge ", charge) vvc = readfield(tmp_dir, 'zz_vvc_' + calib) # read the theoretical vortex field vvc = proper.prop_shift_center(vvc) scale_psf = wfo._wfarr[0, 0] psf_num = readfield(tmp_dir, 'zz_psf_' + calib) # read the pre-vortex field psf0 = psf_num[0, 0] psf_num = psf_num / psf0 * scale_psf perf_num = readfield(tmp_dir, 'zz_perf_' + calib) # read the perfect-result vortex field perf_num = perf_num / psf0 * scale_psf wfo._wfarr = ( wfo._wfarr - psf_num ) * vvc + perf_num # the wavefront takes into account the real pupil with the perfect-result vortex field else: # CAL==1: # create the vortex for a perfectly circular pupil if (Debug_print == True): print("Charge ", charge) wfo1 = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) proper.prop_circular_aperture(wfo1, diam / 2) proper.prop_define_entrance(wfo1) proper.prop_propagate(wfo1, f_lens, 'inizio') # propagate wavefront proper.prop_lens( wfo1, f_lens, 'focusing lens vortex') # propagate through a lens proper.prop_propagate(wfo1, f_lens, 'VC') # propagate wavefront writefield(tmp_dir, 'zz_psf_' + calib, wfo1.wfarr) # write the pre-vortex field nramp = int(n * ramp_oversamp) #oversamp # create the vortex by creating a matrix (theta) representing the ramp (created by atan 2 gradually varying matrix, x and y) y1 = np.ones((nramp, ), dtype=np.int) y2 = np.arange(0, nramp, 1.) - (nramp / 2) - int(ramp_oversamp) / 2 y = np.outer(y2, y1) x = np.transpose(y) theta = np.arctan2(y, x) x = 0 y = 0 vvc_tmp = np.exp(1j * (ofst + ramp_sign * charge * theta)) theta = 0 vvc_real_resampled = cv2.resize( vvc_tmp.real, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc_imag_resampled = cv2.resize( vvc_tmp.imag, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc = np.array(vvc_real_resampled, dtype=complex) vvc.imag = vvc_imag_resampled vvcphase = np.arctan2(vvc.imag, vvc.real) # create the vortex phase vvc_complex = np.array(np.zeros((n, n)), dtype=complex) vvc_complex.imag = vvcphase vvc = np.exp(vvc_complex) vvc_tmp = 0. writefield(tmp_dir, 'zz_vvc_' + calib, vvc) # write the theoretical vortex field proper.prop_multiply(wfo1, vvc) proper.prop_propagate(wfo1, f_lens, 'OAP2') proper.prop_lens(wfo1, f_lens) proper.prop_propagate(wfo1, f_lens, 'forward to Lyot Stop') proper.prop_circular_obscuration( wfo1, 1., NORM=True) # null the amplitude iside the Lyot Stop proper.prop_propagate(wfo1, -f_lens) # back-propagation proper.prop_lens(wfo1, -f_lens) proper.prop_propagate(wfo1, -f_lens) writefield(tmp_dir, 'zz_perf_' + calib, wfo1.wfarr) # write the perfect-result vortex field vvc = readfield(tmp_dir, 'zz_vvc_' + calib) vvc = proper.prop_shift_center(vvc) scale_psf = wfo._wfarr[0, 0] psf_num = readfield(tmp_dir, 'zz_psf_' + calib) # read the pre-vortex field psf0 = psf_num[0, 0] psf_num = psf_num / psf0 * scale_psf perf_num = readfield(tmp_dir, 'zz_perf_' + calib) # read the perfect-result vortex field perf_num = perf_num / psf0 * scale_psf wfo._wfarr = ( wfo._wfarr - psf_num ) * vvc + perf_num # the wavefront takes into account the real pupil with the perfect-result vortex field proper.prop_propagate(wfo, f_lens, "propagate to pupil reimaging lens") proper.prop_lens(wfo, f_lens, "apply pupil reimaging lens") proper.prop_propagate(wfo, f_lens, "lyot stop") return wfo
def apodization(wf, r_obstr, npupil, RAVC, phase_apodizer_file, amplitude_apodizer_file, apodizer_misalignment, Debug_print, Debug): n = int(proper.prop_get_gridsize(wf)) if (RAVC == True): t1_opt = 1. - 1. / 4 * ( r_obstr**2 + r_obstr * (math.sqrt(r_obstr**2 + 8.)) ) # define the apodizer transmission [Mawet2013] R1_opt = (r_obstr / math.sqrt(1. - t1_opt) ) # define the apodizer radius [Mawet2013] if (Debug_print == True): print("r1_opt: ", R1_opt) print("t1_opt: ", t1_opt) apodizer = circular_apodization(wf, R1_opt, 1., t1_opt, xc=apodizer_misalignment[0], yc=apodizer_misalignment[1], NORM=True) # define the apodizer apodizer = proper.prop_shift_center(apodizer) if (isinstance(phase_apodizer_file, (list, tuple, np.ndarray)) == True): xc_pixels = int(apodizer_misalignment[3] * npupil) yc_pixels = int(apodizer_misalignment[4] * npupil) apodizer_pixels = (phase_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( apodizer_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = cv2.resize( phase_apodizer_file.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid apodizer = np.exp(1j * apodizer_large) if (isinstance(amplitude_apodizer_file, (list, tuple, np.ndarray)) == True): xc_pixels = int(apodizer_misalignment[0] * npupil) yc_pixels = int(apodizer_misalignment[1] * npupil) apodizer_pixels = (amplitude_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( apodizer_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = cv2.resize( amplitude_apodizer_file.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid apodizer = apodizer_large proper.prop_multiply(wf, apodizer) return
def errormap(wf, dm_map, xshift=0., yshift=0., **kwargs): """Read in a surface, wavefront, or amplitude error map from a FITS file. Map is assumed to be in meters of surface error. One (and only one) of the MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE switches must be specified in order to properly apply the map to the wavefront. For surface or wavefront error maps, the map values are assumed to be in meters, unless the NM or MICRONS switches are used to specify the units. The amplitude map must range from 0 to 1. The map will be interpolated to match the current wavefront sampling if necessary. Parameters ---------- wf : obj WaveFront class object dm_map : 2D numpy array the DM map in units of surface deformation xshify, yshift : float Amount to shift map (meters) in X,Y directions Returns ------- DMAP : numpy ndarray Returns map (after any necessary resampling) if set. Other Parameters ---------------- XC_MAP, YC_MAP : float Pixel coordinates of map center (Assumed n/2,n/2) SAMPLING : float Sampling of map in meters ROTATEMAP : float Degrees counter-clockwise to rotate map, after any resampling and shifting MULTIPLY : float Multiplies the map by the specified factor MAGNIFY : float Spatially magnify the map by a factor of "constant" from its default size; do not use if SAMPLING is specified MIRROR_SURFACE : bool Indicates file contains a mirror surface height error map; It assumes a positive value indicates a surface point higher than the mean surface. The map will be multiplied by -2 to convert it to a wavefront map to account for reflection and wavefront delay (a low region on the surface causes a positive increase in the phase relative to the mean) WAVEFRONT : bool Indicates file contains a wavefront error map AMPLITUDE : bool Indicates file contains an amplitude error map NM or MICRONS : bool Indicates map values are in nanometers or microns. For surface or wavefront maps only Raises ------ SystemExit: If AMPLITUDE and (NM or MICRONS) parameters are input. SystemExit: If NM and MICRONS parameteres are input together. ValueError: If map type is MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE. """ if ("AMPLITUDE" in kwargs and kwargs["AMPLITUDE"]) \ and (("NM" in kwargs and kwargs["NM"]) \ or ("MICRONS" in kwargs and kwargs["MICRONS"])): raise SystemExit( "ERRORMAP: Cannot specify NM or MICRON for an amplitude map") if ("NM" in kwargs and kwargs["NM"]) and \ ("MICRONS" in kwargs and kwargs["MICRONS"]): raise SystemExit("ERRORMAP: Cannot specify both NM and MICRONS") if not "XC_MAP" in kwargs: s = dm_map.shape xc = s[ 0] // 2 # center of map read-in (xc should be 25 for SCExAO DM maps) yc = s[1] // 2 else: xc = kwargs["XC_MAP"] yc = kwargs["YC_MAP"] # KD edit: try to get the dm map to apply only in regions of the beam n = proper.prop_get_gridsize(wf) # new_sampling = proper.prop_get_sampling( wf) #kwargs["SAMPLING"] #*dm_map.shape[0]/npix_across_beam if new_sampling > (kwargs["SAMPLING"] + kwargs["SAMPLING"]*.1) or \ new_sampling < (kwargs["SAMPLING"] - kwargs["SAMPLING"]*.1): dm_map = proper.prop_resamplemap(wf, dm_map, kwargs["SAMPLING"], 0, 0) dm_map = dm_map[n // 2:n // 2 + xc * 4, n // 2:n // 2 + xc * 4] # print(f'User-defined samping is {kwargs["SAMPLING"]:.6f} but proper wavefront has sampling of ' # f'{new_sampling:.6f}') warnings.warn( f'User-defined beam ratio does not produce aperture sampling consistent with SCExAO actuator ' f'spacing. Resampling Map') # resample dm_map to size of beam in the simulation # grid = proper.prop_resamplemap(wf, dm_map, new_sampling, xc, yc, xshift, yshift) dmap = np.zeros((wf.wfarr.shape[0], wf.wfarr.shape[1])) r = dmap.shape xrc = r[0] // 2 yrc = r[1] // 2 dmap[xrc - xc * 2:xrc + xc * 2, yrc - yc * 2:yrc + yc * 2] = dm_map # Create mask to eliminate resampling artifacts outside of beam if ("MASKING" in kwargs and kwargs["MASKING"]): h, w = wf.wfarr.shape[:2] center = (int(w / 2), int(h / 2)) radius = np.ceil(h * kwargs['BR'] / 2) # # Making the Circular Boolean Mask Y, X = np.mgrid[:h, :w] dist_from_center = np.sqrt((X - center[0])**2 + (Y - center[1])**2) inds = dist_from_center <= radius # Applying the Mask to the dm_map mask = np.zeros_like(dmap) mask[inds] = 1 dmap *= mask # Shift the center of dmap to 0,0 dmap = proper.prop_shift_center(dmap) if kwargs['PLOT']: import matplotlib.pyplot as plt from matplotlib.colors import LogNorm, SymLogNorm fig, subplot = plt.subplots(nrows=1, ncols=2, figsize=(12, 5)) ax1, ax2 = subplot.flatten() fig.suptitle(f'RTC DM Voltage Maps') ax1.imshow( dm_map, norm=SymLogNorm(1e-2) ) # LogNorm(vmin=np.min(dm_map),vmax=np.max(dm_map)) SymLogNorm(1e-2) ax1.set_title('DM Map Read In') ax2.imshow(proper.prop_shift_center( dmap)) # , cmap='hsv' must shift the center because # proper assumes dmap center is 0,0, so we need to shift it back to plot properly ax2.set_title('DM Map in Center of Proper simulated beam') if "ROTATEMAP" in kwargs or "MAGNIFY" in kwargs: # readmap stores map with center at (0,0), so shift # before and after rotation dmap = proper.prop_shift_center(dmap) if "ROTATEMAP" in kwargs: dmap = proper.prop_rotate(dmap, kwargs["ROTATEMAP"], CUBIC=-0.5, MISSING=0.0) if "MAGNIFY" in kwargs: dmap = proper.prop_magnify(dmap, kwargs["MAGNIFY"], dmap.shape[0]) dmap = proper.prop_shift_center(dmap) if ("MICRONS" in kwargs and kwargs["MICRONS"]): dmap *= 1.e-6 if ("NM" in kwargs and kwargs["NM"]): dmap *= 1.e-9 if "MULTIPLY" in kwargs: dmap *= kwargs["MULTIPLY"] i = complex(0., 1.) if ("MIRROR_SURFACE" in kwargs and kwargs["MIRROR_SURFACE"]): wf.wfarr *= np.exp(-4 * np.pi * i / wf.lamda * dmap) # Krist version elif "WAVEFRONT" in kwargs: wf.wfarr *= np.exp(2 * np.pi * i / wf.lamda * dmap) elif "AMPLITUDE" in kwargs: wf.wfarr *= dmap else: raise ValueError( "ERRORMAP: Unspecified map type: Use MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE" ) # check1 = proper.prop_get_sampling(wf) # print(f"\n\tErrormap Sampling\n" # f"sampling in errormap.py is {check1 * 1e3:.4f} mm\n") return dmap
def lyotstop(wf, conf, RAVC): input_dir = conf['INPUT_DIR'] diam = conf['DIAM'] npupil = conf['NPUPIL'] spiders_angle = conf['SPIDERS_ANGLE'] r_obstr = conf['R_OBSTR'] Debug = conf['DEBUG'] Debug_print = conf['DEBUG_PRINT'] LS_amplitude_apodizer_file = conf['AMP_APODIZER'] LS_misalignment = np.array(conf['LS_MIS_ALIGN']) if conf['PHASE_APODIZER_FILE'] == 0: LS_phase_apodizer_file = 0 else: LS_phase_apodizer_file = fits.getdata(input_dir + '/' + conf['PHASE_APODIZER_FILE']) LS = conf['LYOT_STOP'] LS_parameters = np.array(conf['LS_PARA']) n = proper.prop_get_gridsize(wf) if (RAVC == True): # define the inner radius of the Lyot Stop t1_opt = 1. - 1. / 4 * ( r_obstr**2 + r_obstr * (math.sqrt(r_obstr**2 + 8.)) ) # define the apodizer transmission [Mawet2013] R1_opt = (r_obstr / math.sqrt(1. - t1_opt) ) # define teh apodizer radius [Mawet2013] r_LS = R1_opt + LS_parameters[ 1] # when a Ring apodizer is present, the inner LS has to have at least the value of the apodizer radius else: r_LS = r_obstr + LS_parameters[ 1] # when no apodizer, the LS has to have at least the radius of the pupil central obstruction if LS == True: # apply the LS if (Debug_print == True): print("LS parameters: ", LS_parameters) proper.prop_circular_aperture(wf, LS_parameters[0], LS_misalignment[0], LS_misalignment[1], NORM=True) proper.prop_circular_obscuration(wf, r_LS, LS_misalignment[0], LS_misalignment[1], NORM=True) if (LS_parameters[2] != 0): for iter in range(0, len(spiders_angle)): if (Debug_print == True): print("LS_misalignment: ", LS_misalignment) proper.prop_rectangular_obscuration( wf, LS_parameters[2], 2 * diam, LS_misalignment[0], LS_misalignment[1], ROTATION=spiders_angle[iter]) # define the spiders if (Debug == True): out_dir = str('./output_files/') fits.writeto( out_dir + '_Lyot_stop.fits', proper.prop_get_amplitude(wf)[int(n / 2) - int(npupil / 2 + 50):int(n / 2) + int(npupil / 2 + 50), int(n / 2) - int(npupil / 2 + 50):int(n / 2) + int(npupil / 2 + 50)], overwrite=True) if (isinstance(LS_phase_apodizer_file, (list, tuple, np.ndarray)) == True): xc_pixels = int(LS_misalignment[3] * npupil) yc_pixels = int(LS_misalignment[4] * npupil) apodizer_pixels = (LS_phase_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( apodizer_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation # scaling_factor = float(npupil)/float(pupil_pixels) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = cv2.resize( LS_phase_apodizer_file.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid phase_multiply = np.array(np.zeros((n, n)), dtype=complex) # create a complex array phase_multiply.imag = apodizer_large # define the imaginary part of the complex array as the atm screen apodizer = np.exp(phase_multiply) proper.prop_multiply(wf, apodizer) if (Debug == True): fits.writeto('LS_apodizer.fits', proper.prop_get_phase(wf), overwrite=True) if (isinstance(LS_amplitude_apodizer_file, (list, tuple, np.ndarray)) == True): print('4th') xc_pixels = int(LS_misalignment[0] * npupil) yc_pixels = int(LS_misalignment[1] * npupil) apodizer_pixels = ( LS_amplitude_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( pupil_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = cv2.resize( amplitude_apodizer_file.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("grid_size: ", n) print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid apodizer = apodizer_large proper.prop_multiply(wf, apodizer) if (Debug == True): fits.writeto('LS_apodizer.fits', proper.prop_get_amplitude(wf), overwrite=True) return wf
def propcustom_dm(wf, dm_z0, dm_xc, dm_yc, spacing=0., **kwargs): """ Generate a deformable mirror surface almost exactly like PROPER. Simulate a deformable mirror of specified actuator spacing, including the effects of the DM influence function. Has two more optional keywords compared to proper.prop_dm Parameters ---------- wf : obj WaveFront class object dm_z0 : str or numpy ndarray Either a 2D numpy array containing the surface piston of each DM actuator in meters or the name of a 2D FITS image file containing the above dm_xc, dm_yc : list or numpy ndarray The location of the optical axis (center of the wavefront) on the DM in actuator units (0 ro num_actuator-1). The center of the first actuator is (0.0, 0.0) spacing : float Defines the spacing in meters between actuators; must not be used when n_act_across_pupil is specified. Returns ------- dmap : numpy ndarray Returns DM surface (not wavefront) map in meters Other Parameters ---------------- FIT : bool Switch that tells routine that the values in "dm_z" are the desired surface heights rather than commanded actuator heights, and so the routine should fit this map, accounting for actuator influence functions, to determine the necessary actuator heights. An iterative error-minimizing loop is used for the fit. NO_APPLY : bool If set, the DM pattern is not added to the wavefront. Useful if the DM surface map is needed but should not be applied to the wavefront N_ACT_ACROSS_PUPIL : int Specifies the number of actuators that span the X-axis beam diameter. If it is a whole number, the left edge of the left pixel is aligned with the left edge of the beam, and the right edge of the right pixel with the right edge of the beam. This determines the spacing and size of the actuators. Should not be used when "spacing" value is specified. XTILT, YTILT, ZTILT : float Specify the rotation of the DM surface with respect to the wavefront plane in degrees about the X, Y, Z axes, respectively, with the origin at the center of the wavefront. The DM surface is interpolated and orthographically projected onto the wavefront grid. The coordinate system assumes that the wavefront and initial DM surface are in the X,Y plane with a lower left origin with Z towards the observer. The rotations are left handed. The default rotation order is X, Y, then Z unless the /ZYX switch is set. XYZ or ZYX : bool Specifies the rotation order if two or more of XTILT, YTILT, or ZTILT are specified. The default is /XYZ for X, Y, then Z rotations. inf_fn : string specify a new influence function as a FITS file with the same header keywords as PROPER's default influence function. Needs these values in info.PrimaryData.Keywords: 'P2PDX_M' % pixel width x (m) 'P2PDY_M' % pixel width y (m) 'C2CDX_M' % actuator pitch x (m) 'C2CDY_M' % actuator pitch y (m) inf_sign : {+,-} specifies the sign (+/-) of the influence function. Given as an option because the default influence function file is positive, but positive DM actuator commands make a negative deformation for Xinetics and BMC DMs. Raises ------ ValueError: User cannot specify both actuator spacing and N_ACT_ACROSS_PUPIL ValueError: User must specify either actuator spacing or N_ACT_ACROSS_PUPIL """ if "ZYX" in kwargs and "XYZ" in kwargs: raise ValueError('PROP_DM: Error: Cannot specify both XYZ and ZYX ' + 'rotation orders. Stopping') elif "ZYX" not in kwargs and 'XYZ' not in kwargs: XYZ = 1 # default is rotation around X, then Y, then Z # ZYX = 0 elif "ZYX" in kwargs: # ZYX = 1 XYZ = 0 elif "XYZ" in kwargs: XYZ = 1 # ZYX = 0 if "XTILT" in kwargs: xtilt = kwargs["XTILT"] else: xtilt = 0. if "YTILT" in kwargs: ytilt = kwargs["YTILT"] else: ytilt = 0. if "ZTILT" in kwargs: ztilt = kwargs["ZTILT"] else: ztilt = 0. if type(dm_z0) == str: dm_z = proper.prop_fits_read(dm_z0) # Read DM setting from FITS file else: dm_z = dm_z0 if "inf_fn" in kwargs: inf_fn = kwargs["inf_fn"] else: inf_fn = "influence_dm5v2.fits" if "inf_sign" in kwargs: if(kwargs["inf_sign"] == '+'): sign_factor = 1. elif(kwargs["inf_sign"] == '-'): sign_factor = -1. else: sign_factor = 1. n = proper.prop_get_gridsize(wf) dx_surf = proper.prop_get_sampling(wf) # sampling of surface in meters beamradius = proper.prop_get_beamradius(wf) # Default influence function sampling is 0.1 mm, peak at (x,y)=(45,45) # Default influence function has shape = 1x91x91. Saving it as a 2D array # before continuing with processing dir_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data") inf = proper.prop_fits_read(os.path.join(dir_path, inf_fn)) inf = sign_factor*np.squeeze(inf) s = inf.shape nx_inf = s[1] ny_inf = s[0] xc_inf = nx_inf // 2 yc_inf = ny_inf // 2 dx_inf = 0.1e-3 # influence function spacing in meters dx_dm_inf = 1.0e-3 # nominal spacing between DM actuators in meters inf_mag = 10 if spacing != 0 and "N_ACT_ACROSS_PUPIL" in kwargs: raise ValueError("PROP_DM: User cannot specify both actuator spacing" + "and N_ACT_ACROSS_PUPIL. Stopping.") if spacing == 0 and "N_ACT_ACROSS_PUPIL" not in kwargs: raise ValueError("PROP_DM: User must specify either actuator spacing" + " or N_ACT_ACROSS_PUPIL. Stopping.") if "N_ACT_ACROSS_PUPIL" in kwargs: dx_dm = 2. * beamradius / int(kwargs["N_ACT_ACROSS_PUPIL"]) else: dx_dm = spacing dx_inf = dx_inf * dx_dm / dx_dm_inf # Influence function sampling scaled # to specified DM actuator spacing if "FIT" in kwargs: x = (np.arange(5, dtype=np.float64) - 2) * dx_dm if proper.use_cubic_conv: inf_kernel = proper.prop_cubic_conv(inf.T, x/dx_inf+xc_inf, x/dx_inf+yc_inf, GRID=True) else: xygrid = np.meshgrid(x/dx_inf+xc_inf, x/dx_inf+yc_inf) inf_kernel = map_coordinates(inf.T, xygrid, order=3, mode="nearest") (dm_z_commanded, dms) = proper.prop_fit_dm(dm_z, inf_kernel) else: dm_z_commanded = dm_z s = dm_z.shape nx_dm = s[1] ny_dm = s[0] # Create subsampled DM grid margin = 9 * inf_mag nx_grid = nx_dm * inf_mag + 2 * margin ny_grid = ny_dm * inf_mag + 2 * margin xoff_grid = margin + inf_mag/2 # pixel location of 1st actuator center in subsampled grid yoff_grid = xoff_grid dm_grid = np.zeros([ny_grid, nx_grid], dtype = np.float64) x = np.arange(nx_dm, dtype=int) * int(inf_mag) + int(xoff_grid) y = np.arange(ny_dm, dtype=int) * int(inf_mag) + int(yoff_grid) dm_grid[np.tile(np.vstack(y), (nx_dm,)), np.tile(x, (ny_dm, 1))] = dm_z_commanded dm_grid = ss.fftconvolve(dm_grid, inf, mode='same') # 3D rotate DM grid and project orthogonally onto wavefront xdim = int(np.round(np.sqrt(2) * nx_grid * dx_inf / dx_surf)) # grid dimensions (pix) projected onto wavefront ydim = int(np.round(np.sqrt(2) * ny_grid * dx_inf / dx_surf)) if xdim > n: xdim = n if ydim > n: ydim = n x = np.ones((ydim, 1), dtype=int) * ((np.arange(xdim) - xdim // 2) * dx_surf) y = (np.ones((xdim, 1), dtype=int) * ((np.arange(ydim) - ydim // 2) * dx_surf)).T a = xtilt * np.pi / 180 b = ytilt * np.pi / 180 g = ztilt * np.pi /180 if XYZ: m = np.array([[cos(b)*cos(g), -cos(b)*sin(g), sin(b), 0], [cos(a)*sin(g) + sin(a)*sin(b)*cos(g), cos(a)*cos(g)-sin(a)*sin(b)*sin(g), -sin(a)*cos(b), 0], [sin(a)*sin(g)-cos(a)*sin(b)*cos(g), sin(a)*cos(g)+cos(a)*sin(b)*sin(g), cos(a)*cos(b), 0], [0, 0, 0, 1] ]) else: m = np.array([ [cos(b)*cos(g), cos(g)*sin(a)*sin(b)-cos(a)*sin(g), cos(a)*cos(g)*sin(b)+sin(a)*sin(g), 0], [cos(b)*sin(g), cos(a)*cos(g)+sin(a)*sin(b)*sin(g), -cos(g)*sin(a)+cos(a)*sin(b)*sin(g), 0], [-sin(b), cos(b)*sin(a), cos(a)*cos(b), 0], [0, 0, 0, 1] ]) # Forward project a square edge = np.array([[-1.0, -1.0, 0.0, 0.0], [1.0, -1.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [-1.0, 1.0, 0.0, 0.0]]) new_xyz = np.dot(edge, m) # determine backward projection for screen-raster-to-DM-surce computation dx_dxs = (new_xyz[0, 0] - new_xyz[1, 0]) / (edge[0, 0] - edge[1, 0]) dx_dys = (new_xyz[1, 0] - new_xyz[2, 0]) / (edge[1, 1] - edge[2, 1]) dy_dxs = (new_xyz[0, 1] - new_xyz[1, 1]) / (edge[0, 0] - edge[1, 0]) dy_dys = (new_xyz[1, 1] - new_xyz[2, 1]) / (edge[1, 1] - edge[2, 1]) xs = (x/dx_dxs - y*dx_dys/(dx_dxs*dy_dys)) / \ (1 - dy_dxs*dx_dys/(dx_dxs*dy_dys)) ys = (y/dy_dys - x*dy_dxs/(dx_dxs*dy_dys)) / \ (1 - dx_dys*dy_dxs/(dx_dxs*dy_dys)) xdm = (xs + dm_xc * dx_dm) / dx_inf + xoff_grid ydm = (ys + dm_yc * dx_dm) / dx_inf + yoff_grid if proper.use_cubic_conv: grid = proper.prop_cubic_conv(dm_grid.T, xdm, ydm, GRID = False) grid = grid.reshape([xdm.shape[1], xdm.shape[0]]) else: grid = map_coordinates(dm_grid.T, [xdm, ydm], order=3, mode="nearest", prefilter = True) dmap = np.zeros([n, n], dtype=np.float64) nx_grid, ny_grid = grid.shape xmin, xmax = n // 2 - xdim // 2, n // 2 - xdim // 2 + nx_grid ymin, ymax = n // 2 - ydim // 2, n // 2 - ydim // 2 + ny_grid dmap[ymin:ymax, xmin:xmax] = grid if "NO_APPLY" not in kwargs: proper.prop_add_phase(wf, 2 * dmap) # convert surface to WFE return dmap
def get_intensity(wf_array, sp, logAmp=True, show=False, save=True, phase=False): if show == True: wfo = wf_array[0, 0] after_dm = proper.prop_get_amplitude(wfo) phase_afterdm = proper.prop_get_phase(wfo) fig = plt.figure(figsize=(14, 10)) ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2) ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2) ax3 = plt.subplot2grid((3, 2), (2, 0)) ax4 = plt.subplot2grid((3, 2), (2, 1)) if logAmp: ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r", norm=LogNorm()) else: ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r") ax2.imshow(phase_afterdm, origin='lower', cmap="YlGnBu_r") #, vmin=-0.5, vmax=0.5) ax3.plot(after_dm[int(tp.grid_size / 2)]) ax3.plot(np.sum(np.eye(tp.grid_size) * after_dm, axis=1)) # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128]) ax4.plot(phase_afterdm[int(tp.grid_size / 2)]) # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1)) plt.xlim([0, proper.prop_get_gridsize(wfo)]) fig.set_tight_layout(True) plt.show() if save: shape = wf_array.shape ws = sp.get_ints['w'] cs = sp.get_ints['c'] int_maps = np.empty((0, tp.grid_size, tp.grid_size)) for iw in ws: for iwf in cs: # int_maps.append(proper.prop_shift_center(np.abs(wf_array[iw, iwf].wfarr) ** 2)) if phase: int_map = proper.prop_get_phase(wf_array[iw, iwf]) # int_map = proper.prop_shift_center(np.abs(wf_array[iw, iwf].wfarr) ** 2) else: int_map = proper.prop_shift_center( np.abs(wf_array[iw, iwf].wfarr)**2) int_maps = np.vstack((int_maps, [int_map])) # quicklook_im(int_map)#, logAmp=True) # int_maps = np.array(int_maps) # view_datacube(int_maps) import pickle, os if os.path.exists(iop.int_maps): # "with" statements are very handy for opening files. with open(iop.int_maps, 'rb') as rfp: # dprint(np.array(pickle.load(rfp)).shape) int_maps = np.vstack((int_maps, pickle.load(rfp))) with open(iop.int_maps, 'wb') as wfp: pickle.dump(int_maps, wfp, protocol=pickle.HIGHEST_PROTOCOL)
def vortex(wfo, CAL, charge, f_lens, path, Debug_print): n = int(proper.prop_get_gridsize(wfo)) ofst = 0 # no offset ramp_sign = 1 #sign of charge is positive #sampling = n ramp_oversamp = 11. # vortex is oversampled for a better discretization if charge != 0: if CAL == 1: # create the vortex for a perfectly circular pupil if (Debug_print == True): print("CAL:1, charge ", charge) writefield(path, 'zz_psf', wfo.wfarr) # write the pre-vortex field nramp = int(n * ramp_oversamp) #oversamp # create the vortex by creating a matrix (theta) representing the ramp (created by atan 2 gradually varying matrix, x and y) y1 = np.ones((nramp, ), dtype=np.int) y2 = np.arange(0, nramp, 1.) - (nramp / 2) - int(ramp_oversamp) / 2 y = np.outer(y2, y1) x = np.transpose(y) theta = np.arctan2(y, x) x = 0 y = 0 #vvc_tmp_complex = np.array(np.zeros((nramp,nramp)), dtype=complex) #vvc_tmp_complex.imag = ofst + ramp_sign*charge*theta #vvc_tmp = np.exp(vvc_tmp_complex) vvc_tmp = np.exp(1j * (ofst + ramp_sign * charge * theta)) theta = 0 vvc_real_resampled = cv2.resize( vvc_tmp.real, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc_imag_resampled = cv2.resize( vvc_tmp.imag, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc = np.array(vvc_real_resampled, dtype=complex) vvc.imag = vvc_imag_resampled vvcphase = np.arctan2(vvc.imag, vvc.real) # create the vortex phase vvc_complex = np.array(np.zeros((n, n)), dtype=complex) vvc_complex.imag = vvcphase vvc = np.exp(vvc_complex) vvc_tmp = 0. writefield(path, 'zz_vvc', vvc) # write the theoretical vortex field wfo0 = wfo proper.prop_multiply(wfo, vvc) proper.prop_propagate(wfo, f_lens, 'OAP2') proper.prop_lens(wfo, f_lens) proper.prop_propagate(wfo, f_lens, 'forward to Lyot Stop') proper.prop_circular_obscuration( wfo, 1., NORM=True) # null the amplitude iside the Lyot Stop proper.prop_propagate(wfo, -f_lens) # back-propagation proper.prop_lens(wfo, -f_lens) proper.prop_propagate(wfo, -f_lens) writefield(path, 'zz_perf', wfo.wfarr) # write the perfect-result vortex field wfo = wfo0 else: if (Debug_print == True): print("CAL:0, charge ", charge) vvc = readfield(path, 'zz_vvc') # read the theoretical vortex field vvc = proper.prop_shift_center(vvc) scale_psf = wfo._wfarr[0, 0] psf_num = readfield(path, 'zz_psf') # read the pre-vortex field psf0 = psf_num[0, 0] psf_num = psf_num / psf0 * scale_psf perf_num = readfield( path, 'zz_perf') # read the perfect-result vortex field perf_num = perf_num / psf0 * scale_psf wfo._wfarr = ( wfo._wfarr - psf_num ) * vvc + perf_num # the wavefront takes into account the real pupil with the perfect-result vortex field return
def occulter(self, wf): n = int(proper.prop_get_gridsize(wf)) ofst = 0 # no offset ramp_sign = 1 # sign of charge is positive ramp_oversamp = 11. # vortex is oversampled for a better discretization # f_lens = tp.f_lens #conf['F_LENS'] # diam = tp.diam#conf['DIAM'] charge = 2 #conf['CHARGE'] pixelsize = 5 #conf['PIXEL_SCALE'] Debug_print = False #conf['DEBUG_PRINT'] coron_temp = os.path.join(iop.testdir, 'coron_maps/') if not os.path.exists(coron_temp): os.mkdir(coron_temp) if charge != 0: wavelength = proper.prop_get_wavelength(wf) gridsize = proper.prop_get_gridsize(wf) beam_ratio = pixelsize * 4.85e-9 / (wavelength / tp.entrance_d) # dprint((wavelength,gridsize,beam_ratio)) calib = str(charge) + str('_') + str(int( beam_ratio * 100)) + str('_') + str(gridsize) my_file = str(coron_temp + 'zz_perf_' + calib + '_r.fits') if (os.path.isfile(my_file) == True): if (Debug_print == True): print("Charge ", charge) vvc = self.readfield( coron_temp, 'zz_vvc_' + calib) # read the theoretical vortex field vvc = proper.prop_shift_center(vvc) scale_psf = wf._wfarr[0, 0] psf_num = self.readfield(coron_temp, 'zz_psf_' + calib) # read the pre-vortex field psf0 = psf_num[0, 0] psf_num = psf_num / psf0 * scale_psf perf_num = self.readfield( coron_temp, 'zz_perf_' + calib) # read the perfect-result vortex field perf_num = perf_num / psf0 * scale_psf wf._wfarr = ( wf._wfarr - psf_num ) * vvc + perf_num # the wavefront takes into account the real pupil with the perfect-result vortex field else: # CAL==1: # create the vortex for a perfectly circular pupil if (Debug_print == True): dprint(f"Vortex Charge= {charge}") f_lens = 200.0 * tp.entrance_d wf1 = proper.prop_begin(tp.entrance_d, wavelength, gridsize, beam_ratio) proper.prop_circular_aperture(wf1, tp.entrance_d / 2) proper.prop_define_entrance(wf1) proper.prop_propagate(wf1, f_lens, 'inizio') # propagate wavefront proper.prop_lens( wf1, f_lens, 'focusing lens vortex') # propagate through a lens proper.prop_propagate(wf1, f_lens, 'VC') # propagate wavefront self.writefield(coron_temp, 'zz_psf_' + calib, wf1.wfarr) # write the pre-vortex field nramp = int(n * ramp_oversamp) # oversamp # create the vortex by creating a matrix (theta) representing the ramp (created by atan 2 gradually varying matrix, x and y) y1 = np.ones((nramp, ), dtype=np.int) y2 = np.arange(0, nramp, 1.) - (nramp / 2) - int(ramp_oversamp) / 2 y = np.outer(y2, y1) x = np.transpose(y) theta = np.arctan2(y, x) x = 0 y = 0 vvc_tmp = np.exp(1j * (ofst + ramp_sign * charge * theta)) theta = 0 vvc_real_resampled = cv2.resize( vvc_tmp.real, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc_imag_resampled = cv2.resize( vvc_tmp.imag, (0, 0), fx=1 / ramp_oversamp, fy=1 / ramp_oversamp, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions vvc = np.array(vvc_real_resampled, dtype=complex) vvc.imag = vvc_imag_resampled vvcphase = np.arctan2(vvc.imag, vvc.real) # create the vortex phase vvc_complex = np.array(np.zeros((n, n)), dtype=complex) vvc_complex.imag = vvcphase vvc = np.exp(vvc_complex) vvc_tmp = 0. self.writefield(coron_temp, 'zz_vvc_' + calib, vvc) # write the theoretical vortex field proper.prop_multiply(wf1, vvc) proper.prop_propagate(wf1, f_lens, 'OAP2') proper.prop_lens(wf1, f_lens) proper.prop_propagate(wf1, f_lens, 'forward to Lyot Stop') proper.prop_circular_obscuration( wf1, 1., NORM=True) # null the amplitude iside the Lyot Stop proper.prop_propagate(wf1, -f_lens) # back-propagation proper.prop_lens(wf1, -f_lens) proper.prop_propagate(wf1, -f_lens) self.writefield( coron_temp, 'zz_perf_' + calib, wf1.wfarr) # write the perfect-result vortex field vvc = self.readfield(coron_temp, 'zz_vvc_' + calib) vvc = proper.prop_shift_center(vvc) scale_psf = wf._wfarr[0, 0] psf_num = self.readfield(coron_temp, 'zz_psf_' + calib) # read the pre-vortex field psf0 = psf_num[0, 0] psf_num = psf_num / psf0 * scale_psf perf_num = self.readfield( coron_temp, 'zz_perf_' + calib) # read the perfect-result vortex field perf_num = perf_num / psf0 * scale_psf wf._wfarr = ( wf._wfarr - psf_num ) * vvc + perf_num # the wavefront takes into account the real pupil with the perfect-result vortex field return wf
def pupil(wfo, CAL, npupil, diam, r_obstr, spiders_width, spiders_angle, pupil_file, missing_segments_number, Debug, Debug_print): n = int(proper.prop_get_gridsize(wfo)) if (missing_segments_number == 0): if (isinstance(pupil_file, (list, tuple, np.ndarray)) == True): pupil = pupil_file pupil_pixels = (pupil.shape)[0] ## fits file size scaling_factor = float(npupil) / float( pupil_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) pupil_scale = cv2.resize( pupil.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("pupil_resample", pupil_scale.shape) pupil_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) pupil_large[ int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2), int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2 )] = pupil_scale # insert the scaled pupil into the 0s grid proper.prop_circular_aperture( wfo, diam / 2) # create a wavefront with a circular pupil if CAL == 0: # CAL=1 is for the back-propagation if (isinstance(pupil_file, (list, tuple, np.ndarray)) == True): proper.prop_multiply(wfo, pupil_large) # multiply the saved pupil else: proper.prop_circular_obscuration( wfo, r_obstr, NORM=True ) # create a wavefront with a circular central obscuration if (spiders_width != 0): for iter in range(0, len(spiders_angle)): proper.prop_rectangular_obscuration( wfo, spiders_width, 2 * diam, ROTATION=spiders_angle[iter]) # define the spiders else: PACKAGE_PATH = os.path.abspath(os.path.join(__file__, os.pardir)) if (missing_segments_number == 1): pupil = fits.getdata( PACKAGE_PATH + '/ELT_2048_37m_11m_5mas_nospiders_1missing_cut.fits') if (missing_segments_number == 2): pupil = fits.getdata( PACKAGE_PATH + '/ELT_2048_37m_11m_5mas_nospiders_2missing_cut.fits') if (missing_segments_number == 4): pupil = fits.getdata( PACKAGE_PATH + '/ELT_2048_37m_11m_5mas_nospiders_4missing_cut.fits') if (missing_segments_number == 7): pupil = fits.getdata( PACKAGE_PATH + '/ELT_2048_37m_11m_5mas_nospiders_7missing_1_cut.fits') pupil_pixels = (pupil.shape)[0] ## fits file size scaling_factor = float(npupil) / float( pupil_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) pupil_scale = cv2.resize( pupil.astype(np.float32), (0, 0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR ) # scale the pupil to the pupil size of the simualtions if (Debug_print == True): print("pupil_resample", pupil_scale.shape) pupil_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) pupil_large[ int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2), int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2)] = pupil_scale # insert the scaled pupil into the 0s grid if CAL == 0: # CAL=1 is for the back-propagation proper.prop_multiply(wfo, pupil_large) # multiply the saved pupil if (spiders_width != 0): for iter in range(0, len(spiders_angle)): proper.prop_rectangular_obscuration( wfo, spiders_width, 2 * diam, ROTATION=spiders_angle[iter]) # define the spiders return
def build_m1_opd(): return gen_opdmap(opd1_func, proper.prop_get_gridsize(wfo), proper.prop_get_sampling(wfo))
def apodization(wf, RAVC=False, phase_apodizer_file=0, amplitude_apodizer_file=0, apodizer_misalignment=0, Debug_print=False): r_obstr = 0.15 #0.15#conf['R_OBSTR'] npupil = 1 #conf['NPUPIL'] apodizer_misalignment = np.zeros((6)) #np.array(conf['RAVC_MISALIGN']) n = int(proper.prop_get_gridsize(wf)) apodizer = 1 if (RAVC == True): t1_opt = 1. - 1. / 4 * ( r_obstr**2 + r_obstr * (math.sqrt(r_obstr**2 + 8.)) ) # define the apodizer transmission [Mawet2013] R1_opt = (r_obstr / math.sqrt(1. - t1_opt) ) # define the apodizer radius [Mawet2013] if (Debug_print == True): print("r1_opt: ", R1_opt) print("t1_opt: ", t1_opt) apodizer = circular_apodization(wf, R1_opt, 1., t1_opt, xc=apodizer_misalignment[0], yc=apodizer_misalignment[1], NORM=True) # define the apodizer apodizer = proper.prop_shift_center(apodizer) # quicklook_im(apodizer, logAmp=False) if (isinstance(phase_apodizer_file, (list, tuple, np.ndarray)) == True): xc_pixels = int(apodizer_misalignment[3] * npupil) yc_pixels = int(apodizer_misalignment[4] * npupil) apodizer_pixels = (phase_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( apodizer_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = resize(phase_apodizer_file.astype(np.float32), (npupil, npupil), preserve_range=True, mode='reflect') if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid apodizer = np.exp(1j * apodizer_large) if (isinstance(amplitude_apodizer_file, (list, tuple, np.ndarray)) == True): xc_pixels = int(apodizer_misalignment[0] * npupil) yc_pixels = int(apodizer_misalignment[1] * npupil) apodizer_pixels = (amplitude_apodizer_file.shape)[0] ## fits file size scaling_factor = float(npupil) / float( apodizer_pixels ) ## scaling factor between the fits file size and the pupil size of the simulation if (Debug_print == True): print("scaling_factor: ", scaling_factor) apodizer_scale = resize(amplitude_apodizer_file.astype(np.float32), (npupil, npupil), preserve_range=True, mode='reflect') if (Debug_print == True): print("apodizer_resample", apodizer_scale.shape) apodizer_large = np.zeros( (n, n)) # define an array of n-0s, where to insert the pupuil if (Debug_print == True): print("n: ", n) print("npupil: ", npupil) apodizer_large[ int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 + int(npupil / 2) + xc_pixels, int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 + int(npupil / 2) + yc_pixels] = apodizer_scale # insert the scaled pupil into the 0s grid apodizer = apodizer_large # quicklook_wf(wf) proper.prop_multiply(wf, apodizer) # quicklook_wf(wf) return wf