Exemple #1
0
def write_rot_plots(
        indstart,
        nproc,
        out_basedir='/scratch1/scratchdirs/ameisner/unwise_psf_plots',
        band=1):
    atlas = fitsio.read('~/unwise/pro/allsky-atlas.fits')

    ntile = len(atlas)
    indend = min(indstart + nproc, ntile)

    atlas = atlas[indstart:indend]

    for i, row in enumerate(atlas):
        coadd_id = str(row['coadd_id'])
        print i, coadd_id
        rot = get_unwise_psf(band, coadd_id)
        plt.imshow(rot,
                   cmap='gray',
                   interpolation='nearest',
                   norm=LogNorm(vmin=0.1, vmax=1000000),
                   origin='lower')
        plt.xticks([])
        plt.yticks([])
        plt.title('W' + str(band) + ', ' + coadd_id)
        outdir = out_basedir + '/' + coadd_id[0:3]
        outname = outdir + '/' + coadd_id + '.png'
        if not os.path.exists(outdir): os.mkdir(outdir)
        plt.savefig(outname, dpi=250, bbox_inches='tight')
        plt.cla()
Exemple #2
0
def all_coadd_id():
    atlas = fitsio.read('~/unwise/pro/allsky-atlas.fits')

    for i, row in enumerate(atlas):
        print i
        rot = get_unwise_psf(None, str(row['coadd_id']))
        print rot.shape
Exemple #3
0
def test_frames():
    frames = fitsio.read(
        '/global/projecta/projectdirs/cosmo/work/wise/outputs/merge/neo4/fulldepth/000/0000m016/unwise-0000m016-w1-frames.fits'
    )
    psf = unwise_psf.get_unwise_psf(1, '0000m016', frames=frames)

    return psf
Exemple #4
0
def test_tr_w2():

    # http://legacysurvey.org/viewer?ra=163.9380&dec=33.5105&zoom=10&layer=unwise-neo3

    frames = fitsio.read(
        '/global/projecta/projectdirs/cosmo/work/wise/outputs/merge/neo4/e008/163/1637p333/unwise-1637p333-w2-frames.fits'
    )

    psf = unwise_psf.get_unwise_psf(2, '0000m016', frames=frames)

    return psf
Exemple #5
0
def test_poles():

    # http://legacysurvey.org/viewer?ra=97.7563&dec=-66.8662&zoom=8&layer=unwise-neo3

    # what coadd_id does this correspond to ?
    frames = fitsio.read(
        '/global/projecta/projectdirs/cosmo/work/wise/outputs/merge/neo4/fulldepth/096/0964m667/unwise-0964m667-w2-frames.fits'
    )

    psf = unwise_psf.get_unwise_psf(2, '0964m667', frames=frames)

    return psf
Exemple #6
0
def wise_psf(band, coadd_id):
    # psf noise: ~roughly 0.1 count in outskirts of W1 and W2
    if band >= 3:
        raise ValueError('Need to stare at W3+ PSF more!')
    psfnoise = 0.1
    stamp = unwise_psf.get_unwise_psf(band, coadd_id)
    edges = numpy.concatenate(
        [stamp[0, 1:-1], stamp[-1, 1:-1], stamp[1:-1, 0], stamp[1:-1, -1]])
    if band == 1:
        medval = numpy.median(edges[edges != 0]) / 2
    elif band == 2:
        medval = numpy.median(edges[edges != 0]) / 4
    else:
        medval = 0.
    stamp[stamp == 0] = medval
    stamp -= medval
    # stamp = numpy.clip(stamp, -numpy.median(edges[edges != 0]), numpy.inf)
    # print(numpy.median(edges[edges != 0]))
    # print(numpy.min(stamp))
    from scipy import signal
    stamp[stamp < 0] = 0.
    # suppress spurious warnings in signal.wiener
    olderr = numpy.seterr(invalid='ignore', divide='ignore')
    stamp = signal.wiener(stamp, 11, psfnoise)
    numpy.seterr(**olderr)
    # taper linearly over outer 60 pixels?
    stampszo2 = stamp.shape[0] // 2
    xx, yy = numpy.mgrid[-stampszo2:stampszo2 + 1, -stampszo2:stampszo2 + 1]
    edgedist = numpy.clip(stampszo2 - numpy.abs(xx), 0,
                          stampszo2 - numpy.abs(yy))
    stamp = stamp * numpy.clip(edgedist / 60., stamp < 10, 1)
    stamp = stamp / numpy.sum(stamp)
    psf = psfmod.SimplePSF(stamp)
    from functools import partial
    psf.fitfun = partial(psfmod.wise_psf_fit, psfstamp=stamp)
    return psf
