Beispiel #1
0
def _sample_flux_snr(distances,
                     fwhm,
                     plsc,
                     n_injections,
                     flux_min,
                     flux_max,
                     nproc=10,
                     random_seed=42,
                     wavelengths=None,
                     spectrum=None,
                     mode='median',
                     scaling='temp-standard',
                     svd_mode='randsvd'):
    """
    Sensible flux intervals depend on a combination of factors, # of frames,
    range of rotation, correlation, glare intensity.
    """
    if GARRAY.ndim == 3:
        frsize = int(GARRAY.shape[1])
    elif GARRAY.ndim == 4:
        frsize = int(GARRAY.shape[2])
    ninj = n_injections
    random_state = np.random.RandomState(random_seed)
    flux_dist_theta_all = list()
    snrs_list = list()
    fluxes_list = list()
    n_ks = 3

    for i, d in enumerate(distances):
        yy, xx = get_annulus_segments((frsize, frsize), d, 1, 1)[0]
        num_patches = yy.shape[0]

        fluxes_dist = random_state.uniform(flux_min[i], flux_max[i], size=ninj)
        inds_inj = random_state.randint(0, num_patches, size=ninj)

        for j in range(ninj):
            injx = xx[inds_inj[j]]
            injy = yy[inds_inj[j]]
            injx -= frame_center(GARRAY[0])[1]
            injy -= frame_center(GARRAY[0])[0]
            dist = np.sqrt(injx**2 + injy**2)
            theta = np.mod(np.arctan2(injy, injx) / np.pi * 180, 360)
            flux_dist_theta_all.append((fluxes_dist[j], dist, theta))

    # multiprocessing (pool) for each distance
    res = pool_map(nproc, _get_adi_snrs, GARRPSF, GARRPA, fwhm, plsc,
                   iterable(flux_dist_theta_all), wavelengths, spectrum, mode,
                   n_ks, scaling, svd_mode)

    for i in range(len(distances)):
        flux_dist = []
        snr_dist = []
        for j in range(ninj):
            flux_dist.append(res[j + (ninj * i)][0])
            snr_dist.append(res[j + (ninj * i)][1])
        fluxes_list.append(flux_dist)
        snrs_list.append(snr_dist)

    return fluxes_list, snrs_list
Beispiel #2
0
def create_synt_cube(cube,
                     psf,
                     ang,
                     plsc,
                     dist=None,
                     theta=None,
                     flux=None,
                     random_seed=42,
                     verbose=False):
    """
    """
    centy_fr, centx_fr = frame_center(cube[0])
    random_state = np.random.RandomState(random_seed)
    if theta is None:
        theta = random_state.randint(0, 360)

    posy = dist * np.sin(np.deg2rad(theta)) + centy_fr
    posx = dist * np.cos(np.deg2rad(theta)) + centx_fr
    if verbose:
        print('Theta:', theta)
        print('Flux_inj:', flux)
    cubefc = cube_inject_companions(cube,
                                    psf,
                                    ang,
                                    flevel=flux,
                                    plsc=plsc,
                                    rad_dists=[dist],
                                    n_branches=1,
                                    theta=theta,
                                    verbose=verbose)

    return cubefc, posx, posy
Beispiel #3
0
def prepare_patches(cube,
                    angle_list,
                    xy,
                    fwhm,
                    patch_size_px,
                    delta_rot=0.5,
                    normalization='slice',
                    imlib='opencv',
                    interpolation='bilinear',
                    debug=False):
    """ Prepare patches for SODINN-PW.
    """
    centy_fr, centx_fr = frame_center(cube[0])

    angle_list = check_pa_vector(angle_list)

    xy_dist = dist(centy_fr, centx_fr, xy[1], xy[0])
    res = _pairwise_diff_residuals(cube,
                                   angle_list,
                                   ann_center=xy_dist,
                                   fwhm=fwhm,
                                   delta_rot=delta_rot,
                                   debug=False)

    res_der = cube_derotate(res,
                            angle_list,
                            imlib=imlib,
                            interpolation=interpolation)
    res_der_crop = cube_crop_frames(res_der,
                                    patch_size_px,
                                    xy=xy,
                                    verbose=False,
                                    force=True)

    patches = normalize_01_pw(res_der_crop, normalization)

    if debug:
        print('dist : {}'.format(xy_dist))
        plot_frames(
            tuple(patches),
            axis=False,
            colorbar=False,
        )
    return patches
Beispiel #4
0
def get_indices_annulus(shape,
                        inrad,
                        outrad,
                        mask=None,
                        maskrad=None,
                        verbose=False):
    """ mask is a list of tuples X,Y
    # TODO: documentation
    """
    framemp = np.zeros(shape)
    if mask is not None:
        if not isinstance(mask, list):
            raise TypeError('Mask should be a list of tuples')
        if maskrad is None:
            raise ValueError('Fwhm not given')
        for xy in mask:
            # patch_size/2 diameter aperture
            cir = circle(xy[1], xy[0], maskrad, shape)
            framemp[cir] = 1

    annulus_width = outrad - inrad
    cy, cx = frame_center(framemp)
    yy, xx = np.mgrid[:framemp.shape[0], :framemp.shape[1]]
    circ = np.sqrt((xx - cx)**2 + (yy - cy)**2)
    donut_mask = (circ <= (inrad + annulus_width)) & (circ >= inrad)
    y, x = np.where(donut_mask)
    if mask is not None:
        npix = y.shape[0]
        ymask, xmask = np.where(framemp)  # masked pixels where == 1
        inds = []
        for i, tup in enumerate(zip(y, x)):
            if tup in zip(ymask, xmask):
                inds.append(i)
        y = np.delete(y, inds)
        x = np.delete(x, inds)

    if verbose:
        print(y.shape[0], 'pixels in annulus')
    return y, x
Beispiel #5
0
def test_frame_center():
    frames = 39
    nlambda = 2

    res44 = (2.0, 2.0) # replaced (1.5, 1.5) considering new convention
    res55 = (2.0, 2.0)

    # 2D
    assert frame_center(np.zeros((4, 4))) == res44
    assert frame_center(np.zeros((5, 5))) == res55

    # 3D
    assert frame_center(np.zeros((frames, 4, 4))) == res44
    assert frame_center(np.zeros((frames, 5, 5))) == res55

    # 4D
    assert frame_center(np.zeros((nlambda, frames, 4, 4))) == res44
    assert frame_center(np.zeros((nlambda, frames, 5, 5))) == res55
