def SubaruPupil(wf): """ adds Subaru pupil mask to the optical train :param wf: 2D proper wavefront :return: acts upon wfo, applies a spatial mask of s=circular secondary obscuration and possibly spider legs """ # dprint('Applying Subaru Pupil') # M2 shadow proper.prop_circular_obscuration(wf, 14 / 46, NORM=True) # Legs proper.prop_rectangular_obscuration(wf, 1.2, 2 / 46, .5, -.375, ROTATION=-50, NORM=True) proper.prop_rectangular_obscuration(wf, 1.2, 2 / 46, .5, .375, ROTATION=50, NORM=True) proper.prop_rectangular_obscuration(wf, 1.2, 2 / 46, -.5, -.375, ROTATION=50, NORM=True) proper.prop_rectangular_obscuration(wf, 1.2, 2 / 46, -.5, .375, ROTATION=-50, NORM=True) proper.prop_rectangular_obscuration(wf, 1, 1 / 46, .05, .45, ROTATION=-50, NORM=True) # Misc Spots proper.prop_circular_obscuration(wf, .075, -.1, 0.6, NORM=True) proper.prop_circular_obscuration(wf, .075, .5, -.375, NORM=True)
def vortex_init(vortex_calib='', dir_temp='', diam_ext=37, lam=3.8, ngrid=1024, beam_ratio=0.26, focal=660, vc_charge=2, verbose=False, **conf): ''' Creates/writes vortex back-propagation fitsfiles, or loads them if files already exist. The following parameters will be added to conf: vortex_calib, psf_num, perf_num, vvc Returns: conf (updated and sorted) ''' # update conf with local variables (remove unnecessary) conf.update(locals()) [conf.pop(key) for key in ['conf', 'verbose'] if key in conf] # check if back-propagation params already loaded for this calib calib = 'vortex_%s_%s_%3.4f' % (vc_charge, ngrid, beam_ratio) if vortex_calib == calib: return conf else: # check for existing file filename = os.path.join(dir_temp, '%s.fits' % calib) if os.path.isfile(filename): if verbose is True: print(' loading vortex back-propagation params') data = fits.getdata(os.path.join(dir_temp, filename)) # read the pre-vortex field psf_num = data[0] + 1j * data[1] # read the theoretical vortex field vvc = data[2] + 1j * data[3] # read the perfect-result vortex field perf_num = data[4] + 1j * data[5] # create files else: if verbose is True: print(" writing vortex back-propagation params") # create circular pupil wf_tmp = proper.prop_begin(diam_ext, lam, ngrid, beam_ratio) proper.prop_circular_aperture(wf_tmp, 1, NORM=True) # propagate to vortex lens(wf_tmp, focal) # pre-vortex field psf_num = deepcopy(wf_tmp.wfarr) # vortex phase ramp is oversampled for a better discretization ramp_oversamp = 11. nramp = int(ngrid * ramp_oversamp) start = -nramp / 2 - int(ramp_oversamp) / 2 + 0.5 end = nramp / 2 - int(ramp_oversamp) / 2 + 0.5 Vp = np.arange(start, end, 1.) # Pancharatnam Phase = arg<Vref,Vp> (horizontal input polarization) Vref = np.ones(Vp.shape) prod = np.outer(Vref, Vp) phiPan = np.angle(prod + 1j * prod.T) # vortex phase ramp exp(ilphi) ofst = 0 ramp_sign = 1 vvc_tmp = np.exp(1j * (ramp_sign * vc_charge * phiPan + ofst)) vvc = np.array(impro.resize_img(vvc_tmp.real, ngrid), dtype=complex) vvc.imag = impro.resize_img(vvc_tmp.imag, ngrid) phase_ramp = np.angle(vvc) # theoretical vortex field vvc_complex = np.array(np.zeros((ngrid, ngrid)), dtype=complex) vvc_complex.imag = phase_ramp vvc = np.exp(vvc_complex) # apply vortex proper.prop_multiply(wf_tmp, vvc) # null the amplitude inside the Lyot Stop, and back propagate lens(wf_tmp, focal) proper.prop_circular_obscuration(wf_tmp, 1., NORM=True) lens(wf_tmp, -focal) # perfect-result vortex field perf_num = deepcopy(wf_tmp.wfarr) # write all fields data = np.dstack((psf_num.real.T, psf_num.imag.T, vvc.real.T, vvc.imag.T,\ perf_num.real.T, perf_num.imag.T)).T fits.writeto(os.path.join(dir_temp, filename), np.float32(data), overwrite=True) # shift the phase ramp vvc = proper.prop_shift_center(vvc) # add vortex back-propagation parameters at the end of conf conf = {k: v for k, v in sorted(conf.items())} conf.update(vortex_calib=calib, psf_num=psf_num, vvc=vvc, perf_num=perf_num) if verbose is True: print(' vc_charge=%s, ngrid=%s, beam_ratio=%3.4f'%\ (vc_charge, ngrid, beam_ratio)) return conf
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 lyot_stop(wf, mode='RAVC', ravc_r=0.6, ls_dRext=0.03, ls_dRint=0.05, ls_dRspi=0.04, spi_width=0.5, spi_angles=[0, 60, 120], diam_ext=37, diam_int=11, diam_nominal=37, ls_misalign=None, ngrid=1024, npupil=285, f_lyot_stop='', verbose=False, **conf): """ Add a Lyot stop for a focal plane mask. Propagate to detector. """ if mode in ['CVC', 'RAVC', 'CLC']: # load lyot stop from file if provided if os.path.isfile(f_lyot_stop): if verbose is True: print(" apply lyot stop from '%s'" % os.path.basename(f_lyot_stop)) # get amplitude and phase data ls_mask = fits.getdata(f_lyot_stop) # resize to npupil ls_mask = resize_img(ls_mask, npupil) # pad with zeros and add to wavefront proper.prop_multiply(wf, pad_img(ls_mask, ngrid)) # if no lyot stop, create one else: # scale nominal values to pupil external diameter scaling = diam_nominal / diam_ext # LS parameters r_obstr = ravc_r if mode in ['RAVC'] else diam_int / diam_ext ls_int = r_obstr + ls_dRint * scaling ls_ext = 1 - ls_dRext * scaling ls_spi = spi_width / diam_ext + ls_dRspi * scaling # LS misalignments ls_misalign = [0, 0, 0, 0, 0, 0 ] if ls_misalign is None else list(ls_misalign) dx_amp, dy_amp, dz_amp = ls_misalign[0:3] dx_phase, dy_phase, dz_phase = ls_misalign[3:6] # create Lyot stop proper.prop_circular_aperture(wf, ls_ext, dx_amp, dy_amp, NORM=True) if diam_int > 0: proper.prop_circular_obscuration(wf, ls_int, dx_amp, dy_amp, NORM=True) if spi_width > 0: for angle in spi_angles: proper.prop_rectangular_obscuration(wf, 2*ls_spi, 2, \ dx_amp, dy_amp, ROTATION=angle, NORM=True) if verbose is True: print(' apply Lyot stop: ls_int=%s, ls_ext=%s, ls_spi=%s'\ %(round(ls_int, 4), round(ls_ext, 4), round(ls_spi, 4))) # propagate to detector lens(wf, **conf) return wf
def lyotstop(self, wf, RAVC=None, APP=None, get_pupil='no', dnpup=50): """Add a Lyot stop, or an APP.""" # load parameters npupil = 1 #conf['NPUPIL'] pad = int((210 - npupil) / 2) # get LS misalignments LS_misalignment = (np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) * npupil).astype(int) dx_amp, dy_amp, dz_amp = LS_misalignment[0:3] dx_phase, dy_phase, dz_phase = LS_misalignment[3:6] # case 1: Lyot stop (no APP) if APP is not True: # Lyot stop parameters: R_out, dR_in, spi_width # outer radius (absolute %), inner radius (relative %), spider width (m) (R_out, dR_in, spi_width) = [0.98, 0.03, 0] # Lyot stop inner radius at least as large as obstruction radius R_in = 0.15 # case of a ring apodizer if RAVC is True: # define the apodizer transmission and apodizer radius [Mawet2013] # apodizer radius at least as large as obstruction radius T_ravc = 1 - (R_in**2 + R_in * np.sqrt(R_in**2 + 8)) / 4 R_in /= np.sqrt(1 - T_ravc) # oversize Lyot stop inner radius R_in += dR_in # create Lyot stop proper.prop_circular_aperture(wf, R_out, dx_amp, dy_amp, NORM=True) if R_in > 0: proper.prop_circular_obscuration(wf, R_in, dx_amp, dy_amp, NORM=True) if spi_width > 0: for angle in [10]: proper.prop_rectangular_obscuration(wf, 0.05 * 8, 8 * 1.3, ROTATION=20) proper.prop_rectangular_obscuration(wf, 8 * 1.3, 0.05 * 8, ROTATION=20) # proper.prop_rectangular_obscuration(wf, spi_width, 2 * 8, \ # dx_amp, dy_amp, ROTATION=angle) # case 2: APP (no Lyot stop) else: # get amplitude and phase files APP_amp_file = os.path.join(conf['INPUT_DIR'], conf['APP_AMP_FILE']) APP_phase_file = os.path.join(conf['INPUT_DIR'], conf['APP_PHASE_FILE']) # get amplitude and phase data APP_amp = getdata(APP_amp_file) if os.path.isfile(APP_amp_file) \ else np.ones((npupil, npupil)) APP_phase = getdata(APP_phase_file) if os.path.isfile(APP_phase_file) \ else np.zeros((npupil, npupil)) # resize to npupil APP_amp = resize(APP_amp, (npupil, npupil), preserve_range=True, mode='reflect') APP_phase = resize(APP_phase, (npupil, npupil), preserve_range=True, mode='reflect') # pad with zeros to match PROPER gridsize APP_amp = np.pad(APP_amp, [(pad + 1 + dx_amp, pad - dx_amp), \ (pad + 1 + dy_amp, pad - dy_amp)], mode='constant') APP_phase = np.pad(APP_phase, [(pad + 1 + dx_phase, pad - dx_phase), \ (pad + 1 + dy_phase, pad - dy_phase)], mode='constant') # multiply the loaded APP proper.prop_multiply(wf, APP_amp * np.exp(1j * APP_phase)) # get the pupil amplitude or phase for output if get_pupil.lower() in 'amplitude': return wf, proper.prop_get_amplitude(wf)[pad + 1 - dnpup:-pad + dnpup, pad + 1 - dnpup:-pad + dnpup] elif get_pupil.lower() in 'phase': return wf, proper.prop_get_phase(wf)[pad + 1 - dnpup:-pad + dnpup, pad + 1 - dnpup:-pad + dnpup] else: return wf
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(diam, gridsize, spiders_width, spiders_angle, pixelsize, r_obstr, wavelength, pupil_file, missing_segments_number=0, Debug='False', Debug_print='False', prefix='test'): beam_ratio = pixelsize * 4.85e-9 / (wavelength / diam) wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) n = int(gridsize) npupil = np.ceil( gridsize * beam_ratio ) # compute the pupil size --> has to be ODD (proper puts the center in the up right pixel next to the grid center) if npupil % 2 == 0: npupil = npupil + 1 if (Debug_print == True): print("npupil: ", npupil) print("lambda: ", wavelength) 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 (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: if (missing_segments_number == 1): pupil = fits.getdata( input_dir + '/ELT_2048_37m_11m_5mas_nospiders_1missing_cut.fits') if (missing_segments_number == 2): pupil = fits.getdata( input_dir + '/ELT_2048_37m_11m_5mas_nospiders_2missing_cut.fits') if (missing_segments_number == 4): pupil = fits.getdata( input_dir + '/ELT_2048_37m_11m_5mas_nospiders_4missing_cut.fits') if (missing_segments_number == 7): pupil = fits.getdata( input_dir + '/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 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 if (Debug == True): fits.writeto( out_dir + prefix + '_intial_pupil.fits', proper.prop_get_amplitude(wfo)[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) proper.prop_define_entrance(wfo) #define the entrance wavefront wfo.wfarr *= 1. / np.amax(wfo._wfarr) # max(amplitude)=1 return (npupil, wfo)
def create_pupil(nhr=2**10, npupil=285, pupil_img_size=40, diam_ext=37, diam_int=11, spi_width=0.5, spi_angles=[0, 60, 120], seg_width=0, seg_gap=0, seg_rms=0, seg_ny=[ 10, 13, 16, 19, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 19, 16, 13, 10 ], seg_missing=[], seed=123456, **conf): ''' Create a pupil. Args: nhr: int high resolution grid npupil: int number of pixels of the pupil pupil_img_size: float pupil image (for PROPER) in m diam_ext: float outer circular aperture in m diam_int: float central obscuration in m spi_width: float spider width in m spi_angles: list of float spider angles in deg seg_width: float segment width in m seg_gap: float gap between segments in m seg_rms: float rms of the reflectivity of all segments seg_ny: list of int number of hexagonal segments per column (from left to right) seg_missing: list of tupples coordinates of missing segments ''' # create a high res pupil with PROPER of even size (nhr) nhr_size = pupil_img_size * nhr / (nhr - 1) wf_tmp = proper.prop_begin(nhr_size, 1, nhr, diam_ext / nhr_size) if diam_ext > 0: proper.prop_circular_aperture(wf_tmp, 1, NORM=True) if diam_int > 0: proper.prop_circular_obscuration(wf_tmp, diam_int / diam_ext, NORM=True) if spi_width > 0: for angle in spi_angles: proper.prop_rectangular_obscuration(wf_tmp, spi_width/nhr_size, 2, \ ROTATION=angle, NORM=True) pup = proper.prop_get_amplitude(wf_tmp) # crop the pupil to odd size (nhr-1), and resize to npupil pup = pup[1:, 1:] pup = resize_img(pup, npupil) # add segments if seg_width > 0: segments = np.zeros((nhr, nhr)) # sampling in meters/pixel sampling = pupil_img_size / nhr # dist between center of two segments, side by side seg_d = seg_width * np.cos(np.pi / 6) + seg_gap # segment radius seg_r = seg_width / 2 # segment radial distance wrt x and y axis seg_ny = np.array(seg_ny) seg_nx = len(seg_ny) seg_rx = np.arange(seg_nx) - (seg_nx - 1) / 2 seg_ry = (seg_ny - 1) / 2 # loop through segments np.random.seed(seed) for i in range(seg_nx): seg_x = seg_rx[i] * seg_d * np.cos(np.pi / 6) seg_y = -seg_ry[i] * seg_d for j in range(1, seg_ny[i] + 1): # removes secondary and if any missing segment is present if (np.sqrt(seg_x**2 + seg_y**2) <= 4.01*seg_d) \ or ((seg_rx[i], j) in seg_missing): seg_y += seg_d else: # creates one hexagonal segment at x, y position in meters segment = create_hexagon(nhr, seg_r, seg_y, seg_x, sampling) # multiply by segment reflectivity and add to segments seg_refl = np.random.normal(1, seg_rms) segments += segment * seg_refl seg_y += seg_d # need to transpose, due to the orientation of hexagons in create_hexagon segments = segments.T # resize to npupil, and add to pupil segments = resize_img(segments, npupil) pup *= segments return pup
def falco_gen_pupil_WFIRSTcycle6_LS(Nbeam, Dbeam, ID, OD, strut_width, centering, rot180deg=False): strut_width = strut_width * Dbeam # now in meters dx = Dbeam / Nbeam clock_deg = 0 magfacD = 1 xshift = 0 yshift = 0 pad_strut = 0 Dmask = Dbeam # % width of the beam (so can have zero padding if LS is undersized) (meters) diam = Dmask # width of the mask (meters) # minimum even number of points across to fully contain the actual aperture (if interpixel centered) NapAcross = Dmask / dx wf = _init_proper(Dmask, dx, centering) # 0 shift for pixel-centered pupil, or -dx shift for inter-pixel centering if centering == "interpixel": cshift = -dx / 2 elif rot180deg: cshift = -dx else: cshift = 0 # DATA FROM THE VISIO FILE D0 = 8 # inches, pupil diameter in Visio file x0 = -26 # inches, pupil center in x in Visio file y0 = 20.25 # inches, pupil center in y in Visio file Dconv = diam / D0 # conversion factor from inches and Visio units to meters # PRIMARY MIRROR (OUTER DIAMETER) ra_OD = (Dbeam * OD / 2) * magfacD cx_OD = cshift + xshift cy_OD = cshift + yshift proper.prop_circular_aperture(wf, ra_OD, cx_OD, cy_OD) # SECONDARY MIRROR (INNER DIAMETER) ra_ID = (Dbeam * ID / 2) * magfacD cx_ID = cshift + xshift cy_ID = cshift + yshift proper.prop_circular_obscuration(wf, ra_ID, cx_ID, cy_ID) sx_s = magfacD * (3.6 * (diam / D0) + pad_strut) sy_s = magfacD * (strut_width + pad_strut) clock_rot = np.array( [[np.cos(np.radians(clock_deg)), -np.sin(np.radians(clock_deg))], [np.sin(np.radians(clock_deg)), np.cos(np.radians(clock_deg))]]) def _get_strut_cxy(x, y): cx_s = (x - x0) * Dconv cy_s = (y - y0) * Dconv cxy = magfacD * clock_rot.dot([cx_s, cy_s]) + cshift return cxy + [xshift, yshift] # STRUT 1 rot_s1 = 77.56 + clock_deg # degrees cx_s1, cy_s1 = _get_strut_cxy(-24.8566, 22.2242) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s1, cy_s1, ROTATION=rot_s1) # STRUT 2 rot_s2 = -17.56 + clock_deg # degrees cx_s2, cy_s2 = _get_strut_cxy(-23.7187, 20.2742) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s2, cy_s2, ROTATION=rot_s2) # STRUT 3 rot_s3 = -42.44 + clock_deg # degrees cx_s3, cy_s3 = _get_strut_cxy(-24.8566, 18.2758) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s3, cy_s3, ROTATION=rot_s3) # STRUT 4 rot_s4 = 42.44 + clock_deg # degrees cx_s4, cy_s4 = _get_strut_cxy(-27.1434, 18.2758) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s4, cy_s4, ROTATION=rot_s4) # STRUT 5 rot_s5 = 17.56 + clock_deg # degrees cx_s5, cy_s5 = _get_strut_cxy(-28.2813, 20.2742) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s5, cy_s5, ROTATION=rot_s5) # STRUT 6 rot_s6 = 102.44 + clock_deg # degrees cx_s6, cy_s6 = _get_strut_cxy(-27.1434, 22.2242) proper.prop_rectangular_obscuration(wf, sx_s, sy_s, cx_s6, cy_s6, ROTATION=rot_s6) mask = np.fft.ifftshift(np.abs(wf.wfarr)) if rot180deg: mask = np.rot90(mask, 2) return mask
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 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 toliman_prescription_simple(wavelength, gridsize): # Values from Eduardo's RC Toliman system diam = 0.3 # telescope diameter in meters fl_pri = 0.5 * 1.143451 # primary focal length (m) # BN 20180208 d_pri_sec = 0.549337630333726 # primary to secondary separation (m) # d_pri_sec = 0.559337630333726 # primary to secondary separation (m) fl_sec = -0.5 * 0.0467579189727913 # secondary focal length (m) d_sec_to_focus = 0.528110658881 # nominal distance from secondary to focus (from eqn) # d_sec_to_focus = 0.589999999989853 # nominal distance from secondary to focus beam_ratio = 0.2 # initial beam width/grid width m2_rad = 0.059 # Secondary half-diameter (m) m2_strut_width = 0.01 # Width of struts supporting M2 (m) m2_supports = 5 # Define the wavefront wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) # Input aperture proper.prop_circular_aperture(wfo, diam / 2) # NOTE: could prop_propagate() here if some baffling included # Secondary and structs obscuration proper.prop_circular_obscuration(wfo, m2_rad) # secondary mirror obscuration # Spider struts/vanes, arranged evenly radiating out from secondary strut_length = diam / 2 - m2_rad strut_step = 360 / m2_supports strut_centre = m2_rad + strut_length / 2 for i in range(0, m2_supports): angle = i * strut_step radians = math.radians(angle) xoff = math.cos(radians) * strut_centre yoff = math.sin(radians) * strut_centre proper.prop_rectangular_obscuration(wfo, m2_strut_width, strut_length, xoff, yoff, ROTATION=angle + 90) # Define entrance proper.prop_define_entrance(wfo) # Primary mirror (treat as quadratic lens) proper.prop_lens(wfo, fl_pri, "primary") # Propagate the wavefront proper.prop_propagate(wfo, d_pri_sec, "secondary") # Secondary mirror (another quadratic lens) proper.prop_lens(wfo, fl_sec, "secondary") # NOTE: hole through primary? # Focus # BN 20180208 - Need TO_PLANE=True if you want an intermediate plane proper.prop_propagate(wfo, d_sec_to_focus, "focus", TO_PLANE=True) # proper.prop_propagate(wfo, d_sec_to_focus, "focus", TO_PLANE = False) # End (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
def lyotstop(wf, diam, r_obstr, npupil, RAVC, LS, LS_parameters, spiders_angle, LS_phase_apodizer_file, LS_amplitude_apodizer_file, LS_misalignment, path, Debug_print, Debug): 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 (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(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(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 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(path + 'LS_apodizer.fits', proper.prop_get_phase(wf), overwrite=True) if (isinstance(LS_amplitude_apodizer_file, (list, tuple, np.ndarray)) == True): 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("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) if (Debug == True): fits.writeto(path + 'LS_apodizer.fits', proper.prop_get_amplitude(wf), overwrite=True) return
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 prescription_quad(wavelength, gridsize, PASSVALUE={}): # Assign parameters from PASSVALUE struct or use defaults diam = PASSVALUE.get('diam', 0.3) # telescope diameter in meters m1_fl = PASSVALUE.get('m1_fl', 0.5717255) # primary focal length (m) beam_ratio = PASSVALUE.get('beam_ratio', 0.2) # initial beam width/grid width tilt_x = PASSVALUE.get('tilt_x', 0.) # Tilt angle along x (arc seconds) tilt_y = PASSVALUE.get('tilt_y', 0.) # Tilt angle along y (arc seconds) noabs = PASSVALUE.get('noabs', False) # Output complex amplitude? m1_hole_rad = PASSVALUE.get('m1_hole_rad', None) # Inner hole diameter use_caching = PASSVALUE.get('use_caching', False) # Use cached files if available? get_wf = PASSVALUE.get('get_wf', False) # Return wavefront """ Prescription for a single quad lens system """ if 'phase_func' in PASSVALUE: print('DEPRECATED setting "phase_func": use "opd_func" instead') if 'opd_func' not in PASSVALUE: PASSVALUE['opd_func'] = PASSVALUE['phase_func'] elif 'opd_func' not in PASSVALUE: print("no phase function") if 'phase_func_sec' in PASSVALUE: print( 'DEPRECATED setting "phase_func_sec": use "opd_func_sec" instead') if 'opd_func_sec' not in PASSVALUE: PASSVALUE['opd_func_sec'] = PASSVALUE['phase_func_sec'] # Define the wavefront wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) # Point off-axis prop_tilt(wfo, tilt_x, tilt_y) ### # Change to build ciruclar aperture?? # Input aperture proper.prop_circular_aperture(wfo, diam / 2.) ### # Define entrance proper.prop_define_entrance(wfo) proper.prop_lens(wfo, m1_fl, "primary") if 'opd_func' in PASSVALUE: opd1_func = PASSVALUE['opd_func'] def build_m1_opd(): return gen_opdmap(opd1_func, proper.prop_get_gridsize(wfo), proper.prop_get_sampling(wfo)) wfo.wfarr *= build_phase_map( wfo, load_cacheable_grid(opd1_func.__name__, wfo, build_m1_opd, use_caching)) if get_wf: wf = proper.prop_get_wavefront(wfo) print('Got wavefront') if m1_hole_rad is not None: proper.prop_circular_obscuration(wfo, m1_hole_rad) #if get_wf: # wf = proper.prop_get_wavefront(wfo) # print('Got wavefront') # Focus proper.prop_propagate(wfo, m1_fl, "focus", TO_PLANE=True) # End (wfo, sampling) = proper.prop_end(wfo) if get_wf: return (wfo, wf, sampling) else: return (wfo, sampling)
def coronagraph(wfo, f_lens, occulter_type, occult_loc, diam): # proper.prop_lens(wfo, f_lens, "coronagraph imaging lens") # proper.prop_propagate(wfo, f_lens, "occulter") # quicklook_wf(wfo) # occulter sizes are specified here in units of lambda/diameter; # convert lambda/diam to radians then to meters lamda = proper.prop_get_wavelength(wfo) # print lamda occrad = 3 # occulter radius in lam/D occrad_rad = occrad * lamda / diam # occulter radius in radians dx_m = proper.prop_get_sampling(wfo) dx_rad = proper.prop_get_sampling_radians(wfo) occrad_m = occrad_rad * dx_m / dx_rad # occulter radius in meters # print occrad_m, occulter_type # print 'line 22.', occulter_type #plt.figure(figsize=(12,8)) # quicklook_wf(wfo) if occulter_type == "Gaussian": r = proper.prop_radius(wfo) h = np.sqrt(-0.5 * occrad_m**2 / np.log(1 - np.sqrt(0.5))) #*0.8 gauss_spot = 1 - np.exp(-0.5 * (r / h)**2) # print occult_loc # gauss_spot = np.roll(gauss_spot,occult_loc,(0,1)) gauss_spot = shift(gauss_spot, shift=occult_loc, mode='wrap') proper.prop_multiply(wfo, gauss_spot) # quicklook_wf(wfo) #plt.suptitle("Gaussian spot", fontsize = 18) elif occulter_type == "Solid": proper.prop_circular_obscuration(wfo, occrad_m * 4. / 3) #plt.suptitle("Solid spot", fontsize = 18) # quicklook_wf(wfo) elif occulter_type == "8th_Order": proper.prop_8th_order_mask(wfo, occrad * 3. / 4., CIRCULAR=True) # quicklook_wf(wfo) elif occulter_type == 'Vortex': # print('lol') # apodization(wfo, True) vortex(wfo) # lyotstop(wfo, True) #plt.suptitle("8th order band limited spot", fontsize = 18) # quicklook_wf(wfo, logAmp=False, show=True) # After occulter # plt.subplot(1,2,1) # plt.imshow(np.sqrt(proper.prop_get_amplitude(wfo)), origin = "lower", cmap = plt.cm.gray) # plt.text(200, 10, "After Occulter", color = "w") # plt.show() # quicklook_wf(wfo) proper.prop_propagate(wfo, f_lens, "pupil reimaging lens") # quicklook_wf(wfo) proper.prop_lens(wfo, f_lens, "pupil reimaging lens") # quicklook_wf(wfo) proper.prop_propagate(wfo, 2 * f_lens, "lyot stop") # quicklook_wf(wfo) # from numpy.fft import fft2, ifft2 # wfo.wfarr = fft2(wfo.wfarr) #/ np.size(wfo.wfarr) # quicklook_wf(wfo) # plt.subplot(1,2,2) # plt.imshow(proper.prop_get_amplitude(wfo)**0.2, origin = "lower", cmap = plt.cm.gray) # plt.text(200, 10, "Before Lyot Stop", color = "w") # plt.show() # quicklook_wf(wfo,logAmp=False, show=True) if occulter_type == "Gaussian": # quicklook_wf(wfo) proper.prop_circular_aperture(wfo, 0.75, NORM=True) elif occulter_type == "Solid": # quicklook_wf(wfo) proper.prop_circular_aperture(wfo, 0.84, NORM=True) elif occulter_type == "8th_Order": # quicklook_wf(wfo) proper.prop_circular_aperture(wfo, 0.75, NORM=True) #0.5 elif occulter_type == "Vortex": # proper.prop_circular_aperture(wfo, 0.98, NORM = True) #0.5 lyotstop(wfo, True) # quicklook_wf(wfo) elif occulter_type == "None (Lyot Stop)": proper.prop_circular_aperture(wfo, 0.8, NORM=True) proper.prop_propagate(wfo, f_lens, "reimaging lens") # errs = np.sqrt(proper.prop_get_amplitude(wfo)) # # plt.figure() # # plt.imshow(errs) # # plt.show() # quicklook_wf(wfo) proper.prop_lens(wfo, f_lens, "reimaging lens") # quicklook_wf(wfo) proper.prop_propagate(wfo, f_lens, "final focus") # from numpy.fft import fft2, ifft2 # wfo.wfarr = fft2(wfo.wfarr) / np.size(wfo.wfarr) # quicklook_wf(wfo) return