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 open_loop_wfs(wfo, plane_name='wfs'): """ saves the unwrapped phase [arctan2(imag/real)] of the wfo.wf_collection at each wavelength It is an idealized image (exact copy) of the wavefront phase per wavelength. Only the map for the first object (the star) is saved. We have initialized Here we hardmask on the WFS map to be a circle around the beam in the pupil plane. This hard masking prevents the DM from acting on non-beam signal, since the DM modelled by proper is a nxn square array, but the beam is nominally circular for circular apertures. #TODO the way this is saved for naming the WFS_map is going to break if you want to do closed loop WFS on a #TODO woofer-tweeter system :param wfo: wavefront object :param plane_name: name of the plane to enable or disable saving the WFS map :return: array containing only the unwrapped phase delay of the wavefront; shape=[n_wavelengths], units=radians """ star_wf = wfo.wf_collection[:, 0] WFS_map = np.zeros((len(star_wf), sp.grid_size, sp.grid_size)) for iw in range(len(star_wf)): # for each wavelength hardmask_pupil(star_wf[iw]) phasemap = proper.prop_get_phase(star_wf[iw]) WFS_map[iw] = unwrap_phase(phasemap, wrap_around=[False, False]) WFS_map[iw][phasemap == 0] = 0 #TODO is this still necessary? if 'WFS' in sp.save_list or sp.closed_loop: wfo.save_plane(location='WFS') return WFS_map
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, ls_misalign=None, file_app_phase='', file_app_amp='', ngrid=1024, npupil=285, margin=50, get_amp=False, get_phase=False, verbose=False, **conf): """Add a Lyot stop, or an APP.""" # case 1: Lyot stop if mode in ['CVC', 'RAVC']: # LS parameters r_obstr = ravc_r if mode in ['RAVC'] else diam_int/diam_ext ls_int = r_obstr + ls_dRint ls_ext = 1 - ls_dRext ls_spi = spi_width/diam_ext + ls_dRspi # 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, ls_spi, 2, \ dx_amp, dy_amp, ROTATION=angle, NORM=True) if verbose is True: print('Create Lyot stop') print(' ls_int=%3.4f, ls_ext=%3.4f, ls_spi=%3.4f'\ %(ls_int, ls_ext, ls_spi)) print('') # case 2: APP elif mode in ['APP']: if verbose is True: print('Load APP from files\n') # get amplitude and phase data APP_amp = fits.getdata(file_app_amp) if os.path.isfile(file_app_amp) \ else np.ones((npupil, npupil)) APP_phase = fits.getdata(file_app_phase) if os.path.isfile(file_app_phase) \ else np.zeros((npupil, npupil)) # resize to npupil APP_amp = impro.resize_img(APP_amp, npupil) APP_phase = impro.resize_img(APP_phase, npupil) # pad with zeros to match PROPER ngrid APP_amp = impro.pad_img(APP_amp, ngrid, 1) APP_phase = impro.pad_img(APP_phase, ngrid, 0) # multiply the loaded APP proper.prop_multiply(wf, APP_amp*np.exp(1j*APP_phase)) # get the LS amplitude and phase for output LS_amp = impro.crop_img(proper.prop_get_amplitude(wf), npupil, margin)\ if get_amp is True else None LS_phase = impro.crop_img(proper.prop_get_phase(wf), npupil, margin)\ if get_phase is True else None return wf, LS_amp, LS_phase
def imshow_phi(wf, npupil=None, margin=0): amp = proper.prop_get_phase(wf) ngrid = amp.shape[0] if npupil is None: npupil = ngrid start = int((ngrid - npupil + npupil % 2) / 2 - margin) start = 0 if start < 0 else start end = ngrid - start + npupil % 2 plt.imshow(amp[start:end, start:end], origin='lower')
def apodize_pupil(wf): phase_map = proper.prop_get_phase(wf) amp_map = proper.prop_get_amplitude(wf) h, w = wf.wfarr.shape[:2] wavelength = wf.lamda scale = ap.wvl_range[0] / wavelength inds = circular_mask(h, w, radius=scale * 0.95 * h * sp.beam_ratio / 2) #TO DO remove hardcoded sizing, especially if use is default mask = np.zeros_like(phase_map) mask[inds] = 1 smooth_mask = gaussian_filter(mask, 1.5, mode='nearest') #TO DO remove hardcoded sizing, especially if use is default smoothed = phase_map * smooth_mask wf.wfarr = proper.prop_shift_center(amp_map * np.cos(smoothed) + 1j * amp_map * np.sin(smoothed))
def quicklook(self, wf=None, logZ=True, show=True, title=None): """ Produces a figure with an image of amplitude and one of phase as well as 1D slices through these images :param wf: optics.Wavefront :param logZ: bool logarithmic Z scaling :param show: bool display figure now or leave show() to be called by user later on :param title: str :return: """ if wf == None: wf = self.wf_collection[0,0] amp_map = proper.prop_get_amplitude(wf) phase_map = proper.prop_get_phase(wf) fig = plt.figure(figsize=(12, 10)) ax1 = plt.subplot2grid((1, 2), (0, 0), rowspan=2) # ((shape of grid to place axis x,y),(location x,y)) ax2 = plt.subplot2grid((1, 2), (0, 1), rowspan=2) # ax3 = plt.subplot2grid((3, 2), (2, 0)) # ax4 = plt.subplot2grid((3, 2), (2, 1)) if logZ: im1 = ax1.imshow(amp_map, origin='lower', cmap="YlGnBu_r", norm=LogNorm()) else: im1 = ax1.imshow(amp_map, origin='lower', cmap="YlGnBu_r") ax1.set_title('Amplitude Map') plt.colorbar(im1, ax=ax1) im2 = ax2.imshow(phase_map, origin='lower', cmap=sunlight) # , vmin=-0.5, vmax=0.5) ax2.set_title('Phase Map') plt.colorbar(im2, ax=ax2) # ax3.plot(after_dm[int(sp.grid_size / 2)]) # ax3.plot(np.sum(np.eye(sp.grid_size) * after_dm, axis=1), label=f'row {int(sp.grid_size / 2)}') # ax3.legend() # # # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128]) # # ax4.plot(phase_afterdm[int(sp.grid_size / 2)], label=f'row {int(sp.grid_size / 2)}') # ax4.legend() # # ax4.plot(np.sum(np.eye(ap.grid_size)*phase_afterdm,axis=1)) # plt.xlim([0, proper.prop_get_gridsize(wf)]) if not title: title = input(f"Always Add A Title\n " f"Please Enter Plane Name:") fig.suptitle(f"plane: {title}, lambda: {wf.lamda} m, body: {wf.name}", fontsize=18) else: fig.suptitle(f"plane: {title}, lambda: {wf.lamda} m, body: {wf.name}", fontsize=18) plt.subplots_adjust(top=0.9) if show: plt.show(block=True)
def hardmask_pupil(wf): """ hard-edged circular mask of the pupil plane. Masks out the WFS map outside of the beam since the DM modeled by proper can only be a square nxn grid of actuators, and thus the influence function surrounding each DM actuator could be affecting on-beam pixels, even if the actuator is acting on off-beam signal. In other words, even if a cornerDM actuator doesn't actuate on the beam, if there was non-zero signal in the WFS map, it will try to act on it, and it could 'influence' nearby DM actuators that are acting on the beam. This hard-edged mask is different from prop_circular_aperture in that it does not anti-alias the edges of the mask based on the 'fill factor' of the edge pixels. Instead, it has a boolean mask to zero everything > a fixed radius, in this case determined by the grid size and beam ratio of each wavefront passed into it. :param wf: a single wavefront :return: nothing is returned but the wf passed into it has been masked """ phase_map = proper.prop_get_phase(wf) amp_map = proper.prop_get_amplitude(wf) # Sizing the Mask h, w = wf.wfarr.shape[:2] center = (int(w / 2), int(h / 2)) radius = np.floor( sp.grid_size * wf.beam_ratio / 2) # Should scale with wavelength if sp.focused_system=False, # np.ceil used to oversize map so don't clip the beam # 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 Complex Array mask = np.zeros_like(phase_map) mask[inds] = 1 masked = phase_map * mask wf.wfarr = proper.prop_shift_center(amp_map * np.cos(masked) + 1j * amp_map * np.sin(masked))
def quick_wfs(wfo, iter, r0): # CPA_map = proper.prop_get_phase(wfo) import scipy.ndimage from skimage.restoration import unwrap_phase # quicklook_im(CPA_map, logAmp=False) sigma = [1, 1] CPA_map = scipy.ndimage.filters.gaussian_filter(unwrap_phase( proper.prop_get_phase(wfo)), sigma, mode='constant') # CPA_map = unwrap_phase(CPA_map) # quicklook_im(CPA_map, logAmp=False) if tp.piston_error: # from Utils.misc import debprint # var = 0.27*(tp.diam/(r0 * tp.ao_act))*(5/3) var = 0.001 #1e-11 #0.1 wavelengths 0.1*1000e-9 # debprint(var) CPA_map = CPA_map + np.random.normal( 0, var, (CPA_map.shape[0], CPA_map.shape[1])) return CPA_map
def deformable_mirror(wf, WFS_map, iter, previous_output=None, apodize=False, plane_name='', debug=False): """ combine different DM actuator commands into single map to send to prop_dm prop_dm needs an input map of n_actuators x n_actuators in units of actuator command height. quick_ao will handle the conversion to actuator command height, and the CDI probe must be scaled in cdi.probe_amp in params in units of m. Each subroutine is also responsible for creating a map of n_actuators x n_actuators spacing. prop_dm handles the resampling of this map onto the wavefront, including the influence function. Its some wizardry that happens in c, and presumably it is taken care of so you don't have to worry about it. In the call to proper.prop_dm, we apply the flag tp.fit_dm, which switches between two 'modes' of proper's DM surface fitting. If FALSE, the DM is driven to the heights specified by dm_map, and the influence function will act on these heights to define the final surface shape applied to the DM, which may differ substantially from the initial heights specified by dm_map. If TRUE, proper will iterate applying the influence function to the input heights, and adjust the heights until the difference between the influenced-map and input map meets some proper-defined convergence criterea. Setting tp.fit_dm=TRUE will obviously slow down the code, but will (likely) more accurately represent a well-calibrated DM response function. much of this code copied over from example from Proper manual on pg 94 :param wf: single wavefront :param WFS_map: wavefront sensor map, should be in units of phase delay :param previous_output: :param iter: the current index of iteration (which timestep this is) :param plane_name: name of plane (should be 'woofer' or 'tweeter' for best functionality) :return: nothing is returned, but the probe map has been applied to the DM via proper.prop_dm. DM plane post DM application can be saved via the sp.save_list functionality """ assert np.logical_xor(WFS_map is None, previous_output is None) # AO Actuator Count from DM Type if plane_name == 'tweeter' and hasattr(tp, 'act_tweeter'): nact = tp.act_tweeter elif plane_name == 'woofer' and hasattr(tp, 'act_woofer'): nact = tp.act_woofer else: nact = tp.ao_act # DM Coordinates nact_across_pupil = nact - 2 # number of full DM actuators across pupil (oversizing DM extent) dm_xc = ( nact / 2 ) # The location of the optical axis (center of the wavefront) on the DM in dm_yc = ( nact / 2 ) # actuator units. First actuator is centered on (0.0, 0.0). The 0.5 is a # parameter introduced/tuned by Rupert to remove weird errors (address this). # KD verified this needs to be here or else suffer weird errors 9/19 # TODO address/remove the 0.5 in DM x,y coordinates ############################ # Creating DM Surface Map ############################ d_beam = 2 * proper.prop_get_beamradius(wf) # beam diameter act_spacing = d_beam / nact_across_pupil # actuator spacing [m] ####### # AO ####### if previous_output is not None and WFS_map is None: dm_map = update_dm(previous_output) else: dm_map = quick_ao(wf, nact, WFS_map[wf.iw]) ######### # Waffle ######### if tp.satelite_speck['apply'] and plane_name is not 'woofer': waffle = make_speckle_kxy(tp.satelite_speck['xloc'], tp.satelite_speck['yloc'], tp.satelite_speck['amp'], tp.satelite_speck['phase']) waffle += make_speckle_kxy(tp.satelite_speck['xloc'], -tp.satelite_speck['yloc'], tp.satelite_speck['amp'], tp.satelite_speck['phase']) dm_map += waffle ####### # CDI ###### if cdi.use_cdi and plane_name == cdi.which_DM: theta = cdi.phase_series[iter] if not np.isnan(theta): # dprint(f"Applying CDI probe, lambda = {wfo.wsamples[iw]*1e9:.2f} nm") cdi.save_tseries(iter, datetime.datetime.now()) probe = config_probe(theta, nact, iw=wf.iw, ib=wf.ib, tstep=iter) dm_map = dm_map + probe # Add Probe to DM map ######################### # Applying Piston Error ######################### if tp.piston_error: mean_dm_map = np.mean(np.abs(dm_map)) var = 1e-4 # 1e-11 dm_map = dm_map + np.random.normal(0, var, (dm_map.shape[0], dm_map.shape[1])) ######################### # proper.prop_dm ######################### dmap = proper.prop_dm(wf, dm_map, dm_xc, dm_yc, act_spacing, FIT=tp.fit_dm) # if debug and wf.iw == 0 and wf.ib == 0 and iter == 0: dprint(plane_name) check_sampling(wf, iter, plane_name + ' DM pupil plane', getframeinfo(stack()[0][0]), units='mm') quick2D(WFS_map[wf.iw], title=f"WFS map after masking", zlabel='unwrapped phase (rad)', vlim=[-3 * np.pi, 3 * np.pi]) fig, ax = plt.subplots(1, 1) cax = ax.imshow(dm_map * 1e9, interpolation='none', origin='lower') plt.title(f'{plane_name} dm_map (actuator coordinates)') cb = plt.colorbar(cax) cb.set_label('nm') plt.show() post_ao = unwrap_phase( proper.prop_get_phase(wf)) * wf.lamda / (2 * np.pi) # quick2D(pre_ao_dist*1e9, title='unwrapped wavefront before DM', zlabel='nm', show=False) # , vlim=(-0.5e-7,0.5e-7)) # quick2D(np.abs(pre_ao_amp)**2, title='Pre-AO Intensity', show=False)#, vlim=(-0.5e-7,0.5e-7)) # quick2D(dmap, title='the phase map prop_dm is applying', zlabel='distance (m)', show=False)#, vlim=(-0.5e-7,0.5e-7)) # plt.figure() # plt.plot(pre_ao_dist[len(pre_ao_dist)//2], label=f'pre_ao 1D cut, row {len(pre_ao_dist)//2}') # plt.plot(2*dmap[len(dmap)//2], label=f'dmap 1D cut (x2), row {len(dmap)//2}') # plt.plot((pre_ao_dist + (2*dmap))[len(dmap)//2], label='difference') # plt.legend() # plt.xlim(sp.grid_size//2*np.array([1-sp.beam_ratio*1.1, 1+sp.beam_ratio*1.1])) # quick2D(pre_ao + (2*dmap), title='diff', zlabel='m', show=False, vlim=(-0.5e-7,0.5e-7)) # quick2D(post_ao, title='unwrapped wavefront after DM', zlabel='m', show=True, vlim=(-0.5e-7,0.5e-7)) # quick2D(np.abs(proper.prop_get_amplitude(wf))**2, title='wavefront after DM intensity', show=False) # quick2D(proper.prop_get_phase(wf), title='wavefront after DM in phase units', zlabel='Phase', # show=True) # colormap='sunlight', if apodize: hardmask_pupil(wf) return dmap
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 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 apodizer(wf, mode='RAVC', ravc_t=0.8, ravc_r=0.6, ravc_misalign=None, ngrid=1024, npupil=285, file_ravc_amp='', file_ravc_phase='', margin=50, get_amp=False, get_phase=False, verbose=False, **conf): ''' Create a wavefront object at the entrance pupil plane. The pupil is either loaded from a fits file, or created using pupil parameters. Can also select only one petal and mask the others. wf: WaveFront PROPER wavefront object mode: str HCI mode ravc_t: float RA transmittance ravc_r: float RA radius ravc_misalign: list of float RA misalignment ngrid: int number of pixels of the wavefront array npupil: int number of pixels of the pupil file_ravc_amp: str file_ravc_phase: str ring apodizer files (optional) ''' if mode in ['RAVC']: # load apodizer from files if provided if os.path.isfile(file_ravc_amp) and os.path.isfile(file_ravc_phase): if verbose is True: print('Load ring apodizer from files\n') # get amplitude and phase data RAVC_amp = fits.getdata(file_ravc_amp) RAVC_phase = fits.getdata(file_ravc_phase) # resize to npupil RAVC_amp = impro.resize_img(RAVC_amp, npupil) RAVC_phase = impro.resize_img(RAVC_phase, npupil) # pad with zeros to match PROPER gridsize RAVC_amp = impro.pad_img(RAVC_amp, ngrid) RAVC_phase = impro.pad_img(RAVC_phase, ngrid) # build complex apodizer apo = RAVC_amp * np.exp(1j * RAVC_phase) # or else, define the apodizer as a ring (with % misalignments) else: # RAVC misalignments ravc_misalign = [ 0, 0, 0, 0, 0, 0 ] if ravc_misalign is None else list(ravc_misalign) dx_amp, dy_amp, dz_amp = ravc_misalign[0:3] dx_phase, dy_phase, dz_phase = ravc_misalign[3:6] # create apodizer apo = circular_apodization(wf, ravc_r, 1., ravc_t, xc=dx_amp, \ yc=dy_amp, NORM=True) apo = proper.prop_shift_center(apo) if verbose is True: print('Create ring apodizer') print(' ravc_t=%3.4f, ravc_r=%3.4f'\ %(ravc_t, ravc_r)) print(' ravc_misalign=%s' % ravc_misalign) print('') # multiply the loaded apodizer proper.prop_multiply(wf, apo) # get the apodizer amplitude and phase for output apo_amp = impro.crop_img(proper.prop_get_amplitude(wf), npupil,\ margin) if get_amp is True else None apo_phase = impro.crop_img(proper.prop_get_phase(wf), npupil,\ margin) if get_phase is True else None return wf, apo_amp, apo_phase else: # no ring apodizer return wf, None, None
def simple_telescope(wavelength, gridsize): # Define entrance aperture diameter and other quantities d_objective = 5.0 # objective diameter in meters fl_objective = 20.0 * d_objective # objective focal length in meters fl_eyepiece = 0.021 # eyepiece focal length fl_eye = 0.022 # human eye focal length beam_ratio = 0.3 # initial beam width/grid width # Define the wavefront wfo = proper.prop_begin(d_objective, wavelength, gridsize, beam_ratio) # print d_objective, wavelength, gridsize, beam_ratio # Define a circular aperture proper.prop_circular_aperture(wfo, d_objective / 2) # proper.prop_propagate(wfo, fl_objective) # proper.prop_propagate(wfo, fl_objective) # proper.prop_propagate(wfo, fl_objective) # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # Define entrance proper.prop_define_entrance(wfo) # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # proper.prop_propagate(wfo, fl_objective+fl_eyepiece, "eyepiece") # # plt.imshow(proper.prop_get_amplitude(wfo)) # # plt.show() # # proper.prop_propagate(wfo, fl_objective+fl_eyepiece, "eyepiece") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # # proper.prop_propagate(wfo, fl_objective+fl_eyepiece, "eyepiece") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # Define a lens proper.prop_lens(wfo, fl_objective, "objective") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # Propagate the wavefront proper.prop_propagate(wfo, fl_objective + fl_eyepiece, "eyepiece") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() # Define another lens proper.prop_lens(wfo, fl_eyepiece, "eyepiece") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() exit_pupil_distance = fl_eyepiece / (1 - fl_eyepiece / (fl_objective + fl_eyepiece)) proper.prop_propagate(wfo, exit_pupil_distance, "exit pupil at eye lens") # quicklook_wf(wfo) # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() proper.prop_lens(wfo, fl_eye, "eye") proper.prop_propagate(wfo, fl_eye, "retina") # plt.imshow(proper.prop_get_amplitude(wfo)) # plt.show() quicklook_wf(wfo) phase_map = proper.prop_get_phase(wfo) amp_map = proper.prop_get_amplitude(wfo) # quicklook_im(phase_map) amp_map[80:100, 80:100] = 0 quicklook_im(amp_map, logAmp=True) import numpy as np wfo.wfarr = proper.prop_shift_center(amp_map * np.cos(phase_map) + 1j * amp_map * np.sin(phase_map)) # quicklook_wf(wf_array[iw,0]) proper.prop_propagate(wfo, fl_eye, "retina") proper.prop_lens(wfo, fl_eye, "eye") quicklook_wf(wfo) # End (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
def wfs_measurement(wfo, iter, iw, r0): #, obj_map, wfs_sample): # print 'Including WFS Error' # quicklook_wf(wfo) # quicklook_im(obj_map, show=False, logAmp=False) with open(iop.CPA_meas, 'rb') as handle: CPA_maps, iters = pickle.load(handle) # quicklook_im(proper.prop_get_phase(wfo), logAmp=False) # quicklook_im(CPA_maps[0,iw], logAmp=False) # if iter < 20 or iter > 35: import scipy.ndimage sigma = [1, 1] # dmap = scipy.ndimage.filters.gaussian_filter(dmap, sigma, mode='constant') from skimage.restoration import unwrap_phase CPA_maps[0, iw] += scipy.ndimage.filters.gaussian_filter(unwrap_phase( proper.prop_get_phase(wfo)), sigma, mode='constant') # CPA_maps[0, iw] += proper.prop_get_phase(wfo) # quicklook_im(CPA_maps[0,iw], logAmp=False) # for d in [1]:#range(0,2,1): # quicklook_im(CPA_maps[0], logAmp=False) # CPA_maps[0,iw] = unwrap_phase(CPA_maps[0,iw]) # quicklook_im(CPA_maps[0], logAmp=False) # CPA_maps[0] += proper.prop_get_phase(wfo) # loop_frames(CPA_maps, logAmp=False) # # print tp.servo_error if tp.servo_error: # print 'This might produce garbage if several processes are run in parrallel' # loop_frames(tp.obj_maps, logAmp=False) CPA_maps[:, iw] = np.roll(CPA_maps[:, iw], 1, 0) # tp.obj_maps[0] = CPA_maps[0] # if tp.servo_lag: required_servo = int(tp.servo_error[0]) # delay # obj_map = tp.obj_maps[required_servo] required_band = int(tp.servo_error[1]) # averaging # if tp.wfs_bandwidth_error: CPA_maps[0, iw] = np.sum(CPA_maps[required_servo:-1, iw], axis=0) / required_band # loop_frames(tp.obj_maps, logAmp=False) # print obj_map # quicklook_im(obj_map, logAmp=False) # if tp.piston_error: # # from Utils.misc import debprint # # var = 0.27*(tp.diam/(r0 * tp.ao_act))*(5/3) # var = 0.001#1e-11 #0.1 wavelengths 0.1*1000e-9 # # debprint(var) # CPA_maps[0,iw] = CPA_maps[0,iw] + np.random.normal(0,var,(CPA_maps[0,iw].shape[0],CPA_maps[0,iw].shape[1])) # if tp.wfs_measurement_error: # '''Downsample subarrays and then convert back to original size (interpolation might # be better than repeats''' # # print '*** not used in a while! ***' # def sub_sums_ophion(arr, nrows, ncols): # h, w = arr.shape # h = (h // nrows)*nrows # w = (w // ncols)*ncols # arr = arr[:h,:w] # return np.einsum('ijkl->ik', arr.reshape(h // nrows, nrows, -1, ncols)) # # down_sample = sub_sums_ophion(obj_map, tp.wfs_sample, tp.wfs_sample) / tp.wfs_sample**2 # obj_map = np.repeat(np.repeat(down_sample,tp.wfs_sample, axis=0), tp.wfs_sample, axis=1) # return obj_map # if tp.active_null: # with open(iop.NCPA_meas, 'rb') as handle: # _, NCPA_map, _ = pickle.load(handle) # # quicklook_im(CPA_map, logAmp=False) # # dprint('CPA_map') # # quicklook_im(NCPA_map, logAmp=False) # # dprint('NCPA_map') # CPA_maps[0] += NCPA_map # # quicklook_im(CPA_map, logAmp=False) with open(iop.CPA_meas, 'wb') as handle: pickle.dump((CPA_maps, iters + 1), handle, protocol=pickle.HIGHEST_PROTOCOL)
def speck_killing_loop(wfo): #configfilename = 'speckle_null_config.ini' #config = ConfigObj(configfilename) configfilename = tp.FPWFSdir + 'speckle_null_config_Rupe.ini' hardwareconfigfile = tp.FPWFSdir + 'speckle_instruments.ini' configspecfile = tp.FPWFSdir + 'speckle_null_config.spec' print(configfilename) config = ConfigObj(configfilename, configspec=configspecfile) val = Validator() check = config.validate(val) #pharo = hardware.PHARO_COM('PHARO', # configfile = hardwareconfigfile) #p3k = hardware.P3K_COM('P3K_COM', configfile = hardwareconfigfile) camera = hardware.camera() ao = hardware.ao() apmask = False if not apmask: aperturemask = np.ones((66, 66)) if apmask: aperturemask = dm.annularmask(66, 12, 33) # nulled_field = None im_params = config['IM_PARAMS'] null_params = config['NULLING'] abc = config['INTENSITY_CAL']['abc'] use_centoffs = config['NULLING']['cent_off'] #bgds = flh.setup_bgd_dict(config) fake_bgds = { 'bkgd': np.zeros((tp.grid_size, tp.grid_size)), 'masterflat': np.ones((tp.grid_size, tp.grid_size)), 'badpix': np.zeros((tp.grid_size, tp.grid_size)) } print("WARNING: USING FAKE BGDS") bgds = fake_bgds.copy() # controlregion = pf.open(tp.FPWFSdir+config['CONTROLREGION']['filename'])[0].data # controlregion = controlregion[512-int(tp.grid_size/2):512+int(tp.grid_size/2), 512-int(tp.grid_size/2):512+int(tp.grid_size/2)] # controlregion = np.roll(controlregion, 15) # controlregion[:,0:70] = 0 # controlregion[:80] = 0 # controlregion[-80:] = 0 controlregion = np.zeros((tp.grid_size, tp.grid_size)) controlregion[50:80, 35:50] = 1 boarder = get_ctrlrgnBoarder(controlregion) quicklook_im(controlregion, logAmp=False) # plt.show(block=True) #Notes==>scale exptime in snr exp = config['INTENSITY_CAL']['exptime'] #Setup # initial_flatmap = ao.grab_current_flatmap() # initial_centoffs= ao.grab_current_centoffs() defaultim = np.ones((tp.grid_size, tp.grid_size)) vertsx = config['CONTROLREGION']['verticesx'] vertsy = config['CONTROLREGION']['verticesy'] # plt.ion() fig = plt.figure(figsize=(12, 12)) ax1 = plt.subplot2grid((4, 4), (0, 0), rowspan=2, colspan=2) ax2 = plt.subplot2grid((4, 4), (0, 2), rowspan=2, colspan=2) ax3 = plt.subplot2grid((4, 4), (3, 0)) ax4 = plt.subplot2grid((4, 4), (2, 2), rowspan=2, colspan=2) #ax5 = plt.subplot2grid((4,4),(3,2)) # ax1b =plt.subplot2grid((4,4),(0, 0), rowspan =2, colspan = 2) title = fig.suptitle('Speckle destruction') ax1.set_title('Image') ax2.set_title('Control region') ax3.set_title('RMS in region') ax4.set_title('Image') #ax5.set_title('Intensity') w1 = ax1.imshow(np.log10(np.abs(defaultim)) + boarder, origin='lower', interpolation='nearest') current_cmap = cm.get_cmap() current_cmap.set_bad(color='white') # w1b = ax1b.imshow(boarder*10, origin='lower', interpolation = 'none') # ax1.set_xlim(min(vertsx), max(vertsx)) # ax1.set_ylim(min(vertsy), max(vertsy)) #ax1.set_ylim(int(im_params['centery'])-25, int(im_params['centery']+50)) w2 = ax2.imshow(np.log(np.abs(controlregion * defaultim)), origin='lower', interpolation='nearest') # ax2.set_xlim(min(vertsx), max(vertsx)) # ax2.set_ylim(min(vertsy), max(vertsy)) w3 = ax3.plot([], []) ax3.set_xlim(0, 10) w4, = ax4.plot(np.abs(defaultim[64])) # w4 = ax4.imshow(np.log10(np.abs(defaultim))+boarder, origin='lower', interpolation = 'nearest') current_cmap = cm.get_cmap() current_cmap.set_bad(color='white') N_iterations = 10 itcounter = [] maxfluxes = [] meanfluxes = [] totalfluxes = [] rmsfluxes = [] #w5 = ax5.plot(np.arange(10)*0, np.arange(10)*0) # w4 = ax4.imshow(np.log(camera.take_src_return_imagedata(exptime =exp)[242:758, 242:758]), origin='lower', interpolation = 'nearest') plt.show() tstamp = time.strftime("%Y%m%d-%H%M%S").replace(' ', '_') cubeoutputdir = os.path.join(null_params['outputdir'], tstamp) if not os.path.exists(cubeoutputdir): os.makedirs(cubeoutputdir) print(configfilename) result_imagecube = output_imagecube(N_iterations, tp.grid_size, filepath=os.path.join( cubeoutputdir, 'test_' + tstamp + '.fits'), comment='fun', configfile=configfilename) clean_imagecube = output_imagecube(N_iterations, tp.grid_size, filepath=os.path.join( cubeoutputdir, 'test_clean_' + tstamp + '.fits'), comment='fun', configfile=configfilename) cal_imagecube = output_imagecube(4, tp.grid_size, filepath=os.path.join( cubeoutputdir, 'test_cals_' + tstamp + '.fits'), comment='fun', configfile=configfilename) cal_imagecube.update(controlregion) cal_imagecube.update(bgds['bkgd']) cal_imagecube.update(bgds['masterflat']) cal_imagecube.update(bgds['badpix']) ####MAIN LOOP STARTS HERE##### print("BEGINNING NULLING LOOP") for iteration in range(N_iterations): # quicklook_wf(wfo) itcounter.append(iteration) if use_centoffs == False: current_flatmap = ao.grab_current_flatmap() if use_centoffs == True: current_centoffs = ao.grab_current_centoffs() # quicklook_im(current_flatmap, logAmp=False, show=True) print("Taking image of speckle field") # if nulled_field != None: # raw_im = nulled_field # else: raw_im = camera.take_src_return_imagedata(wfo, exptime=exp) result_imagecube.update(raw_im) field_im = pre.equalize_image(raw_im, **bgds) clean_imagecube.update(field_im) field_ctrl = field_im * controlregion # print np.shape(field_im), np.shape(controlregion), np.shape(field_ctrl) # plt.figure() # plt.imshow(field_im) # plt.figure() # plt.imshow(controlregion) # plt.figure() # plt.imshow(field_ctrl) # plt.show(block=True) meanfluxes.append(np.mean(field_ctrl[field_ctrl > 0])) maxfluxes.append(np.max(field_ctrl[field_ctrl > 0])) totalfluxes.append(np.sum(field_ctrl)) rmsfluxes.append( np.std(field_ctrl[field_ctrl > 0]) / config['NULLING']['referenceval']) ax3.plot(itcounter, rmsfluxes) #w5 = plt.plot(itcounter, maxfluxes) #w5 = plt.plot(itcounter, totalfluxes) ax1.set_title('Iteration: ' + str(iteration) + ', Mean: ' + str(meanfluxes[iteration]) + ', Max: ' + str(maxfluxes[iteration])) w1.set_data(np.log(np.abs(field_ctrl)) + boarder) w1.autoscale() plt.draw() plt.pause(0.02) if iteration > 0: try: printstats(field_im, speckleslist) except: pass flh.writeout(current_flatmap, 'latestiteration.fits') # ans = raw_input('Do you want to run a speckle nulling iteration[Y/N]?') ans = 'Y' if ans == 'N': flatmapans = eval( input('Do you want to reload the' + 'flatmap/centoffs you started with[Y/N]?')) if flatmapans == 'Y': print("Reloading initial flatmap/centoffs") if use_centoffs == False: status = ao.load_new_flatmap((initial_flatmap)) if use_centoffs == True: status = ao.load_new_centoffs((initial_centoffs)) #ao.load_new_flatmap(initial_flatmap) break print(('Iteration ' + str(iteration) + ' total_intensity: ' + str(np.sum(field_ctrl)))) #return a list of points print("computing interesting bright spots") #note indices and coordinates are reversed ijofinterest = identify_bright_points(field_ctrl, controlregion) xyofinterest = [p[::-1] for p in ijofinterest] print(("computed ", str(len(xyofinterest)), " bright spots")) max_specks = config['DETECTION']['max_speckles'] #if len(xyofinterest)>50: # xyofinterest = xyofinterest[0:49] if len(xyofinterest) < max_specks: max_specks = len(xyofinterest) fps = filterpoints(xyofinterest, max=max_specks, rad=config['NULLING']['exclusionzone']) print(fps) print("creating speckle objects") speckleslist = [speckle(field_im, xy[0], xy[1], config) for xy in fps] speckleslist = [x for x in speckleslist if (x.intensity) > 0] #old way--filter the speckles #speckleslist =[speckle(field_im, xy[0], xy[1], config) for xy in xyofinterest] #speckleslist = filterspeckles(speckleslist, max = max_specks) phases = null_params['phases'] # phases = [-np.pi/2.,0,np.pi/2.,np.pi] for idx, phase in enumerate(phases): print(("Phase ", phase)) phaseflat = 0 allspeck_aps = 0 #put in sine waves at speckle locations for speck in speckleslist: #XXX # phaseflat= phaseflat+speck.generate_flatmap(phase) phaseflat = speck.generate_flatmap(phase) # quicklook_im(speck.generate_flatmap(phase), logAmp=False) allspeck_aps = allspeck_aps + speck.aperture print('here') ax2.set_title('Phase: ' + str(phase)) if idx == 0: # w1.set_data( (allspeck_aps*field_ctrl)-0.95*field_ctrl) w1.set_data( np.log(np.abs((allspeck_aps * field_im) - 0.95 * field_im)) + boarder) w1.autoscale() plt.draw() #w1.set_data(field_ctrl); w1.autoscale(); plt.draw() phaseflat = phaseflat * aperturemask # plt.figure() # plt.imshow(allspeck_aps) # plt.show() # ans = raw_input('press enter') wf_temp = copy.copy(wfo) if use_centoffs == False: status = ao.load_new_flatmap(current_flatmap + phaseflat, wf_temp) # if use_centoffs == True: # status = ao.load_new_centoffs(current_centoffs+ # fmf.convert_flatmap_centoffs(phaseflat)) tp.variable = proper.prop_get_phase(wf_temp)[20, 20] print(('speck phase', tp.variable, 'intensity', proper.prop_get_amplitude(wf_temp)[20, 20])) # plt.show(block=True) # quicklook_wf(wf_temp, show=True) phaseim = camera.take_src_return_imagedata(wf_temp, exptime=exp) # quicklook_im(phaseim, show=True) phaseim = pre.equalize_image(phaseim, **bgds) # quicklook_im(phaseim, show=True) w2.set_data(np.log(np.abs(phaseim * controlregion))) w2.autoscale() plt.draw() plt.pause(0.02) # w4.set_data(range(128), np.sum(np.eye(tp.grid_size)*proper.prop_get_amplitude(wf_temp),axis=1))#ax4.plot(range(128), proper.prop_get_amplitude(wf_temp)[20])#np.abs(field_im[20]))#+boarder) w4.set_data( list(range(128)), proper.prop_get_amplitude(wf_temp)[64] ) # ax4.plot(range(128), proper.prop_get_amplitude(wf_temp)[20])#np.abs(field_im[20]))#+boarder) ax4.set_xlim([0, 128]) ax4.set_ylim([0, 0.2]) print("recomputing intensities") for speck in speckleslist: phase_int = speck.recompute_intensity(phaseim) speck.phase_intensities[idx] = phase_int print((speck.phase_intensities)) time.sleep(3) # if use_centoffs == False: # ao.load_new_flatmap(current_flatmap, wf_temp) # # if use_centoffs == True: # # ao.load_new_centoffs(current_centoffs) if config['NULLING']['null_gain'] == False: defaultgain = config['NULLING']['default_flatmap_gain'] nullmap = generate_phase_nullmap(speckleslist, defaultgain, phases) nullmap = nullmap * aperturemask if use_centoffs == False: # ao.load_new_flatmap(current_flatmap + nullmap, wfo) ao.load_new_flatmap(nullmap, wfo) # FPWFS.quicklook_wf(wfo) # camera.take_src_return_imagedata(wfo, exptime=exp) # if use_centoffs == True: # ao.load_new_centoffs(current_centoffs+ fmf.convert_flatmap_centoffs(nullmap)) # ans = raw_input('press enter') if config['NULLING']['null_gain'] == True: ##NOW CALCULATE GAIN NULLS print("DETERMINING NULL GAINS") gains = config['NULLING']['amplitudegains'] for idx, gain in enumerate(gains): print("Checking optimal gains") nullmap = generate_phase_nullmap(speckleslist, gain, phases) nullmap = nullmap * aperturemask wf_temp = copy.copy(wfo) if use_centoffs == False: ao.load_new_flatmap(current_flatmap + nullmap, wf_temp) # if use_centoffs == True: # ao.load_new_centoffs(current_centoffs+ fmf.convert_flatmap_centoffs(nullmap)) ampim = camera.take_src_return_imagedata(wf_temp, exptime=exp) # quicklook_wf(wf_temp) ampim = pre.equalize_image(ampim, **bgds) w2.set_data(np.log(np.abs(ampim * controlregion))) ax2.set_title('Gain: ' + str(gain)) w2.autoscale() plt.draw() plt.pause(0.02) for speck in speckleslist: amp_int = speck.recompute_intensity(ampim) speck.gain_intensities[idx] = amp_int print((speck.gain_intensities)) w4.set_data( list(range(128)), proper.prop_get_amplitude(wf_temp)[64] ) #ax4.plot(range(128), proper.prop_get_amplitude(wf_temp)[20])#np.abs(field_im[20]))#+boarder) ax4.set_xlim([0, 128]) ax4.set_ylim([0, 0.2]) for speck in speckleslist: speck.compute_null_gain() supernullmap = generate_super_nullmap(speckleslist, phases) print( "Loading supernullmap now that optimal gains have been found!") supernullmap = supernullmap * aperturemask if use_centoffs == False: # ao.load_new_flatmap(current_flatmap + supernullmap, wfo) ao.load_new_flatmap(supernullmap, wfo) # FPWFS.quicklook_wf(wfo) # quicklook_im(supernullmap,logAmp=False, show=True) # camera.take_src_return_imagedata(wfo, exptime=exp) # if use_centoffs == True: # ao.load_new_centoffs(current_centoffs+ fmf.convert_flatmap_centoffs(supernullmap)) #ao.load_new_flatmap(supernullmap) # w3.set_data(nullmap) # plt.draw() # w4.set_data(np.log(np.abs(field_im))+boarder) # plt.draw() # w4.autoscale(); # quicklook_im(field_im, logAmp=False) quicklook_wf(wfo) plt.show(block=True) # ans = raw_input('press enter') # try: # check = raw_input("would you like to continue?: ") # except EOFError: # print ("Error: EOF or empty input!") # check = "" # print check plt.show(block=False)
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 scexao_model(lmda, grid_size, kwargs): """ propagates instantaneous complex E-field thru Subaru from the DM through SCExAO uses PyPROPER3 to generate the complex E-field at the pupil plane, then propagates it through SCExAO 50x50 DM, then coronagraph, to the focal plane :returns spectral cube at instantaneous time in the focal_plane() """ # print("Propagating Broadband Wavefront Through Subaru") # Initialize the Wavefront in Proper wfo = proper.prop_begin(entrance_d, lmda, grid_size, beam_ratio) # Defines aperture (baffle-before primary) proper.prop_circular_aperture(wfo, entrance_d / 2) proper.prop_define_entrance(wfo) # normalizes abs intensity # Test Sampling if kwargs['verbose'] and kwargs['ix'] == 0: check1 = proper.prop_get_sampling(wfo) print( f"\n\tDM Pupil Plane\n" f"sampling at aperture is {check1 * 1e3:.4f} mm\n" f"Total Sim is {check1 * 1e3 * grid_size:.2f}x{check1 * 1e3 * grid_size:.2f} mm\n" f"Diameter of beam is {check1 * 1e3 * grid_size * beam_ratio:.4f} mm over {grid_size * beam_ratio} pix" ) # SCExAO Reimaging 1 proper.prop_lens( wfo, fl_SxOAPG) # produces f#14 beam (approx exit beam of AO188) proper.prop_propagate(wfo, fl_SxOAPG * 2) # move to second pupil if kwargs['verbose'] and kwargs['ix'] == 0: print(f"initial f# is {proper.prop_get_fratio(wfo):.2f}\n") ######################################## # Import/Apply Actual DM Map # ####################################### plot_flag = False if kwargs['verbose'] and kwargs['ix'] == 0: plot_flag = True dm_map = kwargs['map'] errormap(wfo, dm_map, SAMPLING=dm_pitch, MIRROR_SURFACE=True, MASKING=True, BR=beam_ratio, PLOT=plot_flag) # MICRONS=True if kwargs['verbose'] and kwargs['ix'] == 0: fig, subplot = plt.subplots(nrows=1, ncols=2, figsize=(12, 5)) ax1, ax2 = subplot.flatten() fig.suptitle('SCExAO Model WFO after errormap', fontweight='bold', fontsize=14) ax1.imshow(proper.prop_get_amplitude(wfo), interpolation='none' ) # np.abs(proper.prop_shift_center(wfo.wfarr))**2 ax1.set_title('Amplitude') ax2.imshow( proper.prop_get_phase(wfo), interpolation= 'none', # np.angle(proper.prop_shift_center(wfo.wfarr)) vmin=-1 * np.pi, vmax=1 * np.pi, cmap='hsv') # , cmap='hsv' ax2.set_title('Phase') # ------------------------------------------------ # SCExAO Reimaging 2 proper.prop_lens(wfo, fl_SxOAPG) proper.prop_propagate(wfo, fl_SxOAPG) # focus at exit of DM telescope system proper.prop_lens(wfo, fl_SxOAPG) proper.prop_propagate(wfo, fl_SxOAPG) # focus at exit of DM telescope system # # Coronagraph SubaruPupil(wfo) # focal plane mask # if kwargs['verbose'] and kwargs['ix']==0: # fig, subplot = plt.subplots(nrows=1, ncols=2, figsize=(12, 5)) # ax1, ax2 = subplot.flatten() # fig.suptitle('SCExAO Model WFO after FPM', fontweight='bold', fontsize=14) # # ax.imshow(dm_map, interpolation='none') # ax1.imshow(np.abs(proper.prop_shift_center(wfo.wfarr))**2, interpolation='none', norm=LogNorm(vmin=1e-7,vmax=1e-2)) # ax1.set_title('Amplitude') # ax2.imshow(np.angle(proper.prop_shift_center(wfo.wfarr)), interpolation='none', # vmin=-2*np.pi, vmax=2*np.pi, cmap='hsv') # , cmap='hsv' # ax2.set_title('Phase') proper.prop_propagate(wfo, fl_SxOAPG) proper.prop_lens(wfo, fl_SxOAPG) proper.prop_propagate(wfo, fl_SxOAPG) # middle of 2f system proper.prop_circular_aperture(wfo, lyot_size, NORM=True) # lyot stop proper.prop_propagate(wfo, fl_SxOAPG) # proper.prop_lens(wfo, fl_SxOAPG) # exit lens of gaussian telescope proper.prop_propagate(wfo, fl_SxOAPG) # to focus # MEC Pickoff reimager. proper.prop_propagate(wfo, mec_parax_fl) # to another pupil proper.prop_lens( wfo, mec_parax_fl) # collimating lens, pupil size should be 8 mm proper.prop_propagate(wfo, mec1_fl + .0142557) # mec1_fl .054 mec1_fl+.0101057 # if kwargs['verbose'] and kwargs['ix']==0: # current = proper.prop_get_beamradius(wfo) # print(f'Beam Radius after SCExAO exit (at MEC foreoptics entrance) is {current*1e3:.3f} mm\n' # f'current f# is {proper.prop_get_fratio(wfo):.2f}\n') # ################################## # MEC Optics Box # ################################### proper.prop_circular_aperture(wfo, 0.00866) # reading off the zemax diameter proper.prop_lens(wfo, mec1_fl) # MEC lens 1 proper.prop_propagate(wfo, mec_l1_l2) # there is a image plane at z=mec1_fl proper.prop_lens(wfo, mec2_fl) # MEC lens 2 (tiny lens) proper.prop_propagate(wfo, mec_l2_l3) proper.prop_lens(wfo, mec3_fl) # MEC lens 3 proper.prop_propagate(wfo, mec3_fl, TO_PLANE=False) # , TO_PLANE=True mec_l3_focus # ####################################### # Focal Plane # ####################################### # Check Sampling in focal plane # shifts wfo from Fourier Space (origin==lower left corner) to object space (origin==center) # wf, samp = proper.prop_end(wfo, NoAbs=True) wf = proper.prop_shift_center(wfo.wfarr) wf = extract_center(wf, new_size=np.array(kwargs['psf_size'])) samp = proper.prop_get_sampling(wfo) smp_asec = proper.prop_get_sampling_arcsec(wfo) if kwargs['verbose'] and kwargs['ix'] == 0: fig, subplot = plt.subplots(nrows=1, ncols=2, figsize=(12, 5)) fig.subplots_adjust(left=0.08, hspace=.4, wspace=0.2) ax1, ax2 = subplot.flatten() fig.suptitle('SCExAO Model Focal Plane', fontweight='bold', fontsize=14) tic_spacing, tic_labels, axlabel = scale_lD( wfo, newsize=kwargs['psf_size'][0]) tic_spacing[0] = tic_spacing[0] + 1 # hack for edge effects tic_spacing[-1] = tic_spacing[-1] - 1 # hack for edge effects im = ax1.imshow( np.abs(wf)**2, interpolation='none', norm=LogNorm( vmin=1e-7, vmax=1e-2)) # np.abs(proper.prop_shift_center(wfo.wfarr))**2 ax1.set_xticks(tic_spacing) ax1.set_xticklabels(tic_labels) ax1.set_yticks(tic_spacing) ax1.set_yticklabels(tic_labels) ax1.set_ylabel(axlabel, fontsize=8) add_colorbar(im) im = ax2.imshow(np.angle(wf), interpolation='none', vmin=-np.pi, vmax=np.pi, cmap='hsv') ax2.set_xticks(tic_spacing) ax2.set_xticklabels(tic_labels) ax2.set_yticks(tic_spacing) ax2.set_yticklabels(tic_labels) ax2.set_ylabel(axlabel, fontsize=8) add_colorbar(im) if kwargs['verbose'] and kwargs['ix'] == 0: print( f"\nFocal Plane\n" f"sampling at focal plane is {samp*1e6:.1f} um ~= {smp_asec * 1e3:.4f} mas\n" f"\tfull FOV is {samp * kwargs['psf_size'][0] * 1e3:.2f} x {samp * kwargs['psf_size'][1] * 1e3:.2f} mm " ) # s_rad = proper.prop_get_sampling_radians(wfo) # print(f"sampling at focal plane is {s_rad * 1e6:.6f} urad") print(f'final focal ratio is {proper.prop_get_fratio(wfo)}') print(f"Finished simulation") return wf, samp
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