Beispiel #6
0
def make_mlar_samples_ann_signal(input_array, angle_list, psf, n_samples,
                                 cevr_thresh, n_ks, force_klen, inrad, outrad,
                                 patch_size, flux_low, flux_high, plsc=0.01,
                                 normalize='slice', nproc=1, nproc2=1,
                                 interp='bilinear', lr_mode='eigen',
                                 mode='mlar', kss_window=None, tss_window=None,
                                 random_seed=42, verbose=False):
    """
    n_samples : For ONEs, half_n_samples SVDs

    mask is a list of tuples X,Y

    inputarr is a 3d array or list of 3d arrays

    orig_zero_patches : percentage of original zero patches

    """
    dist_flux_p1 = flux_low
    dist_flux_p2 = flux_high
    collapse_func = np.mean
    scaling = None  # 'temp-mean'
    random_state = np.random.RandomState(random_seed)

    # making ones, injecting companions. The other half of n_samples
    if verbose:
        print("Creating the ONEs samples")

    frsize = int(input_array.shape[1])
    cube = input_array
    # if frsize > outrad + outrad + patch_size + 2:
    #     frsize = int(outrad + outrad + patch_size + 2)
    #     cube = cube_crop_frames(input_array, frsize, force=True,
    #                             verbose=False)

    width = outrad - inrad
    yy, xx = get_annulus_segments((frsize, frsize), inrad, width, 1)[0]
    num_patches = yy.shape[0]

    k_list = get_cumexpvar(cube, 'annular', inrad, outrad, patch_size,
                           k_list=None, cevr_thresh=cevr_thresh, n_ks=n_ks,
                           match_len=force_klen, verbose=False)

    n_req_inject = n_samples
    if mode == 'mlar':
        # 4D: n_samples/2, n_k_list, patch_size, patch_size
        X_ones_array = np.empty((n_req_inject, len(k_list), patch_size,
                                patch_size))
    elif mode == 'tmlar':
        nfr = cube.shape[0]
        X_ones_array = np.empty((n_req_inject, nfr, patch_size, patch_size))

    elif mode == 'tmlar4d':
        nfr = cube.shape[0]
        X_ones_array = np.empty((n_req_inject, nfr, len(k_list), patch_size,
                                 patch_size))

    if verbose:
        print("{} injections".format(n_req_inject))

    if not dist_flux_p2 > dist_flux_p1:
        err_msg = 'dist_flux_p2 must be larger than dist_flux_p1'
        raise ValueError(err_msg)
    fluxes = random_state.uniform(dist_flux_p1, dist_flux_p2,
                                  size=n_req_inject)
    fluxes = np.sort(fluxes)
    inds_inj = random_state.randint(0, num_patches, size=n_req_inject)

    dists = []
    thetas = []
    for m in range(n_req_inject):
        injx = xx[inds_inj[m]]
        injy = yy[inds_inj[m]]
        injx -= frame_center(cube[0])[1]
        injy -= frame_center(cube[0])[0]
        dist = np.sqrt(injx**2+injy**2)
        theta = np.mod(np.arctan2(injy, injx) / np.pi * 180, 360)
        dists.append(dist)
        thetas.append(theta)

    if not nproc:
        nproc = int((cpu_count()/4))

    if nproc == 1:
        for m in range(n_req_inject):
            cufc, cox, coy = create_synt_cube(cube, psf, angle_list,
                                              plsc, theta=thetas[m],
                                              flux=fluxes[m], dist=dists[m],
                                              verbose=False)
            cox = int(np.round(cox))
            coy = int(np.round(coy))

            cube_residuals = svd_decomp(cufc, angle_list, patch_size,
                                        inrad, outrad, scaling, k_list,
                                        collapse_func, neg_ang=False,
                                        lr_mode=lr_mode, nproc=nproc2,
                                        interp=interp, mode=mode)

            # one patch residuals per injection
            X_ones_array[m] = cube_crop_frames(np.asarray(cube_residuals),
                                               patch_size, xy=(cox, coy),
                                               verbose=False, force=True)

    elif nproc > 1:
        if lr_mode in ['cupy', 'randcupy', 'eigencupy']:
            raise RuntimeError('CUPY does not play well with multiproc')

        if get_start_method() == 'fork' and lr_mode in ['pytorch',
                                                        'eigenpytorch',
                                                        'randpytorch']:
            raise RuntimeError("Cannot use pytorch and multiprocessing "
                               "outside main (i.e. from a jupyter cell). "
                               "See: http://pytorch.org/docs/0.3.1/notes/"
                               "multiprocessing.html.")

        flux_dist_theta = zip(fluxes, dists, thetas)

        res = pool_map(nproc, _inject_FC, cube, psf, angle_list, plsc,
                       inrad, outrad, iterable(flux_dist_theta), k_list,
                       scaling, collapse_func, patch_size, lr_mode, interp,
                       mode)

        for m in range(n_req_inject):
            X_ones_array[m] = res[m]

    # Moving-subsampling
    move_subsampling = 'median'
    if mode == 'mlar' and kss_window is not None:
        X_ones_array = cube_move_subsample(X_ones_array, kss_window, axis=1,
                                           mode=move_subsampling)
    elif mode == 'tmlar' and tss_window is not None:
        X_ones_array = cube_move_subsample(X_ones_array, kss_window, axis=1,
                                           mode=move_subsampling)
    elif mode == 'tmlar4d':
        if tss_window is not None:
            X_ones_array = cube_move_subsample(X_ones_array, tss_window,
                                               axis=1, mode=move_subsampling)
        if kss_window is not None:
            X_ones_array = cube_move_subsample(X_ones_array, kss_window,
                                               axis=2, mode=move_subsampling)

    if normalize is not None:
        if mode == 'tmlar4d':
            for i in range(X_ones_array.shape[0]):
                X_ones_array[i] = normalize_01(X_ones_array[i], normalize)
        else:
            X_ones_array = normalize_01(X_ones_array, normalize)

    return X_ones_array.astype('float32')
