예제 #1
0
def conesearch(catalog, ra, dec, radius):
    '''
    Returns the cone search result. It uses an auxiliary
    function.

    Args:
        catalog (string): catalog name according to the available catalog
        in the docs.
        ra (float): ra coordinate of the point to search in degrees.
        dec (float): dec coordinate of the point to search in degrees.
        radius (float): radius to search in arcsec.
    Returns:
        A dictionary containing the cone search result for a single catalog.
    '''
    # call catsHTM cone search
    match, catalog_columns, column_units = cone_search(catalog, ra, dec,
                                                       radius, path)
    # no results, empty dictionary
    if match.size == 0:
        return {}
    # generate dictionaries with response values
    results = format_cone_results(match, catalog_columns, column_units)
    # add catalog real name and append to final result
    result_with_catname = {}
    result_with_catname[catalog_map.get(catalog, catalog)] = results
    return result_with_catname
    def is_star_in_gaia(self, transient):
        """
			match tranient position with GAIA DR2 and uses parallax 
			and proper motion to evaluate star-likeliness
			
			returns: True (is a star) or False otehrwise.
		"""

        transient_coords = SkyCoord(transient['ra'],
                                    transient['dec'],
                                    unit='deg')
        srcs, colnames, colunits = cone_search('GAIADR2',
                                               transient_coords.ra.rad,
                                               transient_coords.dec.rad,
                                               self.gaia_rs,
                                               catalogs_dir=self.catshtm_path)
        my_keys = [
            'RA', 'Dec', 'Mag_G', 'PMRA', 'ErrPMRA', 'PMDec', 'ErrPMDec',
            'Plx', 'ErrPlx'
        ]
        if len(srcs) > 0:
            gaia_tab = Table(srcs, names=colnames)
            gaia_tab = gaia_tab[my_keys]
            gaia_coords = SkyCoord(gaia_tab['RA'], gaia_tab['Dec'], unit='rad')

            # compute distance
            gaia_tab['DISTANCE'] = transient_coords.separation(
                gaia_coords).arcsec
            gaia_tab['DISTANCE_NORM'] = (1.8 + 0.6 * exp(
                (20 - gaia_tab['Mag_G']) / 2.05) > gaia_tab['DISTANCE']
                                         )  #TODO: vary mag_G exclusion
            gaia_tab['FLAG_PROX'] = [
                True if x['DISTANCE_NORM'] == True and
                (self.gaia_veto_gmag_min <= x['Mag_G'] <=
                 self.gaia_veto_gmag_max) else False for x in gaia_tab
            ]

            # check for proper motion and parallax conditioned to distance
            gaia_tab['FLAG_PMRA'] = abs(
                gaia_tab['PMRA'] / gaia_tab['ErrPMRA']) > self.gaia_pm_signif
            gaia_tab['FLAG_PMDec'] = abs(
                gaia_tab['PMDec'] / gaia_tab['ErrPMDec']) > self.gaia_pm_signif
            gaia_tab['FLAG_Plx'] = abs(
                gaia_tab['Plx'] / gaia_tab['ErrPlx']) > self.gaia_plx_signif
            if (any(gaia_tab['FLAG_PMRA'] == True)
                    or any(gaia_tab['FLAG_PMDec'] == True)
                    or any(gaia_tab['FLAG_Plx'] == True)) and any(
                        gaia_tab['FLAG_PROX'] == True):
                return True
        return False
예제 #3
0
def crossmatch(catalog, ra, dec, radius):
    '''
    This function returns the crossmatch result for a catalog.

    Args:
        catalog (string): catalog name according to the available catalog
        in the docs.
        ra (float): ra coordinate of the point to search in degrees.
        dec (float): dec coordinate of the point to search in degrees.
        radius (float): radius to search in arcsec.
    Returns:
        A dictionary with the crossmatch result.
    '''
    # call catsHTM cone search
    match, catalog_columns, column_units = cone_search(catalog, ra, dec,
                                                       radius, path)
    if match.size != 0:
        return format_crossmatch_results(match, catalog, ra, dec,
                                         catalog_columns, column_units)
    else:
        return {}
