示例#1
0
def NCPA_application(wf, npupil, NCPA, path, Debug_print, Debug):
    
    n = int(proper.prop_get_gridsize(wf))
    lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda
    
    if (Debug_print == True):
        print ("NCPA", NCPA.shape)
    NCPA_pixels = (NCPA.shape)[0] # size of the phase screen
    scaling_factor_NCPA = float(npupil)/float(NCPA_pixels) # scaling factor the phase screen to the simulation pupil size
    if (Debug_print == True):
        print ("scaling_factor_NCPA: ", scaling_factor_NCPA)
    NCPA_scale = cv2.resize(NCPA.astype(np.float32), (0,0), fx=scaling_factor_NCPA, fy=scaling_factor_NCPA, interpolation=cv2.INTER_LINEAR) # scale the the phase screen to the simulation pupil size
    if (Debug_print == True):
        print ("NCPA_resample", NCPA_scale.shape)
    NCPA_large = np.zeros((n,n)) # define an array of n-0s, where to insert the screen
    if (Debug_print == True):
        print("n: ", n)
        print("npupil: ", npupil)
    NCPA_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =NCPA_scale # insert the scaled screen into the 0s grid
    if (Debug == 1):
        fits.writeto(path + 'NCPA_large.fits', NCPA_large, overwrite=True) # fits file for the screen
    
    
    lambda2=lamda/(1e-6) # need to use lambda in microns
    screen_atm = np.exp(1j*NCPA_large/lambda2*2*math.pi)
    proper.prop_multiply(wf, screen_atm) # multiply the atm screen to teh wavefront

#rms_error = NCPA[0]#30nm-- RMS wavefront error in meters
#c_freq = NCPA[1]# 5  ;-- correlation frequency (cycles/meter)
#high_power = NCPA[2]#3.0  ;-- high frequency falloff (r^-high_power)
#RANDOM_MAP = readfits('RANDOM_MAP.fits')
#proper.prop_psd_errormap_mod( wf, rms_error, c_freq, high_power, RMS=True)
    
    
    return
示例#2
0
def quicklook_IQ(wfo, logAmp=False, show=True):
    I = np.real(wfo.wfarr)
    Q = np.imag(wfo.wfarr)

    print(np.sum(I), np.sum(Q))

    I = proper.prop_shift_center(I)
    Q = proper.prop_shift_center(Q)

    fig = plt.figure(figsize=(14, 10))
    ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2)
    ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2)
    ax3 = plt.subplot2grid((3, 2), (2, 0))
    ax4 = plt.subplot2grid((3, 2), (2, 1))
    if logAmp:
        ax1.imshow(I, origin='lower', cmap="YlGnBu_r", norm=LogNorm())
    else:
        ax1.imshow(I, origin='lower', cmap="YlGnBu_r")
    ax2.imshow(Q, origin='lower', cmap="YlGnBu_r")  # , vmin=-0.5, vmax=0.5)

    ax3.plot(I[int(tp.grid_size / 2)])
    ax3.plot(np.sum(np.eye(tp.grid_size) * I, axis=1))

    # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128])

    ax4.plot(Q[int(tp.grid_size / 2)])
    # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1))
    plt.xlim([0, proper.prop_get_gridsize(wfo)])
    fig.set_tight_layout(True)
    if show == True:
        plt.show()
示例#3
0
def atmosphere(wf, npupil, atm_screen, Debug_print, Debug):
    
    n = int(proper.prop_get_gridsize(wf))
    lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda

    screen = atm_screen # read the phase screen
    if (Debug_print == True):
        print ("screen", screen.shape)
    screen_pixels = (screen.shape)[0] # size of the phase screen
    scaling_factor_screen = float(npupil)/float(screen_pixels) # scaling factor the phase screen to the simulation pupil size
    if (Debug_print == True):
        print ("scaling_factor_screen: ", scaling_factor_screen)
    screen_scale = cv2.resize(screen.astype(np.float32), (0,0), fx=scaling_factor_screen, fy=scaling_factor_screen, interpolation=cv2.INTER_LINEAR) # scale the the phase screen to the simulation pupil size
    if (Debug_print == True):
        print ("screen_resample", screen_scale.shape)
    screen_large = np.zeros((n,n)) # define an array of n-0s, where to insert the screen
    if (Debug_print == True):
        print("n: ", n)
        print("npupil: ", npupil)
    screen_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =screen_scale # insert the scaled screen into the 0s grid


    lambda2=lamda/(1e-6) # need to use lambda in microns
    screen_atm = np.exp(1j*screen_large/lambda2*2*math.pi)
    proper.prop_multiply(wf, screen_atm) # multiply the atm screen to teh wavefront


    return wf
示例#4
0
def static_ncpa(wf, npupil, screen):

    n = int(proper.prop_get_gridsize(wf))

    screen_pixels = (screen.shape)[0]  # size of the phase screen
    sf = float(npupil) / float(
        screen_pixels
    )  # scaling factor the phase screen to the simulation pupil size

    screen_scale = cv2.resize(
        screen.astype(np.float32), (0, 0),
        fx=sf,
        fy=sf,
        interpolation=cv2.INTER_LINEAR
    )  # scale the the phase screen to the simulation pupil size
    screen_large = np.zeros(
        (n, n))  # define an array of n-0s, where to insert the screen
    screen_large[
        int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 + int(npupil / 2),
        int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 +
        int(npupil /
            2)] = screen_scale  # insert the scaled screen into the 0s grid

    proper.prop_add_phase(wf, screen_large)

    return wf
示例#5
0
def quicklook_wf(wfo, logAmp=True, show=True):

    after_dm = proper.prop_get_amplitude(wfo)
    phase_afterdm = proper.prop_get_phase(wfo)

    fig = plt.figure(figsize=(14, 10))
    ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2)
    ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2)
    ax3 = plt.subplot2grid((3, 2), (2, 0))
    ax4 = plt.subplot2grid((3, 2), (2, 1))
    if logAmp:
        ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r", norm=LogNorm())
    else:
        ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r")
    ax2.imshow(phase_afterdm, origin='lower',
               cmap="YlGnBu_r")  #, vmin=-0.5, vmax=0.5)

    ax3.plot(after_dm[int(tp.grid_size / 2)])
    ax3.plot(np.sum(np.eye(tp.grid_size) * after_dm, axis=1))

    # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128])

    ax4.plot(phase_afterdm[int(tp.grid_size / 2)])
    # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1))
    plt.xlim([0, proper.prop_get_gridsize(wfo)])
    fig.set_tight_layout(True)
    if show == True:
        plt.show()