Exemple #7
0
def unwise_forcedphot(cat,
                      tiles,
                      bands=[1, 2, 3, 4],
                      roiradecbox=None,
                      unwise_dir='.',
                      use_ceres=True,
                      ceres_block=8,
                      save_fits=False,
                      get_models=False,
                      ps=None,
                      psf_broadening=None,
                      pixelized_psf=False):
    '''
    Given a list of tractor sources *cat*
    and a list of unWISE tiles *tiles* (a fits_table with RA,Dec,coadd_id)
    runs forced photometry, returning a FITS table the same length as *cat*.
    '''

    # # Severely limit sizes of models
    for src in cat:
        if isinstance(src, PointSource):
            src.fixedRadius = 20
        else:
            src.halfsize = 20

    wantims = ((ps is not None) or save_fits or get_models)
    wanyband = 'w'
    if get_models:
        models = {}

    fskeys = [
        'prochi2', 'pronpix', 'profracflux', 'proflux', 'npix', 'pronexp'
    ]

    Nsrcs = len(cat)
    phot = fits_table()
    phot.tile = np.array(['        '] * Nsrcs)

    ra = np.array([src.getPosition().ra for src in cat])
    dec = np.array([src.getPosition().dec for src in cat])

    for band in bands:
        print('Photometering WISE band', band)
        wband = 'w%i' % band

        # The tiles have some overlap, so for each source, keep the
        # fit in the tile whose center is closest to the source.
        tiledists = np.empty(Nsrcs)
        tiledists[:] = 1e100
        flux_invvars = np.zeros(Nsrcs, np.float32)
        fitstats = dict([(k, np.zeros(Nsrcs, np.float32)) for k in fskeys])
        nexp = np.zeros(Nsrcs, np.int16)
        mjd = np.zeros(Nsrcs, np.float64)

        for tile in tiles:
            print('Reading tile', tile.coadd_id)

            tim = get_unwise_tractor_image(unwise_dir,
                                           tile.coadd_id,
                                           band,
                                           bandname=wanyband,
                                           roiradecbox=roiradecbox)
            if tim is None:
                print('Actually, no overlap with tile', tile.coadd_id)
                continue

            if pixelized_psf:
                import unwise_psf
                psfimg = unwise_psf.get_unwise_psf(band, tile.coadd_id)
                print('PSF postage stamp', psfimg.shape, 'sum', psfimg.sum())
                from tractor.psf import PixelizedPSF
                psfimg /= psfimg.sum()
                tim.psf = PixelizedPSF(psfimg)
                print('### HACK ### normalized PSF to 1.0')
                print('Set PSF to', tim.psf)

                if False:
                    ph, pw = psfimg.shape
                    px, py = np.meshgrid(np.arange(ph), np.arange(pw))
                    cx = np.sum(psfimg * px)
                    cy = np.sum(psfimg * py)
                    print('PSF center of mass: %.2f, %.2f' % (cx, cy))

                    for sz in range(1, 11):
                        middle = pw // 2
                        sub = (slice(middle - sz, middle + sz + 1),
                               slice(middle - sz, middle + sz + 1))
                        cx = np.sum((psfimg * px)[sub]) / np.sum(psfimg[sub])
                        cy = np.sum((psfimg * py)[sub]) / np.sum(psfimg[sub])
                        print('Size', sz,
                              ': PSF center of mass: %.2f, %.2f' % (cx, cy))

                    import fitsio
                    fitsio.write('psfimg-%s-w%i.fits' % (tile.coadd_id, band),
                                 psfimg,
                                 clobber=True)

            if psf_broadening is not None and not pixelized_psf:
                # psf_broadening is a factor by which the PSF FWHMs
                # should be scaled; the PSF is a little wider
                # post-reactivation.
                psf = tim.getPsf()
                from tractor import GaussianMixturePSF
                if isinstance(psf, GaussianMixturePSF):
                    #
                    print('Broadening PSF: from', psf)
                    p0 = psf.getParams()
                    #print('Params:', p0)
                    pnames = psf.getParamNames()
                    #print('Param names:', pnames)
                    p1 = [
                        p * psf_broadening**2 if 'var' in name else p
                        for (p, name) in zip(p0, pnames)
                    ]
                    #print('Broadened:', p1)
                    psf.setParams(p1)
                    print('Broadened PSF:', psf)
                else:
                    print(
                        'WARNING: cannot apply psf_broadening to WISE PSF of type',
                        type(psf))

            print('Read image with shape', tim.shape)

            # Select sources in play.
            wcs = tim.wcs.wcs
            H, W = tim.shape
            ok, x, y = wcs.radec2pixelxy(ra, dec)
            x = (x - 1.).astype(np.float32)
            y = (y - 1.).astype(np.float32)
            margin = 10.
            I = np.flatnonzero((x >= -margin) * (x < W + margin) *
                               (y >= -margin) * (y < H + margin))
            print(len(I), 'within the image + margin')

            inbox = ((x[I] >= -0.5) * (x[I] < (W - 0.5)) * (y[I] >= -0.5) *
                     (y[I] < (H - 0.5)))
            print(sum(inbox), 'strictly within the image')

            # Compute L_inf distance to (full) tile center.
            tilewcs = unwise_tile_wcs(tile.ra, tile.dec)
            cx, cy = tilewcs.crpix
            ok, tx, ty = tilewcs.radec2pixelxy(ra[I], dec[I])
            td = np.maximum(np.abs(tx - cx), np.abs(ty - cy))
            closest = (td < tiledists[I])
            tiledists[I[closest]] = td[closest]

            keep = inbox * closest

            # Source indices (in the full "cat") to keep (the fit values for)
            srci = I[keep]

            if not len(srci):
                print('No sources to be kept; skipping.')
                continue

            phot.tile[srci] = tile.coadd_id
            nexp[srci] = tim.nuims[
                np.clip(np.round(y[srci]).astype(int), 0, H - 1),
                np.clip(np.round(x[srci]).astype(int), 0, W - 1)]

            # Source indices in the margins
            margi = I[np.logical_not(keep)]

            # sources in the box -- at the start of the subcat list.
            subcat = [cat[i] for i in srci]

            # include *copies* of sources in the margins
            # (that way we automatically don't save the results)
            subcat.extend([cat[i].copy() for i in margi])
            assert (len(subcat) == len(I))

            # FIXME -- set source radii, ...?

            minsb = 0.
            fitsky = False

            # Look in image and set radius based on peak height??

            tractor = Tractor([tim], subcat)
            if use_ceres:
                from tractor.ceres_optimizer import CeresOptimizer
                tractor.optimizer = CeresOptimizer(BW=ceres_block,
                                                   BH=ceres_block)
            tractor.freezeParamsRecursive('*')
            tractor.thawPathsTo(wanyband)

            kwa = dict(fitstat_extras=[('pronexp', [tim.nims])])
            t0 = Time()

            R = tractor.optimize_forced_photometry(minsb=minsb,
                                                   mindlnp=1.,
                                                   sky=fitsky,
                                                   fitstats=True,
                                                   variance=True,
                                                   shared_params=False,
                                                   wantims=wantims,
                                                   **kwa)
            print('unWISE forced photometry took', Time() - t0)

            if use_ceres:
                term = R.ceres_status['termination']
                print('Ceres termination status:', term)
                # Running out of memory can cause failure to converge
                # and term status = 2.
                # Fail completely in this case.
                if term != 0:
                    raise RuntimeError('Ceres terminated with status %i' %
                                       term)

            if wantims:
                ims0 = R.ims0
                ims1 = R.ims1
            IV, fs = R.IV, R.fitstats

            if save_fits:
                import fitsio
                (dat, mod, ie, chi, roi) = ims1[0]
                wcshdr = fitsio.FITSHDR()
                tim.wcs.wcs.add_to_header(wcshdr)

                tag = 'fit-%s-w%i' % (tile.coadd_id, band)
                fitsio.write('%s-data.fits' % tag,
                             dat,
                             clobber=True,
                             header=wcshdr)
                fitsio.write('%s-mod.fits' % tag,
                             mod,
                             clobber=True,
                             header=wcshdr)
                fitsio.write('%s-chi.fits' % tag,
                             chi,
                             clobber=True,
                             header=wcshdr)

            if get_models:
                (dat, mod, ie, chi, roi) = ims1[0]
                models[(tile.coadd_id, band)] = (mod, tim.roi)

            if ps:
                tag = '%s W%i' % (tile.coadd_id, band)
                (dat, mod, ie, chi, roi) = ims1[0]

                sig1 = tim.sig1
                plt.clf()
                plt.imshow(dat,
                           interpolation='nearest',
                           origin='lower',
                           cmap='gray',
                           vmin=-3 * sig1,
                           vmax=10 * sig1)
                plt.colorbar()
                plt.title('%s: data' % tag)
                ps.savefig()

                plt.clf()
                plt.imshow(mod,
                           interpolation='nearest',
                           origin='lower',
                           cmap='gray',
                           vmin=-3 * sig1,
                           vmax=10 * sig1)
                plt.colorbar()
                plt.title('%s: model' % tag)
                ps.savefig()

                plt.clf()
                plt.imshow(chi,
                           interpolation='nearest',
                           origin='lower',
                           cmap='gray',
                           vmin=-5,
                           vmax=+5)
                plt.colorbar()
                plt.title('%s: chi' % tag)
                ps.savefig()

            # Save results for this tile.
            # the "keep" sources are at the beginning of the "subcat" list
            flux_invvars[srci] = IV[:len(srci)].astype(np.float32)
            if hasattr(tim, 'mjdmin') and hasattr(tim, 'mjdmax'):
                mjd[srci] = (tim.mjdmin + tim.mjdmax) / 2.
            if fs is None:
                continue
            for k in fskeys:
                x = getattr(fs, k)
                # fitstats are returned only for un-frozen sources
                fitstats[k][srci] = np.array(x).astype(np.float32)[:len(srci)]

        # Note, this is *outside* the loop over tiles.
        # The fluxes are saved in the source objects, and will be set based on
        # the 'tiledists' logic above.
        nm = np.array([src.getBrightness().getBand(wanyband) for src in cat])
        nm_ivar = flux_invvars

        # Sources out of bounds, eg, never change from their default
        # (1-sigma or whatever) initial fluxes.  Zero them out instead.
        nm[nm_ivar == 0] = 0.

        phot.set(wband + '_nanomaggies', nm.astype(np.float32))
        phot.set(wband + '_nanomaggies_ivar', nm_ivar)
        dnm = np.zeros(len(nm_ivar), np.float32)
        okiv = (nm_ivar > 0)
        dnm[okiv] = (1. / np.sqrt(nm_ivar[okiv])).astype(np.float32)
        okflux = (nm > 0)
        mag = np.zeros(len(nm), np.float32)
        mag[okflux] = (NanoMaggies.nanomaggiesToMag(nm[okflux])).astype(
            np.float32)
        dmag = np.zeros(len(nm), np.float32)
        ok = (okiv * okflux)
        dmag[ok] = (np.abs(
            (-2.5 / np.log(10.)) * dnm[ok] / nm[ok])).astype(np.float32)
        mag[np.logical_not(okflux)] = np.nan
        dmag[np.logical_not(ok)] = np.nan

        phot.set(wband + '_mag', mag)
        phot.set(wband + '_mag_err', dmag)
        for k in fskeys:
            phot.set(wband + '_' + k, fitstats[k])
        phot.set(wband + '_nexp', nexp)

        if not np.all(mjd == 0):
            phot.set(wband + '_mjd', mjd)

    if get_models:
        return phot, models
    return phot
