Ejemplo n.º 1
0
def query_target(target_coord, df, dist=1*u.arcsec, verbose=True):
    """Cross-match target coordinates to HARPS database.

    Parameters
    ----------
    targ_coord : astropy.coordinates.SkyCoord
        targ_coord
    df : pandas.DataFrame
        dataframe/ table of HARPS data downloaded previously
    dist : astropy.units
        angular distance within which to find nearest HARPS object
    verbose : bool
        print texts

    Returns
    -------
    res : pandas.DataFrame
        resulting masked dataframe
    """
    if verbose:
        print('\nQuerying objects within {} of ra,dec=({},{})\n'.format(dist,
                target_coord.ra.value,target_coord.dec.value))
    coords = SkyCoord(ra=df['RA_deg'], dec=df['DEC_deg'], unit=u.deg)

    #compute angular separation between target and all HARPS objects and
    #check which falls within `dist`
    idxs = target_coord.separation(coords)<dist

    #check if there is any within search space
    if idxs.sum() > 0:
        #result may be multiple objects
        res = df[idxs]

        if verbose:
            msg='There are {} matches: {}'.format(len(res), res['Target'].values)
            print(msg)
#             logging.info(msg)
            print('{}\n\n'.format(df.loc[idxs,df.columns[7:14]].T))
        return res

    else:
        #find the nearest HARPS object in the database to target
        idx, sep2d, dist3d = match_coordinates_3d(target_coord, coords, nthneighbor=1)
        nearest_obj = df.iloc[[idx]]['Target'].values[0]
        ra,dec = df.iloc[[idx]][['RA_deg','DEC_deg']].values[0]
        msg='Nearest HARPS obj to target is\n{}: ra,dec=({:.4f},{:.4f})\n'.format(nearest_obj,ra,dec)
        print(msg)
#         logging.info(msg)
        print('Try angular distance larger than d={:.4f}\"\n'.format(sep2d.arcsec[0]))
        return None
Ejemplo n.º 2
0
def catalog_match_track(
    coords,
    catalog,
    affine_param,
    adj_sep_sgn=True,
):
    """Map Astropy SkyCoord to orbit catalog.

    Does a ``match_coordinates_3d`` using orbit as catalog
    to get the time index and 3d distance.

    Returns
    -------
    afn : Quantity
        the affine parameter
    x, y : Quantity
        distance in tangent plane

    """
    idx, _, sep3d, = match_coordinates_3d(coords, catalog)
    afn = affine_param[idx]

    # now project onto plane orthogonal to curve
    # TODO more rigorous tangent vector method
    cart = catalog.cartesian
    tvec = cart[idx + 1] - cart[idx - 1]  # tangent vector
    that = tvec / np.linalg.norm(tvec)

    # define vectors along curve from central point
    delta2 = cart[idx + 1] - cart[idx]
    delta1 = cart[idx - 1] - cart[idx]

    fac = np.divide(np.dot(that, delta1), np.dot(that, delta2))

    xvec = delta1 - fac * delta2
    xhat = xvec / np.linalg.norm(xvec)

    yvec = np.cross(that, xvec)
    yhat = yvec / np.linalg.norm(yvec)

    # basic projection
    # https://en.wikipedia.org/wiki/Vector_projection
    x = np.dot(sep3d, xhat)
    y = np.dot(sep3d, yhat)
    # and error in projection
    d_afn = np.dot(sep3d, that)

    return afn, x, y, d_afn