示例#6
0
def detector(wfo, conf):
    f_lens = conf['F_LENS']
    nd = conf['N_D']
    mode = conf['MODE']
    prefix = conf['PREFIX']
    Debug = conf['DEBUG']

    n = proper.prop_get_gridsize(wfo)
    if (n >= nd):
        proper.prop_propagate(wfo, f_lens, "to reimaging lens")
        proper.prop_lens(wfo, f_lens, "apply reimaging lens")
        proper.prop_propagate(wfo, f_lens, "final focus")
        (wfo, sampling) = proper.prop_end(
            wfo, NOABS=False
        )  # conclude the simulation --> noabs= the wavefront array will be complex
    else:
        print('Error: final image is bigger than initial grid size')
    psf = wfo[int(n / 2 - nd / 2):int(n / 2 + nd / 2),
              int(n / 2 - nd / 2):int(n / 2 + nd / 2)]
    out_dir = str('./output_files/')
    if (Debug == True):
        fits.writeto(out_dir + prefix + '_' + mode + '_PSF' + '.fits',
                     psf,
                     overwrite=True)
    return psf
示例#7
0
def gen_cached_name(label, wfo):
    prefix = 'cached'
    # Most of the cacheable steps depend upon wfo for:
    ngrid = proper.prop_get_gridsize(wfo)
    beamradius = proper.prop_get_beamradius(wfo)
    sampling = proper.prop_get_sampling(wfo)
    return '{}_{}_{}_{}_{}.npy'.format(prefix, label, ngrid, sampling,
                                       beamradius)
示例#8
0
def prop_tilt(wf, tilt_x, tilt_y):
    """Tilt a wavefront in X and Y.
    
    based on tilt(self, Xangle, Yangle) from Poppy (poppy_core.py)
    
    From that function's docs:
    The sign convention is chosen such that positive Yangle tilts move the star upwards in the
    array at the focal plane. (This is sort of an inverse of what physically happens in the propagation
    to or through focus, but we're ignoring that here and trying to just work in sky coords)
    
    Parameters
    ----------
    wf : obj
        WaveFront class object
                
    tilt_x : float
        Tilt angle along x in arc seconds
    
    tilt_y : float
        Tilt angle along y in arc seconds
        
    Returns
    -------
        None
        Modifies wavefront array in wf object.
    """

    if np.abs(tilt_x) > 0 or np.abs(tilt_y) > 0:
        sampling = proper.prop_get_sampling(wf)  # m/pixel
        xangle_rad = tilt_x * np.pi / 648000.  # rad.
        yangle_rad = tilt_y * np.pi / 648000.  # rad.

        ngrid = proper.prop_get_gridsize(wf)  # pixels
        U, V = np.indices(wf.wfarr.shape, dtype=float)
        U -= (ngrid - 1) / 2.0  # pixels X
        U *= sampling  # m
        V -= (ngrid - 1) / 2.0  # pixels Y
        V *= sampling  # m

        # Not totally comfortable that these are combined linearly
        # but go with Poppy for now
        phase = V * xangle_rad + U * yangle_rad  # rad. m
        proper.prop_add_phase(wf, phase)
示例#9
0
def island_effect_piston(wf, npupil, Island_Piston, path, Debug_print, Debug):

    n = int(proper.prop_get_gridsize(wf))
    lamda=proper.prop_get_wavelength(wf) #save the wavelength value [m] into lamda

    PACKAGE_PATH = os.path.abspath(os.path.join(__file__, os.pardir))
    petal1 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal1_243px.fits')
    petal2 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal2_243px.fits')
    petal3 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal3_243px.fits')
    petal4 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal4_243px.fits')
    petal5 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal5_243px.fits')
    petal6 = fits.getdata(PACKAGE_PATH+'/1024_pixelsize5mas_petal6_243px.fits')

    piston_petal1 = Island_Piston[0]*petal1
    piston_petal2 = Island_Piston[1]*petal2
    piston_petal3 = Island_Piston[2]*petal3
    piston_petal4 = Island_Piston[3]*petal4
    piston_petal5 = Island_Piston[4]*petal5
    piston_petal6 = Island_Piston[5]*petal6

    piston = piston_petal1 + piston_petal2 + piston_petal3 + piston_petal4 + piston_petal5 + piston_petal6

    piston_pixels = (piston.shape)[0]## fits file size
    scaling_factor = float(npupil)/float(piston_pixels) ## scaling factor between the fits file size and the pupil size of the simulation
    if (Debug_print==True):
        print ("scaling_factor: ", scaling_factor)
    piston_scale = cv2.resize(piston.astype(np.float32), (0,0), fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_LINEAR) # scale the pupil to the pupil size of the simualtions
    if (Debug_print==True):
        print ("piston_resample", piston_scale.shape)
    piston_large = np.zeros((n,n)) # define an array of n-0s, where to insert the pupuil
    if (Debug_print==True):
        print("n: ", n)
        print("npupil: ", npupil)
    piston_large[int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2),int(n/2)+1-int(npupil/2)-1:int(n/2)+1+int(npupil/2)] =piston_scale # insert the scaled pupil into the 0s grid
    if (Debug == 1):
        fits.writeto(path+'piston_phase.fits', piston_large, overwrite=True) # fits file for the screen


    lambda2=lamda/(1e-6) # need to use lambda in microns
    piston_phase = np.exp(1j*piston_large/lambda2*2*math.pi)
    proper.prop_multiply(wf, piston_phase) # multiply the atm screen to teh wavefront

    return
示例#10
0
def detector(wfo, f_lens, nd, coronagraph_type, prefix, Debug=False):
    n = proper.prop_get_gridsize(wfo)
    if (n >= nd):
        proper.prop_propagate(wfo, f_lens, "to reimaging lens")
        proper.prop_lens(wfo, f_lens, "apply reimaging lens")
        proper.prop_propagate(wfo, f_lens, "final focus")
        (wfo, sampling) = proper.prop_end(
            wfo, NOABS=False
        )  # conclude the simulation --> noabs= the wavefront array will be complex
    else:
        print('Error: final image is bigger than initial grid size')
    psf = wfo[int(n / 2 - nd / 2):int(n / 2 + nd / 2),
              int(n / 2 - nd / 2):int(n / 2 + nd / 2)]
    out_dir = str('./output_files/')
    if (Debug == True):
        fits.writeto(out_dir + prefix + '_' + coronagraph_type + '_PSF' +
                     '.fits',
                     psf,
                     overwrite=True)
    return psf