Exemple #8
0
def unwise_forcedphot(cat, tiles, band=1, roiradecbox=None,
                      use_ceres=True, ceres_block=8,
                      save_fits=False, get_models=False, ps=None,
                      psf_broadening=None,
                      pixelized_psf=False,
                      get_masks=None,
                      move_crpix=False,
                      modelsky_dir=None):
    '''
    Given a list of tractor sources *cat*
    and a list of unWISE tiles *tiles* (a fits_table with RA,Dec,coadd_id)
    runs forced photometry, returning a FITS table the same length as *cat*.

    *get_masks*: the WCS to resample mask bits into.
    '''
    from tractor import NanoMaggies, PointSource, Tractor, ExpGalaxy, DevGalaxy, FixedCompositeGalaxy

    if not pixelized_psf and psf_broadening is None:
        # PSF broadening in post-reactivation data, by band.
        # Newer version from Aaron's email to decam-chatter, 2018-06-14.
        broadening = { 1: 1.0405, 2: 1.0346, 3: None, 4: None }
        psf_broadening = broadening[band]

    if False:
        from astrometry.util.plotutils import PlotSequence
        ps = PlotSequence('wise-forced-w%i' % band)
    plots = (ps is not None)
    if plots:
        import pylab as plt
    
    wantims = (plots or save_fits or get_models)
    wanyband = 'w'
    if get_models:
        models = {}

    wband = 'w%i' % band

    fskeys = ['prochi2', 'pronpix', 'profracflux', 'proflux', 'npix',
              'pronexp']

    Nsrcs = len(cat)
    phot = fits_table()
    # Filled in based on unique tile overlap
    phot.wise_coadd_id = np.array(['        '] * Nsrcs)
    phot.set(wband + '_psfdepth', np.zeros(len(phot), np.float32))

    ra  = np.array([src.getPosition().ra  for src in cat])
    dec = np.array([src.getPosition().dec for src in cat])

    nexp = np.zeros(Nsrcs, np.int16)
    mjd  = np.zeros(Nsrcs, np.float64)
    central_flux = np.zeros(Nsrcs, np.float32)

    fitstats = {}
    tims = []

    if get_masks:
        mh,mw = get_masks.shape
        maskmap = np.zeros((mh,mw), np.uint32)
    
    for tile in tiles:
        print('Reading WISE tile', tile.coadd_id, 'band', band)

        tim = get_unwise_tractor_image(tile.unwise_dir, tile.coadd_id, band,
                                       bandname=wanyband, roiradecbox=roiradecbox)
        if tim is None:
            print('Actually, no overlap with tile', tile.coadd_id)
            continue

        if plots:
            sig1 = tim.sig1
            plt.clf()
            plt.imshow(tim.getImage(), interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-3 * sig1, vmax=10 * sig1)
            plt.colorbar()
            tag = '%s W%i' % (tile.coadd_id, band)
            plt.title('%s: tim data' % tag)
            ps.savefig()

            plt.clf()
            plt.hist((tim.getImage() * tim.inverr)[tim.inverr > 0].ravel(),
                     range=(-5,10), bins=100)
            plt.xlabel('Per-pixel intensity (Sigma)')
            plt.title(tag)
            ps.savefig()

        if move_crpix and band in [1, 2]:
            realwcs = tim.wcs.wcs
            x,y = realwcs.crpix
            tile_crpix = tile.get('crpix_w%i' % band)
            dx = tile_crpix[0] - 1024.5
            dy = tile_crpix[1] - 1024.5
            realwcs.set_crpix(x+dx, y+dy)
            #print('CRPIX', x,y, 'shift by', dx,dy, 'to', realwcs.crpix)

        if modelsky_dir and band in [1, 2]:
            fn = os.path.join(modelsky_dir, '%s.%i.mod.fits' % (tile.coadd_id, band))
            if not os.path.exists(fn):
                raise RuntimeError('WARNING: does not exist:', fn)
            x0,x1,y0,y1 = tim.roi
            bg = fitsio.FITS(fn)[2][y0:y1, x0:x1]
            #print('Read background map:', bg.shape, bg.dtype, 'vs image', tim.shape)

            if plots:
                plt.clf()
                plt.subplot(1,2,1)
                plt.imshow(tim.getImage(), interpolation='nearest', origin='lower',
                           cmap='gray', vmin=-3 * sig1, vmax=5 * sig1)
                plt.subplot(1,2,2)
                plt.imshow(bg, interpolation='nearest', origin='lower',
                           cmap='gray', vmin=-3 * sig1, vmax=5 * sig1)
                tag = '%s W%i' % (tile.coadd_id, band)
                plt.suptitle(tag)
                ps.savefig()

                plt.clf()
                ha = dict(range=(-5,10), bins=100, histtype='step')
                plt.hist((tim.getImage() * tim.inverr)[tim.inverr > 0].ravel(),
                         color='b', label='Original', **ha)
                plt.hist(((tim.getImage()-bg) * tim.inverr)[tim.inverr > 0].ravel(),
                         color='g', label='Minus Background', **ha)
                plt.axvline(0, color='k', alpha=0.5)
                plt.xlabel('Per-pixel intensity (Sigma)')
                plt.legend()
                plt.title(tag + ': background')
                ps.savefig()

            # Actually subtract the background!
            tim.data -= bg

        # Floor the per-pixel variances
        if band in [1,2]:
            # in Vega nanomaggies per pixel
            floor_sigma = {1: 0.5, 2: 2.0}
            with np.errstate(divide='ignore'):
                new_ie = 1. / np.hypot(1./tim.inverr, floor_sigma[band])
            new_ie[tim.inverr == 0] = 0.

            if plots:
                plt.clf()
                plt.plot((1. / tim.inverr[tim.inverr>0]).ravel(), (1./new_ie[tim.inverr>0]).ravel(), 'b.')
                plt.title('unWISE per-pixel error: %s band %i' % (tile.coadd_id, band))
                plt.xlabel('original')
                plt.ylabel('floored')
                ps.savefig()

            tim.inverr = new_ie

        # Read mask file?
        if get_masks:
            from astrometry.util.resample import resample_with_wcs, OverlapError
            # unwise_dir can be a colon-separated list of paths
            tilemask = None
            for d in tile.unwise_dir.split(':'):
                fn = os.path.join(d, tile.coadd_id[:3], tile.coadd_id,
                                  'unwise-%s-msk.fits.gz' % tile.coadd_id)
                if os.path.exists(fn):
                    print('Reading unWISE mask file', fn)
                    x0,x1,y0,y1 = tim.roi
                    tilemask = fitsio.FITS(fn)[0][y0:y1,x0:x1]
                    break
            if tilemask is None:
                print('unWISE mask file for tile', tile.coadd_id, 'does not exist')
            else:
                try:
                    tanwcs = tim.wcs.wcs
                    assert(tanwcs.shape == tilemask.shape)
                    Yo,Xo,Yi,Xi,_ = resample_with_wcs(get_masks, tanwcs, intType=np.int16)
                    # Only deal with mask pixels that are set.
                    I, = np.nonzero(tilemask[Yi,Xi] > 0)
                    # Trim to unique area for this tile
                    rr,dd = get_masks.pixelxy2radec(Yo[I]+1, Xo[I]+1)
                    good = radec_in_unique_area(rr, dd, tile.ra1, tile.ra2, tile.dec1, tile.dec2)
                    I = I[good]
                    maskmap[Yo[I],Xo[I]] = tilemask[Yi[I], Xi[I]]
                except OverlapError:
                    # Shouldn't happen by this point
                    print('No overlap between WISE tile', tile.coadd_id, 'and brick')

        # The tiles have some overlap, so zero out pixels outside the
        # tile's unique area.
        th,tw = tim.shape
        xx,yy = np.meshgrid(np.arange(tw), np.arange(th))
        rr,dd = tim.wcs.wcs.pixelxy2radec(xx+1, yy+1)
        unique = radec_in_unique_area(rr, dd, tile.ra1, tile.ra2, tile.dec1, tile.dec2)
        #print(np.sum(unique), 'of', (th*tw), 'pixels in this tile are unique')
        tim.inverr[unique == False] = 0.
        del xx,yy,rr,dd,unique

        if plots:
            sig1 = tim.sig1
            plt.clf()
            plt.imshow(tim.getImage() * (tim.inverr > 0),
                       interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-3 * sig1, vmax=10 * sig1)
            plt.colorbar()
            tag = '%s W%i' % (tile.coadd_id, band)
            plt.title('%s: tim data (unique)' % tag)
            ps.savefig()

        if pixelized_psf:
            import unwise_psf
            if (band == 1) or (band == 2):
                # we only have updated PSFs for W1 and W2
                psfimg = unwise_psf.get_unwise_psf(band, tile.coadd_id, 
                                                   modelname='neo4_unwisecat')
            else:
                psfimg = unwise_psf.get_unwise_psf(band, tile.coadd_id)

            if band == 4:
                # oversample (the unwise_psf models are at native W4 5.5"/pix,
                # while the unWISE coadds are made at 2.75"/pix.
                ph,pw = psfimg.shape
                subpsf = np.zeros((ph*2-1, pw*2-1), np.float32)
                from astrometry.util.util import lanczos3_interpolate
                xx,yy = np.meshgrid(np.arange(0., pw-0.51, 0.5, dtype=np.float32),
                                    np.arange(0., ph-0.51, 0.5, dtype=np.float32))
                xx = xx.ravel()
                yy = yy.ravel()
                ix = xx.astype(np.int32)
                iy = yy.astype(np.int32)
                dx = (xx - ix).astype(np.float32)
                dy = (yy - iy).astype(np.float32)
                psfimg = psfimg.astype(np.float32)
                rtn = lanczos3_interpolate(ix, iy, dx, dy, [subpsf.flat], [psfimg])

                if plots:
                    plt.clf()
                    plt.imshow(psfimg, interpolation='nearest', origin='lower')
                    plt.title('Original PSF model')
                    ps.savefig()
                    plt.clf()
                    plt.imshow(subpsf, interpolation='nearest', origin='lower')
                    plt.title('Subsampled PSF model')
                    ps.savefig()

                psfimg = subpsf
                del xx, yy, ix, iy, dx, dy

            from tractor.psf import PixelizedPSF
            psfimg /= psfimg.sum()
            fluxrescales = {1: 1.04, 2: 1.005, 3: 1.0, 4: 1.0}
            psfimg *= fluxrescales[band]
            tim.psf = PixelizedPSF(psfimg)

        if psf_broadening is not None and not pixelized_psf:
            # psf_broadening is a factor by which the PSF FWHMs
            # should be scaled; the PSF is a little wider
            # post-reactivation.
            psf = tim.getPsf()
            from tractor import GaussianMixturePSF
            if isinstance(psf, GaussianMixturePSF):
                #
                print('Broadening PSF: from', psf)
                p0 = psf.getParams()
                pnames = psf.getParamNames()
                p1 = [p * psf_broadening**2 if 'var' in name else p
                      for (p, name) in zip(p0, pnames)]
                psf.setParams(p1)
                print('Broadened PSF:', psf)
            else:
                print('WARNING: cannot apply psf_broadening to WISE PSF of type', type(psf))

        wcs = tim.wcs.wcs
        ok,x,y = wcs.radec2pixelxy(ra, dec)
        x = np.round(x - 1.).astype(int)
        y = np.round(y - 1.).astype(int)
        good = (x >= 0) * (x < tw) * (y >= 0) * (y < th)
        # Which sources are in this brick's unique area?
        usrc = radec_in_unique_area(ra, dec, tile.ra1, tile.ra2, tile.dec1, tile.dec2)
        I, = np.nonzero(good * usrc)

        nexp[I] = tim.nuims[y[I], x[I]]
        if hasattr(tim, 'mjdmin') and hasattr(tim, 'mjdmax'):
            mjd[I] = (tim.mjdmin + tim.mjdmax) / 2.
        phot.wise_coadd_id[I] = tile.coadd_id

        central_flux[I] = tim.getImage()[y[I], x[I]]
        del x,y,good,usrc

        # PSF norm for depth
        psf = tim.getPsf()
        h,w = tim.shape
        patch = psf.getPointSourcePatch(h//2, w//2).patch
        psfnorm = np.sqrt(np.sum(patch**2))
        # To handle zero-depth, we return 1/nanomaggies^2 units rather than mags.
        psfdepth = 1. / (tim.sig1 / psfnorm)**2
        phot.get(wband + '_psfdepth')[I] = psfdepth

        tim.tile = tile
        tims.append(tim)

    if plots:
        plt.clf()
        mn,mx = 0.1, 20000
        plt.hist(np.log10(np.clip(central_flux, mn, mx)), bins=100,
                 range=(np.log10(mn), np.log10(mx)))
        logt = np.arange(0, 5)
        plt.xticks(logt, ['%i' % i for i in 10.**logt])
        plt.title('Central fluxes (W%i)' % band)
        plt.axvline(np.log10(20000), color='k')
        plt.axvline(np.log10(1000), color='k')
        ps.savefig()

    # Eddie's non-secret recipe:
    #- central pixel <= 1000: 19x19 pix box size
    #- central pixel in 1000 - 20000: 59x59 box size
    #- central pixel > 20000 or saturated: 149x149 box size
    #- object near "bright star": 299x299 box size
    nbig = nmedium = nsmall = 0
    for src,cflux in zip(cat, central_flux):
        if cflux > 20000:
            R = 100
            nbig += 1
        elif cflux > 1000:
            R = 30
            nmedium += 1
        else:
            R = 15
            nsmall += 1
        if isinstance(src, PointSource):
            src.fixedRadius = R
        else:
            ### FIXME -- sizes for galaxies..... can we set PSF size separately?
            galrad = 0
            # RexGalaxy is a subclass of ExpGalaxy
            if isinstance(src, (ExpGalaxy, DevGalaxy)):
                galrad = src.shape.re
            elif isinstance(src, FixedCompositeGalaxy):
                galrad = max(src.shapeExp.re, src.shapeDev.re)
            pixscale = 2.75
            src.halfsize = int(np.hypot(R, galrad * 5 / pixscale))

    #print('Set WISE source sizes:', nbig, 'big', nmedium, 'medium', nsmall, 'small')

    minsb = 0.
    fitsky = False

    tractor = Tractor(tims, cat)
    if use_ceres:
        from tractor.ceres_optimizer import CeresOptimizer
        tractor.optimizer = CeresOptimizer(BW=ceres_block, BH=ceres_block)
    tractor.freezeParamsRecursive('*')
    tractor.thawPathsTo(wanyband)

    kwa = dict(fitstat_extras=[('pronexp', [tim.nims for tim in tims])])
    t0 = Time()

    R = tractor.optimize_forced_photometry(
        minsb=minsb, mindlnp=1., sky=fitsky, fitstats=True,
        variance=True, shared_params=False,
        wantims=wantims, **kwa)
    print('unWISE forced photometry took', Time() - t0)

    if use_ceres:
        term = R.ceres_status['termination']
        # Running out of memory can cause failure to converge
        # and term status = 2.
        # Fail completely in this case.
        if term != 0:
            print('Ceres termination status:', term)
            raise RuntimeError(
                'Ceres terminated with status %i' % term)

    if wantims:
        ims1 = R.ims1
    flux_invvars = R.IV
    if R.fitstats is not None:
        for k in fskeys:
            x = getattr(R.fitstats, k)
            fitstats[k] = np.array(x).astype(np.float32)

    if save_fits:
        for i,tim in enumerate(tims):
            tile = tim.tile
            (dat, mod, ie, chi, roi) = ims1[i]
            wcshdr = fitsio.FITSHDR()
            tim.wcs.wcs.add_to_header(wcshdr)
            tag = 'fit-%s-w%i' % (tile.coadd_id, band)
            fitsio.write('%s-data.fits' %
                         tag, dat, clobber=True, header=wcshdr)
            fitsio.write('%s-mod.fits' % tag,  mod,
                         clobber=True, header=wcshdr)
            fitsio.write('%s-chi.fits' % tag,  chi,
                         clobber=True, header=wcshdr)

    if plots:
        # Create models for just the brightest sources
        bright_cat = [src for src in cat
                      if src.getBrightness().getBand(wanyband) > 1000]
        print('Bright soures:', len(bright_cat))
        btr = Tractor(tims, bright_cat)
        for tim in tims:
            mod = btr.getModelImage(tim)
            tile = tim.tile
            tag = '%s W%i' % (tile.coadd_id, band)
            sig1 = tim.sig1
            plt.clf()
            plt.imshow(mod, interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-3 * sig1, vmax=25 * sig1)
            plt.colorbar()
            plt.title('%s: bright-star models' % tag)
            ps.savefig()

    if get_models:
        for i,tim in enumerate(tims):
            tile = tim.tile
            (dat, mod, ie, chi, roi) = ims1[i]
            models[(tile.coadd_id, band)] = (mod, dat, ie, tim.roi, tim.wcs.wcs)

    if plots:
        for i,tim in enumerate(tims):
            tile = tim.tile
            tag = '%s W%i' % (tile.coadd_id, band)
            (dat, mod, ie, chi, roi) = ims1[i]
            sig1 = tim.sig1
            plt.clf()
            plt.imshow(dat, interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-3 * sig1, vmax=25 * sig1)
            plt.colorbar()
            plt.title('%s: data' % tag)
            ps.savefig()
            plt.clf()
            plt.imshow(mod, interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-3 * sig1, vmax=25 * sig1)
            plt.colorbar()
            plt.title('%s: model' % tag)
            ps.savefig()

            plt.clf()
            plt.imshow(chi, interpolation='nearest', origin='lower',
                       cmap='gray', vmin=-5, vmax=+5)
            plt.colorbar()
            plt.title('%s: chi' % tag)
            ps.savefig()


    nm = np.array([src.getBrightness().getBand(wanyband) for src in cat])
    nm_ivar = flux_invvars
    # Sources out of bounds, eg, never change from their default
    # (1-sigma or whatever) initial fluxes.  Zero them out instead.
    nm[nm_ivar == 0] = 0.

    phot.set(wband + '_nanomaggies', nm.astype(np.float32))
    phot.set(wband + '_nanomaggies_ivar', nm_ivar.astype(np.float32))
    dnm = np.zeros(len(nm_ivar), np.float32)
    okiv = (nm_ivar > 0)
    dnm[okiv] = (1. / np.sqrt(nm_ivar[okiv])).astype(np.float32)
    okflux = (nm > 0)
    mag = np.zeros(len(nm), np.float32)
    mag[okflux] = (NanoMaggies.nanomaggiesToMag(nm[okflux])
                   ).astype(np.float32)
    dmag = np.zeros(len(nm), np.float32)
    ok = (okiv * okflux)
    dmag[ok] = (np.abs((-2.5 / np.log(10.)) * dnm[ok] / nm[ok])
                ).astype(np.float32)
    mag[np.logical_not(okflux)] = np.nan
    dmag[np.logical_not(ok)] = np.nan

    phot.set(wband + '_mag', mag)
    phot.set(wband + '_mag_err', dmag)

    for k in fskeys:
        phot.set(wband + '_' + k, fitstats[k])
    phot.set(wband + '_nexp', nexp)
    if not np.all(mjd == 0):
        phot.set(wband + '_mjd', mjd)

    rtn = wphotduck()
    rtn.phot = phot
    rtn.models = None
    rtn.maskmap = None
    if get_models:
        rtn.models = models
    if get_masks:
        rtn.maskmap = maskmap
    return rtn
Exemple #9
0
def work_(coadd_id, coadds, out):
    """
    coadd_id: ID (name/tile name) of the coadd, such as 1194p212
    coadds: Pandas DataFrame describing coadd metadata, such as epochs, bands, dates
    out: directory where to write results
    """
    coadds = coadds.copy()

    w2_meta = coadds.loc[(coadd_id, slice(None), 2), :]

    # Plan synthetic planets
    # Pick positions and fluxes
    # TODO: Need to re-add epochal offsets from orbit simulation
    mov_x = []
    mov_y = []
    mov_flx = []
    mjdstart = None
    for i, meta in w2_meta.iterrows():
        if mjdstart is None:
            mjddiff = 0
        else:
            mjddiff = meta["MJDMEAN"] - mjdstart
        xs = []
        ys = []
        flxs = []
        random.seed(0)
        for j in xrange(10):  # Number of synths to create
            # Pick pixel positions x, y, and pick flux
            px = random.uniform(0 + 10, 2048 - 10)
            py = random.uniform(0 + 10, 2048 - 10)
            flx = (random.uniform(50, 400) / 6.)
            # TODO: re-add orbit/parallax motion
            xs.append(px)
            ys.append(py)
            flxs.append(flx)
        mov_x.append(xs)
        mov_y.append(ys)
        mov_flx.append(flxs)

    # Store x,y,flx for use when coadding
    coadds.loc[w2_meta.index, "SYNTH_X"] = pd.Series(mov_x, w2_meta.index)
    coadds.loc[w2_meta.index, "SYNTH_Y"] = pd.Series(mov_y, w2_meta.index)
    coadds.loc[w2_meta.index, "SYNTH_FLUX"] = pd.Series(mov_flx, w2_meta.index)

    # Subset metadata for this coadd, epoch 0 only
    w1_meta = coadds.loc[(coadd_id, 0, 1), :]
    w2_meta = coadds.loc[(coadd_id, 0, 2), :]

    # Download coadds and add synths
    # (only doing W2, but using same func to grab w1 coadd)
    w1_hdr, w1_im = add_synths(w1_meta)
    w2_hdr, w2_im = add_synths(w2_meta)

    # Convert to fits format
    w1_fits = make_fits_image(w1_hdr, w1_im)
    w2_fits = make_fits_image(w2_hdr, w2_im)

    # Write out resulting files
    # (only doing W2)
    open("%s/%s_w2_synths.fits" % (out, coadd_id), "wb").write(w2_fits)

    # Extract sources & check missed fakes
    def check_f_b(meta, srcs):
        xs = meta["SYNTH_X"][:]
        ys = meta["SYNTH_Y"][:]
        flxs = meta["SYNTH_FLUX"][:]
        sources = []
        miss_xs = []
        miss_ys = []
        miss_flxs = []
        for i in xrange(len(xs)):
            x, y, flx = xs[i], ys[i], flxs[i]

            # Find detection within 3 pixel width square centered on synth
            tmp = srcs[((abs(srcs["XWIN_IMAGE"] - (x + 1)) < 1) &
                        (abs(srcs["YWIN_IMAGE"] - (y + 1)) < 1))].to_pandas()

            if tmp.shape[0] == 0:
                miss_xs.append(x)
                miss_ys.append(y)
                miss_flxs.append(flx)
                continue

            tmp = tmp.iloc[[0]].copy()
            tmp["SYNTH_X"] = x
            tmp["SYNTH_Y"] = y
            tmp["SYNTH_FLUX"] = flx
            sources.append(tmp)

        tps = None
        if len(sources) > 0:
            tps = pd.concat(sources, ignore_index=True)
        return tps, pd.DataFrame({
            "SYNTH_X": miss_xs,
            "SYNTH_Y": miss_ys,
            "SYNTH_FLUX": miss_flxs,
        })

    # Get PSFs from unwise_psf
    # TODO: Still need to fiure out whether to pass W2 PSF
    # for both W1 and W2
    w1_psf = unwise_psf.get_unwise_psf(1, coadd_id)
    w2_psf = unwise_psf.get_unwise_psf(2, coadd_id)

    # Try crowdsource

    # First, make a PSF
    #w2_psf_vp = cs_psf.VariablePixelizedPSF(cs_psf.central_stamp(w2_psf),normalize=-1)
    # TODO: need to decide whether this is the right mechanism for creating a PSF, and
    #       whether to twiddle any knobs (normalize?)
    w2_psf_s = cs_psf.SimplePSF(w2_psf)

    # Then, call fit_im with the image and PSF

    # Need to pass in a weight or crowdsource will crash.

    # Should recommend considering a weight default = 1
    coadd_id, epoch, band = w2_meta.name
    invvar = aif.open(get_invvar(coadd_id, epoch, band))[0].data

    # Also edited code to ignore a None "dq" when writing the flags column
    # need to figure out what that's all about

    # These results (x, y, flux, model, psf) have changed since the crowdsource
    # code that described fit_im's usage was written. Now, "x" contains the
    # source table, along with x, y, and so on

    # TODO: Figure out correct weight to use. coverage maps?
    x, y, flux, model, psf = crowdsource.fit_im(w2_im, w2_psf_s, weight=invvar)

    # Convert that source table to a dataframe for easy csv-ing
    df = pd.DataFrame(x)

    # Write to disk
    df.to_csv("%s/%s_cs_sources.csv" % (out, coadd_id), index=False)

    # Convert PSFs to fits for sextractor
    # Following sewpy's code, but there's some parameters we've left empty. Those may
    # be important.
    # TODO: what should they be?
    w1_psf_fits = make_fits_psf(w1_psf)
    w2_psf_fits = make_fits_psf(w2_psf)

    # Run sextractor
    sources = run_extract_sources(w1_fits,
                                  w2_fits,
                                  w1_psf=w1_psf_fits,
                                  w2_psf=w2_psf_fits)

    # Identify detected vs undetected synths from sextractor results
    # TODO: do this for crowdsource, too.
    tps, fns = check_f_b(w2_meta, sources)

    if tps is not None:
        tps.to_csv("%s/%s_se_tps.csv" % (out, coadd_id), index=False)
    fns.to_csv("%s/%s_se_fns.csv" % (out, coadd_id), index=False)

    sources.write("%s/%s_se_sources" % (out, coadd_id),
                  overwrite=True,
                  format="ascii")