Esempio n. 1
0
def normalise_sampling(wavefronts, samplings, common_sampling, npsf):
    """Resample each wavefront to a common grid
    
    Parameters
    ----------
    wavefronts : list of PROPER WaveFront class objects
        The wavefronts to be resampled
        
    samplings: list of floats 
        wavefront samplings for each wavefront, in metres
    
    common_sampling : float
        Target wavefront sampling in metres
        
    npsf : int
        Dimension of new image (npsf by npsf).
        
    Returns
    -------
    out : numpy ndarray
        Returns stack of wavefronts with common dimensions and sampling.
    """
    n = len(wavefronts)
    out = np.zeros([n, npsf, npsf], dtype=np.float64)
    # Resample and weight
    for i in range(n):
        wf = wavefronts[i]  # np.abs(wavefronts[i])**2
        mag = samplings[i] / common_sampling
        out[i, :, :] = proper.prop_magnify(wf, mag, npsf, CONSERVE=True)
    return out
Esempio n. 2
0
def fix_prop_pixellate(image_in,
                       sampling_in,
                       sampling_out,
                       n_out=0,
                       offset=None):
    """Integrate a sampled PSF onto detector pixels. 
    
    This routine takes as input a sampled PSF and integrates it over pixels of a 
    specified size. This is done by convolving the Fourier transform of the input 
    PSF with a sinc function representing the transfer function of an idealized 
    square pixel and transforming back. This result then represents the PSF 
    integrated onto detector-sized pixels with the same sampling as the PSF. 
    The result is interpolated to get detector-sized pixels at detector-pixel 
    spacing.
    
    Parameters
    ----------
    image_in : numpy ndarray
        2D floating image containing PSF
        
    sampling_in : float
        Sampling of image_in in meters/pixel
        
    sampling_out : float
        Size(=sampling) of detector pixels
        
    n_out : int
        Output image dimension (n_out by n_out)
        
    offset: tuple
        image offset in metres, as (dx,dy)
        
    Returns
    -------
    new : numpy ndarray
        Returns image integrated over square detector pixels.
    """
    n_in = image_in.shape[0]

    w = int(n_in / 2)

    # Compute pixel transfer function (MTF)
    psize = 0.5 * (sampling_out / sampling_in)
    mag = sampling_in / sampling_out
    t = np.roll(np.arange(-w, w, dtype=np.float64), -w, 0) * psize * np.pi / w
    #    t = np.arange(-w, w, dtype = np.float64) * psize * np.pi / w
    y = np.zeros(n_in, dtype=np.float64)
    y[1:] = np.sin(t[1:]) / t[1:]
    y[0] = 1.
    y = np.roll(y, +w, 0)

    pixel_mtf = np.dot(y[:, np.newaxis], y[np.newaxis, :])

    # Convolve image with detector pixel
    image_mtf = np.fft.fftshift(fft2(image_in))

    # shift image
    if offset is not None:
        (dx, dy) = offset
        vals = np.arange(-w, w)
        xoff, yoff = np.meshgrid(vals, vals)
        image_mtf *= np.exp(-1j * 2 * np.pi * (xoff * dx + yoff * dy) /
                            (n_in * sampling_in))

    image_mtf /= image_in.size
    image_mtf *= pixel_mtf

    convolved_image = np.abs(
        ifft2(np.fft.ifftshift(image_mtf)) * image_mtf.size)
    image_mtf = 0
    convolved_image /= mag**2  #np.fft.ifftshift(convolved_image/mag**2)
    # Image is integrated over pixels but has original sampling; now, resample
    # pixel sampling
    if n_out == 0:
        n_out = int(np.fix(n_in * mag))

    new = proper.prop_magnify(convolved_image, mag, n_out)

    return new