示例#11
0
def polmap( wavefront, polfile, pupil_diam_pix, condition, MUF=1.0 ):
    n = proper.prop_get_gridsize( wavefront )
    lambda_m = proper.prop_get_wavelength(wavefront)

    if condition <= 2:
        (amp, pha) = polab( polfile, lambda_m, pupil_diam_pix, condition )
    elif condition == 5:
        (amp_m45_x, pha_m45_x) = polab( polfile, lambda_m, pupil_diam_pix, -1 )
        (amp_p45_x, pha_p45_x) = polab( polfile, lambda_m, pupil_diam_pix, +1 )
        amp = (amp_m45_x + amp_p45_x) / 2
        pha = (pha_m45_x + pha_p45_x) / 2
    elif condition == 6:
        (amp_m45_y, pha_m45_y) = polab( polfile, lambda_m, pupil_diam_pix, -2 )
        (amp_p45_y, pha_p45_y) = polab( polfile, lambda_m, pupil_diam_pix, +2 )
        amp = (amp_m45_y + amp_p45_y) / 2
        pha = (pha_m45_y + pha_p45_y) / 2
    elif condition == 10:
        (amp_m45_x, pha_m45_x) = polab( polfile, lambda_m, pupil_diam_pix, -1 )
        (amp_p45_x, pha_p45_x) = polab( polfile, lambda_m, pupil_diam_pix, +1 )
        (amp_m45_y, pha_m45_y) = polab( polfile, lambda_m, pupil_diam_pix, -2 )
        (amp_p45_y, pha_p45_y) = polab( polfile, lambda_m, pupil_diam_pix, +2 )
        amp = (amp_m45_x + amp_p45_x + amp_m45_y + amp_p45_y) / 4
        pha = (pha_m45_x + pha_p45_x + pha_m45_y + pha_p45_y) / 4
    else:
        raise Exception( 'POLMAP: unmatched condition' )

    proper.prop_multiply( wavefront, trim(amp,n) ) 
    proper.prop_add_phase( wavefront, trim(MUF*pha,n) )

    amp = 0
    phase = 0
    amp_p45x = 0
    amp_m45x = 0
    amp_p45y = 0
    amp_m45y = 0
    pha_p45x = 0
    pha_m45x = 0
    pha_p45y = 0
    pha_m45y = 0

    return 
示例#12
0
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
示例#13
0
def apodization(wf, r_obstr, npupil, RAVC, phase_apodizer_file,
                amplitude_apodizer_file, apodizer_misalignment, Debug_print,
                Debug):

    n = int(proper.prop_get_gridsize(wf))

    if (RAVC == True):
        t1_opt = 1. - 1. / 4 * (
            r_obstr**2 + r_obstr * (math.sqrt(r_obstr**2 + 8.))
        )  # define the apodizer transmission [Mawet2013]
        R1_opt = (r_obstr / math.sqrt(1. - t1_opt)
                  )  # define the apodizer radius [Mawet2013]
        if (Debug_print == True):
            print("r1_opt: ", R1_opt)
            print("t1_opt: ", t1_opt)
        apodizer = circular_apodization(wf,
                                        R1_opt,
                                        1.,
                                        t1_opt,
                                        xc=apodizer_misalignment[0],
                                        yc=apodizer_misalignment[1],
                                        NORM=True)  # define the apodizer
        apodizer = proper.prop_shift_center(apodizer)

    if (isinstance(phase_apodizer_file, (list, tuple, np.ndarray)) == True):
        xc_pixels = int(apodizer_misalignment[3] * npupil)
        yc_pixels = int(apodizer_misalignment[4] * npupil)
        apodizer_pixels = (phase_apodizer_file.shape)[0]  ## fits file size
        scaling_factor = float(npupil) / float(
            apodizer_pixels
        )  ## scaling factor between the fits file size and the pupil size of the simulation
        if (Debug_print == True):
            print("scaling_factor: ", scaling_factor)
        apodizer_scale = cv2.resize(
            phase_apodizer_file.astype(np.float32), (0, 0),
            fx=scaling_factor,
            fy=scaling_factor,
            interpolation=cv2.INTER_LINEAR
        )  # scale the pupil to the pupil size of the simualtions
        if (Debug_print == True):
            print("apodizer_resample", apodizer_scale.shape)
        apodizer_large = np.zeros(
            (n, n))  # define an array of n-0s, where to insert the pupuil
        if (Debug_print == True):
            print("n: ", n)
            print("npupil: ", npupil)
        apodizer_large[
            int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 +
            int(npupil / 2) + xc_pixels,
            int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 +
            int(npupil / 2) +
            yc_pixels] = apodizer_scale  # insert the scaled pupil into the 0s grid
        apodizer = np.exp(1j * apodizer_large)

    if (isinstance(amplitude_apodizer_file,
                   (list, tuple, np.ndarray)) == True):
        xc_pixels = int(apodizer_misalignment[0] * npupil)
        yc_pixels = int(apodizer_misalignment[1] * npupil)
        apodizer_pixels = (amplitude_apodizer_file.shape)[0]  ## fits file size
        scaling_factor = float(npupil) / float(
            apodizer_pixels
        )  ## scaling factor between the fits file size and the pupil size of the simulation
        if (Debug_print == True):
            print("scaling_factor: ", scaling_factor)
        apodizer_scale = cv2.resize(
            amplitude_apodizer_file.astype(np.float32), (0, 0),
            fx=scaling_factor,
            fy=scaling_factor,
            interpolation=cv2.INTER_LINEAR
        )  # scale the pupil to the pupil size of the simualtions
        if (Debug_print == True):
            print("apodizer_resample", apodizer_scale.shape)
        apodizer_large = np.zeros(
            (n, n))  # define an array of n-0s, where to insert the pupuil
        if (Debug_print == True):
            print("n: ", n)
            print("npupil: ", npupil)
        apodizer_large[
            int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 +
            int(npupil / 2) + xc_pixels,
            int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 +
            int(npupil / 2) +
            yc_pixels] = apodizer_scale  # insert the scaled pupil into the 0s grid
        apodizer = apodizer_large

    proper.prop_multiply(wf, apodizer)

    return