Ejemplo n.º 3
0
    def _clean_source_list(self, sources):
        """
        Function to clean surces from the catalog removing sources near the borders,
        with 10 pixel tolerance, and to remove blended sources (within 8")

        Parameters
        ----------
        sources : pandas.DataFrame
            Catalog with sources to be removed

        Returns
        -------
        sources : pandas.DataFrame
            Clean catalog
        """

        print("Cleaning sources table...")

        # find sources inside the image with 10 pix of inward tolerance
        inside = ((sources.row > 10)
                  & (sources.row < 1014)
                  & (sources.col > 10)
                  & (sources.col < 1090))
        sources = sources[inside].reset_index(drop=True)

        # find well separated sources
        s_coords = SkyCoord(sources.ra, sources.dec, unit=("deg"))
        midx, mdist = match_coordinates_3d(s_coords, s_coords,
                                           nthneighbor=2)[:2]
        # remove sources closer than 8" = 2 pix
        closest = mdist.arcsec < 8.0
        blocs = np.vstack([midx[closest], np.where(closest)[0]])
        bmags = np.vstack([
            sources.phot_g_mean_mag[midx[closest]],
            sources.phot_g_mean_mag[np.where(closest)[0]],
        ])
        faintest = [
            blocs[idx][s] for s, idx in enumerate(np.argmax(bmags, axis=0))
        ]
        unresolved = np.in1d(np.arange(len(sources)), faintest)
        del s_coords, midx, mdist, closest, blocs, bmags

        sources = sources[~unresolved].reset_index(drop=True)

        return sources
Ejemplo n.º 4
0
def clean_source_list(sources):

    print("Cleaning sources table:")
    # remove bright/faint objects
    # sources = sources[
    #     (sources.phot_g_mean_flux > 1e3) & (sources.phot_g_mean_flux < 1e6)
    # ].reset_index(drop=True)
    # print(sources.shape)

    # find well separated sources
    s_coords = SkyCoord(sources.ra, sources.dec, unit=("deg"))
    midx, mdist = match_coordinates_3d(s_coords, s_coords, nthneighbor=2)[:2]
    # remove sources closer than 4" = 1 pix
    closest = mdist.arcsec < 8.0
    blocs = np.vstack([midx[closest], np.where(closest)[0]])
    bmags = np.vstack(
        [
            sources.phot_g_mean_mag[midx[closest]],
            sources.phot_g_mean_mag[np.where(closest)[0]],
        ]
    )
    faintest = [blocs[idx][s] for s, idx in enumerate(np.argmax(bmags, axis=0))]
    unresolved = np.in1d(np.arange(len(sources)), faintest)
    del s_coords, midx, mdist, closest, blocs, bmags

    sources = sources[~unresolved].reset_index(drop=True)
    print(sources.shape)

    # find sources inside the image with 10 pix of inward tolerance
    inside = (
        (sources.row > 10)
        & (sources.row < 1014)
        & (sources.col > 10)
        & (sources.col < 1090)
    )
    sources = sources[inside].reset_index(drop=True)
    print(sources.shape)
    print("done!")

    return sources
Ejemplo n.º 5
0
            def track_fn(coords,
                         tol=None,
                         init_sampler: T.Union[float, int] = 1e4):
                """Map coordinates to catalog projection.

                .. todo::

                    change defualt `tol` to something else

                Parameters
                ----------
                coords: SkyCoord
                tol : float or None, optional
                    If None (default), does catalog match but no further
                    minimization. The catalog is the evaluation of the "method"
                    function with affine parameter linearly sampled with
                    `init_sampler` points between "afn_bounds"
                init_sampler : int or float, optional
                    the number of points in ``np.linspace`` for an inital
                    sampling of the affine parameter.

                """
                _aff = np.linspace(  # affine parameter
                    *t_bnds, num=int(init_sampler))[1:-2]
                catalog = _track_fn(_aff)

                if tol is None:
                    return catalog_match_track(
                        coords,
                        catalog=catalog,
                        affine_param=_aff,
                        adj_sep_sgn=True,
                    )
                else:  # TODO actual minimization
                    # initial guess
                    idx, sep2d, _, = match_coordinates_3d(coords, catalog)
                    raise ValueError("Not yet implemented")

                return idx