예제 #4
0
def _make_sim_transient(inputrow, match_rad=2, search_rad=1.8, min_mag=16, max_mag=20.2, glx_search_scale=600,
                        glx_jitter=5, stamp_size=55, resid_every=0, rng_state=42):
    """
    internal routine to generate transients

    :param inputrow: row of dataframe (from label_data.get_images()) generated by iterrows()
    :param match_rad: radius for MP cross-matching - should be ~1" for optimal results
    :param search_rad: size of radius for initial MP crossmatch - should be ~ size of image
    :param min_mag: bright limit for minor planets - remove detections that completely overpower host galaxy
    :param max_mag: faint limit for minor planets - should be brighter than normal to ensure decent SNR transients
    :param glx_search_scale: maximum distance from each minor planet to choose a galaxy from - coherency of PSF
    :param glx_jitter: uniform scatter to apply to galaxy stamp to simulate offset transients
    :param stamp_size: size of stamps to extract
    :param resid_every: number of galaxy residuals
    :param rng_state: testing seed for repeatability
    :return:
    """

    np.random.seed(rng_state)
    _, row = inputrow

    ut = int(row['instrument'][-1])

    fits_filepath = work_to_storage_filepath(row.filepath)
    try:
        fits_file = fits.open(fits_filepath)
    except FileNotFoundError:
        try:
            fits_file = fits.open(swap_dataloc(fits_filepath))
        except FileNotFoundError:
            logger.warning("corresponding file not found")
            return

    indiv_images_df = get_individual_images(row.relatedimage)
    try:
        indiv_fits_files = [fits.open(work_to_storage_filepath(row.filepath)) for _, row in indiv_images_df.iterrows()]
    except FileNotFoundError:
        try:
            indiv_fits_files = [fits.open(swap_dataloc(work_to_storage_filepath(row.filepath))) for _, row in
                                indiv_images_df.iterrows()]
        except FileNotFoundError:
            logger.warning("Individual FITS files missing: {}".format(work_to_storage_filepath(row.filepath)))
            return

    mpc_search = query_skybot(row.ra_c, row.dec_c, search_rad, row.jd, maglim=max_mag)

    try:
        logger.info("found {} MPs in image".format(len(mpc_search)))
        mpc_ras = list(mpc_search['RA(h)'])
        mpc_decs = list(mpc_search['DE(deg)'])
        mpc_coo = SkyCoord(mpc_ras, mpc_decs, unit=(u.hourangle, u.deg))
    except TypeError:
        logger.warning("no MPs found!")
        return

    try:
        diff_phot_tbl = Table(fits_file[DIFFERENCE_PHOT_EXT].data)
    except KeyError:
        logger.warning("couldn't find {} extension in {}".format(DIFFERENCE_PHOT_EXT, fits_filepath))
        return
    if len(diff_phot_tbl) < 2:
        logger.info('(almost) empty table found in {}[{}]'.format(fits_filepath, DIFFERENCE_PHOT_EXT))
        return
    diff_coo = SkyCoord(diff_phot_tbl['ra'], diff_phot_tbl['dec'], unit='deg')

    try:
        idx, d2d, _ = mpc_coo.match_to_catalog_sky(diff_coo)
    except ValueError:
        logger.error('failed with {}'.format(fits_filepath))
        return
    # noinspection PyUnresolvedReferences
    matches = diff_phot_tbl[idx[(d2d <= match_rad * u.arcsec)]]
    if 'realbogus' not in matches.colnames:
        logger.warning('no realbogus column in difference photometry for {}'.format(fits_filepath))
        return
    # little safety net to avoid spuriously matching to RB=0 dets too often
    matches = matches[matches['realbogus'] > 0.01]
    logger.info('found {} mpc cross-matches'.format(len(matches)))

    if matches is None:
        logger.warning("no minor planets found!")
        return

    # optimise - query GLADE once per image to save time
    glade_data, glade_cols, _ = catsHTM.cone_search("GLADE", (np.pi / 180) * row.ra_c, (np.pi / 180) * row.dec_c,
                                                    search_rad * 3600,
                                                    catalogs_dir=CATSHTM_DIR)
    glade_tab = Table(glade_data, names=glade_cols)
    glade_tab = glade_tab[np.isfinite(glade_tab["B"])]  # clean the NaNs to avoid warnings
    glade_tab = glade_tab[glade_tab["B"] < 18]  # maglim cut

    logger.info("{} GLADE galaxies in frame".format(len(glade_tab)))

    results = []
    for match in matches:
        # remove detections that are too bright and saturate the detector.
        if match["mag"] < min_mag:
            logger.warning("MP exceeds bright threshold")
            continue

        glade_coords = SkyCoord(glade_tab["RA"], glade_tab["Dec"], unit=u.rad)  # keep in to update table on iter
        mp_coords = SkyCoord(match["ra"], match["dec"], unit=u.deg)
        nn_dists = mp_coords.separation(glade_coords).to(u.arcsec).value
        temp_tab = glade_tab[nn_dists < glx_search_scale]

        if len(temp_tab) == 0:
            logger.warning("No GLADE galaxies within {} arcsec".format(glx_search_scale))
            continue

        glx_sel = temp_tab[np.argmin(temp_tab["B"])]  # greedily select bright galaxies

        try:
            glx_stamp, glx_indiv = _get_stamps(fits_file,
                                    (180 / np.pi) * glx_sel["RA"] + np.random.uniform(-glx_jitter, glx_jitter) / 3600,
                                    (180 / np.pi) * glx_sel["Dec"] + np.random.uniform(-glx_jitter, glx_jitter) / 3600,
                                    stamp_size,
                                    indiv_fits_files, return_indiv_stamps=True)
        except NoOverlapError:
            logger.warning("galaxy located off edge of stamp")
            continue

        # HACK need to work out what the problem is with this...
        except (TypeError, ValueError):
            logger.warning("problem with stamp extraction")
            continue

        try:
            mp_stamp, mp_indiv = _get_stamps(fits_file, match["ra"], match["dec"], stamp_size, indiv_fits_files,
                                             return_indiv_stamps=True)

        # HACK similarly here
        except (TypeError, ValueError):
            logger.warning("Problem with stamp extraction")
            continue

        comb_stamp = mp_stamp + glx_stamp
        assert mp_indiv.shape == glx_indiv.shape # crash if the stamps aren't equiv
        indiv_img_comb = mp_indiv + glx_indiv

        # need to respect addition of p2p stamps correctly.
        comb_stamp[:, :, 3] = np.ptp(np.atleast_3d(indiv_img_comb), axis=2) # peak to peak
        comb_stamp[:, :, 4] = np.min(np.atleast_3d(indiv_img_comb), axis=2) # min
        comb_stamp[:, :, 5] = np.max(np.atleast_3d(indiv_img_comb), axis=2) # max

        if comb_stamp is not None:
            res = dict(
                id=str(uuid.uuid4()),
                stamps=comb_stamp,
                image_id=row['id'],
                x=match['x'],
                y=match['y'],
                ra=match['ra'],
                dec=match['dec'],
                mag=match['mag'],
                fwhm=match['fwhm'],
                realbogus=match['realbogus'],
                fits_filepath=fits_filepath,
                ut=ut,
                ncoadd=len(indiv_fits_files),
                label=1,  # synthetic real transient
                metalabel='syntransient'
            )
            results.append(res)

        # avoid duplicate seed galaxies
        glade_tab.remove_row(
            np.int(np.argwhere((glade_tab["RA"] == glx_sel["RA"]) & (glade_tab["Dec"] == glx_sel["Dec"]))))

    if len(glade_tab) == 0:
        logger.info("ran out of galaxies")
        return results

    if resid_every == 0:
        return results

    glade_tab.sort("B") # sort in ascending order (i.e. bright end first)
    temp_coo = SkyCoord((180/np.pi) * glade_tab["RA"], (180/np.pi) * glade_tab["Dec"], unit=u.deg)

    ndets = int(min(resid_every*len(matches), len(matches))) # make sure we never exceed the number of matches
    im_wcs = WCS(fits_file[1].header) # parse WCS for computing detector position
    for idx, c in enumerate(temp_coo[:ndets]):
        try:
            glx_resid_stamp = _get_stamps(fits_file,
                                          c.ra.value + np.random.uniform(-glx_jitter/2, glx_jitter/2) / 3600,
                                          c.dec.value + np.random.uniform(-glx_jitter/2, glx_jitter/2) / 3600,
                                          stamp_size, indiv_fits_files)
        except NoOverlapError:
            logger.warning("galaxy located off edge of stamp")
            continue

        # the top except should catch any problems with this.
        xdet, ydet = im_wcs.all_world2pix([c.ra.value], [c.dec.value], 0)

        if glx_resid_stamp is not None:
            res = dict(
                id=str(uuid.uuid4()),
                stamps=glx_resid_stamp,
                image_id=row['id'],
                x=xdet[0],
                y=ydet[0],
                ra=c.ra.value,
                dec=c.dec.value,
                mag=glade_tab["B"][idx],
                fwhm=99.99, # this is ill-defined here, since we're not using detections
                realbogus=99.99, # similarly. b
                fits_filepath=fits_filepath,
                ut=ut,
                ncoadd=len(indiv_fits_files),
                label=0,  # galaxy residual
                metalabel='glxresid'
            )
            results.append(res)

    logger.info("{} transients and residuals added".format(len(results)))
    return results