Esempio n. 3
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
Esempio n. 4
0
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 wfirst_phaseb_compact(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']

    cor_type = 'hlc'  # coronagraph type ('hlc', 'spc', 'none')
    input_field_rootname = ''  # rootname of files containing aberrated pupil
    polaxis = 0  # polarization condition (only used with input_field_rootname)
    source_x_offset = 0  # source offset in lambda0_m/D radians (tilt applied at primary)
    source_y_offset = 0
    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)
    fpm_axis = 'p'  # HLC FPM axis: '', 's', 'p'
    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 'cor_type' in PASSVALUE: cor_type = PASSVALUE['cor_type']
        if 'fpm_axis' in PASSVALUE: fpm_axis = PASSVALUE['fpm_axis']

    is_hlc = False
    is_spc = 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.69pol' + fpm_axis + '_'
            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_small = 1024  # gridsize in non-critical areas
        n_big = 2048  # gridsize to/from FPM
    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'
        ]
        fpm_axis = 's'
        lam_occs = [
            prefix + 'occ_lam' + s + 'theta6.69pol' + fpm_axis + '_'
            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_small = 1024  # gridsize in non-critical areas
        n_big = 2048  # gridsize to/from FPM
    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_rotated.fits'
        fpm_file = file_dir + 'fpm_0.05lamdivD.fits'
        fpm_sampling = 0.05  # sampling in lambda0/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 + 'lyotstop_0.5mag.fits'
        n_small = 2048  # gridsize in non-critical areas
        n_big = 1400  # gridsize to FPM (propagation to/from FPM handled by MFT)
    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_rotated.fits'
        fpm_file = file_dir + 'fpm_0.05lamdivD.fits'
        fpm_sampling = 0.05  # sampling in lambda0/D of FPM mask
        lyot_stop_file = file_dir + 'LS_half_symm_CGI180718_Str3.20pct_38D91_N500_pixel.fits'
        fpm_sampling_lambda_m = 0.825e-6
        lambda0_m = 0.825e-6  # FPM scaled for this central wavelength
        n_small = 2048  # gridsize in non-critical areas
        n_big = 1400  # gridsize to FPM (propagation to/from FPM handled by MFT)
    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'
        use_fpm = 0
        use_lyot_stop = 0
        n_small = 1024
        n_big = 1024
    else:
        raise Exception('wfirst_phaseb_compact: Unsuported 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']
        if 'input_field_rootname' in PASSVALUE:
            input_field_rootname = PASSVALUE['input_field_rootname']
        if 'polaxis' in PASSVALUE: polaxis = PASSVALUE['polaxis']
        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 '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 'final_sampling_lam0' in PASSVALUE:
            final_sampling_lam0 = PASSVALUE['final_sampling_lam0']
        if 'output_dim' in PASSVALUE: output_dim = PASSVALUE['output_dim']

    if polaxis != 0 and input_field_rootname == '':
        raise Exception(
            'wfirst_phaseb_compact: polaxis can only be defined when input_field_rootname is given'
        )

    diam_at_dm1 = 0.0463
    d_dm1_dm2 = 1.0

    n = n_small  # start off with less padding

    wavefront = proper.prop_begin(diam_at_dm1, lambda_m, n,
                                  float(pupil_diam_pix) / n)
    if input_field_rootname == '':
        pupil = proper.prop_fits_read(pupil_file)
        proper.prop_multiply(wavefront, trim(pupil, n))
        pupil = 0
    else:
        lams = format(lambda_m * 1e6, "6.4f")
        pols = format(int(round(polaxis)))
        rval = proper.prop_fits_read(input_field_rootname + '_' + lams +
                                     'um_' + pols + '_real.fits')
        ival = proper.prop_fits_read(input_field_rootname + '_' + lams +
                                     'um_' + pols + '_imag.fits')
        proper.prop_multiply(wavefront, trim(rval + 1j * ival, n))
        rval = 0
        ival = 0
    proper.prop_define_entrance(wavefront)
    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 use_dm1 != 0:
        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 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:
        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 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_dm1_dm2, 'back to DM1')

    (wavefront, sampling_m) = proper.prop_end(wavefront, NOABS=True)

    # apply shape pupil mask

    if is_spc == True:
        pupil_mask = proper.prop_fits_read(pupil_mask_file)
        wavefront *= trim(pupil_mask, n)
        pupil_mask = 0

    # propagate to FPM and apply FPM

    if is_hlc == True:
        n = n_big
        wavefront = trim(wavefront, n)
        wavefront = ffts(wavefront, -1)  # to focus
        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)
        wavefront *= trim(occ, n)
        occ_r = 0
        occ_i = 0
        occ = 0
        wavefront = ffts(wavefront, +1)  # to lyot stop
    elif is_spc == True:
        n = n_big
        wavefront = trim(wavefront, n)
        fpm = proper.prop_fits_read(fpm_file)
        nfpm = fpm.shape[1]
        fpm_sampling_lam = fpm_sampling * fpm_sampling_lambda_m / lambda_m
        wavefront = mft2(wavefront, fpm_sampling_lam, pupil_diam_pix, nfpm,
                         -1)  # MFT to highly-sampled focal plane
        wavefront *= fpm
        fpm = 0
        pupil_diam_pix = pupil_diam_pix / 2.0  # Shrink pupil by 1/2
        wavefront = mft2(wavefront, fpm_sampling_lam, pupil_diam_pix,
                         int(pupil_diam_pix),
                         +1)  # MFT to Lyot stop with 1/2 magnification

    n = n_small
    wavefront = trim(wavefront, n)
    lyot = proper.prop_fits_read(lyot_stop_file)
    wavefront *= trim(lyot, n)
    lyot = 0

    wavefront *= n
    wavefront = ffts(wavefront, -1)  # to focus

    # rotate to convention used by full prescription

    wavefront[:, :] = np.rot90(wavefront, 2)
    wavefront[:, :] = np.roll(wavefront, 1, axis=0)
    wavefront[:, :] = np.roll(wavefront, 1, axis=1)

    if final_sampling_lam0 != 0:
        mag = (float(pupil_diam_pix) / n) / final_sampling_lam0 * (lambda_m /
                                                                   lambda0_m)
        wavefront = proper.prop_magnify(wavefront,
                                        mag,
                                        output_dim,
                                        AMP_CONSERVE=True)
    else:
        wavefront = trim(wavefront, output_dim)

    sampling_m = 0.0
    return wavefront, sampling_m