示例#14
0
def errormap(wf, dm_map, xshift=0., yshift=0., **kwargs):
    """Read in a surface, wavefront, or amplitude error map from a FITS file. 
    
    Map is assumed to be in meters of surface error. One (and only one) of the 
    MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE switches must be specified in order 
    to properly apply the map to the wavefront.  For surface or wavefront error 
    maps, the map values are assumed to be in meters, unless the NM or MICRONS 
    switches are used to specify the units. The amplitude map must range 
    from 0 to 1.  The map will be interpolated to match the current wavefront 
    sampling if necessary.
    
    Parameters
    ----------
    wf : obj
        WaveFront class object
        
    dm_map : 2D numpy array
        the DM map in units of surface deformation
        
    xshify, yshift : float
        Amount to shift map (meters) in X,Y directions
    
    Returns
    -------
    DMAP : numpy ndarray
        Returns map (after any necessary resampling) if set.
    
    
    Other Parameters
    ----------------
    XC_MAP, YC_MAP : float
        Pixel coordinates of map center (Assumed n/2,n/2)
        
    SAMPLING : float
        Sampling of map in meters
        
    ROTATEMAP : float
        Degrees counter-clockwise to rotate map, after any resampling and 
        shifting
        
    MULTIPLY : float
        Multiplies the map by the specified factor
        
    MAGNIFY : float
        Spatially magnify the map by a factor of "constant" from its default
        size; do not use if SAMPLING is specified
        
    MIRROR_SURFACE : bool
        Indicates file contains a mirror surface height error map; It assumes a 
        positive value indicates a surface point higher than the mean surface.  
        The map will be multiplied by -2 to convert it to a wavefront map to 
        account for reflection and wavefront delay (a low region on the surface 
        causes a positive increase in the phase relative to the mean)
        
    WAVEFRONT : bool
        Indicates file contains a wavefront error map
        
    AMPLITUDE : bool
        Indicates file contains an amplitude error map
        
    NM or MICRONS : bool
        Indicates map values are in nanometers or microns. For surface or 
        wavefront maps only
        
    Raises
    ------
    SystemExit:
        If AMPLITUDE and (NM or MICRONS) parameters are input.
        
    SystemExit:
        If NM and MICRONS parameteres are input together. 
        
    ValueError:
        If map type is MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE.
    """
    if ("AMPLITUDE" in kwargs and kwargs["AMPLITUDE"]) \
       and (("NM" in kwargs and kwargs["NM"]) \
       or ("MICRONS" in kwargs and kwargs["MICRONS"])):
        raise SystemExit(
            "ERRORMAP: Cannot specify NM or MICRON for an amplitude map")

    if ("NM" in kwargs and kwargs["NM"]) and \
       ("MICRONS" in kwargs and kwargs["MICRONS"]):
        raise SystemExit("ERRORMAP: Cannot specify both NM and MICRONS")

    if not "XC_MAP" in kwargs:
        s = dm_map.shape
        xc = s[
            0] // 2  # center of map read-in (xc should be 25 for SCExAO DM maps)
        yc = s[1] // 2
    else:
        xc = kwargs["XC_MAP"]
        yc = kwargs["YC_MAP"]

    # KD edit: try to get the dm map to apply only in regions of the beam
    n = proper.prop_get_gridsize(wf)  #
    new_sampling = proper.prop_get_sampling(
        wf)  #kwargs["SAMPLING"]  #*dm_map.shape[0]/npix_across_beam
    if new_sampling > (kwargs["SAMPLING"] + kwargs["SAMPLING"]*.1) or \
        new_sampling < (kwargs["SAMPLING"] - kwargs["SAMPLING"]*.1):
        dm_map = proper.prop_resamplemap(wf, dm_map, kwargs["SAMPLING"], 0, 0)
        dm_map = dm_map[n // 2:n // 2 + xc * 4, n // 2:n // 2 + xc * 4]
        # print(f'User-defined samping is {kwargs["SAMPLING"]:.6f} but proper wavefront has sampling of '
        #       f'{new_sampling:.6f}')
        warnings.warn(
            f'User-defined beam ratio does not produce aperture sampling consistent with SCExAO actuator '
            f'spacing. Resampling Map')

    # resample dm_map to size of beam in the simulation
    # grid = proper.prop_resamplemap(wf, dm_map, new_sampling, xc, yc, xshift, yshift)
    dmap = np.zeros((wf.wfarr.shape[0], wf.wfarr.shape[1]))
    r = dmap.shape
    xrc = r[0] // 2
    yrc = r[1] // 2
    dmap[xrc - xc * 2:xrc + xc * 2, yrc - yc * 2:yrc + yc * 2] = dm_map

    # Create mask to eliminate resampling artifacts outside of beam
    if ("MASKING" in kwargs and kwargs["MASKING"]):
        h, w = wf.wfarr.shape[:2]
        center = (int(w / 2), int(h / 2))
        radius = np.ceil(h * kwargs['BR'] / 2)  #
        # Making the Circular Boolean Mask
        Y, X = np.mgrid[:h, :w]
        dist_from_center = np.sqrt((X - center[0])**2 + (Y - center[1])**2)
        inds = dist_from_center <= radius
        # Applying the Mask to the dm_map
        mask = np.zeros_like(dmap)
        mask[inds] = 1
        dmap *= mask

    # Shift the center of dmap to 0,0
    dmap = proper.prop_shift_center(dmap)

    if kwargs['PLOT']:
        import matplotlib.pyplot as plt
        from matplotlib.colors import LogNorm, SymLogNorm

        fig, subplot = plt.subplots(nrows=1, ncols=2, figsize=(12, 5))
        ax1, ax2 = subplot.flatten()
        fig.suptitle(f'RTC DM Voltage Maps')

        ax1.imshow(
            dm_map, norm=SymLogNorm(1e-2)
        )  # LogNorm(vmin=np.min(dm_map),vmax=np.max(dm_map))  SymLogNorm(1e-2)
        ax1.set_title('DM Map Read In')
        ax2.imshow(proper.prop_shift_center(
            dmap))  #  , cmap='hsv' must shift the center because
        # proper assumes dmap center is 0,0, so we need to shift it back to plot properly
        ax2.set_title('DM Map in Center of Proper simulated beam')

    if "ROTATEMAP" in kwargs or "MAGNIFY" in kwargs:
        # readmap stores map with center at (0,0), so shift
        # before and after rotation
        dmap = proper.prop_shift_center(dmap)
        if "ROTATEMAP" in kwargs:
            dmap = proper.prop_rotate(dmap,
                                      kwargs["ROTATEMAP"],
                                      CUBIC=-0.5,
                                      MISSING=0.0)
        if "MAGNIFY" in kwargs:
            dmap = proper.prop_magnify(dmap, kwargs["MAGNIFY"], dmap.shape[0])
            dmap = proper.prop_shift_center(dmap)

    if ("MICRONS" in kwargs and kwargs["MICRONS"]):
        dmap *= 1.e-6

    if ("NM" in kwargs and kwargs["NM"]):
        dmap *= 1.e-9

    if "MULTIPLY" in kwargs:
        dmap *= kwargs["MULTIPLY"]

    i = complex(0., 1.)

    if ("MIRROR_SURFACE" in kwargs and kwargs["MIRROR_SURFACE"]):
        wf.wfarr *= np.exp(-4 * np.pi * i / wf.lamda * dmap)  # Krist version
    elif "WAVEFRONT" in kwargs:
        wf.wfarr *= np.exp(2 * np.pi * i / wf.lamda * dmap)
    elif "AMPLITUDE" in kwargs:
        wf.wfarr *= dmap
    else:
        raise ValueError(
            "ERRORMAP: Unspecified map type: Use MIRROR_SURFACE, WAVEFRONT, or AMPLITUDE"
        )

    # check1 = proper.prop_get_sampling(wf)
    # print(f"\n\tErrormap Sampling\n"
    #       f"sampling in errormap.py is {check1 * 1e3:.4f} mm\n")

    return dmap
示例#15
0
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
示例#16
0
def propcustom_dm(wf, dm_z0, dm_xc, dm_yc, spacing=0., **kwargs):
    """
    Generate a deformable mirror surface almost exactly like PROPER.

    Simulate a deformable mirror of specified actuator spacing, including the
    effects of the DM influence function. Has two more optional keywords
    compared to  proper.prop_dm

    Parameters
    ----------
    wf : obj
        WaveFront class object
    dm_z0 : str or numpy ndarray
        Either a 2D numpy array containing the surface piston of each DM
        actuator in meters or the name of a 2D FITS image file containing the
        above
    dm_xc, dm_yc : list or numpy ndarray
        The location of the optical axis (center of the wavefront) on the DM in
        actuator units (0 ro num_actuator-1). The center of the first actuator
        is (0.0, 0.0)
    spacing : float
        Defines the spacing in meters between actuators; must not be used when
        n_act_across_pupil is specified.

    Returns
    -------
    dmap : numpy ndarray
        Returns DM surface (not wavefront) map in meters

    Other Parameters
    ----------------
    FIT : bool
        Switch that tells routine that the values in "dm_z" are the desired
        surface heights rather than commanded actuator heights, and so the
        routine should fit this map, accounting for actuator influence functions,
        to determine the necessary actuator heights. An iterative error-minimizing
        loop is used for the fit.
    NO_APPLY : bool
        If set, the DM pattern is not added to the wavefront. Useful if the DM
        surface map is needed but should not be applied to the wavefront
    N_ACT_ACROSS_PUPIL : int
        Specifies the number of actuators that span the X-axis beam diameter. If
        it is a whole number, the left edge of the left pixel is aligned with
        the left edge of the beam, and the right edge of the right pixel with
        the right edge of the beam. This determines the spacing and size of the
        actuators. Should not be used when "spacing" value is specified.
    XTILT, YTILT, ZTILT : float
        Specify the rotation of the DM surface with respect to the wavefront plane
        in degrees about the X, Y, Z axes, respectively, with the origin at the
        center of the wavefront. The DM surface is interpolated and orthographically
        projected onto the wavefront grid. The coordinate system assumes that
        the wavefront and initial DM surface are in the X,Y plane with a lower
        left origin with Z towards the observer. The rotations are left handed.
        The default rotation order is X, Y, then Z unless the /ZYX switch is set.
    XYZ or ZYX : bool
        Specifies the rotation order if two or more of XTILT, YTILT, or ZTILT
        are specified. The default is /XYZ for X, Y, then Z rotations.
    inf_fn : string
        specify a new influence function as a FITS file with the same header keywords as 
        PROPER's default influence function. Needs these values in info.PrimaryData.Keywords:
            'P2PDX_M' % pixel width x (m)
            'P2PDY_M' % pixel width y (m)
            'C2CDX_M' % actuator pitch x (m)
            'C2CDY_M' % actuator pitch y (m)
    inf_sign : {+,-}
        specifies the sign (+/-) of the influence function. Given as an option because 
        the default influence function file is positive, but positive DM actuator 
        commands make a negative deformation for Xinetics and BMC DMs.

    Raises
    ------
    ValueError:
        User cannot specify both actuator spacing and N_ACT_ACROSS_PUPIL
    ValueError:
        User must specify either actuator spacing or N_ACT_ACROSS_PUPIL
    """
    if "ZYX" in kwargs and "XYZ" in kwargs:
        raise ValueError('PROP_DM: Error: Cannot specify both XYZ and ZYX ' +
                         'rotation orders. Stopping')
    elif "ZYX" not in kwargs and 'XYZ' not in kwargs:
        XYZ = 1  # default is rotation around X, then Y, then Z
        # ZYX = 0
    elif "ZYX" in kwargs:
        # ZYX = 1
        XYZ = 0
    elif "XYZ" in kwargs:
        XYZ = 1
        # ZYX = 0

    if "XTILT" in kwargs:
        xtilt = kwargs["XTILT"]
    else:
        xtilt = 0.

    if "YTILT" in kwargs:
        ytilt = kwargs["YTILT"]
    else:
        ytilt = 0.

    if "ZTILT" in kwargs:
        ztilt = kwargs["ZTILT"]
    else:
        ztilt = 0.

    if type(dm_z0) == str:
        dm_z = proper.prop_fits_read(dm_z0)  # Read DM setting from FITS file
    else:
        dm_z = dm_z0
    
    if "inf_fn" in kwargs:
        inf_fn = kwargs["inf_fn"]
    else:
        inf_fn = "influence_dm5v2.fits"
        
    if "inf_sign" in kwargs:
        if(kwargs["inf_sign"] == '+'):
            sign_factor = 1.
        elif(kwargs["inf_sign"] == '-'):
            sign_factor = -1.
    else:
        sign_factor = 1.

    n = proper.prop_get_gridsize(wf)
    dx_surf = proper.prop_get_sampling(wf)  # sampling of surface in meters
    beamradius = proper.prop_get_beamradius(wf)

    # Default influence function sampling is 0.1 mm, peak at (x,y)=(45,45)
    # Default influence function has shape = 1x91x91. Saving it as a 2D array
    # before continuing with processing
    dir_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                            "data")
    inf = proper.prop_fits_read(os.path.join(dir_path, inf_fn))
    inf = sign_factor*np.squeeze(inf)
    
    s = inf.shape
    nx_inf = s[1]
    ny_inf = s[0]
    xc_inf = nx_inf // 2
    yc_inf = ny_inf // 2
    dx_inf = 0.1e-3  # influence function spacing in meters
    dx_dm_inf = 1.0e-3  # nominal spacing between DM actuators in meters
    inf_mag = 10

    if spacing != 0 and "N_ACT_ACROSS_PUPIL" in kwargs:
        raise ValueError("PROP_DM: User cannot specify both actuator spacing" +
                         "and N_ACT_ACROSS_PUPIL. Stopping.")

    if spacing == 0 and "N_ACT_ACROSS_PUPIL" not in kwargs:
        raise ValueError("PROP_DM: User must specify either actuator spacing" +
                         " or N_ACT_ACROSS_PUPIL. Stopping.")

    if "N_ACT_ACROSS_PUPIL" in kwargs:
        dx_dm = 2. * beamradius / int(kwargs["N_ACT_ACROSS_PUPIL"])
    else:
        dx_dm = spacing

    dx_inf = dx_inf * dx_dm / dx_dm_inf  # Influence function sampling scaled
                                         # to specified DM actuator spacing

    if "FIT" in kwargs:
        x = (np.arange(5, dtype=np.float64) - 2) * dx_dm

        if proper.use_cubic_conv:
            inf_kernel = proper.prop_cubic_conv(inf.T, x/dx_inf+xc_inf,
                                                x/dx_inf+yc_inf, GRID=True)
        else:
            xygrid = np.meshgrid(x/dx_inf+xc_inf, x/dx_inf+yc_inf)
            inf_kernel = map_coordinates(inf.T, xygrid, order=3,
                                         mode="nearest")

        (dm_z_commanded, dms) = proper.prop_fit_dm(dm_z, inf_kernel)
    else:
        dm_z_commanded = dm_z

    s = dm_z.shape
    nx_dm = s[1]
    ny_dm = s[0]

    # Create subsampled DM grid
    margin = 9 * inf_mag
    nx_grid = nx_dm * inf_mag + 2 * margin
    ny_grid = ny_dm * inf_mag + 2 * margin
    xoff_grid = margin + inf_mag/2           # pixel location of 1st actuator center in subsampled grid
    yoff_grid = xoff_grid
    dm_grid = np.zeros([ny_grid, nx_grid], dtype = np.float64)

    x = np.arange(nx_dm, dtype=int) * int(inf_mag) + int(xoff_grid)
    y = np.arange(ny_dm, dtype=int) * int(inf_mag) + int(yoff_grid)
    dm_grid[np.tile(np.vstack(y), (nx_dm,)),
            np.tile(x, (ny_dm, 1))] = dm_z_commanded
    dm_grid = ss.fftconvolve(dm_grid, inf, mode='same')
    
    # 3D rotate DM grid and project orthogonally onto wavefront
    xdim = int(np.round(np.sqrt(2) * nx_grid * dx_inf / dx_surf)) # grid dimensions (pix) projected onto wavefront
    ydim = int(np.round(np.sqrt(2) * ny_grid * dx_inf / dx_surf))

    if xdim > n: xdim = n

    if ydim > n: ydim = n

    x = np.ones((ydim, 1), dtype=int) * ((np.arange(xdim) - xdim // 2) * dx_surf)
    y = (np.ones((xdim, 1), dtype=int) * ((np.arange(ydim) - ydim // 2) * dx_surf)).T

    a = xtilt * np.pi / 180
    b = ytilt * np.pi / 180
    g = ztilt * np.pi /180

    if XYZ:
        m = np.array([[cos(b)*cos(g), -cos(b)*sin(g), sin(b), 0],
            [cos(a)*sin(g) + sin(a)*sin(b)*cos(g), cos(a)*cos(g)-sin(a)*sin(b)*sin(g), -sin(a)*cos(b), 0],
            [sin(a)*sin(g)-cos(a)*sin(b)*cos(g), sin(a)*cos(g)+cos(a)*sin(b)*sin(g), cos(a)*cos(b), 0],
            [0, 0, 0, 1] ])
    else:
        m = np.array([	[cos(b)*cos(g), cos(g)*sin(a)*sin(b)-cos(a)*sin(g), cos(a)*cos(g)*sin(b)+sin(a)*sin(g), 0],
            [cos(b)*sin(g), cos(a)*cos(g)+sin(a)*sin(b)*sin(g), -cos(g)*sin(a)+cos(a)*sin(b)*sin(g), 0],
            [-sin(b), cos(b)*sin(a), cos(a)*cos(b), 0],
            [0, 0, 0, 1] ])

    # Forward project a square
    edge = np.array([[-1.0, -1.0, 0.0, 0.0],
                     [1.0, -1.0, 0.0, 0.0],
                     [1.0, 1.0, 0.0, 0.0],
                     [-1.0, 1.0, 0.0, 0.0]])
    new_xyz = np.dot(edge, m)

    # determine backward projection for screen-raster-to-DM-surce computation
    dx_dxs = (new_xyz[0, 0] - new_xyz[1, 0]) / (edge[0, 0] - edge[1, 0])
    dx_dys = (new_xyz[1, 0] - new_xyz[2, 0]) / (edge[1, 1] - edge[2, 1])
    dy_dxs = (new_xyz[0, 1] - new_xyz[1, 1]) / (edge[0, 0] - edge[1, 0])
    dy_dys = (new_xyz[1, 1] - new_xyz[2, 1]) / (edge[1, 1] - edge[2, 1])

    xs = (x/dx_dxs - y*dx_dys/(dx_dxs*dy_dys)) / \
        (1 - dy_dxs*dx_dys/(dx_dxs*dy_dys))
    ys = (y/dy_dys - x*dy_dxs/(dx_dxs*dy_dys)) / \
        (1 - dx_dys*dy_dxs/(dx_dxs*dy_dys))

    xdm = (xs + dm_xc * dx_dm) / dx_inf + xoff_grid
    ydm = (ys + dm_yc * dx_dm) / dx_inf + yoff_grid

    if proper.use_cubic_conv:
        grid = proper.prop_cubic_conv(dm_grid.T, xdm, ydm, GRID = False)
        grid = grid.reshape([xdm.shape[1], xdm.shape[0]])
    else:
        grid = map_coordinates(dm_grid.T, [xdm, ydm], order=3,
                               mode="nearest", prefilter = True)

    dmap = np.zeros([n, n], dtype=np.float64)
    nx_grid, ny_grid = grid.shape
    xmin, xmax = n // 2 - xdim // 2, n // 2 - xdim // 2 + nx_grid
    ymin, ymax = n // 2 - ydim // 2, n // 2 - ydim // 2 + ny_grid
    dmap[ymin:ymax, xmin:xmax] = grid

    if "NO_APPLY" not in kwargs:
        proper.prop_add_phase(wf, 2 * dmap)  # convert surface to WFE

    return dmap
示例#17
0
def get_intensity(wf_array,
                  sp,
                  logAmp=True,
                  show=False,
                  save=True,
                  phase=False):

    if show == True:
        wfo = wf_array[0, 0]
        after_dm = proper.prop_get_amplitude(wfo)
        phase_afterdm = proper.prop_get_phase(wfo)

        fig = plt.figure(figsize=(14, 10))
        ax1 = plt.subplot2grid((3, 2), (0, 0), rowspan=2)
        ax2 = plt.subplot2grid((3, 2), (0, 1), rowspan=2)
        ax3 = plt.subplot2grid((3, 2), (2, 0))
        ax4 = plt.subplot2grid((3, 2), (2, 1))
        if logAmp:
            ax1.imshow(after_dm,
                       origin='lower',
                       cmap="YlGnBu_r",
                       norm=LogNorm())
        else:
            ax1.imshow(after_dm, origin='lower', cmap="YlGnBu_r")
        ax2.imshow(phase_afterdm, origin='lower',
                   cmap="YlGnBu_r")  #, vmin=-0.5, vmax=0.5)

        ax3.plot(after_dm[int(tp.grid_size / 2)])
        ax3.plot(np.sum(np.eye(tp.grid_size) * after_dm, axis=1))

        # plt.plot(np.sum(after_dm,axis=1)/after_dm[128,128])

        ax4.plot(phase_afterdm[int(tp.grid_size / 2)])
        # ax4.plot(np.sum(np.eye(tp.grid_size)*phase_afterdm,axis=1))
        plt.xlim([0, proper.prop_get_gridsize(wfo)])
        fig.set_tight_layout(True)

        plt.show()

    if save:
        shape = wf_array.shape
        ws = sp.get_ints['w']
        cs = sp.get_ints['c']

        int_maps = np.empty((0, tp.grid_size, tp.grid_size))
        for iw in ws:
            for iwf in cs:
                # int_maps.append(proper.prop_shift_center(np.abs(wf_array[iw, iwf].wfarr) ** 2))
                if phase:
                    int_map = proper.prop_get_phase(wf_array[iw, iwf])

                # int_map = proper.prop_shift_center(np.abs(wf_array[iw, iwf].wfarr) ** 2)
                else:
                    int_map = proper.prop_shift_center(
                        np.abs(wf_array[iw, iwf].wfarr)**2)
                int_maps = np.vstack((int_maps, [int_map]))
                # quicklook_im(int_map)#, logAmp=True)

        # int_maps = np.array(int_maps)
        # view_datacube(int_maps)
        import pickle, os
        if os.path.exists(iop.int_maps):
            # "with" statements are very handy for opening files.
            with open(iop.int_maps, 'rb') as rfp:
                # dprint(np.array(pickle.load(rfp)).shape)
                int_maps = np.vstack((int_maps, pickle.load(rfp)))

        with open(iop.int_maps, 'wb') as wfp:
            pickle.dump(int_maps, wfp, protocol=pickle.HIGHEST_PROTOCOL)
示例#18
0
def vortex(wfo, CAL, charge, f_lens, path, Debug_print):

    n = int(proper.prop_get_gridsize(wfo))
    ofst = 0  # no offset
    ramp_sign = 1  #sign of charge is positive
    #sampling = n
    ramp_oversamp = 11.  # vortex is oversampled for a better discretization

    if charge != 0:
        if CAL == 1:  # create the vortex for a perfectly circular pupil
            if (Debug_print == True):
                print("CAL:1, charge ", charge)
            writefield(path, 'zz_psf', wfo.wfarr)  # write the pre-vortex field
            nramp = int(n * ramp_oversamp)  #oversamp
            # create the vortex by creating a matrix (theta) representing the ramp (created by atan 2 gradually varying matrix, x and y)
            y1 = np.ones((nramp, ), dtype=np.int)
            y2 = np.arange(0, nramp, 1.) - (nramp / 2) - int(ramp_oversamp) / 2
            y = np.outer(y2, y1)
            x = np.transpose(y)
            theta = np.arctan2(y, x)
            x = 0
            y = 0
            #vvc_tmp_complex = np.array(np.zeros((nramp,nramp)), dtype=complex)
            #vvc_tmp_complex.imag = ofst + ramp_sign*charge*theta
            #vvc_tmp = np.exp(vvc_tmp_complex)
            vvc_tmp = np.exp(1j * (ofst + ramp_sign * charge * theta))
            theta = 0
            vvc_real_resampled = cv2.resize(
                vvc_tmp.real, (0, 0),
                fx=1 / ramp_oversamp,
                fy=1 / ramp_oversamp,
                interpolation=cv2.INTER_LINEAR
            )  # scale the pupil to the pupil size of the simualtions
            vvc_imag_resampled = cv2.resize(
                vvc_tmp.imag, (0, 0),
                fx=1 / ramp_oversamp,
                fy=1 / ramp_oversamp,
                interpolation=cv2.INTER_LINEAR
            )  # scale the pupil to the pupil size of the simualtions
            vvc = np.array(vvc_real_resampled, dtype=complex)
            vvc.imag = vvc_imag_resampled
            vvcphase = np.arctan2(vvc.imag,
                                  vvc.real)  # create the vortex phase
            vvc_complex = np.array(np.zeros((n, n)), dtype=complex)
            vvc_complex.imag = vvcphase
            vvc = np.exp(vvc_complex)
            vvc_tmp = 0.
            writefield(path, 'zz_vvc',
                       vvc)  # write the theoretical vortex field
            wfo0 = wfo
            proper.prop_multiply(wfo, vvc)
            proper.prop_propagate(wfo, f_lens, 'OAP2')
            proper.prop_lens(wfo, f_lens)
            proper.prop_propagate(wfo, f_lens, 'forward to Lyot Stop')
            proper.prop_circular_obscuration(
                wfo, 1., NORM=True)  # null the amplitude iside the Lyot Stop
            proper.prop_propagate(wfo, -f_lens)  # back-propagation
            proper.prop_lens(wfo, -f_lens)
            proper.prop_propagate(wfo, -f_lens)
            writefield(path, 'zz_perf',
                       wfo.wfarr)  # write the perfect-result vortex field
            wfo = wfo0
        else:
            if (Debug_print == True):
                print("CAL:0, charge ", charge)
            vvc = readfield(path,
                            'zz_vvc')  # read the theoretical vortex field
            vvc = proper.prop_shift_center(vvc)
            scale_psf = wfo._wfarr[0, 0]
            psf_num = readfield(path, 'zz_psf')  # read the pre-vortex field
            psf0 = psf_num[0, 0]
            psf_num = psf_num / psf0 * scale_psf
            perf_num = readfield(
                path, 'zz_perf')  # read the perfect-result vortex field
            perf_num = perf_num / psf0 * scale_psf
            wfo._wfarr = (
                wfo._wfarr - psf_num
            ) * vvc + perf_num  # the wavefront takes into account the real pupil with the perfect-result vortex field

    return
示例#19
0
    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
示例#20
0
def pupil(wfo, CAL, npupil, diam, r_obstr, spiders_width, spiders_angle,
          pupil_file, missing_segments_number, Debug, Debug_print):

    n = int(proper.prop_get_gridsize(wfo))

    if (missing_segments_number == 0):
        if (isinstance(pupil_file, (list, tuple, np.ndarray)) == True):
            pupil = pupil_file
            pupil_pixels = (pupil.shape)[0]  ## fits file size
            scaling_factor = float(npupil) / float(
                pupil_pixels
            )  ## scaling factor between the fits file size and the pupil size of the simulation
            if (Debug_print == True):
                print("scaling_factor: ", scaling_factor)
            pupil_scale = cv2.resize(
                pupil.astype(np.float32), (0, 0),
                fx=scaling_factor,
                fy=scaling_factor,
                interpolation=cv2.INTER_LINEAR
            )  # scale the pupil to the pupil size of the simualtions
            if (Debug_print == True):
                print("pupil_resample", pupil_scale.shape)
            pupil_large = np.zeros(
                (n, n))  # define an array of n-0s, where to insert the pupuil
            if (Debug_print == True):
                print("n: ", n)
                print("npupil: ", npupil)
            pupil_large[
                int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 +
                int(npupil / 2),
                int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 +
                int(npupil / 2
                    )] = pupil_scale  # insert the scaled pupil into the 0s grid

        proper.prop_circular_aperture(
            wfo, diam / 2)  # create a wavefront with a circular pupil

        if CAL == 0:  # CAL=1 is for the back-propagation
            if (isinstance(pupil_file, (list, tuple, np.ndarray)) == True):
                proper.prop_multiply(wfo,
                                     pupil_large)  # multiply the saved pupil
            else:
                proper.prop_circular_obscuration(
                    wfo, r_obstr, NORM=True
                )  # create a wavefront with a circular central obscuration
            if (spiders_width != 0):
                for iter in range(0, len(spiders_angle)):
                    proper.prop_rectangular_obscuration(
                        wfo,
                        spiders_width,
                        2 * diam,
                        ROTATION=spiders_angle[iter])  # define the spiders

    else:
        PACKAGE_PATH = os.path.abspath(os.path.join(__file__, os.pardir))
        if (missing_segments_number == 1):
            pupil = fits.getdata(
                PACKAGE_PATH +
                '/ELT_2048_37m_11m_5mas_nospiders_1missing_cut.fits')
        if (missing_segments_number == 2):
            pupil = fits.getdata(
                PACKAGE_PATH +
                '/ELT_2048_37m_11m_5mas_nospiders_2missing_cut.fits')
        if (missing_segments_number == 4):
            pupil = fits.getdata(
                PACKAGE_PATH +
                '/ELT_2048_37m_11m_5mas_nospiders_4missing_cut.fits')
        if (missing_segments_number == 7):
            pupil = fits.getdata(
                PACKAGE_PATH +
                '/ELT_2048_37m_11m_5mas_nospiders_7missing_1_cut.fits')

        pupil_pixels = (pupil.shape)[0]  ## fits file size
        scaling_factor = float(npupil) / float(
            pupil_pixels
        )  ## scaling factor between the fits file size and the pupil size of the simulation
        if (Debug_print == True):
            print("scaling_factor: ", scaling_factor)
        pupil_scale = cv2.resize(
            pupil.astype(np.float32), (0, 0),
            fx=scaling_factor,
            fy=scaling_factor,
            interpolation=cv2.INTER_LINEAR
        )  # scale the pupil to the pupil size of the simualtions
        if (Debug_print == True):
            print("pupil_resample", pupil_scale.shape)
        pupil_large = np.zeros(
            (n, n))  # define an array of n-0s, where to insert the pupuil
        if (Debug_print == True):
            print("n: ", n)
            print("npupil: ", npupil)
        pupil_large[
            int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 +
            int(npupil / 2),
            int(n / 2) + 1 - int(npupil / 2) - 1:int(n / 2) + 1 +
            int(npupil /
                2)] = pupil_scale  # insert the scaled pupil into the 0s grid

        if CAL == 0:  # CAL=1 is for the back-propagation
            proper.prop_multiply(wfo, pupil_large)  # multiply the saved pupil
            if (spiders_width != 0):
                for iter in range(0, len(spiders_angle)):
                    proper.prop_rectangular_obscuration(
                        wfo,
                        spiders_width,
                        2 * diam,
                        ROTATION=spiders_angle[iter])  # define the spiders

    return
 def build_m1_opd():
     return gen_opdmap(opd1_func, proper.prop_get_gridsize(wfo), proper.prop_get_sampling(wfo))
示例#22
0
def apodization(wf,
                RAVC=False,
                phase_apodizer_file=0,
                amplitude_apodizer_file=0,
                apodizer_misalignment=0,
                Debug_print=False):

    r_obstr = 0.15  #0.15#conf['R_OBSTR']
    npupil = 1  #conf['NPUPIL']

    apodizer_misalignment = np.zeros((6))  #np.array(conf['RAVC_MISALIGN'])
    n = int(proper.prop_get_gridsize(wf))
    apodizer = 1
    if (RAVC == True):
        t1_opt = 1. - 1. / 4 * (
            r_obstr**2 + r_obstr * (math.sqrt(r_obstr**2 + 8.))
        )  # define the apodizer transmission [Mawet2013]
        R1_opt = (r_obstr / math.sqrt(1. - t1_opt)
                  )  # define the apodizer radius [Mawet2013]
        if (Debug_print == True):
            print("r1_opt: ", R1_opt)
            print("t1_opt: ", t1_opt)
        apodizer = circular_apodization(wf,
                                        R1_opt,
                                        1.,
                                        t1_opt,
                                        xc=apodizer_misalignment[0],
                                        yc=apodizer_misalignment[1],
                                        NORM=True)  # define the apodizer
        apodizer = proper.prop_shift_center(apodizer)
        # quicklook_im(apodizer, logAmp=False)

    if (isinstance(phase_apodizer_file, (list, tuple, np.ndarray)) == True):
        xc_pixels = int(apodizer_misalignment[3] * npupil)
        yc_pixels = int(apodizer_misalignment[4] * npupil)
        apodizer_pixels = (phase_apodizer_file.shape)[0]  ## fits file size
        scaling_factor = float(npupil) / float(
            apodizer_pixels
        )  ## scaling factor between the fits file size and the pupil size of the simulation
        if (Debug_print == True):
            print("scaling_factor: ", scaling_factor)
        apodizer_scale = resize(phase_apodizer_file.astype(np.float32),
                                (npupil, npupil),
                                preserve_range=True,
                                mode='reflect')
        if (Debug_print == True):
            print("apodizer_resample", apodizer_scale.shape)
        apodizer_large = np.zeros(
            (n, n))  # define an array of n-0s, where to insert the pupuil
        if (Debug_print == True):
            print("n: ", n)
            print("npupil: ", npupil)
        apodizer_large[
            int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 +
            int(npupil / 2) + xc_pixels,
            int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 +
            int(npupil / 2) +
            yc_pixels] = apodizer_scale  # insert the scaled pupil into the 0s grid
        apodizer = np.exp(1j * apodizer_large)

    if (isinstance(amplitude_apodizer_file,
                   (list, tuple, np.ndarray)) == True):
        xc_pixels = int(apodizer_misalignment[0] * npupil)
        yc_pixels = int(apodizer_misalignment[1] * npupil)
        apodizer_pixels = (amplitude_apodizer_file.shape)[0]  ## fits file size
        scaling_factor = float(npupil) / float(
            apodizer_pixels
        )  ## scaling factor between the fits file size and the pupil size of the simulation
        if (Debug_print == True):
            print("scaling_factor: ", scaling_factor)
        apodizer_scale = resize(amplitude_apodizer_file.astype(np.float32),
                                (npupil, npupil),
                                preserve_range=True,
                                mode='reflect')
        if (Debug_print == True):
            print("apodizer_resample", apodizer_scale.shape)
        apodizer_large = np.zeros(
            (n, n))  # define an array of n-0s, where to insert the pupuil
        if (Debug_print == True):
            print("n: ", n)
            print("npupil: ", npupil)
        apodizer_large[
            int(n / 2) + 1 - int(npupil / 2) - 1 + xc_pixels:int(n / 2) + 1 +
            int(npupil / 2) + xc_pixels,
            int(n / 2) + 1 - int(npupil / 2) - 1 + yc_pixels:int(n / 2) + 1 +
            int(npupil / 2) +
            yc_pixels] = apodizer_scale  # insert the scaled pupil into the 0s grid
        apodizer = apodizer_large
    # quicklook_wf(wf)
    proper.prop_multiply(wf, apodizer)
    # quicklook_wf(wf)

    return wf