Ejemplo n.º 6
0
def indices_xmatch_coords(
    catalog,
    other,
    maxdist=1 * u.arcsec,
    obstime=None,
    nthneighbor: int = 1,
):
    """Basic 2-catalog x-match. Returns match indices.

    see https://docs.astropy.org/en/stable/coordinates/matchsep.html

    Parameters
    ----------
    catalog, other : SkyCoord or BaseCoordinateFrame
        `catalog` is the "catalogcoord", `other` are the "matchcoord".
        Note that this is in the opposite order as Astropy.
    maxdist : `~astropy.units.Angle` or `~astropy.coordinates.Distance`, optional
        The maximum separation to be considered a match.
        If Angle, does an on-sky x-match using
        :func:`~astropy.coordinates.match_coordinates_sky`.
        If Distance, does a 3d x-match using
        :func:`~astropy.coordinates.match_coordinates_3d`.
    obstime : Time, optional
        If provided, the "epoch" at which to x-match the coords.
        If None (default), will use the obstime of the first catalog to have
        an obstime. An absence of obstime in a catalog means the coordinates
        are *right now*, if this is not the case, ensure the catalog has this
        information.

    Returns
    -------
    catalog_idx : integer array
        indices into `catalog` for the x-match.
    info : dict
        Useful information.

            - sep2d : on-sky separation (Angle)
            - dist3d : 3D distance (Quantity)

    Other Parameters
    ----------------
    nthneighbor : int
        The nthneighbor to use in ``match_coordinates_``.
        TODO, rename to "_nth" but "quantity_input" can't handle underscores.

    See Also
    --------
    :func:`~astropy.coordinates.match_coordinates_sky`
    :func:`~astropy.coordinates.match_coordinates_3d`

    """
    if u.get_physical_type(maxdist.unit) == "angle":
        idx, sep2d, dist3d = coord.match_coordinates_sky(
            matchcoord=other,
            catalogcoord=catalog,
            nthneighbor=nthneighbor,
        )
        sep = sep2d  # separation constraints on this
    elif u.get_physical_type(maxdist.unit) == "length":
        idx, sep2d, dist3d = coord.match_coordinates_3d(
            matchcoord=other,
            catalogcoord=catalog,
            nthneighbor=nthneighbor,
        )
        sep = dist3d  # separation constraints on this

    # separation constraints
    midx = np.where(sep < maxdist)[0]

    if len(midx) == 0:  # no matches
        return False, False, {}
    elif len(midx) == 1:  # correct for case of only 1 match
        midx = midx[0]
        sel = ...
    else:
        sel = midx

    catalog_idx = idx[sel]
    other_idx = midx

    info = {"sep2d": sep2d[sel], "dist3d": dist3d[sel]}

    return catalog_idx, other_idx, info
Ejemplo n.º 7
0
# take a moment to calculate what the matched catalog contains
c_master = coord.SkyCoord(x=hosts_data_master['halo_x'],
                          y=hosts_data_master['halo_y'],
                          z=hosts_data_master['halo_z'],
                          unit='Mpc',
                          frame='icrs',
                          representation='cartesian')
c_matching = coord.SkyCoord(x=hosts_data['halo_x'],
                            y=hosts_data['halo_y'],
                            z=hosts_data['halo_z'],
                            unit='Mpc',
                            frame='icrs',
                            representation='cartesian')

matchid, _, sep3d = coord.match_coordinates_3d(c_matching, c_master)
mask = (sep3d.value <= hosts_data['halo_rvir'] * .001 * .1)

print 'Matches found: ', np.sum(mask)

# let the calculation be done to add additional marks to the main catalog (and satellite number) and then
# make a cut with only the matched galaxies. We won't do satellite numbers because it will likely
# not work terribly well due to statistics.

# add calculated marks
vratio_temp = hosts_data['halo_vmax'] / (np.sqrt(
    gnewton * hosts_data['halo_mass'] / hosts_data['halo_rvir']))
cnfw_temp = hosts_data['halo_rvir'] / hosts_data['halo_rs']