예제 #5
0
def cat_search(ra_in, dec_in, srad):
    ra_in, dec_in = float(ra_in), float(dec_in)

    # give catsHTM rootpath
    catsHTM_rootpath = par['catsHTM_rootpath']

    srad = Angle(float(srad) * u.arcsec)
    cats_srad = srad.arcsec
    # Convert center-position into Astropy SkyCoords:
    position = skycoord(ra=ra_in * u.degree,
                        dec=dec_in * u.degree,
                        frame="icrs")

    def get_center_offset(pos):
        # get separation between two positions
        sep_angle = pos.separation(position)
        return sep_angle.arcsec

    # all catalogs used to check
    all_catalogs = [
        'TMASS', 'TMASSxsc', 'AAVSO_VSX', 'AKARI', 'APASS', 'DECaLS', 'FIRST',
        'GAIADR1', 'GAIADR2', 'GALEX', 'GLADE', 'IPHAS', 'NEDz', 'PS1',
        'PTFpc', 'ROSATfsc', 'SkyMapper', 'SpecSDSS', 'SAGE', 'IRACgc',
        'UCAC4', 'UKIDSS', 'VISTAviking', 'VSTkids', 'WISE', 'XMM', 'simbad',
        'tns'
    ]

    # define the result table
    useful_col = ['catname', 'ra', 'dec', 'offset', 'otype']
    all_items_df = pd.DataFrame(columns=useful_col)

    for cat in all_catalogs:  # loop through all catalogs
        if cat == 'simbad':
            sb_cols, sb_units, sb_out, sb_success = simbad_check(
                ra=position.ra.degree,
                dec=position.dec.degree,
                srad=srad.arcsec)
            if (sb_success == 1) and (len(sb_out) > 0):
                itemframe = pd.DataFrame(sb_out, columns=sb_cols)
                itemframe['offset'] = [
                    Angle(float(off) * u.arcsec).arcsec
                    for off in itemframe['offset'].values
                ]
                itemframe = itemframe[useful_col[1:]]
                itemframe[useful_col[1:-1]] = itemframe[
                    useful_col[1:-1]].astype('float')
                itemframe['catname'] = 'simbad'
                itemframe = itemframe[useful_col]
                itemframe = itemframe.sort_values('offset',
                                                  ascending=1).iloc[[0]]
                all_items_df = pd.concat([all_items_df, itemframe], axis=0)
                # if 'Galaxy' in itemframe.otype.values:
                # 	break
        elif cat == 'tns':
            tns_cols, tns_units, tns_out, tns_success = tns_check(
                ra=position.ra.degree,
                dec=position.dec.degree,
                srad=srad.arcsec)
            tns_cols = [tns_cols[i].lower() for i in range(len(tns_cols))]

            if tns_success == True and len(tns_out) > 0:
                itemframe = pd.DataFrame(tns_out, columns=tns_cols)
                itemframe['skycoord'] = skycoord(itemframe['ra'],
                                                 itemframe['dec'],
                                                 unit=u.deg,
                                                 frame='icrs')
                itemframe['offset'] = itemframe['skycoord'].apply(
                    get_center_offset)

                itemframe['otype'] = itemframe['obj. type']
                itemframe = itemframe[useful_col[1:]]
                itemframe['catname'] = 'tns'
                itemframe = itemframe[useful_col]
                itemframe = itemframe.sort_values('offset',
                                                  ascending=1).iloc[[0]]
                all_items_df = pd.concat([all_items_df, itemframe], axis=0)
        else:
            # make sure the catalog name is string
            cat = str(cat)
            # cone search with catsHTM
            if not cat == 'GLADE':
                cat_out, colcell, colunits = catsHTM.cone_search(
                    cat,
                    position.ra.radian,
                    position.dec.radian,
                    cats_srad,
                    catalogs_dir=catsHTM_rootpath,
                    verbose=False)
            else:
                cat_out, colcell, colunits = catsHTM.cone_search(
                    cat,
                    position.ra.radian,
                    position.dec.radian,
                    Angle(float(30) * u.arcsec).arcsec,
                    catalogs_dir=catsHTM_rootpath,
                    verbose=False)

            colcell = [colcell[i].lower() for i in range(len(colcell))]
            colunits = [colunits[i].lower() for i in range(len(colunits))]
            # create dict for unit of each column
            colunits = {colcell[i]: colunits[i] for i in range(len(colcell))}

            if len(cat_out) > 0:
                # create empty cross-match result table if the cross-match result is not None
                itemframe = pd.DataFrame(cat_out, columns=colcell)
                # change the unit of RA and Dec as degrees
                if colunits['ra'] == 'rad' and colunits['dec'] == 'rad':
                    itemframe['ra'] = itemframe['ra'].apply(degrees)
                    itemframe['dec'] = itemframe['dec'].apply(degrees)
                elif colunits['ra'] == 'radians' and colunits[
                        'dec'] == 'radians':
                    itemframe['ra'] = itemframe['ra'].apply(degrees)
                    itemframe['dec'] = itemframe['dec'].apply(degrees)
                elif colunits['ra'] == 'deg' and colunits['dec'] == 'deg':
                    itemframe['ra'] = itemframe['ra'].apply(float)
                    itemframe['dec'] = itemframe['dec'].apply(float)
                # create new column for SkyCoord onjects
                itemframe['skycoord'] = skycoord(itemframe['ra'],
                                                 itemframe['dec'],
                                                 unit=u.deg,
                                                 frame='icrs')
                # get angular difference between the cross-matched objects and the input position
                itemframe['offset'] = itemframe['skycoord'].apply(
                    get_center_offset)
                # sort dataframe by offset
                itemframe = itemframe.sort_values('offset',
                                                  ascending=1).iloc[[0]]
                # define type of the crossmatched object
                if cat == 'AAVSO_VSX':
                    itemframe['otype'] = 'VS'
                elif cat == 'GLADE':
                    itemframe['otype'] = 'Galaxy'
                else:
                    itemframe['otype'] = 'Unknown'
                # add catalog name
                itemframe['catname'] = cat
                # select useful col only
                itemframe = itemframe[useful_col]
                all_items_df = pd.concat([all_items_df, itemframe], axis=0)

    if all_items_df.empty:
        return all_items_df
    else:
        all_items_df = all_items_df.sort_values('offset', ascending=1)
        all_items_df = all_items_df.reset_index().drop("index", axis=1)
        return all_items_df