Beispiel #7
0
def noise_per_annulus(array,
                      separation,
                      fwhm,
                      init_rad=None,
                      wedge=(0, 360),
                      verbose=False,
                      debug=False,
                      mask=None):
    """ Measures the noise as the standard deviation of apertures defined in
    each annulus with a given separation.

    Parameters
    ----------
    array : array_like
        Input frame.
    separation : float
        Separation in pixels of the centers of the annuli measured from the
        center of the frame.
    fwhm : float
        FWHM in pixels.
    init_rad : float
        Initial radial distance to be used. If None then the init_rad = FWHM.
    wedge : tuple of floats, optional
        Initial and Final angles for using a wedge. For example (-90,90) only
        considers the right side of an image. Be careful when using small
        wedges, this leads to computing a standard deviation of very small
        samples (<10 values).
    verbose : bool, optional
        If True prints information.
    debug : bool, optional
        If True plots the positioning of the apertures.

    Returns
    -------
    noise : array_like
        Vector with the noise value per annulus.
    vector_radd : array_like
        Vector with the radial distances values.

    """

    # dprint(fwhm)
    def find_coords(rad, sep, init_angle, fin_angle):
        angular_range = fin_angle - init_angle
        npoints = (np.deg2rad(angular_range) * rad) / sep  #(2*np.pi*rad)/sep
        ang_step = angular_range / npoints  #360/npoints
        x = []
        y = []
        for i in range(int(npoints)):
            newx = rad * np.cos(np.deg2rad(ang_step * i + init_angle))
            newy = rad * np.sin(np.deg2rad(ang_step * i + init_angle))
            x.append(newx)
            y.append(newy)
        return np.array(y), np.array(x)

    ###

    if array.ndim != 2:
        raise TypeError('Input array is not a frame or 2d array')
    if not isinstance(wedge, tuple):
        raise TypeError('Wedge must be a tuple with the initial and final '
                        'angles')

    init_angle, fin_angle = wedge
    centery, centerx = frame_center(array)
    n_annuli = int(np.floor((centery) / separation)) - 1
    noise = []
    vector_radd = []
    if verbose:
        print('{} annuli'.format(n_annuli))

    if init_rad is None:
        init_rad = fwhm

    if debug:
        _, ax = plt.subplots(figsize=(6, 6))
        ax.imshow(array,
                  origin='lower',
                  interpolation='nearest',
                  alpha=0.5,
                  cmap='gray')

    for i in range(n_annuli):
        y = centery + init_rad + separation * i
        rad = dist(centery, centerx, y, centerx)
        yy, xx = find_coords(rad, fwhm, init_angle, fin_angle)
        yy += centery
        xx += centerx

        apertures = photutils.CircularAperture((xx, yy), fwhm / 2)
        mask = np.bool_(mask)
        # quick2D(mask)
        if mask:
            fluxes = photutils.aperture_photometry(array, apertures, mask=mask)
        else:
            fluxes = photutils.aperture_photometry(array, apertures)
        fluxes = np.array(fluxes['aperture_sum'])

        noise_ann = np.std(fluxes)
        noise.append(noise_ann)
        vector_radd.append(rad)
        # if debug:
        #     if i == 1:
        #         plt.plot(fluxes)

        # plt.figure()
        # plt.hist(fluxes, bins=50)

        if debug:
            for j in range(xx.shape[0]):
                # Circle takes coordinates as (X,Y)
                aper = plt.Circle((xx[j], yy[j]),
                                  radius=fwhm / 2,
                                  color='r',
                                  fill=False,
                                  alpha=0.8)
                ax.add_patch(aper)
                cent = plt.Circle((xx[j], yy[j]),
                                  radius=0.8,
                                  color='r',
                                  fill=True,
                                  alpha=0.5)
                ax.add_patch(cent)

        if verbose:
            print('Radius(px) = {}, Noise = {:.3f} '.format(rad, noise_ann))

    return np.array(noise), np.array(vector_radd)
Beispiel #8
0
    def __init__(self,
                 cube,
                 psf,
                 distances,
                 angles,
                 fwhm,
                 plsc,
                 wavelengths=None,
                 spectrum=None,
                 n_injections=30,
                 algo='pca',
                 min_snr=1,
                 max_snr=3,
                 inter_extrap=False,
                 svd_mode='randsvd',
                 inter_extrap_dist=None,
                 random_seed=42,
                 n_proc=2):
        """ Initialization of the flux estimator object.
        """
        global GARRAY
        global GARRPSF
        global GARRWL
        global GARRPA
        GARRAY = cube
        GARRPSF = psf
        GARRPA = angles
        GARRWL = wavelengths

        check_array(cube, dim=(3, 4), msg='cube')
        check_array(psf, dim=(2, 3), msg='psf')
        check_array(angles, dim=1, msg='angles')
        check_array(distances, dim=1, msg='distances')

        if isinstance(min_snr, (tuple, list)):
            if not len(min_snr) == len(distances):
                raise ValueError('`min_snr` length does not match `distances`')
        elif isinstance(min_snr, (int, float)):
            min_snr = [min_snr] * len(distances)
        else:
            raise TypeError('`min_snr` must be a float/int or a list/tuple')

        if isinstance(max_snr, (tuple, list)):
            if not len(max_snr) == len(distances):
                raise ValueError('`max_snr` length does not match `distances`')
        elif isinstance(max_snr, (int, float)):
            max_snr = [max_snr] * len(distances)
        else:
            raise TypeError('`max_snr` must be a float/int or a list/tuple')

        if cube.ndim == 4:
            if wavelengths is None:
                raise ValueError('`wavelengths` must be provided when `cube` '
                                 'is a 4d array')
            if spectrum is None:
                raise ValueError(
                    '`spectrum` must be provided when `cube` is a '
                    '4d array')
            check_array(wavelengths, dim=1, msg='wavelengths')
            check_array(spectrum, dim=1, msg='spectrum')

            cy, cx = frame_center(cube)
            maxd = cy - 5 * fwhm
            if not max(distances) <= maxd:
                raise ValueError('`distances` contains a value that is too '
                                 'high wrt the frame size. Values must be '
                                 'smaller than {:.2f}'.format(maxd))

        self.starttime = time_ini()
        self.min_fluxes = None
        self.max_fluxes = None
        self.radprof = None
        self.sampled_fluxes = None
        self.sampled_snrs = None
        self.estimated_fluxes_low = None
        self.estimated_fluxes_high = None
        self.distances = distances
        self.angles = angles
        self.fwhm = fwhm
        self.plsc = plsc
        if cube.ndim == 4:
            self.scaling = 'temp-standard'
        elif cube.ndim == 3:
            self.scaling = None
        self.wavelengths = wavelengths
        self.spectrum = spectrum
        self.n_injections = n_injections
        self.algo = algo
        self.svd_mode = svd_mode
        self.min_snr = min_snr
        self.max_snr = max_snr
        self.random_seed = random_seed
        self.n_proc = n_proc
        self.inter_extrap = inter_extrap
        self.inter_extrap_dist = inter_extrap_dist
        self.n_dist = range(len(self.distances))
        self.fluxes_list = list()
        self.snrs_list = list()
