Example #1
0
    def __init__(self,
                 filepath,
                 prefix,
                 outputdir,
                 contrast,
                 sep,
                 theta,
                 fwhm=None,
                 ghostpath='ghost.fits',
                 highpass=False,
                 klipparams=[6, 3, [1, 5, 50]],
                 usegaussian=False):
        '''
        Initializes the class, injecting hole as specified and prepared for
        KLIP reduction via "run_KLIP" function
        Initialized: William B. 9/22/2020
        '''
        self.filelist = glob.glob(filepath + '\\*.fits')
        self.head = fits.getheader(self.filelist[0])
        self.highpass = highpass
        self.dataset = MagAO.MagAOData(self.filelist, highpass=self.highpass)
        self.contrast = contrast
        self.sep = sep
        self.theta = theta
        if fwhm is None:
            self.fwhm = self.head["0PCTFWHM"]
        else:
            self.fwhm = fwhm
        self.ghostpath = ghostpath
        self.usegaussian = usegaussian
        self.psf = self.instrPSF(self.ghostpath)

        self.inpflux = np.zeros((self.dataset.input.shape[0],
                                 self.psf.shape[0], self.psf.shape[1]))

        for i in range(self.dataset.input.shape[0]):
            self.inpflux[i] = self.contrast * self.psf

        fakes.inject_planet(self.dataset.input,
                            self.dataset.centers,
                            self.inpflux,
                            self.dataset.wcs,
                            self.sep,
                            self.theta,
                            fwhm=self.fwhm)

        self.outputdir = outputdir
        self.pfx = prefix
        self.numann, self.movm, self.KLlist = klipparams
Example #2
0
    def __init__(self,
                 filepath,
                 prefix,
                 outputdir,
                 highpass=False,
                 klipparams=[1, 10, [1, 5, 50]]):
        '''
        Initializes the class, injecting hole as specified and prepared for
        KLIP reduction via "run_KLIP" function
        Initialized: William B. 10/21/2020
        '''
        self.filelist = glob.glob(filepath + '/*.fits')
        self.head = fits.getheader(self.filelist[0])
        self.highpass = highpass
        self.dataset = MagAO.MagAOData(self.filelist, highpass=self.highpass)

        self.outputdir = outputdir
        if os.path.exists(outputdir) is False:
            os.mkdir(outputdir)

        self.pfx = prefix
        self.numann, self.movm, self.KLlist = klipparams
Example #3
0
    def __init__(self,
                 filepaths,
                 output,
                 prefix,
                 KLmode,
                 sep,
                 pa,
                 contrast,
                 annuli,
                 move,
                 scale,
                 ePSF=None,
                 FWHM=None,
                 cores=1,
                 highpass=True,
                 **kwargs):
        if __name__ == '__main__':  # An important precaution for Windows
            __spec__ = None  # Important for ipynb compatibility
            # all the stuff goes here
        if ePSF is None:
            print('You have not provided a path to your instrumental psf')
            cubepath = input(
                'Enter the path to your MagAO image cube to generate one, or enter \'Gaussian\' to use a simple gaussian psf: '
            )
            if cubepath == 'Gaussian':
                ePSF = 'doGaussian'
            else:
                ePSF = 'ghost.fits'

        # set paths to sliced dataset, call dataset into KLIP format
        # set up variables needed for KLIP calls
        self.filepaths = filepaths
        self.filelist = glob.glob(self.filepaths)
        self.dataset = MagAO.MagAOData(self.filelist)
        self.head = fits.getheader(self.filelist[0])
        self.pre = prefix
        try:
            a = len(annuli)
            self.annulus_bounds = [annuli]  # annulus centered on the planet
        except TypeError:
            self.annulus_bounds = annuli
        self.move = move
        self.fwhm = FWHM
        self.cores = cores
        self.ePSF = ePSF

        if 'outside_psf' in kwargs:
            self.outside_psf = kwargs['outside_psf']
        else:
            self.outside_psf = None

        # setup FM guesses
        if 'numbasis' in kwargs:
            self.numbasis = np.array([kwargs['numbasis']
                                      ])  # KL basis cutoffs you want
        else:
            self.numbasis = np.array([KLmode])
        self.guesssep = sep  # estimate of separation in pixels
        self.guesspa = pa  # estimate of position angle, in degrees
        self.guessflux = contrast  # estimated contrast
        self.dn_per_contrast = np.zeros((self.dataset.input.shape[0]))
        for i in range(self.dn_per_contrast.shape[0]):
            self.dn_per_contrast[i] = scale  # factor to scale PSF to star
        self.guessspec = np.array([1])  # our data is 1D in wavelength

        # PSF subtraction parameters
        self.outputdir = output  # where to write the output files
        self.prefix = prefix  # fileprefix for the output files
        self.subsections = 1  # we are not breaking up the annulus
        self.padding = 0  # we are not padding our zones
        self.movement = move
        self.hpf = highpass

        print('Parameters set, ready to begin forward modeling... ')
