Exemplo n.º 1
0
    def get_bricks(self):
        '''
        Returns a table of bricks.  The caller owns the table.

        For read-only purposes, see *get_bricks_readonly()*, which
        uses a cached version.
        '''
        return fits_table(self.find_file('bricks'))
Exemplo n.º 2
0
def sky_fibers_for_brick(
        survey,
        brickname,
        nskies=144,
        bands=['g', 'r', 'z'],
        apertures_arcsec=[0.5, 0.75, 1., 1.5, 2., 3.5, 5., 7.]):
    """Produce DESI sky fiber locations in a brick, derived at the pixel-level

    Parameters
    ----------
    survey : :class:`object`
        `LegacySurveyData` object for a given Data Release of the Legacy Surveys; see
        :func:`~desitarget.skyutilities.legacypipe.util.LegacySurveyData` for details.
    brickname : :class:`str`
        Name of the brick in which to generate sky locations.
    nskies : :class:`float`, optional, defaults to 144 (12 x 12)
        The minimum DENSITY of sky fibers to generate
    bands : :class:`list`, optional, defaults to ['g', 'r', 'z']
        List of bands to be used to define good sky locations.
    apertures_arcsec : :class:`list`, optional, defaults to [0.5,0.75,1.,1.5,2.,3.5,5.,7.]
        Radii in arcsec of apertures for which to derive flux at a sky location.

    Returns
    -------
    :class:`object`
        A FITS table that includes:
        - the brickid
        - the brickname
        - the x and y pixel positions of the fiber location from the blobs file
        - the distance from the nearest blob of this fiber location
        - the RA and Dec positions of the fiber location
        - the aperture flux and ivar at the passed `apertures_arcsec`

    Notes
    -----
        - Initial version written by Dustin Lang (@dstndstn).
    """

    fn = survey.find_file('blobmap', brick=brickname)
    # ADM if the file doesn't exist, warn and return immediately.
    if not os.path.exists(fn):
        log.warning('blobmap {} does not exist!!!'.format(fn))
        return None
    blobs = fitsio.read(fn)
    # log.info('Blob maximum value and minimum value in brick {}: {} {}'
    #         .format(brickname,blobs.min(),blobs.max()))
    header = fitsio.read_header(fn)
    wcs = WCS(header)

    goodpix = (blobs == -1)
    # ADM while looping through bands, check there's an image in
    # ADM at least one band, otherwise the blob map has no meaning
    # ADM for these bands, and aperture photometry is not possible
    onegoodband = False
    for band in bands:
        fn = survey.find_file('nexp', brick=brickname, band=band)
        if not os.path.exists(fn):
            # Skip
            continue
        nexp = fitsio.read(fn)
        goodpix[nexp == 0] = False
        onegoodband = True
    # ADM if there were no images in the passed bands, fail
    if not onegoodband:
        log.fatal('No images for passed bands: {}'.format(bands))
        raise ValueError

    # Cut to unique brick area... required since the blob map drops
    # blobs that are completely outside the brick's unique area, thus
    # those locations are not masked.
    brick = survey.get_brick_by_name(brickname)
    # ADM the width and height of the image in pixels is just the
    # ADM shape of the input blobs file
    H, W = blobs.shape
    U = find_unique_pixels(wcs, W, H, None, brick.ra1, brick.ra2, brick.dec1,
                           brick.dec2)
    goodpix[U == 0] = False
    del U

    # ADM the minimum safe grid size is the number of pixels along an
    # ADM axis divided by the number of sky locations along any axis.
    gridsize = np.min(blobs.shape / np.sqrt(nskies)).astype('int16')
    # log.info('Gridding at {} pixels in brick {}...t = {:.1f}s'
    #         .format(gridsize,brickname,time()-start))
    x, y, blobdist = sky_fiber_locations(goodpix, gridsize=gridsize)
    skyfibers = fits_table()
    skyfibers.brickid = np.zeros(len(x), np.int32) + brick.brickid
    skyfibers.brickname = np.array([brickname] * len(x))
    skyfibers.x = x.astype(np.int16)
    skyfibers.y = y.astype(np.int16)
    skyfibers.blobdist = blobdist
    # ADM start at pixel 0,0 in the top-left (the numpy standard).
    skyfibers.ra, skyfibers.dec = wcs.all_pix2world(x, y, 0)

    # ADM find the pixel scale using the square root of the determinant
    # ADM of the CD matrix (and convert from degrees to arcseconds).
    pixscale = np.sqrt(np.abs(np.linalg.det(wcs.wcs.cd))) * 3600.
    apertures = np.array(apertures_arcsec) / pixscale
    naps = len(apertures)

    # Now, do aperture photometry at these points in the coadd images.
    for band in bands:
        imfn = survey.find_file('image', brick=brickname, band=band)
        ivfn = survey.find_file('invvar', brick=brickname, band=band)

        # ADM set the apertures for every band regardless of whether
        # ADM the file exists, so that we get zeros for missing bands.
        apflux = np.zeros((len(skyfibers), naps), np.float32)
        # ADM set any zero flux to have an infinite error (zero ivar).
        apiv = np.zeros((len(skyfibers), naps), np.float32)
        skyfibers.set('apflux_%s' % band, apflux)
        skyfibers.set('apflux_ivar_%s' % band, apiv)

        if not (os.path.exists(imfn) and os.path.exists(ivfn)):
            continue

        coimg = fitsio.read(imfn)
        coiv = fitsio.read(ivfn)

        with np.errstate(divide='ignore', invalid='ignore'):
            imsigma = 1. / np.sqrt(coiv)
            imsigma[coiv == 0] = 0
        apxy = np.vstack((skyfibers.x, skyfibers.y)).T
        for irad, rad in enumerate(apertures):
            aper = photutils.CircularAperture(apxy, rad)
            p = photutils.aperture_photometry(coimg, aper, error=imsigma)
            apflux[:, irad] = p.field('aperture_sum')
            err = p.field('aperture_sum_err')
            # ADM where the error is 0, that actually means infinite error
            # ADM so, in reality, set the ivar to 0 for those cases and
            # ADM retain the true ivars where the error is non-zero.
            # ADM also catch the occasional NaN (which are very rare).
            ii = np.isnan(err)
            err[ii] = 0.0
            wzero = np.where(err == 0)
            wnonzero = np.where(err > 0)
            apiv[:, irad][wnonzero] = 1. / err[wnonzero]**2
            apiv[:, irad][wzero] = 0.

    header = fitsio.FITSHDR()
    for i, ap in enumerate(apertures_arcsec):
        header.add_record(
            dict(name='AP%i' % i, value=ap,
                 comment='Aperture radius (arcsec)'))
    skyfibers._header = header

    return skyfibers