Beispiel #9
0
def _get_adi_snrs(psf,
                  angle_list,
                  fwhm,
                  plsc,
                  flux_dist_theta_all,
                  wavelengths=None,
                  spectrum=None,
                  mode='pca',
                  n_ks=3,
                  scaling='temp-standard',
                  svd_mode='randsvd',
                  debug=False):
    """ Get the mean S/N (at 3 equidistant positions) for a given flux and
    distance, on a residual frame.
    """
    theta = flux_dist_theta_all[2]
    flux = flux_dist_theta_all[0]
    dist = flux_dist_theta_all[1]

    if GARRAY.ndim == 3:
        spectrum = 1
    elif GARRAY.ndim == 4:
        # grey spectrum (same flux in all wls)
        if spectrum is None:
            spectrum = np.ones((GARRAY.shape[0]))

    snrs = []
    # 3 equidistant azimuthal positions, 1 or several K values
    for ang in [theta, theta + 120, theta + 240]:
        cube_fc, pos = cube_inject_companions(GARRAY,
                                              psf,
                                              angle_list,
                                              flevel=flux * spectrum,
                                              plsc=plsc,
                                              rad_dists=[dist],
                                              theta=ang,
                                              verbose=False,
                                              full_output=True)
        posy, posx = pos[0]
        fr_temp = _compute_residual_frame(cube_fc, angle_list, dist, fwhm,
                                          wavelengths, mode, n_ks, svd_mode,
                                          scaling, 'median', 'opencv',
                                          'bilinear')
        # handling the case of mode='median'
        if isinstance(fr_temp, np.ndarray):
            fr_temp = [fr_temp]
        snrs_ks = []
        for i in range(len(fr_temp)):
            res = snr(fr_temp[i],
                      source_xy=(posx, posy),
                      fwhm=fwhm,
                      exclude_negative_lobes=True)
            snrs_ks.append(res)

        maxsnr_ks = max(snrs_ks)
        if np.isinf(maxsnr_ks) or np.isnan(maxsnr_ks) or maxsnr_ks < 0:
            maxsnr_ks = 0.01

        snrs.append(maxsnr_ks)

        if debug:
            print(' ')
            cy, cx = frame_center(GARRAY[0])
            label = 'Flux: {:.1f}, Max S/N: {:.2f}'.format(flux, maxsnr_ks)
            hp.plot_frames(tuple(np.array(fr_temp)),
                           axis=False,
                           horsp=0.05,
                           colorbar=False,
                           circle=((posx, posy), (cx, cy)),
                           circle_radius=(5, dist),
                           label=label,
                           dpi=60)

    # max of mean S/N at 3 equidistant positions
    snr_value = np.max(snrs)

    return flux, snr_value
def find_AGPM_or_star(self,
                      file_list,
                      rel_AGPM_pos_xy=(50.5, 6.5),
                      size=101,
                      verbose=True,
                      debug=False):
    """
        added by Iain to prevent dust grains being picked up as the AGPM
        
        This method will find the location of the AGPM or star (even when sky frames are mixed with science frames), by
        using the known relative distance of the AGPM from the frame center in all VLT/NaCO datasets. It then creates a
        subset square image around the expected location and applies a low pass filter + max search method and returns
        the (y,x) location of the AGPM/star
        
        Parameters
        ----------
        file_list : list of str
            List containing all science cube names
        rel_AGPM_pos_xy : tuple, float
            relative location of the AGPM from the frame center in pixels, should be left unchanged. This is used to
            calculate how many pixels in x and y the AGPM is from the center and can be applied to almost all datasets
            with VLT/NaCO as the AGPM is always in the same approximate position
        size : int
            pixel dimensions of the square to sample for the AGPM/star (ie size = 100 is 100 x 100 pixels)
        verbose : bool
            If True extra messages are shown
        debug : bool, False by default
            Enters pdb once the location has been found

        Returns
        ----------
        [ycom, xcom] : location of AGPM or star        
        """
    sci_cube = open_fits(self.inpath +
                         file_list[0])  # opens first sci/sky cube
    nz, ny, nx = sci_cube.shape  # gets size of it. science and sky cubes have same shape. assumes all cubes are the same ny and nx (they should be!)

    cy, cx = frame_center(sci_cube,
                          verbose=verbose)  # find central pixel coordinates
    # then the position will be that plus the relative shift in y and x
    rel_shift_x = rel_AGPM_pos_xy[
        0]  # 50.5 is pixels from frame center to AGPM in x in an example data set, thus providing the relative shift
    rel_shift_y = rel_AGPM_pos_xy[
        1]  # 6.5 is pixels from frame center to AGPM in y in an example data set, thus providing the relative shift

    # the center of the square to apply the low pass filter to - is the approximate position of the AGPM/star based on previous observations
    y_tmp = cy + rel_shift_y
    x_tmp = cx + rel_shift_x
    median_all_cubes = np.zeros([len(file_list), ny, nx])  # makes empty array
    for sc, fits_name in enumerate(file_list):  # loops over all images
        tmp = open_fits(self.inpath + fits_name,
                        verbose=debug)  # opens the cube
        median_all_cubes[sc] = tmp[
            -1]  # takes the last entry (the median) and adds it to the empty array
    median_frame = np.median(median_all_cubes,
                             axis=0)  # median of all median frames

    # define a square of 100 x 100 with the center being the approximate AGPM/star position
    median_frame, cornery, cornerx = get_square(median_frame,
                                                size=size,
                                                y=y_tmp,
                                                x=x_tmp,
                                                position=True,
                                                verbose=True)
    # apply low pass filter
    median_frame = frame_filter_lowpass(median_frame,
                                        median_size=7,
                                        mode='median')
    median_frame = frame_filter_lowpass(median_frame,
                                        mode='gauss',
                                        fwhm_size=5)
    # find coordiates of max flux in the square
    ycom_tmp, xcom_tmp = np.unravel_index(np.argmax(median_frame),
                                          median_frame.shape)
    # AGPM/star is the bottom-left corner coordinates plus the location of the max in the square
    ycom = cornery + ycom_tmp
    xcom = cornerx + xcom_tmp

    if verbose:
        print('The location of the AGPM/star is', 'ycom =', ycom, 'xcom =',
              xcom)
    if debug:
        pdb.set_trace()
    return [ycom, xcom]