Example #4
0
def explore_params(path_to_files, outfile_name, iwa, klmodes, annuli_start, annuli_stop, movement_start, 
    movement_stop, FWHM, ra, pa, wid, annuli_inc=1, movement_inc=1, subsections_start=False, subsections_stop=False, subsections_inc=False,  
    smooth=False, input_contrast=False, time_collapse='median', highpass = True, owa=False,
    saveSNR = True, singleAnn = False, boundary=False, verbose = False, snrsmt = False,
    calibrate_flux=False):

    #default is 1 subsection
    if subsections_start == False:
        if (subsections_stop != False) or (subsections_inc != False):
            print("must set subsections_start, subsections_stop, and subsections_inc together")
            return()
        subsections_start = 1
        subsections_stop = 1
        subsections_inc = 1

    #pre-klip smooth off = smoothing value of 0
    if smooth == False:
        smooth=0.0
    
    if verbose is True:
        print(f"File Path = {path_to_files}")   
        print()
        print(f"Output Filename = {outfile_name}")
        print("Parameters to explore:")
        print(f"Annuli: start = {annuli_start}; end = {annuli_stop}; increment = {annuli_inc}")
        print(f"Subsections: start = {subsections_start}; end = {subsections_stop}; increment = {subsections_inc} ")
        print(f"Movement: start = {movement_start}; end = {movement_stop}; increment = {movement_inc} ")
        print(f"IWA = {iwa}, KL Modes = {klmodes}, FWHM = {FWHM}, Smoothing Value = {smooth}")
        print()
        print("Planet Parameters")
        print(f"Radius= {ra}, Position Angle = {pa}, Mask Width = {wid}, Input Contrast - {input_contrast}") #, X Positions = {x_positions}, Y Positions = {y_positions} ")
        print()
        print("reading: " + path_to_files + "/*.fits")

    # create directory to save ouput to
    if not os.path.exists(path_to_files + "_klip"):
        os.makedirs(path_to_files + "_klip")
    
    # create tuples for easier eventual string formatting when saving files
    annuli = (annuli_start, annuli_stop, annuli_inc)
    movement = (movement_start, movement_stop, movement_inc)
    subsections = (subsections_start, subsections_stop, subsections_inc)

    # if only one parameter is iterated over, makes sure increment is 1 and changes touple to single int
    if(annuli_start == annuli_stop):
        annuli_inc = 1
        annuli = annuli_start

    # if parameter is not set to change, makes sure increment is 1 and changes touple to single int
    if(movement_start == movement_stop):
        movement_inc = 1
        movement = movement_start

    # if parameter is not set to change, makes sure increment is 1 and changes touple to single int
    if(subsections_start == subsections_stop):
        subsections_inc = 1
        subsections = subsections_start

    # check that position angle and radius lists have the same number of elements
    if len(ra) != len(pa):
        print("List of separations is not equal in length to list of position angles. Duplicating to match.")
        ra=np.repeat(ra,len(pa))

    # object to hold mask parameters for snr map 
    mask = (ra, pa, wid)

    nplanets = len(ra)
    if verbose is True:
        print(nplanets, "planets with separations ", ra, "and PAs ", pa)
    
    # Add suffix to filenames depending on user-specified values
    suff = ''    
    if singleAnn is True:
        suff += '_min-annuli'
    
    if highpass is True:
        suff += '_highpass'

    if type(highpass)!=bool:
        suff+= '_hp'+str(highpass)

    if verbose is True:
    
        print("Reading: " + path_to_files + "/*.fits")
        
        start_time = time.time()
        print("Start clock time is", time.time())
        
        start_process_time = time.process_time()
        print("Start process time is", time.process_time())
        
    # grab generic header from a generic single image
    hdr = fits.getheader(path_to_files + '/sliced_1.fits')

    # erase values that change through image cube
    del hdr['ROTOFF']
    try:
        del hdr['GSTPEAK']
    except:
        print('NOT a saturated dataset')
    del hdr['STARPEAK']
    
    # reads in files
    filelist = glob.glob(path_to_files + '/*.fits')

    # Get star peaks
    starpeak = []
    for i in np.arange(len(filelist)):
        head = fits.getheader(filelist[i])
        starpeak.append(head["STARPEAK"])

    dataset = MagAO.MagAOData(filelist)
    #make a clean copy of the dataset that will be pulled each time (parallelized modifies dataset.input object)
    dataset_input_clean = np.copy(dataset.input)

    palist = sorted(dataset._PAs)
    palist_clean = [pa if (pa < 360) else pa-360 for pa in palist]
    palist_clean_sorted = sorted(palist_clean)
    totrot = palist_clean_sorted[-1]-palist_clean_sorted[0]

    if verbose is True:
        print(f"total rotation for this dataset is {totrot} degrees")

    # set IWA and OWA
    dataset.IWA = iwa

    if owa is False:
        xDim = dataset._input.shape[2]
        yDim = dataset._input.shape[1]
        dataset.OWA = min(xDim,yDim)/2
        owa = dataset.OWA
    else:
        dataset.OWA = owa

    # Make function to write out data 
    def writeData(im, prihdr, annuli, movement, subsections, snrmap = False, pre = ''):
        #function writes out fits files with important info captured in fits headers
        
        #if program iterates over several parameter values, formats these for fits headers and file names
        if (isinstance(annuli, tuple)):
            annuli_fname = str(annuli[0]) + '-' + str(annuli[1]) + 'x' + str(annuli[2])
            annuli_head = str(annuli[0]) + 'to' + str(annuli[1]) + 'by' + str(annuli[2])  
        else: 
            annuli_fname = annuli
            annuli_head = annuli

        if (isinstance(movement, tuple)):
            movement_fname = str(movement[0]) + '-' + str(movement[1]) + 'x' + str(movement[2])
            movement_head = str(movement[0]) + 'to' + str(movement[1]) + 'by' + str(movement[2])
        else: 
            movement_head = movement
            movement_fname = movement

        if (isinstance(subsections, tuple)):
            subsections_head = str(subsections[0]) + 'to' + str(subsections[1]) + 'by' + str(subsections[2])
            subsections_fname = str(subsections[0]) + '-' + str(subsections[1]) + '-' + str(subsections[2])
        else:
            subsections_head = subsections
            subsections_fname = subsections


        #shortens file path to bottom 4 directories so it will fit in fits header
        try:
            path_to_files_short = '/'.join(path_to_files.split(os.path.sep)[-4:])
        except:
            path_to_files_short = path_to_files
                
        #adds info to fits headers
        prihdr['ANNULI']=str(annuli_head)
        prihdr['MOVEMENT']=str(movement_head)
        prihdr['SUBSCTNS']=str(subsections_head)
        prihdr['IWA'] = str(iwa)
        prihdr['KLMODES']=str(klmodes)
        prihdr['FILEPATH']=str(path_to_files_short)
        prihdr['OWA']=str(dataset.OWA)
        prihdr['TIMECOLL']=str(time_collapse)
        prihdr['CALIBFLUX']=str(calibrate_flux)
        prihdr["HIGHPASS"]=str(highpass)

    
        if(snrmap):
            rad, pa, wid = mask 
            prihdr['MASK_RAD']=str(rad)
            prihdr['MASK_PA']=str(pa)
            prihdr['MASK_WID']=str(wid)
            prihdr['SNRSMTH']=str(smooth)
            prihdr['SNRFWHM']=str(FWHM)

        if isinstance(annuli, tuple):
            prihdr["SLICE1"]="planet peak value under mask in standard deviation noise map"
            prihdr["SLICE2"] = "planet peak value under mask in median absolute value noise map"
            prihdr["SLICE3"] = "average value of positive pixels under mask in standard deviation noise map"
            prihdr["SLICE4"] = "average value of positive pixels under mask in median absolute value noise map"
            prihdr["SLICE5"] = "total number of pixels >5sigma outside of mask in standard deviation noise map"
            prihdr["SLICE6"] = "total number of pixels >5sigma outside of mask in median absolute value noise map"
            prihdr["SLICE7"] = "total number of pixels >5sigma outside of mask and at similar radius in standard deviation noise map"
            prihdr["SLICE8"] = "total number of pixels >5sigma outside of mask and at similar radius in median absolute value noise map"
            prihdr["SLICE9"] = "calibrated contrast value of planet/s at a given separation"

        #writes out files
        fits.writeto(str(path_to_files) + "_klip/" + str(pre)  + outfile_name + "_a" + str(annuli_fname) + "m" + str(
            movement_fname) + "s" + str(subsections_fname) + "iwa" + str(iwa) + suff + '-KLmodes-all.fits', im, prihdr, overwrite=True)

        return


    # create cube to eventually hold parameter explorer data
    PECube = np.zeros((9,int((subsections_stop-subsections_start)/subsections_inc+1), len(klmodes), int(nplanets),
                        int((annuli_stop-annuli_start)/annuli_inc+1),
                        int((movement_stop-movement_start)/movement_inc+1)))
    
    # BEGIN LOOPS OVER ANNULI, MOVEMENT AND SUBSECTION PARAMETERS
    
    # used for indexing: keeps track of number of annuli values that have been tested
    acount = 0
    
    for a in range(annuli_start, annuli_stop+1, annuli_inc):
    
        # calculate size of annular zones
        dr = float(owa-iwa)/a

        # creates list of zone radii
        all_bounds = [dr*rad+iwa for rad in range(a+1)]

        planet_annuli = [a for a in all_bounds if (a<ra[-1]+dr) and (a>ra[0])]
        nplanet_anns = len(planet_annuli)

     
        ann_cen_rad = [ a - dr/2 for a in planet_annuli ]

        if verbose is True:
            print("planets span ", nplanet_anns, "annular zones for annuli = ", a)

        # print('annuli bounds are', all_bounds)
        numAnn = a
        
        if(singleAnn):
            #find maximum annulus boundary radius that is still inside innermost planet injection radius
            lowBound = max([b for b in all_bounds if (min(ra)>b)])
            #find minimum exterior boundary radius that is outside outermost planet injection radius
            upBound = min([b for b in all_bounds if (max(ra)<b)])
            #list of zone boundaries for planets between the two bounds
            all_bounds = [b for b in all_bounds if (b>=lowBound and b<=upBound)]
            numAnn = int(round((upBound-lowBound)/dr))
            #reset iwa and owa to correspond to annulus
            dataset.IWA = lowBound
            dataset.OWA = upBound
    
        #if boundary keyword is set, check to see if any planets are too close to annuli boundaries
        if boundary != False:
            #is planet within +/- number set as boundary pixels
            if not (len( [b for b in all_bounds for r in ra if(b <= r+boundary and b >= r-boundary)] ) == 0):
                print([b for b in all_bounds for r in ra if(b <= r+boundary and b >= r-boundary)])
                print("A planet is near annulus boundary; skipping KLIP for annuli = " + str(a))
                #assign a unique value as a flag for these cases in the parameter explorer map
                PECube[:,:,:,:,acount,:] = np.nan
                #break out of annuli loop before KLIPing
                acount=1
                continue

        # used for indexing: keeps track of number of movement values that have been tested
        mcount = 0
    
        for m in tqdm(np.arange(movement_start, movement_stop+1, movement_inc)):

            #figure out whether there is enough range 

            if np.arctan(m/ann_cen_rad[0])*180/np.pi>totrot:
                if verbose is True:
                    print("movement", m, "=" "%5.1f" % (np.arctan(m/ann_cen_rad[0])*180/np.pi), 
                        "deg. for inner planet annulus. Only ", "%5.1f" % (totrot), 
                        "available. skipping this movement/annuli combo") 
                PECube[:,:,:,:,acount,mcount] = np.nan
                mcount+=1
                continue

            else:
                scount = 0
        
                for s in range(subsections_start, subsections_stop+1, subsections_inc):

                    klipstr = "_a" + str(a) + "m" + str(m) + "s" + str(s) + "iwa" + str(iwa) 
                    fname  = str(path_to_files) + "_klip/" + outfile_name + klipstr+ suff + '-KLmodes-all.fits'

                    if verbose is True:  
                        if(singleAnn):
                            print("Parameters: movement = %s; subections = %d" %(m,s))
                            print("Running for %d annuli, equivalent to single annulus of width %s pixels" %(annuli_start+acount, dr))
                        else:
                            print("Parameters: annuli = %d; movement = %s; subections = %d" %(a, m,s))
            
                        # create cube to hold snr maps 
                        #snrMapCube = np.zeros((2,len(klmodes),yDim,xDim))
                    runKLIP = True
                    
                    if os.path.isfile(fname):
                        print(outfile_name+klipstr+suff, fname)
                        incube = fits.getdata(fname)
                        head = fits.getheader(fname)
                        klmodes2 = head['KLMODES'][1:-1]
                        klmodes2 = list(map(int, klmodes2.split(",")))
        
                        if (len([k for k in klmodes if not k in klmodes2]) == 0):
                            if verbose is True:
                                print("Found KLIP processed images for same parameters saved to disk. Reading in data.")
                            #don't re-run KLIP
                            runKLIP = False
        
                    if (runKLIP):
                        if verbose is True:
                            print("Starting KLIP")
                        #run klip for given parameters
                        #read in a fresh copy of dataset so no compound highpass filtering
                        dataset.input = dataset_input_clean
                        parallelized.klip_dataset(dataset, outputdir=(path_to_files + "_klip/"), fileprefix=outfile_name+klipstr+suff, 
                            annuli=numAnn, subsections=s, movement=m, numbasis=klmodes, calibrate_flux=calibrate_flux, 
                            mode="ADI", highpass = highpass, time_collapse=time_collapse, verbose = verbose)

                        #read in the final image and header
                        print(outfile_name+klipstr+suff, fname)
                        #read in file that was created so can add to header
                        incube = fits.getdata(fname)
                        head = fits.getheader(fname)

                        #add KLMODES keyword to header
                        #this also has the effect of giving the file a single header instead of pyklip's double
                        head["KLMODES"]=str(klmodes)
                        fits.writeto(fname, incube, head, overwrite=True)
                    
                    if input_contrast is not False:
                        dataset_copy = np.copy(incube)
                    
                        # Find planet x and y positions from pa and sep
                        x_positions = [r*np.cos((np.radians(p+90)))+ dataset.centers[0][0] for r, p in zip(ra, pa)]
                        y_positions = [r*np.sin((np.radians(p+90)))+ dataset.centers[0][0] for r, p in zip(ra, pa)]
                    
                        # Loop through kl modes
                        cont_meas = np.zeros((len(klmodes), 1))
                        for k in range(len(klmodes)):
                        
                            dataset_contunits = dataset_copy[k]/np.median(starpeak)
                            
                            
                            
                            if runKLIP is False:
                                w = wcsgen.generate_wcs(parangs = 0, center = dataset.centers[0])
                            else:
                                w = dataset.output_wcs[0]
                            

                            # Retrieve flux of injected planet
                            planet_fluxes = []
                            for sep, p in zip(ra, pa):
                                fake_flux = fakes.retrieve_planet_flux(dataset_contunits, dataset.centers[0], w, sep, p, searchrad=7)
                                planet_fluxes.append(fake_flux)

                        
                            # Calculate the throughput
                            tpt = np.array(planet_fluxes)/np.array(input_contrast)
                            

                            # Create an array with the indices are that of KL mode frame with index 2
                            ydat, xdat = np.indices(dataset_contunits.shape)

                        

                            # Mask the planets
                            for x, y in zip(x_positions, y_positions):

                                # Create an array with the indices are that of KL mode frame with index 2
                                distance_from_star = np.sqrt((xdat - x) ** 2 + (ydat - y) ** 2)

                                # Mask
                                dataset_contunits[np.where(distance_from_star <= 2 * FWHM)] = np.nan
                                masked_cube = dataset_contunits

                            # Measure the raw contrast
                            contrast_seps, contrast = klip.meas_contrast(dat=masked_cube, iwa=iwa, owa=dataset.OWA, resolution=(7), center=dataset.centers[0], low_pass_filter=True)

                            # Find the contrast to be used 
                            use_contrast = np.interp(np.median(ra), contrast_seps, contrast)
                            

                            # Calibrate the contrast
                            cal_contrast = use_contrast/np.median(tpt)
                            cont_meas[k] = -cal_contrast
                            
        
                    # makes SNR map
                    snrmaps, peaksnr, snrsums, snrspurious= snr.create_map(fname, FWHM, smooth=snrsmt, planets=mask, saveOutput=False, sigma = 5, checkmask=False, verbose = verbose)

                    PECube[0:2, scount, :, :, acount, mcount] = peaksnr
                    PECube[2:4, scount, :, :, acount, mcount] = snrsums
                    PECube[4:6, scount, :, :, acount, mcount] = snrspurious[:,:,None,0]
                    PECube[6:8, scount, :, :, acount, mcount] = snrspurious[:,:,None,1]
                    PECube[8, scount, :, :, acount, mcount] = cont_meas

                    if(runKLIP) and np.nanmedian(peaksnr)>3:
                        writeData(incube, hdr, a, m, s)
                    if verbose is True:
                        print("Median peak SNR > 3. Writing median image combinations to " + path_to_files + "_klip/")
                        
                    if saveSNR is True:
                        writeData(snrmaps, hdr, a, m, s, snrmap = True, pre = 'snrmap_')
                        if verbose is True:
                            print("Writing SNR maps to " + path_to_files + "_klip/")
        
                
                    scount+=1
                mcount+=1                
        acount+=1
    
    if verbose is True:        
        print("Writing parameter explorer file to " + path_to_files + "_klip/")

    #write parameter explorer cube to disk
    writeData(PECube, hdr, annuli, movement, subsections, snrmap = True, pre = 'paramexplore_')

    if verbose is True: 
        print("KLIP automation complete")    
        print("End clock time is", time.time())
        print("End process time is", time.process_time())
        print("Total clock runtime: ", time.time()- start_time)
        print("Total process runtime:", time.process_time()-start_process_time)

    return(PECube)
Example #5
0
print()

#grab generic header from a generic single image
hdr = fits.getheader(pathToFiles + '/sliced_1.fits')
#erase values that change through image cube
del hdr['ROTOFF']
try:
    del hdr['GSTPEAK']
except:
    print('not a saturated dataset')
del hdr['STARPEAK']

#reads in files
filelist = glob.glob(pathToFiles + '/*.fits')
dataset = MagAO.MagAOData(filelist)

#set iwa and owa
dataset.IWA = iwa
xDim = dataset._input.shape[2]
yDim = dataset._input.shape[1]
owa = min(xDim, yDim) / 2

#creates cube to eventually hold parameter explorer data
PECube = np.zeros(
    (8, int((subsections_stop - subsections_start) / subsections_inc + 1),
     len(klmodes), int((annuli_stop - annuli_start) / annuli_inc + 1),
     int((movement_stop - movement_start) / movement_inc + 1), int(nplanets)))

###BEGIN LOOPS OVER ANNULI, MOVEMENT AND SUBSECTION PARAMETERS