def get_wf(wavelength, gridsize, PASSVALUE={}): 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.99) # 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) beam_ratio = 0.99 # Define the wavefront wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) # Point off-axis prop_tilt(wfo, tilt_x, tilt_y) # Input aperture proper.prop_circular_aperture(wfo, diam / 2.) # Define entrance proper.prop_define_entrance(wfo) proper.prop_lens(wfo, m1_fl, "primary") 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, False)) wf = proper.prop_get_wavefront(wfo) return wf
def simple_telescope(wavelength, gridsize): d_objective = 0.060 fl_objective = 15.0 * d_objective fl_eyepiece = 0.021 fl_eye = 0.022 beam_ratio = 0.5 wfo = proper.propbegin(d_objective, wavelength, gridsize, beam_ratio) proper.prop_circular_aperture(wfo, d_objective / 2) proper.prop_define_entrance(wfo) proper.prop_lens(wfo, fl_objective, "objective") proper.prop_propagate(wfo, fl_objective + fl_eyepiece, "eyepiece") proper.prop_lens(wfo, fl_eyepiece, "eyepiece") exit_pupil_distance = fl_eyepiece / (1 - fl_eyepiece / (fl_objective + fl_eyepiece)) proper.prop_propagate(wfo, exit_pupil_distance, "exit pupil at eye lens:") proper.prop_lenx(wfo, fl_eye, "eye") proper.prop_propagate(wfo, fl_eye, "retina") (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
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 falco_gen_annular_FPM(pixresFPM, rhoInner, rhoOuter, FPMampFac, centering, rot180=False): dxiUL = 1.0 / pixresFPM # lambda_c/D per pixel. "UL" for unitless if np.isinf(rhoOuter): if centering == "interpixel": # number of points across the inner diameter of the FPM. Narray = utils.ceil_even((2 * rhoInner / dxiUL)) else: # number of points across the inner diameter of the FPM. Another half pixel added for pixel-centered masks. Narray = utils.ceil_even(2 * (rhoInner / dxiUL + 0.5)) else: if centering == "interpixel": # number of points across the outer diameter of the FPM. Narray = utils.ceil_even(2 * rhoOuter / dxiUL) else: # number of points across the outer diameter of the FPM. Another half pixel added for pixel-centered masks. Narray = utils.ceil_even(2 * (rhoOuter / dxiUL + 0.5)) xshift = 0 # translation in x of FPM (in lambda_c/D) yshift = 0 # translation in y of FPM (in lambda_c/D) Darray = Narray * dxiUL # width of array in lambda_c/D diam = Darray wl_dummy = 1e-6 # wavelength (m); Dummy value--no propagation here, so not used. if centering == "interpixel": cshift = -diam / 2 / Narray elif rot180: cshift = -diam / Narray else: cshift = 0 wf = proper.prop_begin(diam, wl_dummy, Narray, 1.0) if not np.isinf(rhoOuter): # Outer opaque ring of FPM cx_OD = 0 + cshift + xshift cy_OD = 0 + cshift + yshift proper.prop_circular_aperture(wf, rhoOuter, cx_OD, cy_OD) # Inner spot of FPM (Amplitude transmission can be nonzero) ra_ID = (rhoInner) cx_ID = 0 + cshift + xshift cy_ID = 0 + cshift + yshift innerSpot = proper.prop_ellipse( wf, rhoInner, rhoInner, cx_ID, cy_ID, DARK=True) * (1 - FPMampFac) + FPMampFac mask = np.fft.ifftshift(np.abs(wf.wfarr)) # undo PROPER's fftshift return mask * innerSpot # Include the inner FPM spot
def falco_gen_DM_stop(dx, Dmask, centering): diam = Dmask # diameter 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 cshift = -dx / 2 * (centering == "interpixel") # Outer diameter of aperture proper.prop_circular_aperture(wf, diam / 2, cshift, cshift) return np.fft.ifftshift(np.abs(wf.wfarr))
def initialize_proper(self, set_up_beam=False): """ Initialize the Wavefronts in Proper :param set_up_beam: bool applies prop_circular_aperture and prop_define_entrance before spectral scaling instead of during prescription wher it normally goes returns wf_colllection attribute array of wavefronts """ for iw, wavelength in enumerate(self.wsamples): # Scale beam ratio by wavelength for polychromatic imaging # see Proper manual pg 37 # Proper is devised such that you get a Nyquist sampled image in the focal plane. If you optical system # goes directly from pupil plane to focal plane, then you need to scale the beam ratio such that sampling # in the focal plane is constant. You can check this with check_sampling, which returns the value from # prop_get_sampling. If the optical system does not go directly from pupil-to-object plane at each optical # plane, the beam ratio does not need to be scaled by wavelength, because of some optics wizardry that # I don't fully understand. KD 2019 if sp.focused_sys: beam_ratio = sp.beam_ratio else: beam_ratio = sp.beam_ratio * ap.wvl_range[0] / wavelength # dprint(f"iw={iw}, w={w}, beam ratio is {self.beam_ratios[iw]}") # Initialize the wavefront at entrance pupil wfp = proper.prop_begin(tp.entrance_d, wavelength, sp.grid_size, beam_ratio) if set_up_beam: proper.prop_circular_aperture(wfp, radius = tp.entrance_d / 2) proper.prop_define_entrance(wfp) # normalizes the intensity wfp.wfarr = np.multiply(wfp.wfarr, np.sqrt(self.spectra[0][iw]), out=wfp.wfarr, casting='unsafe') wfs = [wfp] names = ['star'] # Initiate wavefronts for companion(s) if ap.companion: for ix in range(len(ap.contrast)): wfc = proper.prop_begin(tp.entrance_d, wavelength, sp.grid_size, beam_ratio) if set_up_beam: proper.prop_circular_aperture(wfc, radius=tp.entrance_d / 2) proper.prop_define_entrance(wfc) # normalizes the intensity wfc.wfarr = np.multiply(wfc.wfarr, np.sqrt(self.spectra[ix][iw]), out=wfc.wfarr, casting='unsafe') wfs.append(wfc) names.append('companion_%i' % ix) for io, (name, wf) in enumerate(zip(names, wfs)): self.wf_collection[iw, io] = Wavefront(wf, wavelength, name, beam_ratio, iw, io)
def prefocal_image(wavelength, gridsize, PASSVAL): diam = PASSVAL['diam'] focal_length = PASSVAL['focal_length'] beam_ratio = PASSVAL['beam_ratio'] wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) proper.prop_circular_aperture(wfo, diam/2) proper.prop_define_entrance(wfo) proper.prop_zernikes(wfo, [i+1 for i in range(len(PASSVAL['ZERN']))], PASSVAL['ZERN']) #print(proper.prop_get_phase(wfo)[gridsize//2,:]) proper.prop_lens(wfo, focal_length) proper.prop_propagate(wfo, focal_length - PASSVAL['DEFOCUS'], TO_PLANE=False) (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
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, file_lyot_stop='', verbose=False, **conf): """ Add a Lyot stop for a focal plane mask """ if mode in ['CVC', 'RAVC', 'CLC']: # load lyot stop from file if provided if os.path.isfile(file_lyot_stop): if verbose is True: print(" apply lyot stop from '%s'"%os.path.basename(file_lyot_stop)) # get amplitude and phase data ls_mask = fits.getdata(file_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))) return wf
def simple_telescope(wavelength, gridsize): diam = 1.0 focal_ratio = 15.0 focal_length = diam * focal_ratio beam_ratio = 0.5 wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) proper.prop_circular_aperture(wfo, diam / 2) proper.prop_zernikes(wfo, [5], [1e-6]) proper.prop_define_entrance(wfo) proper.prop_lens(wfo, focal_length * 0.98) proper.prop_propagate(wfo, focal_length) (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
def lens(wf, focal=660, offset_before=0, offset_after=0, offset_light_trap=0, diam_light_trap=0, **conf): # propagation before lens proper.prop_propagate(wf, focal + offset_before) # Fourier transform of an image using a lens proper.prop_lens(wf, focal) # check for light trap if offset_light_trap == 0 and diam_light_trap == 0: # propagation after lens proper.prop_propagate(wf, focal + offset_after) else: # add light trap proper.prop_propagate(wf, focal - offset_light_trap) proper.prop_circular_aperture(wf, diam_light_trap, 0, 0, NORM=True) proper.prop_propagate(wf, offset_light_trap + offset_after)
def coronagraph(wfo, f_lens, occulter_type, diam): proper.prop_lens(wfo, f_lens, "coronagraph imaging lens") proper.prop_propagate(wfo, f_lens, "occulter") # occulter sizes are specified here in units of lambda/diameter; # convert lambda/diam to radians then to meters lamda = proper.prop_get_wavelength(wfo) occrad = 4. # 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 plt.figure(figsize=(12,8)) if occulter_type == "GAUSSIAN": r = proper.prop_radius(wfo) h = np.sqrt(-0.5 * occrad_m**2 / np.log(1 - np.sqrt(0.5))) gauss_spot = 1 - np.exp(-0.5 * (r/h)**2) proper.prop_multiply(wfo, gauss_spot) plt.suptitle("Gaussian spot", fontsize = 18) elif occulter_type == "SOLID": proper.prop_circular_obscuration(wfo, occrad_m) plt.suptitle("Solid spot", fontsize = 18) elif occulter_type == "8TH_ORDER": proper.prop_8th_order_mask(wfo, occrad, CIRCULAR = True) plt.suptitle("8th order band limited spot", fontsize = 18) # 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") proper.prop_propagate(wfo, f_lens, "pupil reimaging lens") proper.prop_lens(wfo, f_lens, "pupil reimaging lens") proper.prop_propagate(wfo, 2*f_lens, "lyot stop") 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() if occulter_type == "GAUSSIAN": proper.prop_circular_aperture(wfo, 0.25, NORM = True) elif occulter_type == "SOLID": proper.prop_circular_aperture(wfo, 0.84, NORM = True) elif occulter_type == "8TH_ORDER": proper.prop_circular_aperture(wfo, 0.50, NORM = True) proper.prop_propagate(wfo, f_lens, "reimaging lens") proper.prop_lens(wfo, f_lens, "reimaging lens") proper.prop_propagate(wfo, f_lens, "final focus") return
def prescription_quad_tiltafter(wavelength, gridsize, PASSVALUE = {'diam': 0.3, 'm1_fl': 0.5717255, 'beam_ratio': 0.2, 'tilt_x': 0.0, 'tilt_y': 0.0 }): diam = PASSVALUE['diam'] # telescope diameter in meters m1_fl = PASSVALUE['m1_fl'] # primary focal length (m) beam_ratio = PASSVALUE['beam_ratio'] # initial beam width/grid width tilt_x = PASSVALUE['tilt_x'] # Tilt angle along x (arc seconds) tilt_y = PASSVALUE['tilt_y'] # Tilt angle along y (arc seconds) # Define the wavefront wfo = proper.prop_begin(diam, wavelength, gridsize, beam_ratio) # Input aperture proper.prop_circular_aperture(wfo, diam/2) # Define entrance proper.prop_define_entrance(wfo) # Primary mirror (treat as quadratic lens) proper.prop_lens(wfo, m1_fl, "primary") # Point off-axis prop_tilt(wfo, tilt_x, tilt_y) # Focus proper.prop_propagate(wfo, m1_fl, "focus", TO_PLANE=True) # End (wfo, sampling) = proper.prop_end(wfo) return (wfo, sampling)
def apply_lyot(self, wf): """ applies the appropriately sized Lyot stop depending on the coronagraph type :param wf: 2D wavefront :return: """ if not hasattr(tp, 'lyot_size'): raise ValueError( "must set tp.lyot_size in units fraction of the beam radius at the current surface" ) if self.mode is 'Gaussian': proper.prop_circular_aperture(wf, tp.lyot_size, NORM=True) elif self.mode is 'Solid': proper.prop_circular_aperture(wf, tp.lyot_size, NORM=True) elif self.mode is '8th_Order': proper.prop_circular_aperture(wf, tp.lyot_size, NORM=True)
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 wfirst_phaseb(lambda_m, output_dim0, PASSVALUE={'dummy': 0}): # "output_dim" is used to specify the output dimension in pixels at the final image plane. # Computational grid sizes are hardcoded for each coronagraph. # Based on Zemax prescription "WFIRST_CGI_DI_LOWFS_Sep24_2018.zmx" by Hong Tang. data_dir = wfirst_phaseb_proper.data_dir if 'PASSVALUE' in locals(): if 'data_dir' in PASSVALUE: data_dir = PASSVALUE['data_dir'] map_dir = data_dir + wfirst_phaseb_proper.map_dir polfile = data_dir + wfirst_phaseb_proper.polfile cor_type = 'hlc' # coronagraph type ('hlc', 'spc', 'none') source_x_offset_mas = 0 # source offset in mas (tilt applied at primary) source_y_offset_mas = 0 source_x_offset = 0 # source offset in lambda0_m/D radians (tilt applied at primary) source_y_offset = 0 polaxis = 0 # polarization axis aberrations: # -2 = -45d in, Y out # -1 = -45d in, X out # 1 = +45d in, X out # 2 = +45d in, Y out # 5 = mean of modes -1 & +1 (X channel polarizer) # 6 = mean of modes -2 & +2 (Y channel polarizer) # 10 = mean of all modes (no polarization filtering) use_errors = 1 # use optical surface phase errors? 1 or 0 zindex = np.array([0, 0]) # array of Zernike polynomial indices zval_m = np.array([0, 0]) # array of Zernike coefficients (meters RMS WFE) use_aperture = 0 # use apertures on all optics? 1 or 0 cgi_x_shift_pupdiam = 0 # X,Y shear of wavefront at FSM (bulk displacement of CGI); normalized relative to pupil diameter cgi_y_shift_pupdiam = 0 cgi_x_shift_m = 0 # X,Y shear of wavefront at FSM (bulk displacement of CGI) in meters cgi_y_shift_m = 0 fsm_x_offset_mas = 0 # offset in focal plane caused by tilt of FSM in mas fsm_y_offset_mas = 0 fsm_x_offset = 0 # offset in focal plane caused by tilt of FSM in lambda0/D fsm_y_offset = 0 end_at_fsm = 0 # end propagation after propagating to FSM (no FSM errors) focm_z_shift_m = 0 # offset (meters) of focus correction mirror (+ increases path length) use_hlc_dm_patterns = 0 # use Dwight's HLC default DM wavefront patterns? 1 or 0 use_dm1 = 0 # use DM1? 1 or 0 use_dm2 = 0 # use DM2? 1 or 0 dm_sampling_m = 0.9906e-3 # actuator spacing in meters dm1_xc_act = 23.5 # for 48x48 DM, wavefront centered at actuator intersections: (0,0) = 1st actuator center dm1_yc_act = 23.5 dm1_xtilt_deg = 0 # tilt around X axis (deg) dm1_ytilt_deg = 5.7 # effective DM tilt in deg including 9.65 deg actual tilt and pupil ellipticity dm1_ztilt_deg = 0 # rotation of DM about optical axis (deg) dm2_xc_act = 23.5 # for 48x48 DM, wavefront centered at actuator intersections: (0,0) = 1st actuator center dm2_yc_act = 23.5 dm2_xtilt_deg = 0 # tilt around X axis (deg) dm2_ytilt_deg = 5.7 # effective DM tilt in deg including 9.65 deg actual tilt and pupil ellipticity dm2_ztilt_deg = 0 # rotation of DM about optical axis (deg) use_pupil_mask = 1 # SPC only: use SPC pupil mask (0 or 1) mask_x_shift_pupdiam = 0 # X,Y shear of shaped pupil mask; normalized relative to pupil diameter mask_y_shift_pupdiam = 0 mask_x_shift_m = 0 # X,Y shear of shaped pupil mask in meters mask_y_shift_m = 0 use_fpm = 1 # use occulter? 1 or 0 fpm_x_offset = 0 # FPM x,y offset in lambda0/D fpm_y_offset = 0 fpm_x_offset_m = 0 # FPM x,y offset in meters fpm_y_offset_m = 0 fpm_z_shift_m = 0 # occulter offset in meters along optical axis (+ = away from prior optics) pinhole_diam_m = 0 # FPM pinhole diameter in meters end_at_fpm_exit_pupil = 0 # return field at FPM exit pupil? output_field_rootname = '' # rootname of FPM exit pupil field file (must set end_at_fpm_exit_pupil=1) use_lyot_stop = 1 # use Lyot stop? 1 or 0 lyot_x_shift_pupdiam = 0 # X,Y shear of Lyot stop mask; normalized relative to pupil diameter lyot_y_shift_pupdiam = 0 lyot_x_shift_m = 0 # X,Y shear of Lyot stop mask in meters lyot_y_shift_m = 0 use_field_stop = 1 # use field stop (HLC)? 1 or 0 field_stop_radius_lam0 = 0 # field stop radius in lambda0/D (HLC or SPC-wide mask only) field_stop_x_offset = 0 # field stop offset in lambda0/D field_stop_y_offset = 0 field_stop_x_offset_m = 0 # field stop offset in meters field_stop_y_offset_m = 0 use_pupil_lens = 0 # use pupil imaging lens? 0 or 1 use_defocus_lens = 0 # use defocusing lens? Options are 1, 2, 3, 4, corresponding to +18.0, +9.0, -4.0, -8.0 waves P-V @ 550 nm defocus = 0 # instead of specific lens, defocus in waves P-V @ 550 nm (-8.7 to 42.0 waves) final_sampling_m = 0 # final sampling in meters (overrides final_sampling_lam0) final_sampling_lam0 = 0 # final sampling in lambda0/D output_dim = output_dim0 # dimension of output in pixels (overrides output_dim0) if 'PASSVALUE' in locals(): if 'use_fpm' in PASSVALUE: use_fpm = PASSVALUE['use_fpm'] if 'cor_type' in PASSVALUE: cor_type = PASSVALUE['cor_type'] is_spc = False is_hlc = False if cor_type == 'hlc': is_hlc = True file_directory = data_dir + '/hlc_20190210/' # must have trailing "/" prefix = file_directory + 'run461_' pupil_diam_pix = 309.0 pupil_file = prefix + 'pupil_rotated.fits' lyot_stop_file = prefix + 'lyot.fits' lambda0_m = 0.575e-6 lam_occ = [ 5.4625e-07, 5.49444444444e-07, 5.52638888889e-07, 5.534375e-07, 5.55833333333e-07, 5.59027777778e-07, 5.60625e-07, 5.62222222222e-07, 5.65416666667e-07, 5.678125e-07, 5.68611111111e-07, 5.71805555556e-07, 5.75e-07, 5.78194444444e-07, 5.81388888889e-07, 5.821875e-07, 5.84583333333e-07, 5.87777777778e-07, 5.89375e-07, 5.90972222222e-07, 5.94166666667e-07, 5.965625e-07, 5.97361111111e-07, 6.00555555556e-07, 6.0375e-07 ] lam_occs = [ '5.4625e-07', '5.49444444444e-07', '5.52638888889e-07', '5.534375e-07', '5.55833333333e-07', '5.59027777778e-07', '5.60625e-07', '5.62222222222e-07', '5.65416666667e-07', '5.678125e-07', '5.68611111111e-07', '5.71805555556e-07', '5.75e-07', '5.78194444444e-07', '5.81388888889e-07', '5.821875e-07', '5.84583333333e-07', '5.87777777778e-07', '5.89375e-07', '5.90972222222e-07', '5.94166666667e-07', '5.965625e-07', '5.97361111111e-07', '6.00555555556e-07', '6.0375e-07' ] lam_occs = [ prefix + 'occ_lam' + s + 'theta6.69polp_' for s in lam_occs ] # find nearest matching FPM wavelength wlam = (np.abs(lambda_m - np.array(lam_occ))).argmin() occulter_file_r = lam_occs[wlam] + 'real.fits' occulter_file_i = lam_occs[wlam] + 'imag.fits' n_default = 1024 # gridsize in non-critical areas if use_fpm == 1: n_to_fpm = 2048 else: n_to_fpm = 1024 n_from_lyotstop = 1024 field_stop_radius_lam0 = 9.0 elif cor_type == 'hlc_erkin': is_hlc = True file_directory = data_dir + '/hlc_20190206_v3/' # must have trailing "/" prefix = file_directory + 'dsn17d_run2_pup310_fpm2048_' pupil_diam_pix = 310.0 pupil_file = prefix + 'pupil.fits' lyot_stop_file = prefix + 'lyot.fits' lambda0_m = 0.575e-6 lam_occ = [ 5.4625e-07, 5.4944e-07, 5.5264e-07, 5.5583e-07, 5.5903e-07, 5.6222e-07, 5.6542e-07, 5.6861e-07, 5.7181e-07, 5.75e-07, 5.7819e-07, 5.8139e-07, 5.8458e-07, 5.8778e-07, 5.9097e-07, 5.9417e-07, 5.9736e-07, 6.0056e-07, 6.0375e-07 ] lam_occs = [ '5.4625e-07', '5.4944e-07', '5.5264e-07', '5.5583e-07', '5.5903e-07', '5.6222e-07', '5.6542e-07', '5.6861e-07', '5.7181e-07', '5.75e-07', '5.7819e-07', '5.8139e-07', '5.8458e-07', '5.8778e-07', '5.9097e-07', '5.9417e-07', '5.9736e-07', '6.0056e-07', '6.0375e-07' ] lam_occs = [ prefix + 'occ_lam' + s + 'theta6.69pols_' for s in lam_occs ] # find nearest matching FPM wavelength wlam = (np.abs(lambda_m - np.array(lam_occ))).argmin() occulter_file_r = lam_occs[wlam] + 'real_rotated.fits' occulter_file_i = lam_occs[wlam] + 'imag_rotated.fits' n_default = 1024 # gridsize in non-critical areas if use_fpm == 1: n_to_fpm = 2048 else: n_to_fpm = 1024 n_from_lyotstop = 1024 field_stop_radius_lam0 = 9.0 elif cor_type == 'spc-ifs_short' or cor_type == 'spc-ifs_long' or cor_type == 'spc-spec_short' or cor_type == 'spc-spec_long': is_spc = True file_dir = data_dir + '/spc_20190130/' # must have trailing "/" pupil_diam_pix = 1000.0 pupil_file = file_dir + 'pupil_SPC-20190130_rotated.fits' pupil_mask_file = file_dir + 'SPM_SPC-20190130.fits' fpm_file = file_dir + 'fpm_0.05lamdivD.fits' fpm_sampling = 0.05 # sampling in fpm_sampling_lambda_m/D of FPM mask if cor_type == 'spc-ifs_short' or cor_type == 'spc-spec_short': fpm_sampling_lambda_m = 0.66e-6 lambda0_m = 0.66e-6 else: fpm_sampling_lambda_m = 0.73e-6 lambda0_m = 0.73e-6 # FPM scaled for this central wavelength lyot_stop_file = file_dir + 'LS_SPC-20190130.fits' n_default = 2048 # gridsize in non-critical areas n_to_fpm = 2048 # gridsize to/from FPM n_mft = 1400 # gridsize to FPM (propagation to/from FPM handled by MFT) n_from_lyotstop = 4096 elif cor_type == 'spc-wide': is_spc = True file_dir = data_dir + '/spc_20181220/' # must have trailing "/" pupil_diam_pix = 1000.0 pupil_file = file_dir + 'pupil_SPC-20181220_1k_rotated.fits' pupil_mask_file = file_dir + 'SPM_SPC-20181220_1000_rounded9_gray.fits' fpm_file = file_dir + 'fpm_0.05lamdivD.fits' fpm_sampling = 0.05 # sampling in lambda0/D of FPM mask fpm_sampling_lambda_m = 0.825e-6 lambda0_m = 0.825e-6 # FPM scaled for this central wavelength lyot_stop_file = file_dir + 'LS_SPC-20181220_1k.fits' n_default = 2048 # gridsize in non-critical areas n_to_fpm = 2048 # gridsize to/from FPM n_mft = 1400 n_from_lyotstop = 4096 elif cor_type == 'none': file_directory = data_dir + '/hlc_20190210/' # must have trailing "/" prefix = file_directory + 'run461_' pupil_diam_pix = 309.0 pupil_file = prefix + 'pupil_rotated.fits' lambda0_m = 0.575e-6 use_fpm = 0 use_lyot_stop = 0 use_field_stop = 0 n_default = 1024 n_to_fpm = 1024 n_from_lyotstop = 1024 else: raise Exception('ERROR: Unsupported cor_type: ' + cor_type) if 'PASSVALUE' in locals(): if 'lam0' in PASSVALUE: lamba0_m = PASSVALUE['lam0'] * 1.0e-6 if 'lambda0_m' in PASSVALUE: lambda0_m = PASSVALUE['lambda0_m'] mas_per_lamD = lambda0_m * 360.0 * 3600.0 / ( 2 * np.pi * 2.363) * 1000 # mas per lambda0/D if 'source_x_offset' in PASSVALUE: source_x_offset = PASSVALUE['source_x_offset'] if 'source_y_offset' in PASSVALUE: source_y_offset = PASSVALUE['source_y_offset'] if 'source_x_offset_mas' in PASSVALUE: source_x_offset = PASSVALUE['source_x_offset_mas'] / mas_per_lamD if 'source_y_offset_mas' in PASSVALUE: source_y_offset = PASSVALUE['source_y_offset_mas'] / mas_per_lamD if 'use_errors' in PASSVALUE: use_errors = PASSVALUE['use_errors'] if 'polaxis' in PASSVALUE: polaxis = PASSVALUE['polaxis'] if 'zindex' in PASSVALUE: zindex = np.array(PASSVALUE['zindex']) if 'zval_m' in PASSVALUE: zval_m = np.array(PASSVALUE['zval_m']) if 'end_at_fsm' in PASSVALUE: end_at_fsm = PASSVALUE['end_at_fsm'] if 'cgi_x_shift_pupdiam' in PASSVALUE: cgi_x_shift_pupdiam = PASSVALUE['cgi_x_shift_pupdiam'] if 'cgi_y_shift_pupdiam' in PASSVALUE: cgi_y_shift_pupdiam = PASSVALUE['cgi_y_shift_pupdiam'] if 'cgi_x_shift_m' in PASSVALUE: cgi_x_shift_m = PASSVALUE['cgi_x_shift_m'] if 'cgi_y_shift_m' in PASSVALUE: cgi_y_shift_m = PASSVALUE['cgi_y_shift_m'] if 'fsm_x_offset' in PASSVALUE: fsm_x_offset = PASSVALUE['fsm_x_offset'] if 'fsm_y_offset' in PASSVALUE: fsm_y_offset = PASSVALUE['fsm_y_offset'] if 'fsm_x_offset_mas' in PASSVALUE: fsm_x_offset = PASSVALUE['fsm_x_offset_mas'] / mas_per_lamD if 'fsm_y_offset_mas' in PASSVALUE: fsm_y_offset = PASSVALUE['fsm_y_offset_mas'] / mas_per_lamD if 'focm_z_shift_m' in PASSVALUE: focm_z_shift_m = PASSVALUE['focm_z_shift_m'] if 'use_hlc_dm_patterns' in PASSVALUE: use_hlc_dm_patterns = PASSVALUE['use_hlc_dm_patterns'] if 'use_dm1' in PASSVALUE: use_dm1 = PASSVALUE['use_dm1'] if 'dm1_m' in PASSVALUE: dm1_m = PASSVALUE['dm1_m'] if 'dm1_xc_act' in PASSVALUE: dm1_xc_act = PASSVALUE['dm1_xc_act'] if 'dm1_yc_act' in PASSVALUE: dm1_yc_act = PASSVALUE['dm1_yc_act'] if 'dm1_xtilt_deg' in PASSVALUE: dm1_xtilt_deg = PASSVALUE['dm1_xtilt_deg'] if 'dm1_ytilt_deg' in PASSVALUE: dm1_ytilt_deg = PASSVALUE['dm1_ytilt_deg'] if 'dm1_ztilt_deg' in PASSVALUE: dm1_ztilt_deg = PASSVALUE['dm1_ztilt_deg'] if 'use_dm2' in PASSVALUE: use_dm2 = PASSVALUE['use_dm2'] if 'dm2_m' in PASSVALUE: dm2_m = PASSVALUE['dm2_m'] if 'dm2_xc_act' in PASSVALUE: dm2_xc_act = PASSVALUE['dm2_xc_act'] if 'dm2_yc_act' in PASSVALUE: dm2_yc_act = PASSVALUE['dm2_yc_act'] if 'dm2_xtilt_deg' in PASSVALUE: dm2_xtilt_deg = PASSVALUE['dm2_xtilt_deg'] if 'dm2_ytilt_deg' in PASSVALUE: dm2_ytilt_deg = PASSVALUE['dm2_ytilt_deg'] if 'dm2_ztilt_deg' in PASSVALUE: dm2_ztilt_deg = PASSVALUE['dm2_ztilt_deg'] if 'use_pupil_mask' in PASSVALUE: use_pupil_mask = PASSVALUE['use_pupil_mask'] if 'mask_x_shift_pupdiam' in PASSVALUE: mask_x_shift_pupdiam = PASSVALUE['mask_x_shift_pupdiam'] if 'mask_y_shift_pupdiam' in PASSVALUE: mask_y_shift_pupdiam = PASSVALUE['mask_y_shift_pupdiam'] if 'mask_x_shift_m' in PASSVALUE: mask_x_shift_m = PASSVALUE['mask_x_shift_m'] if 'mask_y_shift_m' in PASSVALUE: mask_y_shift_m = PASSVALUE['mask_y_shift_m'] if 'fpm_x_offset' in PASSVALUE: fpm_x_offset = PASSVALUE['fpm_x_offset'] if 'fpm_y_offset' in PASSVALUE: fpm_y_offset = PASSVALUE['fpm_y_offset'] if 'fpm_x_offset_m' in PASSVALUE: fpm_x_offset_m = PASSVALUE['fpm_x_offset_m'] if 'fpm_y_offset_m' in PASSVALUE: fpm_y_offset_m = PASSVALUE['fpm_y_offset_m'] if 'fpm_z_shift_m' in PASSVALUE: fpm_z_shift_m = PASSVALUE['fpm_z_shift_m'] if 'pinhole_diam_m' in PASSVALUE: pinhole_diam_m = PASSVALUE['pinhole_diam_m'] if 'end_at_fpm_exit_pupil' in PASSVALUE: end_at_fpm_exit_pupil = PASSVALUE['end_at_fpm_exit_pupil'] if 'output_field_rootname' in PASSVALUE: output_field_rootname = PASSVALUE['output_field_rootname'] if 'use_lyot_stop' in PASSVALUE: use_lyot_stop = PASSVALUE['use_lyot_stop'] if 'lyot_x_shift_pupdiam' in PASSVALUE: lyot_x_shift_pupdiam = PASSVALUE['lyot_x_shift_pupdiam'] if 'lyot_y_shift_pupdiam' in PASSVALUE: lyot_y_shift_pupdiam = PASSVALUE['lyot_y_shift_pupdiam'] if 'lyot_x_shift_m' in PASSVALUE: lyot_x_shift_m = PASSVALUE['lyot_x_shift_m'] if 'lyot_y_shift_m' in PASSVALUE: lyot_y_shift_m = PASSVALUE['lyot_y_shift_m'] if 'use_field_stop' in PASSVALUE: use_field_stop = PASSVALUE['use_field_stop'] if 'field_stop_x_offset' in PASSVALUE: field_stop_x_offset = PASSVALUE['field_stop_x_offset'] if 'field_stop_y_offset' in PASSVALUE: field_stop_y_offset = PASSVALUE['field_stop_y_offset'] if 'field_stop_x_offset_m' in PASSVALUE: field_stop_x_offset_m = PASSVALUE['field_stop_x_offset_m'] if 'field_stop_y_offset_m' in PASSVALUE: field_stop_y_offset_m = PASSVALUE['field_stop_y_offset_m'] if 'use_pupil_lens' in PASSVALUE: use_pupil_lens = PASSVALUE['use_pupil_lens'] if 'use_defocus_lens' in PASSVALUE: use_defocus_lens = PASSVALUE['use_defocus_lens'] if 'defocus' in PASSVALUE: defocus = PASSVALUE['defocus'] if 'output_dim' in PASSVALUE: output_dim = PASSVALUE['output_dim'] if 'final_sampling_m' in PASSVALUE: final_sampling_m = PASSVALUE['final_sampling_m'] if 'final_sampling_lam0' in PASSVALUE: final_sampling_lam0 = PASSVALUE['final_sampling_lam0'] diam = 2.3633372 fl_pri = 2.83459423440 * 1.0013 d_pri_sec = 2.285150515460035 d_focus_sec = d_pri_sec - fl_pri fl_sec = -0.653933011 * 1.0004095 d_sec_focus = 3.580188916677103 diam_sec = 0.58166 d_sec_fold1 = 2.993753476654728 d_fold1_focus = 0.586435440022375 diam_fold1 = 0.09 d_fold1_m3 = 1.680935841598811 fl_m3 = 0.430216463069001 d_focus_m3 = 1.094500401576436 d_m3_pupil = 0.469156807701977 d_m3_focus = 0.708841602661368 diam_m3 = 0.2 d_m3_m4 = 0.943514749358944 fl_m4 = 0.116239114833590 d_focus_m4 = 0.234673014520402 d_m4_pupil = 0.474357941656967 d_m4_focus = 0.230324117970585 diam_m4 = 0.07 d_m4_m5 = 0.429145636743193 d_m5_focus = 0.198821518772608 fl_m5 = 0.198821518772608 d_m5_pupil = 0.716529242882632 diam_m5 = 0.07 d_m5_fold2 = 0.351125431220770 diam_fold2 = 0.06 d_fold2_fsm = 0.365403811661862 d_fsm_oap1 = 0.354826767220001 fl_oap1 = 0.503331895563883 diam_oap1 = 0.06 d_oap1_focm = 0.768005607094041 d_focm_oap2 = 0.314483210543378 fl_oap2 = 0.579156922073536 diam_oap2 = 0.06 d_oap2_dm1 = 0.775775726154228 d_dm1_dm2 = 1.0 d_dm2_oap3 = 0.394833855161549 fl_oap3 = 1.217276467668519 diam_oap3 = 0.06 d_oap3_fold3 = 0.505329955078121 diam_fold3 = 0.06 d_fold3_oap4 = 1.158897671642761 fl_oap4 = 0.446951159052363 diam_oap4 = 0.06 d_oap4_pupilmask = 0.423013568764728 d_pupilmask_oap5 = 0.408810648253099 fl_oap5 = 0.548189351937178 diam_oap5 = 0.06 d_oap5_fpm = 0.548189083164429 d_fpm_oap6 = 0.548189083164429 fl_oap6 = 0.548189083164429 diam_oap6 = 0.06 d_oap6_lyotstop = 0.687567667550736 d_lyotstop_oap7 = 0.401748843470518 fl_oap7 = 0.708251083480054 diam_oap7 = 0.06 d_oap7_fieldstop = 0.708251083480054 d_fieldstop_oap8 = 0.210985967281651 fl_oap8 = 0.210985967281651 diam_oap8 = 0.06 d_oap8_pupil = 0.238185804200797 d_oap8_filter = 0.368452268225530 diam_filter = 0.01 d_filter_lens = 0.170799548215162 fl_lens = 0.246017378417573 + 0.050001306014153 diam_lens = 0.01 d_lens_fold4 = 0.246017378417573 diam_fold4 = 0.02 d_fold4_image = 0.050001578514650 fl_pupillens = 0.149260576823040 n = n_default # start off with less padding wavefront = proper.prop_begin(diam, lambda_m, n, float(pupil_diam_pix) / n) pupil = proper.prop_fits_read(pupil_file) proper.prop_multiply(wavefront, trim(pupil, n)) pupil = 0 if polaxis != 0: polmap(wavefront, polfile, pupil_diam_pix, polaxis) proper.prop_define_entrance(wavefront) proper.prop_lens(wavefront, fl_pri) if source_x_offset != 0 or source_y_offset != 0: # compute tilted wavefront to offset source by xoffset,yoffset lambda0_m/D xtilt_lam = -source_x_offset * lambda0_m / lambda_m ytilt_lam = -source_y_offset * lambda0_m / lambda_m x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) proper.prop_multiply( wavefront, np.exp(complex(0, 1) * np.pi * (xtilt_lam * x + ytilt_lam * y))) x = 0 y = 0 if zindex[0] != 0: proper.prop_zernikes(wavefront, zindex, zval_m) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_PRIMARY_phase_error_V1.0.fits', WAVEFRONT=True) proper.prop_errormap( wavefront, map_dir + 'wfirst_phaseb_GROUND_TO_ORBIT_4.2X_phase_error_V1.0.fits', WAVEFRONT=True) proper.prop_propagate(wavefront, d_pri_sec, 'secondary') proper.prop_lens(wavefront, fl_sec) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_SECONDARY_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_sec / 2.0) proper.prop_propagate(wavefront, d_sec_fold1, 'FOLD_1') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FOLD1_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_fold1 / 2.0) proper.prop_propagate(wavefront, d_fold1_m3, 'M3') proper.prop_lens(wavefront, fl_m3) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_M3_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_m3 / 2.0) proper.prop_propagate(wavefront, d_m3_m4, 'M4') proper.prop_lens(wavefront, fl_m4) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_M4_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_m4 / 2.0) proper.prop_propagate(wavefront, d_m4_m5, 'M5') proper.prop_lens(wavefront, fl_m5) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_M5_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_m5 / 2.0) proper.prop_propagate(wavefront, d_m5_fold2, 'FOLD_2') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FOLD2_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_fold2 / 2.0) proper.prop_propagate(wavefront, d_fold2_fsm, 'FSM') if end_at_fsm == 1: (wavefront, sampling_m) = proper.prop_end(wavefront, NOABS=True) wavefront = trim(wavefront, n) return wavefront, sampling_m if cgi_x_shift_pupdiam != 0 or cgi_y_shift_pupdiam != 0 or cgi_x_shift_m != 0 or cgi_y_shift_m != 0: # bulk coronagraph pupil shear # FFT the field, apply a tilt, FFT back if cgi_x_shift_pupdiam != 0 or cgi_y_shift_pupdiam != 0: # offsets are normalized to pupil diameter xt = -cgi_x_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n yt = -cgi_y_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n else: # offsets are meters d_m = proper.prop_get_sampling(wavefront) xt = -cgi_x_shift_m / d_m * float(pupil_diam_pix) / n yt = -cgi_y_shift_m / d_m * float(pupil_diam_pix) / n x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) tilt = complex(0, 1) * np.pi * (x * xt + y * yt) x = 0 y = 0 wavefront0 = proper.prop_get_wavefront(wavefront) wavefront0 = ffts(wavefront0, -1) wavefront0 *= np.exp(tilt) wavefront0 = ffts(wavefront0, 1) tilt = 0 wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FSM_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_fsm / 2.0) if (fsm_x_offset != 0.0 or fsm_y_offset != 0.0): # compute tilted wavefront to offset source by fsm_x_offset,fsm_y_offset lambda0_m/D xtilt_lam = fsm_x_offset * lambda0_m / lambda_m ytilt_lam = fsm_y_offset * lambda0_m / lambda_m x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) proper.prop_multiply( wavefront, np.exp(complex(0, 1) * np.pi * (xtilt_lam * x + ytilt_lam * y))) x = 0 y = 0 proper.prop_propagate(wavefront, d_fsm_oap1, 'OAP1') proper.prop_lens(wavefront, fl_oap1) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP1_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap1 / 2.0) proper.prop_propagate(wavefront, d_oap1_focm + focm_z_shift_m, 'FOCM') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FOCM_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_focm / 2.0) proper.prop_propagate(wavefront, d_focm_oap2 + focm_z_shift_m, 'OAP2') proper.prop_lens(wavefront, fl_oap2) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP2_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap2 / 2.0) proper.prop_propagate(wavefront, d_oap2_dm1, 'DM1') if use_dm1 != 0: proper.prop_dm(wavefront, dm1_m, dm1_xc_act, dm1_yc_act, dm_sampling_m, XTILT=dm1_xtilt_deg, YTILT=dm1_ytilt_deg, ZTILT=dm1_ztilt_deg) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_DM1_phase_error_V1.0.fits', WAVEFRONT=True) if is_hlc == True and use_hlc_dm_patterns == 1: dm1wfe = proper.prop_fits_read(prefix + 'dm1wfe.fits') proper.prop_add_phase(wavefront, trim(dm1wfe, n)) dm1wfe = 0 proper.prop_propagate(wavefront, d_dm1_dm2, 'DM2') if use_dm2 == 1: proper.prop_dm(wavefront, dm2_m, dm2_xc_act, dm2_yc_act, dm_sampling_m, XTILT=dm2_xtilt_deg, YTILT=dm2_ytilt_deg, ZTILT=dm2_ztilt_deg) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_DM2_phase_error_V1.0.fits', WAVEFRONT=True) if is_hlc == True: if use_hlc_dm_patterns == 1: dm2wfe = proper.prop_fits_read(prefix + 'dm2wfe.fits') proper.prop_add_phase(wavefront, trim(dm2wfe, n)) dm2wfe = 0 dm2mask = proper.prop_fits_read(prefix + 'dm2mask.fits') proper.prop_multiply(wavefront, trim(dm2mask, n)) dm2mask = 0 proper.prop_propagate(wavefront, d_dm2_oap3, 'OAP3') proper.prop_lens(wavefront, fl_oap3) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP3_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap3 / 2.0) proper.prop_propagate(wavefront, d_oap3_fold3, 'FOLD_3') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FOLD3_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_fold3 / 2.0) proper.prop_propagate(wavefront, d_fold3_oap4, 'OAP4') proper.prop_lens(wavefront, fl_oap4) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP4_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap4 / 2.0) proper.prop_propagate(wavefront, d_oap4_pupilmask, 'PUPIL_MASK') # flat/reflective shaped pupil if is_spc == True and use_pupil_mask != 0: pupil_mask = proper.prop_fits_read(pupil_mask_file) pupil_mask = trim(pupil_mask, n) if mask_x_shift_pupdiam != 0 or mask_y_shift_pupdiam != 0 or mask_x_shift_m != 0 or mask_y_shift_m != 0: # shift SP mask by FFTing it, applying tilt, and FFTing back if mask_x_shift_pupdiam != 0 or mask_y_shift_pupdiam != 0: # offsets are normalized to pupil diameter xt = -mask_x_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n yt = -mask_y_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n else: d_m = proper.prop_get_sampling(wavefront) xt = -mask_x_shift_m / d_m * float(pupil_diam_pix) / n yt = -mask_y_shift_m / d_m * float(pupil_diam_pix) / n x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) tilt = complex(0, 1) * np.pi * (x * xt + y * yt) x = 0 y = 0 pupil_mask = ffts(pupil_mask, -1) pupil_mask *= np.exp(tilt) pupil_mask = ffts(pupil_mask, 1) pupil_mask = pupil_mask.real tilt = 0 proper.prop_multiply(wavefront, pupil_mask) pupil_mask = 0 if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_PUPILMASK_phase_error_V1.0.fits', WAVEFRONT=True) # while at a pupil, use more padding to provide 2x better sampling at FPM diam = 2 * proper.prop_get_beamradius(wavefront) (wavefront, dx) = proper.prop_end(wavefront, NOABS=True) n = n_to_fpm wavefront0 = trim(wavefront, n) wavefront = proper.prop_begin(diam, lambda_m, n, float(pupil_diam_pix) / n) wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 proper.prop_propagate(wavefront, d_pupilmask_oap5, 'OAP5') proper.prop_lens(wavefront, fl_oap5) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP5_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap5 / 2.0) proper.prop_propagate(wavefront, d_oap5_fpm + fpm_z_shift_m, 'FPM', TO_PLANE=True) if use_fpm == 1: if fpm_x_offset != 0 or fpm_y_offset != 0 or fpm_x_offset_m != 0 or fpm_y_offset_m != 0: # To shift FPM, FFT field to pupil, apply tilt, FFT back to focus, # apply FPM, FFT to pupil, take out tilt, FFT back to focus if fpm_x_offset != 0 or fpm_y_offset != 0: # shifts are specified in lambda0/D x_offset_lamD = fpm_x_offset * lambda0_m / lambda_m y_offset_lamD = fpm_y_offset * lambda0_m / lambda_m else: d_m = proper.prop_get_sampling(wavefront) x_offset_lamD = fpm_x_offset_m / d_m * float( pupil_diam_pix) / n y_offset_lamD = fpm_y_offset_m / d_m * float( pupil_diam_pix) / n x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) tilt = complex(0, 1) * np.pi * (x * x_offset_lamD + y * y_offset_lamD) x = 0 y = 0 wavefront0 = proper.prop_get_wavefront(wavefront) wavefront0 = ffts(wavefront0, -1) wavefront0 *= np.exp(tilt) wavefront0 = ffts(wavefront0, 1) wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 if is_hlc == True: occ_r = proper.prop_fits_read(occulter_file_r) occ_i = proper.prop_fits_read(occulter_file_i) occ = np.array(occ_r + 1j * occ_i, dtype=np.complex128) proper.prop_multiply(wavefront, trim(occ, n)) occ_r = 0 occ_i = 0 occ = 0 elif is_spc == True: # super-sample FPM wavefront0 = proper.prop_get_wavefront(wavefront) wavefront0 = ffts(wavefront0, 1) # to virtual pupil wavefront0 = trim(wavefront0, n_mft) fpm = proper.prop_fits_read(fpm_file) nfpm = fpm.shape[1] fpm_sampling_lam = fpm_sampling * fpm_sampling_lambda_m / lambda_m wavefront0 = mft2(wavefront0, fpm_sampling_lam, pupil_diam_pix, nfpm, -1) # MFT to highly-sampled focal plane wavefront0 *= fpm fpm = 0 wavefront0 = mft2(wavefront0, fpm_sampling_lam, pupil_diam_pix, n, +1) # MFT to virtual pupil wavefront0 = ffts(wavefront0, -1) # back to normally-sampled focal plane wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 if fpm_x_offset != 0 or fpm_y_offset != 0 or fpm_x_offset_m != 0 or fpm_y_offset_m != 0: wavefront0 = proper.prop_get_wavefront(wavefront) wavefront0 = ffts(wavefront0, -1) wavefront0 *= np.exp(-tilt) wavefront0 = ffts(wavefront0, 1) wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 tilt = 0 if pinhole_diam_m != 0: # "pinhole_diam_m" is pinhole diameter in meters dx_m = proper.prop_get_sampling(wavefront) dx_pinhole_diam_m = pinhole_diam_m / 101.0 # 101 samples across pinhole n_out = 105 m_per_lamD = dx_m * n / float( pupil_diam_pix) # current focal plane sampling in lambda_m/D dx_pinhole_lamD = dx_pinhole_diam_m / m_per_lamD # pinhole sampling in lambda_m/D n_in = int(round(pupil_diam_pix * 1.2)) wavefront0 = proper.prop_get_wavefront(wavefront) wavefront0 = ffts(wavefront0, +1) # to virtual pupil wavefront0 = trim(wavefront0, n_in) m = dx_pinhole_lamD * n_in * float(n_out) / pupil_diam_pix wavefront0 = mft2(wavefront0, dx_pinhole_lamD, pupil_diam_pix, n_out, -1) # MFT to highly-sampled focal plane p = (radius(n_out) * dx_pinhole_diam_m) <= (pinhole_diam_m / 2.0) p = p.astype(np.int) wavefront0 *= p p = 0 wavefront0 = mft2(wavefront0, dx_pinhole_lamD, pupil_diam_pix, n, +1) # MFT back to virtual pupil wavefront0 = ffts(wavefront0, -1) # back to normally-sampled focal plane wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 proper.prop_propagate(wavefront, d_fpm_oap6 - fpm_z_shift_m, 'OAP6') proper.prop_lens(wavefront, fl_oap6) if use_errors != 0 and end_at_fpm_exit_pupil == 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP6_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap6 / 2.0) proper.prop_propagate(wavefront, d_oap6_lyotstop, 'LYOT_STOP') # while at a pupil, switch back to less padding diam = 2 * proper.prop_get_beamradius(wavefront) (wavefront, dx) = proper.prop_end(wavefront, NOABS=True) n = n_from_lyotstop wavefront = trim(wavefront, n) if output_field_rootname != '': lams = format(lambda_m * 1e6, "6.4f") pols = format(int(round(polaxis))) hdu = pyfits.PrimaryHDU() hdu.data = np.real(wavefront) hdu.writeto(output_field_rootname + '_' + lams + 'um_' + pols + '_real.fits', overwrite=True) hdu = pyfits.PrimaryHDU() hdu.data = np.imag(wavefront) hdu.writeto(output_field_rootname + '_' + lams + 'um_' + pols + '_imag.fits', overwrite=True) if end_at_fpm_exit_pupil == 1: return wavefront, dx wavefront0 = wavefront.copy() wavefront = 0 wavefront = proper.prop_begin(diam, lambda_m, n, float(pupil_diam_pix) / n) wavefront.wfarr[:, :] = proper.prop_shift_center(wavefront0) wavefront0 = 0 if use_lyot_stop != 0: lyot = proper.prop_fits_read(lyot_stop_file) lyot = trim(lyot, n) if lyot_x_shift_pupdiam != 0 or lyot_y_shift_pupdiam != 0 or lyot_x_shift_m != 0 or lyot_y_shift_m != 0: # apply shift to lyot stop by FFTing the stop, applying a tilt, and FFTing back if lyot_x_shift_pupdiam != 0 or lyot_y_shift_pupdiam != 0: # offsets are normalized to pupil diameter xt = -lyot_x_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n yt = -lyot_y_shift_pupdiam * pupil_diam_pix * float( pupil_diam_pix) / n else: d_m = proper.prop_get_sampling(wavefront) xt = -lyot_x_shift_m / d_m * float(pupil_diam_pix) / n yt = -lyot_y_shift_m / d_m * float(pupil_diam_pix) / n x = np.tile((np.arange(n) - n // 2) / (pupil_diam_pix / 2.0), (n, 1)) y = np.transpose(x) tilt = complex(0, 1) * np.pi * (x * xt + y * yt) x = 0 y = 0 lyot = ffts(lyot, -1) lyot *= np.exp(tilt) lyot = ffts(lyot, 1) lyot = lyot.real tilt = 0 proper.prop_multiply(wavefront, lyot) lyot = 0 if use_pupil_lens != 0 or pinhole_diam_m != 0: proper.prop_circular_aperture(wavefront, 1.1, NORM=True) proper.prop_propagate(wavefront, d_lyotstop_oap7, 'OAP7') proper.prop_lens(wavefront, fl_oap7) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP7_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap7 / 2.0) proper.prop_propagate(wavefront, d_oap7_fieldstop, 'FIELD_STOP') if use_field_stop != 0 and (cor_type == 'hlc' or cor_type == 'hlc_erkin'): sampling_lamD = float( pupil_diam_pix) / n # sampling at focus in lambda_m/D stop_radius = field_stop_radius_lam0 / sampling_lamD * ( lambda0_m / lambda_m) * proper.prop_get_sampling(wavefront) if field_stop_x_offset != 0 or field_stop_y_offset != 0: # convert offsets in lambda0/D to meters x_offset_lamD = field_stop_x_offset * lambda0_m / lambda_m y_offset_lamD = field_stop_y_offset * lambda0_m / lambda_m pupil_ratio = float(pupil_diam_pix) / n field_stop_x_offset_m = x_offset_lamD / pupil_ratio * proper.prop_get_sampling( wavefront) field_stop_y_offset_m = y_offset_lamD / pupil_ratio * proper.prop_get_sampling( wavefront) proper.prop_circular_aperture(wavefront, stop_radius, -field_stop_x_offset_m, -field_stop_y_offset_m) proper.prop_propagate(wavefront, d_fieldstop_oap8, 'OAP8') proper.prop_lens(wavefront, fl_oap8) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_OAP8_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_oap8 / 2.0) proper.prop_propagate(wavefront, d_oap8_filter, 'filter') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FILTER_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_filter / 2.0) proper.prop_propagate(wavefront, d_filter_lens, 'LENS') if use_pupil_lens == 0 and use_defocus_lens == 0 and defocus == 0: # use imaging lens to create normal focus proper.prop_lens(wavefront, fl_lens) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_LENS_phase_error_V1.0.fits', WAVEFRONT=True) elif use_pupil_lens != 0: # use pupil imaging lens proper.prop_lens(wavefront, fl_pupillens) if use_errors != 0: proper.prop_errormap( wavefront, map_dir + 'wfirst_phaseb_PUPILLENS_phase_error_V1.0.fits', WAVEFRONT=True) else: # table is waves P-V @ 575 nm z4_pv_waves = np.array([ -9.0545, -8.5543, -8.3550, -8.0300, -7.54500, -7.03350, -6.03300, -5.03300, -4.02000, -2.51980, 0.00000000, 3.028000, 4.95000, 6.353600, 8.030000, 10.10500, 12.06000, 14.06000, 20.26000, 28.34000, 40.77500, 56.65700 ]) fl_defocus_lens = np.array([ 5.09118, 1.89323, 1.54206, 1.21198, 0.914799, 0.743569, 0.567599, 0.470213, 0.406973, 0.350755, 0.29601868, 0.260092, 0.24516, 0.236606, 0.228181, 0.219748, 0.213278, 0.207816, 0.195536, 0.185600, 0.176629, 0.169984 ]) # subtract ad-hoc function to make z4 vs f_length more accurately spline interpolatible f = fl_defocus_lens / 0.005 f0 = 59.203738 z4t = z4_pv_waves - (0.005 * (f0 - f - 40)) / f**2 / 0.575e-6 if use_defocus_lens != 0: # use one of 4 defocusing lenses defocus = np.array([18.0, 9.0, -4.0, -8.0]) # waves P-V @ 575 nm f = interp1d(z4_pv_waves, z4t, kind='cubic') z4x = f(defocus) f = interp1d(z4t, fl_defocus_lens, kind='cubic') lens_fl = f(z4x) proper.prop_lens(wavefront, lens_fl[use_defocus_lens - 1]) if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_DEFOCUSLENS' + str(use_defocus_lens) + '_phase_error_V1.0.fits', WAVEFRONT=True) defocus = defocus[use_defocus_lens - 1] else: # specify amount of defocus (P-V waves @ 575 nm) f = interp1d(z4_pv_waves, z4t, kind='cubic') z4x = f(defocus) f = interp1d(z4t, fl_defocus_lens, kind='cubic') lens_fl = f(z4x) proper.prop_lens(wavefront, lens_fl) if use_errors != 0: proper.prop_errormap( wavefront, map_dir + 'wfirst_phaseb_DEFOCUSLENS1_phase_error_V1.0.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_lens / 2.0) proper.prop_propagate(wavefront, d_lens_fold4, 'FOLD_4') if use_errors != 0: proper.prop_errormap(wavefront, map_dir + 'wfirst_phaseb_FOLD4_phase_error_V1.1.fits', WAVEFRONT=True) if use_aperture != 0: proper.prop_circular_aperture(wavefront, diam_fold4 / 2.0) if defocus != 0 or use_defocus_lens != 0: if np.abs(defocus) <= 4: proper.prop_propagate(wavefront, d_fold4_image, 'IMAGE', TO_PLANE=True) else: proper.prop_propagate(wavefront, d_fold4_image, 'IMAGE') else: proper.prop_propagate(wavefront, d_fold4_image, 'IMAGE') (wavefront, sampling_m) = proper.prop_end(wavefront, NOABS=True) if final_sampling_lam0 != 0 or final_sampling_m != 0: if final_sampling_m != 0: mag = sampling_m / final_sampling_m sampling_m = final_sampling_m else: mag = (float(pupil_diam_pix) / n) / final_sampling_lam0 * (lambda_m / lambda0_m) sampling_m = sampling_m / mag wavefront = proper.prop_magnify(wavefront, mag, output_dim, AMP_CONSERVE=True) else: wavefront = trim(wavefront, output_dim) return wavefront, sampling_m
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 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 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_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 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 dummy_telescope(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 # SCExAO Reimaging 1 proper.prop_lens(wfo, fl_SxOAPG) proper.prop_propagate(wfo, fl_SxOAPG * 2) # move to second pupil ######################################## # Import/Apply Actual DM Map # ####################################### plot_flag = False if kwargs['verbose'] and kwargs['ix'] == 0: plot_flag = True dm_map = kwargs['map'] # flat = proper.prop_zernikes(wfo, [2, 3], np.array([5, 1])) # zernike[2,3] = x,y tilt # adding a tilt for shits and giggles # proper.prop_propagate(wfo, fl_SxOAPG) # from tweeter-DM to OAP2 errormap(wfo, dm_map, SAMPLING=dm_pitch, MIRROR_SURFACE=True, MICRONS=True, BR=beam_ratio, PLOT=plot_flag) # WAVEFRONT=True # errormap(wfo, dm_map, SAMPLING=dm_pitch, AMPLITUDE=True, BR=beam_ratio, PLOT=plot_flag) # WAVEFRONT=True # proper.prop_circular_aperture(wfo, entrance_d/2) 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) # ax.imshow(dm_map, interpolation='none') ax1.imshow(np.abs(proper.prop_shift_center(wfo.wfarr))**2, interpolation='none') 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' ax2.set_title('Phase') # ------------------------------------------------ # proper.prop_propagate(wfo, fl_SxOAPG) # from tweeter-DM to OAP2 # 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 # ######################################## # # Focal Plane # # ####################################### # Check Sampling in focal plane # shifts wfo from Fourier Space (origin==lower left corner) to object space (origin==center) # proper.prop_shift_center(wfo.wfarr) # wf, samp = proper.prop_end(wfo, NoAbs=True) wf = proper.prop_shift_center(wfo.wfarr) samp = proper.prop_get_sampling(wfo) # smp_asec = proper.prop_get_sampling_arcsec(wfo) if kwargs['verbose'] and kwargs['ix'] == 0: fig, ax = plt.subplots(nrows=1, ncols=1) fig.suptitle('SCExAO Model Focal Plane', fontweight='bold', fontsize=14) ax.imshow( np.abs(wf)**2, interpolation='none', norm=LogNorm( vmin=1e-7, vmax=1e-2)) # np.abs(proper.prop_shift_center(wfo.wfarr))**2 # # if kwargs['verbose'] and kwargs['ix']==0: # print(f"\n\tFocal Plane\n" # f"sampling at focal plane is {smp_asec * 1e3:.4f} mas\n" # f"\tfull FOV is {smp_asec * grid_size * 1e3:.2f} mas") # s_rad = proper.prop_get_sampling_radians(wfo) # print(f"sampling at focal plane is {s_rad * 1e6:.6f} urad") # print(f"Finished simulation") return wf, samp
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 run_system(empty_lamda, grid_size, PASSVALUE): #'dm_disp':0 passpara = PASSVALUE['params'] ap.__dict__ = passpara[0].__dict__ tp.__dict__ = passpara[1].__dict__ iop.__dict__ = passpara[2].__dict__ # params.ap = passpara[0] # params.tp = passpara[1] # # ap = params.ap # tp = params.tp # print 'line 23', tp.occulter_type # print 'propagating frame:', PASSVALUE['iter'] wsamples = np.linspace(tp.band[0], tp.band[1], tp.nwsamp) / 1e9 # print wsamples datacube = [] # print proper.prop_get_sampling(wfp), proper.prop_get_nyquistsampling(wfp), proper.prop_get_fratio(wfp) # global phase_map, Imaps # Imaps = np.zeros((4,tp.grid_size,tp.grid_size)) # phase_map = np.zeros((tp.grid_size, tp.grid_size)) # wavefronts = np.empty((len(wsamples),1+len(ap.contrast)), dtype=object) for iw, w in enumerate(wsamples): # Define the wavefront beam_ratio = tp.beam_ratio * tp.band[0] / w * 1e-9 wfp = proper.prop_begin(tp.diam, w, tp.grid_size, beam_ratio) wfs = [wfp] names = ['primary'] if ap.companion: for id in range(len(ap.contrast)): wfc = proper.prop_begin(tp.diam, w, tp.grid_size, beam_ratio) wfs.append(wfc) names.append('companion_%i' % id) # proper.prop_circular_aperture(wfo, tp.diam / 2) # for iw, wf in enumerate([wfo, wfc]): wframes = np.zeros((tp.grid_size, tp.grid_size)) for iwf, wf in zip(names, wfs): # wavefronts[iw,iwf] = wf proper.prop_circular_aperture(wf, tp.diam / 2) # quicklook_wf(wf, show=True) if tp.use_atmos: tdm.add_atmos(wf, tp.f_lens, w, atmos_map=PASSVALUE['atmos_map']) if tp.rot_rate: tdm.rotate_atmos(wf, PASSVALUE['atmos_map']) # quicklook_wf(wf, show=True) # if tp.use_spiders: # tdm.add_spiders(wf, tp.diam) if tp.use_hex: tdm.add_hex(wf) proper.prop_define_entrance(wf) # normalizes the intensity if iwf[:9] == 'companion': tdm.offset_companion(wf, int(iwf[10:]), PASSVALUE['atmos_map']) # quicklook_wf(wf, show=True) if tp.use_apod: tdm.do_apod(wf, tp.grid_size, tp.beam_ratio, tp.apod_gaus) # quicklook_wf(wf, show=True) # obj_map = tdm.wfs_measurement(wfo)#, obj_map, tp.wfs_scale) proper.prop_propagate(wf, tp.f_lens) if tp.aber_params['CPA']: tdm.add_aber(wf, tp.f_lens, tp.aber_params, tp.aber_vals, PASSVALUE['iter'], Loc='CPA') # if tp.CPA_type == 'test': # tdm.add_single_speck(wf, PASSVALUE['iter'] ) # if tp.CPA_type == 'Static': # tdm.add_static(wf, tp.f_lens, loc = 'CPA') # if tp.CPA_type == 'Amp': # tdm.add_static(wf, tp.f_lens, loc = 'CPA', type='Amp') # if tp.CPA_type == 'Quasi': # tdm.add_quasi(wf, tp.f_lens, PASSVALUE['iter']) # rawImageIO.save_wf(wf, iop.datadir+'/beforeAO.pkl') # quicklook_wf(wf) # quicklook_im(obj_map, logAmp=False) proper.prop_propagate(wf, tp.f_lens) if tp.quick_ao: if iwf == 'primary': # and PASSVALUE['iter'] == 0: # quicklook_wf(wf, show=True) r0 = float(PASSVALUE['atmos_map'][-10:-5]) # dprint((r0, 'r0')) CPA_map = tdm.quick_wfs(wf, PASSVALUE['iter'], r0=r0) # , obj_map, tp.wfs_scale) # dprint('quick_ao') # quicklook_wf(wf, show=True) if tp.use_ao: tdm.quick_ao(wf, iwf, tp.f_lens, beam_ratio, PASSVALUE['iter'], CPA_map) # dprint('quick_ao') # quicklook_wf(wf, show=True) else: if tp.use_ao: tdm.adaptive_optics(wf, iwf, iw, tp.f_lens, beam_ratio, PASSVALUE['iter']) if iwf == 'primary': # and PASSVALUE['iter'] == 0: # quicklook_wf(wf, show=True) r0 = float(PASSVALUE['atmos_map'][-10:-5]) # dprint((r0, 'r0')) # if iw == np.ceil(tp.nwsamp/2): tdm.wfs_measurement(wf, PASSVALUE['iter'], iw, r0=r0) #, obj_map, tp.wfs_scale) proper.prop_propagate(wf, tp.f_lens) # rawImageIO.save_wf(wf, iop.datadir+'/loopAO_8act.pkl') # if iwf == 'primary': # quicklook_wf(wf, show=True) # if tp.active_modulate: # tdm.modulate(wf, w, PASSVALUE['iter']) # if iwf == 'primary': # quicklook_wf(wf, show=True) if tp.aber_params['NCPA']: tdm.add_aber(wf, tp.f_lens, tp.aber_params, tp.aber_vals, PASSVALUE['iter'], Loc='NCPA') # if tp.NCPA_type == 'Static': # tdm.add_static(wf, tp.f_lens, loc = 'NCPA') # if tp.NCPA_type == 'Wave': # tdm.add_IFS_ab(wf, tp.f_lens, w) # if tp.NCPA_type == 'Quasi': # tdm.add_quasi(wf, tp.f_lens, PASSVALUE['iter']) # quicklook_wf(wf, show=True) # if iwf == 'primary': # NCPA_phasemap = proper.prop_get_phase(wf) # quicklook_im(NCPA_phasemap, logAmp=False, show=False, colormap="jet", vmin=-3.14, vmax=3.14) # if iwf == 'primary': # global obj_map # r0 = float(PASSVALUE['atmos_map'][-10:-5]) # obj_map = tdm.wfs_measurement(wf, r0 = r0)#, obj_map, tp.wfs_scale) # # quicklook_im(obj_map, logAmp=False) proper.prop_propagate(wf, tp.f_lens) # spiders are introduced here for now since the phase unwrapping seems to ignore them and hence so does the DM # Check out http://scikit-image.org/docs/dev/auto_examples/filters/plot_phase_unwrap.html for masking argument if tp.use_spiders: tdm.add_spiders(wf, tp.diam) tdm.prop_mid_optics(wf, tp.f_lens) # if iwf == 'primary': # if PASSVALUE['iter']>ap.numframes-2 or PASSVALUE['iter']==0: # quicklook_wf(wf, show=True) # print proper.prop_get_sampling(wfp), proper.prop_get_sampling_arcsec(wfp), 'here' if tp.satelite_speck and iwf == 'primary': tdm.add_speckles(wf) # tp.variable = proper.prop_get_phase(wfo)[20,20] # print 'speck phase', tp.variable # import cPickle as pickle # dprint('just saved') # with open(iop.phase_ideal, 'wb') as handle: # pickle.dump(proper.prop_get_phase(wf), handle, protocol=pickle.HIGHEST_PROTOCOL) # exit() if tp.active_null and iwf == 'primary': FPWFS.active_null(wf, PASSVALUE['iter'], w) # if tp.speckle_kill and iwf == 'primary': # tdm.speckle_killer(wf) # tdm.speck_kill(wf) # iwf == 'primary': # parent_bright = aper_phot(proper.prop_get_amplitude(wf),0,8) # if iwf == 'primary' and iop.saveIQ: # save_pix_IQ(wf) # complex_map = proper.prop_shift_center(wf.wfarr) # complex_pix = complex_map[64, 64] # print complex_pix # if np.real(complex_pix) < 0.2: # quicklook_IQ(wf) # # if iwf == 'primary': # # print np.sum(proper.prop_get_amplitude(wf)), 'before', aper_phot(proper.prop_get_amplitude(wf),0,4) # quicklook_wf(wf, show=True, logAmp=True) # if iwf == 'primary': # quicklook_wf(wf, show=True) # if tp.active_modulate and PASSVALUE['iter'] >=8: # coronagraph(wf, tp.f_lens, tp.occulter_type, tp.occult_loc, tp.diam) # if not tp.active_modulate: coronagraph(wf, tp.f_lens, tp.occulter_type, tp.occult_loc, tp.diam) # dprint(proper.prop_get_sampling_arcsec(wf)) # exit() # tp.occult_factor = aper_phot(proper.prop_get_amplitude(wf),0,8)/parent_bright # if PASSVALUE['iter'] % 10 == 0: # with open(iop.logfile, 'a') as the_file: # the_file.write('\n', tp.occult_factor) # quicklook_wf(wf, show=True) if tp.occulter_type != 'None' and iwf == 'primary': #kludge for now until more sophisticated coronapraph has been installed wf.wfarr *= 0.1 # # print np.sum(proper.prop_get_amplitude(wf)), 'after', aper_phot(proper.prop_get_amplitude(wf), 0, 4) # quicklook_wf(wf, show=True) # print proper.prop_get_sampling(wfp), proper.prop_get_sampling_arcsec(wfp), 'here' # if iwf == 'primary': # quicklook_wf(wf, show=True) if tp.use_zern_ab: tdm.add_zern_ab(wf, tp.f_lens) (wframe, sampling) = proper.prop_end(wf) # dprint((np.sum(wframe), 'sum')) # wframe = proper.prop_get_amplitude(wf) # planet = np.roll(np.roll(wframe, 20, 1), 20, 0) * 0.1 # [92,92] # if ap.companion: # from scipy.ndimage.interpolation import shift # companion = shift(wframe, shift= np.array(ap.comp_loc[::-1])- np.array([tp.grid_size/2,tp.grid_size/2])) * ap.contrast # # planet = np.roll(wframe, 15, 0) * 0.1 # [92,92] # # wframe = (wframe + companion) # quicklook_im(wframe, logAmp=True) # '''test conserve=True on prop_magnify!''' # wframe = proper.prop_magnify(wframe, (w*1e9)/tp.band[0]) # wframe = tdm.scale_wframe(wframe, w, iwf) # print np.shape(wframe) quicklook_im(wframe) # quicklook_im(wframe) # mid = int(len(wframe)/2) # wframe = wframe[mid - tp.grid_size/2 : mid +tp.grid_size/2, mid - tp.grid_size/2 : mid +tp.grid_size/2] # if max(mp.array_size) < tp.grid_size: # # Photons seeded outside the array cannot have pixel phase uncertainty applied to them. Instead make both grids match in size # wframe = rawImageIO.resize_image(wframe, newsize=(max(mp.array_size),max(mp.array_size))) # dprint(np.sum(wframe)) # dprint(iwf) # if iwf == 'companion_0': wframes += wframe # if sp.show_wframe: # quicklook_im(wframes, logAmp=True, show=True) datacube.append(wframes) datacube = np.array(datacube) datacube = np.abs(datacube) # #normalize # datacube = np.transpose(np.transpose(datacube) / np.sum(datacube, axis=(1, 2)))/float(tp.nwsamp) # print 'Some pixels have negative values, possibly because of some Gaussian uncertainy you introduced. Taking abs for now.' # view_datacube(datacube) # # End # print type(wfo[0,0]), type(wfo) # # proper.prop_savestate(wfo) # # else: # # wfo = proper.prop_state(wfo) return (datacube, sampling)
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 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 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 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 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