# now we want to go host halo by host halo, match pid to subhalos, and
# count the number that meet our requirements and then add this on
Ejemplo n.º 8
0
    def __init__(self, tpfs, radius_limit=6, flux_limit=1e4):
        """ Class to work with TPF collections

        Parameters:
        -----------
        tpfs : lk.TargetPixelFileCollection
            Collection of target pixel files from Kepler or TESS. These TPFs must be
            from the same quarter, campaign or sector (they must share the same time basis)
        radius_limit : int
            The radius in pixels out to which we will consider flux to be part of the PSF
        flux_limit : float
            The limit in flux where we will calculate the PSF model. The PSF model will still be
            applied to fainter targets than this limit.

        Attributes:
        -----------
        time: np.ndarray
            Time array of measurements
        flux: np.ndarray
            Flux values from TPFs with shape (ntimes x npixels)
        flux_err: np.ndarray
            Flux error values from TPFs with shape (ntimes x npixels)
        unw: np.ndarray
            Array which specifies which TPF a pixel came from. Has dimenions of
            ntimes x npixels. (Name is "unw[rap]")
        ra : np.ndarray
            The RA of every pixel
        dec : np.ndarray
            The declination of every pixel
        GaiaData : pyia.GaiaData
            The gaia data for all sources nearby to pixels
        sources : pd.DataFrame
            Dataframe containing all the sources nearby to pixels. This is separate from GaiaData
            attribute for convenience
        dx : np.ndarray
            The x distance from every source at every pixel. Has dimensions nsources x npixels
        dy : np.ndarray
            The y distance from every source at every pixel. Has dimensions nsources x npixels
        gv : np.ndarray
            The gaia flux of each target, has dimensions nsources x npixels
        close_mask : np.ndarray
            Boolean mask, True where the distance to a source is under `radius_limit`.
            Has shape nsources x npixels.
        mask : np.ndarray
            Boolean mask, True where there is expected to be significant flux from the PSF of a source.
            Has shape nsources x npixels.
        xcent : np.ndarray
            The x centroid position of the sources, as a function of time. Has dimensions (ntime)
        ycent : np.ndarray
            The y centroid position of the sources, as a function of time. Has dimensions (ntime)
        """
        self.tpfs = tpfs
        self.time = tpfs[0].time
        bad_cadences = np.hypot(tpfs[0].pos_corr1, tpfs[0].pos_corr2) > 10

        self.flux = np.hstack(
            [np.hstack(tpf.flux.transpose([2, 0, 1])) for tpf in tpfs])
        self.flux_err = np.hstack(
            [np.hstack(tpf.flux_err.transpose([2, 0, 1])) for tpf in tpfs])
        self.flux_err[bad_cadences] *= 1e2

        self.nt = len(self.time)
        self.npixels = self.flux.shape[1]
        self.ntpfs = len(self.tpfs)

        # We need this to know which pixels belong to which TPF later.
        self.unw = np.hstack([
            np.zeros(
                (tpf.shape[0], tpf.shape[1] * tpf.shape[2]), dtype=int) + idx
            for idx, tpf in enumerate(tpfs)
        ])

        # Find the locations of all the pixels
        locs = [
            np.mgrid[tpf.column:tpf.column + tpf.shape[2],
                     tpf.row:tpf.row + tpf.shape[1]].reshape(
                         2, np.product(tpf.shape[1:])) for tpf in tpfs
        ]
        locs = np.hstack(locs)
        self.locs = locs
        self.ra, self.dec = tpfs[0].wcs.wcs_pix2world(
            np.vstack([(locs[0] - tpfs[0].column), (locs[1] - tpfs[0].row)]).T,
            1).T

        # Create a set of sources from Gaia
        c = SkyCoord(self.ra, self.dec, unit='deg')
        self.GaiaData = get_sources(self, magnitude_limit=18)
        sources = self.GaiaData.data.to_pandas()
        coords = SkyCoord(sources.ra, sources.dec, unit=('deg'))

        slocs = np.asarray([
            tpfs[0].wcs.wcs_world2pix(
                np.vstack([sources.ra[idx], sources.dec[idx]]).T, 0.5)[0]
            for idx in range(len(sources))
        ])

        sources['y'] = slocs[:, 1] + tpfs[0].row
        sources['x'] = slocs[:, 0] + tpfs[0].column

        self.dx, self.dy, self.gv = np.asarray([
            np.vstack([
                locs[0] - sources['x'][idx], locs[1] - sources['y'][idx],
                np.zeros(len(locs[0])) + sources.phot_g_mean_flux[idx]
            ]) for idx in range(len(sources))
        ]).transpose([1, 0, 2])

        l, d = match_coordinates_3d(coords, coords, nthneighbor=2)[:2]
        dmag = np.abs(
            np.asarray(sources.phot_g_mean_mag) -
            np.asarray(sources.phot_g_mean_mag[l]))
        pixel_dist = np.abs(d.to('arcsec')).value / 4

        # Find the faint contaminated stars
        bad = pixel_dist < 1.5
        blocs = np.vstack([l[bad], np.where(bad)[0]]).T
        mlocs = np.vstack([
            sources.phot_g_mean_mag[l[bad]],
            sources.phot_g_mean_mag[np.where(bad)[0]]
        ]).T
        faintest = [
            blocs[idx][i] for idx, i in enumerate(np.argmax(mlocs, axis=1))
        ]
        bad = np.in1d(np.arange(len(sources)), faintest)

        r = np.hypot(self.dx, self.dy)
        self.close_mask = (r < radius_limit)
        #        close_to_pixels = (np.hypot(self.dx, self.dy) < 1).sum(axis=1)

        # Identifing targets that are ON silicon
        surrounded = np.zeros(len(sources), bool)
        for t in np.arange(self.ntpfs):
            xok = (np.asarray(sources['x']) > self.locs[0, self.unw[0] == t]
                   [:, None]) & (np.asarray(sources['x']) <
                                 (self.locs[0, self.unw[0] == t][:, None] + 1))
            yok = (np.asarray(sources['y']) > self.locs[1, self.unw[0] == t]
                   [:, None]) & (np.asarray(sources['y']) <
                                 (self.locs[1, self.unw[0] == t][:, None] + 1))
            surrounded |= (xok & yok).any(axis=0)

        bad |= ~surrounded
        self.bad_sources = sources[bad].reset_index(drop=True)

        source_mask = np.asarray([(c.separation(d1).min().arcsec) < 12
                                  for d1 in coords])
        source_mask &= ~bad

        sources = sources[source_mask].reset_index(drop=True)
        self.dx, self.dy, self.gv = self.dx[source_mask], self.dy[
            source_mask], self.gv[source_mask]
        self.GaiaData = self.GaiaData[source_mask]
        self.sources = sources
        self.nsources = len(self.sources)
        r = np.hypot(self.dx, self.dy)
        self.close_mask = (r < radius_limit)
        #

        self._find_PSF_edge(radius_limit=radius_limit, flux_limit=flux_limit)

        m = sparse.csr_matrix(self.mask.astype(float))
        dx1, dy1 = self.dx[self.mask], self.dy[self.mask]
        self.xcents, self.ycents = np.ones(len(self.flux)), np.ones(
            len(self.flux))
        for tdx in range(len(self.flux)):
            self.xcents[tdx] = np.average(dx1,
                                          weights=np.nan_to_num(
                                              m.multiply(self.flux[tdx]).data))
            self.ycents[tdx] = np.average(dy1,
                                          weights=np.nan_to_num(
                                              m.multiply(self.flux[tdx]).data))

        d = SkyCoord(self.sources.ra, self.sources.dec, unit='deg')
        close = match_coordinates_3d(
            d, d, nthneighbor=2)[1].to('arcsecond').value < 8
        sources = SkyCoord(self.sources.ra, self.sources.dec, unit=('deg'))
        tpfloc = SkyCoord(
            [SkyCoord(tpf.ra, tpf.dec, unit='deg') for tpf in tpfs])
        idx = np.asarray(
            [match_coordinates_3d(loc, sources)[0] for loc in tpfloc])
        jdx = np.asarray(
            [match_coordinates_3d(source, tpfloc)[0] for source in sources])
        self.fresh = ~np.in1d(np.arange(0, self.nsources), idx)

        self.mean_model = self._build_model()
        self._fit_model()