Beispiel #11
0
    def do_negfc(self, do_firstguess=True, guess_xy=None, mcmc_negfc=True, inject_neg=True, ncomp=1, algo='pca_annular',
                 nwalkers_ini=120, niteration_min=25, niteration_limit=10000, delta_rot=(0.5, 3), weights=False,
                 coronagraph=False, overwrite=True, save_plot=True, verbose=True):
        """
        Module for estimating the location and flux of a planet. A sub-folder 'negfc' is created for storing all
        output files.

        Using a first guess from the (x,y) coordinates in pixels for the planet, we can estimate a preliminary guess for
        the position and flux for each planet using a Nelder-Mead minimization. Saves the r, theta and flux

        If using MCMC, runs an affine invariant MCMC sampling algorithm in order to determine the position and the flux
        of the planet using the 'Negative Fake Companion' technique. The result of this procedure is a chain with the
        samples from the posterior distributions of each of the 3 parameters.

        Finally, we can inject a negative flux of the planet found in MCMC into the original dataset and apply
        post processing to determine the residuals in the data.

        Parameters:
        ***********
        do_firstguess : bool
            whether to determine a first guess for the position and the flux of a planet (not NEGFC)
        guess_xy : tuple
            if do_firstguess=True, this estimate of the source (x,y) location to be provided to firstguess(). Note
            Python's zero-based indexing
        mcmc_negfc : bool
            whether to run MCMC NEGFC sampling (computationally intensive)
        inject_neg : bool
            whether to inject negative flux of the planet and post process the data without signal from the planet
        ncomp : int, default 1
            number of prinicple components to subtract
        algo : 'pca_annulus', 'pca_annular', 'pca'. default 'pca_annular'
            select which routine to be used to model and subtract the stellar PSF
        nwalkers_ini : int, default 120
            for MCMC, the number of Goodman & Weare 'walkers'
        niteration_min : int, default 25
            for MCMC, the simulation will run at least this number of steps per walker
        niteration_limit : int, default 10000
            for MCMC, stops simulation if this many steps run without having reached the convergence criterion
        delta_rot : tuple
            same as for postprocessing module. Threshold rotation angle for PCA annular
        weights : bool
            should only be used on unsaturated datasets, where the flux of the star can be measured in each image of
            the cube. Applies a correction to each frame to account for variability of the adaptive optics, and hence
            flux from the star (reference Christiaens et al. 2021 2021MNRAS.502.6117C)
        coronagraph : bool
            for MCMC and injecting a negative companion, True if the observation utilised a coronagraph (AGPM).
            The known radial transmission of the NACO+AGPM coronagraph will be used
        overwrite : bool, default True
            whether to run a module and overwrite results if they already exist
        save_plot : bool, default True
            for firstguess the chi2 vs. flux plot is saved. For MCMC results are pickled and saved to the outpath
            along with corner plots
        verbose : bool
            prints more output and interediate files when True
        """

        print("======= Starting NEGFC....=======")
        if guess_xy is None and do_firstguess is True:
            raise ValueError("Enter an approximate location into guess_xy!")

        if weights is True and coronagraph is True:
            raise ValueError("Dataset cannot be both non-coronagraphic and coronagraphic!!")

        outpath_sub = self.outpath + "negfc/"

        if not isdir(outpath_sub):
            os.system("mkdir " + outpath_sub)

        if verbose:
            print('Input path is {}'.format(self.inpath))
            print('Output path is {}'.format(outpath_sub))

        source = self.dataset_dict['source']
        tn_shift = 0.568  # Launhardt et al. 2020, true North offset for NACO

        ADI_cube_name = '{}_master_cube.fits'  # template name for input master cube
        derot_ang_name = 'derot_angles.fits'  # template name for corresponding input derotation angles
        psfn_name = "master_unsat_psf_norm.fits"  # normalised PSF

        ADI_cube = open_fits(self.inpath + ADI_cube_name.format(source), verbose=verbose)
        derot_angles = open_fits(self.inpath + derot_ang_name, verbose=verbose) + tn_shift
        psfn = open_fits(self.inpath + psfn_name, verbose=verbose)
        ref_cube = None

        if algo == 'pca_annular':
            label_pca = 'pca_annular'
            algo = pca_annular
        elif algo == 'pca_annulus':
            label_pca = 'pca_annulus'
            algo = pca_annulus
        elif algo == 'pca':
            label_pca = 'pca'
            algo = pca
        else:
            raise ValueError("Invalid algorithm. Select either pca_annular, pca_annulus or pca!")
        opt_npc = ncomp
        ap_rad = 1 * self.fwhm
        f_range = np.geomspace(0.1, 201, 40)
        asize = 3 * self.fwhm

        if weights:
            nfr = ADI_cube.shape[0]  # number of frames
            star_flux = np.zeros([nfr])  # for storing the star flux found in each frame
            crop_sz_tmp = min(int(10 * self.fwhm),
                              ADI_cube.shape[1] - 2)  # crop around star, either 10*FWHM or size - 2
            if crop_sz_tmp % 2 == 0:  # if it's not even, crop
                crop_sz_tmp -= 1
            for ii in range(nfr):
                _, star_flux[ii], _ = normalize_psf(ADI_cube[ii], fwhm=self.fwhm, size=crop_sz_tmp,
                                                    full_output=True)  # get star flux in 1*FWHM
            weights = star_flux / np.median(star_flux)
            star_flux = np.median(star_flux)  # for use after MCMC when turning the chain into contrast
        else:
            weights = None
            flux_psf_name = "master_unsat-stellarpsf_fluxes.fits"  # flux in a FWHM aperture found in calibration
            star_flux = open_fits(self.inpath + flux_psf_name, verbose=verbose)[1]  # scaled fwhm flux

        if coronagraph:  # radial transmission of the coronagraph, 2 columns (pixels from centre, off-axis transmission)
        #  data provided by Valentin Christiaens. First entry in both columns was not zero, but VIP adds it in anyway
            transmission = np.array([[0, 3.5894626e-10, 5.0611424e-01, 1.0122285e+00, 1.5183427e+00,
                                    2.0244570e+00, 2.5305712e+00, 3.0366855e+00, 3.5427995e+00,
                                    4.0489140e+00, 4.5550284e+00, 5.0611424e+00, 5.5672565e+00,
                                    6.0733705e+00, 6.5794849e+00, 7.0855989e+00, 7.5917134e+00,
                                    8.6039419e+00, 9.1100569e+00, 9.6161709e+00, 1.0628398e+01,
                                    1.1134513e+01, 1.2146742e+01, 1.2652856e+01, 1.3665085e+01,
                                    1.4677314e+01, 1.6195656e+01, 1.7207884e+01, 1.8220114e+01,
                                    1.9738455e+01, 2.1256796e+01, 2.2775141e+01, 2.4293484e+01,
                                    2.6317940e+01, 2.8342396e+01, 3.0366854e+01, 3.2897423e+01,
                                    3.4921883e+01, 3.7452454e+01, 4.0489140e+01, 4.3525822e+01,
                                    4.6562508e+01, 5.0105309e+01, 5.4154221e+01, 5.7697018e+01,
                                    6.2252052e+01, 6.6807076e+01, 7.1868225e+01],
                                    [0, 6.7836474e-05, 3.3822558e-03, 1.7766271e-02, 5.2646037e-02,
                                    1.1413762e-01, 1.9890217e-01, 2.9460809e-01, 3.8605216e-01,
                                    4.6217495e-01, 5.1963091e-01, 5.6185508e-01, 5.9548348e-01,
                                    6.2670821e-01, 6.5912777e-01, 6.9335037e-01, 7.2783405e-01,
                                    7.8866738e-01, 8.1227022e-01, 8.3128709e-01, 8.5912752e-01,
                                    8.6968899e-01, 8.8677746e-01, 8.9409947e-01, 9.0848678e-01,
                                    9.2426234e-01, 9.4704604e-01, 9.5787460e-01, 9.6538281e-01,
                                    9.7379774e-01, 9.8088801e-01, 9.8751044e-01, 9.9255627e-01,
                                    9.9640906e-01, 9.9917024e-01, 1.0009050e+00, 1.0021056e+00,
                                    1.0026742e+00, 1.0027454e+00, 1.0027291e+00, 1.0023015e+00,
                                    1.0016677e+00, 1.0009446e+00, 1.0000550e+00, 9.9953103e-01,
                                    9.9917012e-01, 9.9915260e-01, 9.9922234e-01]])
        else:
            transmission = None

        if (not isfile(outpath_sub + label_pca + "_npc{}_simplex_results.fits".format(opt_npc)) or overwrite) and do_firstguess:

            # find r, theta based on the provided estimate location
            cy, cx = frame_center(ADI_cube[0])
            dy_pl = guess_xy[0][1] - cy
            dx_pl = guess_xy[0][0] - cx
            r_pl = np.sqrt(np.power(dx_pl, 2) + np.power(dy_pl, 2))  # pixel distance to the guess location
            theta_pl = (np.rad2deg(np.arctan2(dy_pl, dx_pl))) % 360  # theta (angle) to the guess location
            print("Estimated (r, theta) before first guess = ({:.1f},{:.1f})".format(r_pl, theta_pl))

            ini_state = firstguess(ADI_cube, derot_angles, psfn, ncomp=opt_npc, plsc=self.pixel_scale,
                                   planets_xy_coord=guess_xy, fwhm=self.fwhm,
                                   annulus_width=12, aperture_radius=ap_rad, cube_ref=ref_cube,
                                   svd_mode='lapack', scaling=None, fmerit='stddev', imlib='opencv',
                                   interpolation='lanczos4', collapse='median', p_ini=None,
                                   transmission=transmission, weights=weights, algo=algo,
                                   f_range=f_range, simplex=True, simplex_options=None, plot=save_plot,
                                   verbose=verbose, save=save_plot)
            # when p_ini is set to None, it gets the value of planets_xy_coord
            ini_state = np.array([ini_state[0][0], ini_state[1][0], ini_state[2][0]])

            write_fits(outpath_sub + label_pca + "_npc{}_simplex_results.fits".format(opt_npc), ini_state,
                       verbose=verbose)  # saves r, theta and flux. No print statement as firstguess() does that for us

        if (not isfile(outpath_sub + "MCMC_results") or overwrite) and mcmc_negfc:
            ini_state = open_fits(outpath_sub + label_pca + "_npc{}_simplex_results.fits".format(opt_npc),
                                  verbose=verbose)

            delta_theta_min = np.rad2deg(np.arctan(4./ r_pl))  # at least the angle corresponding to 2 azimuthal pixels
            delta_theta = max(delta_theta_min, 5.)
            bounds = [(max(r_pl - asize / 2., 1), r_pl + asize / 2.),  # radius
                      (theta_pl - delta_theta, theta_pl + delta_theta),  # angle
                      (0, 5 * abs(ini_state[2]))]

            if ini_state[0] < bounds[0][0] or ini_state[0] > bounds[0][1] or ini_state[1] < bounds[1][0] or \
                    ini_state[1] > bounds[1][1] or ini_state[2] < bounds[2][0] or ini_state[2] > bounds[2][1]:
                print("!!! WARNING: simplex results not in original bounds - NEGFC simplex MIGHT HAVE FAILED !!!")
                ini_state = np.array([r_pl, theta_pl, abs(ini_state[2])])

            if verbose is True:
                verbosity = 2
                print('MCMC NEGFC sampling is about to begin...')
            else:
                verbosity = 0

            final_chain = mcmc_negfc_sampling(ADI_cube, derot_angles, psfn, ncomp=opt_npc, plsc=self.pixel_scale,
                                              initial_state=ini_state, fwhm=self.fwhm, weights=weights,
                                              annulus_width=12, aperture_radius=ap_rad, cube_ref=ref_cube,
                                              svd_mode='lapack', scaling=None, fmerit='stddev',
                                              imlib='opencv', interpolation='lanczos4', transmission=transmission,
                                              collapse='median', nwalkers=nwalkers_ini, bounds=bounds, a=2.0,
                                              ac_c=50, mu_sigma=(0, 1),
                                              burnin=0.3, rhat_threshold=1.01, rhat_count_threshold=1, conv_test='ac',
                                              # use autocorrelation 'ac' to ensure sufficient sampling. sample around
                                              # the area of best likelihood to make distribution
                                              niteration_min=niteration_min, niteration_limit=niteration_limit,
                                              niteration_supp=0, check_maxgap=50, nproc=self.nproc, algo=algo,
                                              output_dir=outpath_sub,
                                              output_file="MCMC_results", display=False, verbosity=verbosity,
                                              save=save_plot)

            final_chain[:, :, 2] = final_chain[:, :, 2] / star_flux  # dividing by the star flux converts to a contrast
            show_walk_plot(final_chain, save=save_plot, output_dir=outpath_sub)
            show_corner_plot(final_chain, burnin=0.5, save=save_plot, output_dir=outpath_sub)

            # determine the highly probable value for each model parameter and the 1-sigma confidence interval
            isamples_flat = final_chain[:,int(final_chain.shape[1]//(1/0.3)):,:].reshape((-1,3))  # 0.3 is the burnin
            vals, err = confidence(isamples_flat, cfd=68.27, bins=100, gaussian_fit=False, weights=weights,
                                   verbose=verbose, save=True, output_dir=outpath_sub, filename='confidence.txt',
                                   plsc=self.pixel_scale)

            labels = ['r', 'theta', 'f']
            mcmc_res = np.zeros([3,3])
            # pull the values and confidence interval out for saving
            for i in range(3):
                mcmc_res[i,0] = vals[labels[i]]
                mcmc_res[i,1] = err[labels[i]][0]
                mcmc_res[i,2] = err[labels[i]][1]
            write_fits(outpath_sub + 'mcmc_results.fits', mcmc_res)

            # now gaussian fit
            gvals, gerr = confidence(isamples_flat, cfd=68.27, bins=100, gaussian_fit=True, weights=weights,
                                     verbose=verbose, save=True, output_dir=outpath_sub,filename='confidence_gauss.txt',
                                     plsc=self.pixel_scale)

            mcmc_res = np.zeros([3,2])
            for i in range(3):
                mcmc_res[i,0] = gvals[i]
                mcmc_res[i,1] = gerr[i]
            write_fits(outpath_sub + 'mcmc_results_gauss.fits', mcmc_res)

        if inject_neg:
            pca_res = np.zeros([ADI_cube.shape[1], ADI_cube.shape[2]])
            pca_res_emp = pca_res.copy()
            planet_params = open_fits(outpath_sub+'mcmc_results.fits')
            flux_psf_name = "master_unsat-stellarpsf_fluxes.fits"
            star_flux = open_fits(self.inpath + flux_psf_name, verbose=verbose)[1]

            ADI_cube_emp = cube_inject_companions(ADI_cube, psfn, derot_angles,
                                                  flevel=-planet_params[2, 0] * star_flux, plsc=self.pixel_scale,
                                                  rad_dists=[planet_params[0, 0]],
                                                  n_branches=1, theta=planet_params[1, 0],
                                                  imlib='opencv', interpolation='lanczos4',
                                                  verbose=verbose, transmission=transmission)
            write_fits(outpath_sub+'ADI_cube_empty.fits', ADI_cube_emp)  # the cube with the negative flux injected

            if algo == pca_annular:
                radius_int = int(np.floor(r_pl-asize/2))  # asize is 3 * FWHM, rounds down. To skip the inner region
                # crop the cube to just larger than the annulus to improve the speed of PCA
                crop_sz = int(2*np.ceil(r_pl+asize+1))  # rounds up
                if not crop_sz % 2:  # make sure the crop is odd
                    crop_sz += 1
                if crop_sz < ADI_cube.shape[1] and crop_sz < ADI_cube.shape[2]:  # crop if crop_sz is smaller than cube
                    pad = int((ADI_cube.shape[1]-crop_sz)/2)
                    crop_cube = cube_crop_frames(ADI_cube, crop_sz, verbose=verbose)
                else:
                    crop_cube = ADI_cube  # dont crop if the cube is already smaller

                pca_res_tmp = pca_annular(crop_cube, derot_angles, cube_ref=ref_cube, radius_int=radius_int,
                                          fwhm=self.fwhm, asize=asize, delta_rot=delta_rot, ncomp=opt_npc,
                                          svd_mode='lapack', scaling=None, imlib='opencv', interpolation='lanczos4',
                                          nproc=self.nproc, min_frames_lib=max(opt_npc, 10), verbose=verbose,
                                          full_output=False)

                pca_res = np.pad(pca_res_tmp, pad, mode='constant', constant_values=0)
                write_fits(outpath_sub + 'pca_annular_res_npc{}.fits'.format(opt_npc), pca_res)

                # emp
                if crop_sz < ADI_cube_emp.shape[1] and crop_sz < ADI_cube_emp.shape[2]:
                    pad = int((ADI_cube_emp.shape[1]-crop_sz)/2)
                    crop_cube = cube_crop_frames(ADI_cube_emp, crop_sz, verbose=verbose)
                else:
                    crop_cube = ADI_cube_emp
                del ADI_cube_emp
                del ADI_cube

                pca_res_tmp = pca_annular(crop_cube, derot_angles, cube_ref=ref_cube, radius_int=radius_int,
                                          fwhm=self.fwhm, asize=asize, delta_rot=delta_rot, ncomp=opt_npc,
                                          svd_mode='lapack', scaling=None, imlib='opencv', interpolation='lanczos4',
                                          nproc=self.nproc, min_frames_lib=max(opt_npc, 10), verbose=verbose,
                                          full_output=False)

                # pad again now
                pca_res_emp = np.pad(pca_res_tmp, pad, mode='constant', constant_values=0)
                write_fits(outpath_sub+'pca_annular_res_empty_npc{}.fits'.format(opt_npc), pca_res_emp)
Beispiel #12
0
    def dark_subtract(self, verbose=True, debug=True):
        sci_list = []
        with open(self.outpath + "sci_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                sci_list.append(line.split('\n')[0])

        sky_list = []
        with open(self.outpath + "sky_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                sky_list.append(line.split('\n')[0])

        unsat_list = []
        with open(self.outpath + "unsat_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                unsat_list.append(line.split('\n')[0])

        unsat_dark_list = []
        with open(self.outpath + "unsat_dark_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                unsat_dark_list.append(line.split('\n')[0])

        flat_list = []
        with open(self.outpath + "flat_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                flat_list.append(line.split('\n')[0])

        flat_dark_list = []
        with open(self.outpath + "flat_dark_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                flat_dark_list.append(line.split('\n')[0])

        sci_dark_list = []
        with open(self.outpath + "sci_dark_list.txt", "r") as f:
            tmp = f.readlines()
            for line in tmp:
                sci_dark_list.append(line.split('\n')[0])
        pixel_scale = fits_info.pixel_scale

        tmp = np.zeros([3, self.com_sz, self.com_sz])
        #creating master dark cubes
        for fd, fd_name in enumerate(flat_dark_list):
            tmp_tmp = open_fits(self.inpath + fd_name,
                                header=False,
                                verbose=debug)
            tmp[fd] = frame_crop(tmp_tmp,
                                 self.com_sz,
                                 force=True,
                                 verbose=debug)
        write_fits(self.outpath + 'flat_dark_cube.fits', tmp)
        if verbose:
            print('Flat dark cubes have been cropped and saved')

        for sd, sd_name in enumerate(sci_dark_list):
            tmp_tmp = open_fits(self.inpath + sd_name,
                                header=False,
                                verbose=debug)
            n_dim = tmp_tmp.ndim
            if sd == 0:
                if n_dim == 2:
                    tmp = np.array([
                        frame_crop(tmp_tmp,
                                   self.com_sz,
                                   force=True,
                                   verbose=debug)
                    ])
                else:
                    tmp = cube_crop_frames(tmp_tmp,
                                           self.com_sz,
                                           force=True,
                                           verbose=debug)
            else:
                if n_dim == 2:
                    tmp = np.append(tmp, [
                        frame_crop(
                            tmp_tmp, self.com_sz, force=True, verbose=debug)
                    ],
                                    axis=0)
                else:
                    tmp = np.append(tmp,
                                    cube_crop_frames(tmp_tmp,
                                                     self.com_sz,
                                                     force=True,
                                                     verbose=debug),
                                    axis=0)
        write_fits(self.outpath + 'sci_dark_cube.fits', tmp)
        if verbose:
            print('Sci dark cubes have been cropped and saved')

        #create an if stament for if the size is larger than sz and less than if less than crop by nx-1
        for sd, sd_name in enumerate(unsat_dark_list):
            tmp_tmp = open_fits(self.inpath + sd_name,
                                header=False,
                                verbose=debug)
            tmp = np.zeros([
                len(sci_dark_list) * tmp_tmp.shape[0], tmp_tmp.shape[1],
                tmp_tmp.shape[2]
            ])
            n_dim = tmp_tmp.ndim
            if sd == 0:
                if n_dim == 2:
                    ny, nx = tmp_tmp.shape
                    if nx <= self.com_sz:
                        tmp = np.array([
                            frame_crop(tmp_tmp,
                                       nx - 1,
                                       force=True,
                                       verbose=debug)
                        ])
                    else:
                        tmp = np.array(
                            [frame_crop(tmp_tmp, self.com_sz, verbose=debug)])
                else:
                    nz, ny, nx = tmp_tmp.shape
                    if nx <= self.com_sz:
                        tmp = cube_crop_frames(tmp_tmp,
                                               nx - 1,
                                               force=True,
                                               verbose=debug)
                    else:
                        tmp = cube_crop_frames(tmp_tmp,
                                               self.com_sz,
                                               force=True,
                                               verbose=debug)
            else:
                if n_dim == 2:
                    ny, nx = tmp_tmp.shape
                    if nx <= self.com_sz:
                        tmp = np.append(tmp, [
                            frame_crop(
                                tmp_tmp, nx - 1, force=True, verbose=debug)
                        ],
                                        axis=0)
                    else:
                        tmp = np.append(tmp, [
                            frame_crop(tmp_tmp,
                                       self.com_sz,
                                       force=True,
                                       verbose=debug)
                        ],
                                        axis=0)
                else:
                    nz, ny, nx = tmp_tmp.shape
                    if nx <= self.com_sz:
                        tmp = cube_crop_frames(tmp,
                                               nx - 1,
                                               force=True,
                                               verbose=debug)
                        tmp = np.append(tmp,
                                        cube_crop_frames(tmp_tmp,
                                                         nx - 1,
                                                         force=True,
                                                         verbose=debug),
                                        axis=0)
                    else:
                        tmp = np.append(tmp,
                                        cube_crop_frames(tmp_tmp,
                                                         self.com_sz,
                                                         force=True,
                                                         verbose=debug),
                                        axis=0)
                tmp = np.zeros([
                    len(sci_dark_list) * tmp_tmp.shape[0], tmp_tmp.shape[1],
                    tmp_tmp.shape[2]
                ])
        write_fits(self.outpath + 'unsat_dark_cube.fits', tmp)
        if verbose:
            print('Unsat dark cubes have been cropped and saved')

        #defining the anulus (this is where we avoid correcting around the star)
        cy, cx = find_agpm_list(self, sci_list)
        self.agpm_pos = (cx, cy)
        if verbose:
            print(' The location of the AGPM has been calculated', 'cy = ', cy,
                  'cx = ', cx)

        agpm_dedge = min(self.agpm_pos[0], self.agpm_pos[1],
                         self.com_sz - self.agpm_pos[0],
                         self.com_sz - self.agpm_pos[1])
        mask_arr = np.ones([self.com_sz, self.com_sz])
        cy, cx = frame_center(mask_arr)
        mask_inner_rad = int(3.0 /
                             pixel_scale)  # 3arcsec min to avoid star emission
        mask_width = agpm_dedge - mask_inner_rad - 1
        mask_AGPM_com = get_annulus_segments(mask_arr,
                                             mask_inner_rad,
                                             mask_width,
                                             mode='mask')[0]
        mask_AGPM_com = frame_shift(
            mask_AGPM_com, self.agpm_pos[1] - cy, self.agpm_pos[0] -
            cx)  # agpm is not centered in the frame so shift the mask
        if verbose:
            print('AGPM mask has been defined')
        if debug:
            tmp = open_fits(self.outpath + sci_list[0])
            #plot_frames(tmp[-1], circle = self.agpm_pos)

        #now begin the dark subtraction useing PCA
        npc_dark = 1  #val found this value gives the best result.
        tmp_tmp = np.zeros([len(flat_list), self.com_sz, self.com_sz])
        tmp_tmp_tmp = open_fits(self.outpath + 'flat_dark_cube.fits')
        for fl, flat_name in enumerate(flat_list):
            tmp = open_fits(self.inpath + flat_name,
                            header=False,
                            verbose=debug)
            tmp_tmp[fl] = frame_crop(tmp,
                                     self.com_sz,
                                     force=True,
                                     verbose=debug)
        tmp_tmp = cube_subtract_sky_pca(tmp_tmp,
                                        tmp_tmp_tmp,
                                        mask_AGPM_com,
                                        ref_cube=None,
                                        ncomp=npc_dark)
        write_fits(self.outpath + '1_crop_flat_cube.fits', tmp_tmp)
        if verbose:
            print('Dark has been subtracted from Flats')
        if debug:
            #plot the median of dark cube median of cube before subtraction median after subtraction
            tmp_tmp_tmp = np.median(tmp_tmp_tmp,
                                    axis=0)  #flat_dark cube median
            tmp = tmp  #flat before subtraction
            tmp_tmp = np.median(tmp_tmp,
                                axis=0)  #median flat after dark subtract
            plot_frames((tmp_tmp_tmp, tmp, tmp_tmp))

        tmp_tmp_tmp = open_fits(self.outpath + 'sci_dark_cube.fits')
        for sc, fits_name in enumerate(sci_list):
            tmp = open_fits(self.inpath + fits_name,
                            header=False,
                            verbose=debug)
            tmp = cube_crop_frames(tmp, self.com_sz, force=True, verbose=debug)
            tmp_tmp = cube_subtract_sky_pca(tmp,
                                            tmp_tmp_tmp,
                                            mask_AGPM_com,
                                            ref_cube=None,
                                            ncomp=npc_dark)
            write_fits(self.outpath + '1_crop_' + fits_name, tmp_tmp)
        if verbose:
            print('Dark has been subtracted from Sci')
        if debug:
            #plot the median of dark cube median of cube before subtraction median after subtraction
            tmp_tmp_tmp = np.median(tmp_tmp_tmp, axis=0)
            tmp = np.median(tmp, axis=0)
            tmp_tmp = np.median(tmp_tmp, axis=0)
            plot_frames((tmp_tmp_tmp, tmp, tmp_tmp))

        tmp_tmp_tmp = open_fits(self.outpath + 'sci_dark_cube.fits')
        for sc, fits_name in enumerate(sky_list):
            tmp = open_fits(self.inpath + fits_name,
                            header=False,
                            verbose=debug)
            tmp = cube_crop_frames(tmp, self.com_sz, force=True, verbose=debug)
            tmp_tmp = cube_subtract_sky_pca(tmp,
                                            tmp_tmp_tmp,
                                            mask_AGPM_com,
                                            ref_cube=None,
                                            ncomp=npc_dark)
            write_fits(self.outpath + '1_crop_' + fits_name, tmp_tmp)
        if verbose:
            print('Dark has been subtracted from Sky')
        if debug:
            #plot the median of dark cube median of cube before subtraction median after subtraction
            tmp_tmp_tmp = np.median(tmp_tmp_tmp, axis=0)
            tmp = np.median(tmp, axis=0)
            tmp_tmp = np.median(tmp_tmp, axis=0)
            plot_frames((tmp_tmp_tmp, tmp, tmp_tmp))

        tmp_tmp_tmp = open_fits(self.outpath + 'master_unsat_dark.fits')
        # no need to crop the unsat frame at the same size as the sci images if they are smaller
        for un, fits_name in enumerate(unsat_list):
            tmp = open_fits(self.inpath + fits_name, header=False)
            #tmp = cube_crop_frames(tmp,nx_unsat_crop)
            if tmp.shape[2] > self.com_sz:
                nx_unsat_crop = self.com_sz
                tmp_tmp = cube_crop_frames(tmp - tmp_tmp_tmp,
                                           nx_unsat_crop,
                                           force=True,
                                           verbose=debug)
            elif tmp.shape[2] % 2 == 0:
                nx_unsat_crop = tmp.shape[2] - 1
                tmp_tmp = cube_crop_frames(tmp - tmp_tmp_tmp,
                                           nx_unsat_crop,
                                           force=True,
                                           verbose=debug)
            else:
                nx_unsat_crop = tmp.shape[2]
                tmp_tmp = tmp - tmp_tmp_tmp
            write_fits(self.outpath + '1_crop_unsat_' + fits_name, tmp_tmp)
        if verbose:
            print('unsat frames have been cropped')
        if debug:
            #plot the median of dark cube median of cube before subtraction median after subtraction
            tmp_tmp_tmp = np.median(tmp_tmp_tmp, axis=0)
            tmp = np.median(tmp, axis=0)
            tmp_tmp = np.median(tmp_tmp, axis=0)
            plot_frames((tmp_tmp_tmp, tmp, tmp_tmp))