hspace=0, wspace=0) # transpose image dimshow(tim.getImage().T, vmin=-2.*tim.sig1, vmax=10.*tim.sig1) plt.title('DECaLS ' + tim.name) ps.savefig() ax = plt.axis() plt.plot(stars.iy, stars.ix, 'r.') plt.axis(ax) plt.title('Pan-STARRS stars in DECaLS ' + tim.name) ps.savefig() iband = ps1cat.ps1band[tim.band] stars.mag = stars.median[:,iband] stars.flux = NanoMaggies.magToNanomaggies(stars.mag) stars.cut(stars.flux > 1.) print len(stars), 'brighter than 22.5' stars.cut(np.argsort(stars.mag)) ima = dict(vmin=-2.*tim.sig1, vmax=10.*tim.sig1, ticks=False) plt.clf() #R,C = 10,14 #R,C = 9,13 R,C = 10,13
E = C[I] print(len(E), 'exposures') E.index = np.arange(len(E)) E.passnum = np.zeros(len(E), np.uint8) E.depthfraction = np.zeros(len(E), np.float32) # Compute which pass number each exposure would be called. zp0 = DecamImage.nominal_zeropoints() # HACK -- this is copied from obsbot kx = dict(g = 0.178, r = 0.094, z = 0.060,) for band in bands: B = E[E.filter == band] B.cut(np.argsort(B.seeing)) Nsigma = 5. sig = NanoMaggies.magToNanomaggies(target[band]) / Nsigma targetiv = 1./sig**2 for exp in B: thisdetiv = 1. / (exp.sig1 / exp.galnorm)**2 # Which pass number would this image be assigned? trans = 10.**(-0.4 * (zp0[band] - exp.ccdzpt - kx[band]*(exp.airmass - 1.))) seeing_good = exp.seeing < 1.3 seeing_fair = exp.seeing < 2.0 trans_good = trans > 0.9 trans_fair = trans > 0.7 if seeing_good and trans_good: E.passnum[exp.index] = 1 elif ((seeing_good and trans_fair) or (seeing_fair and trans_good)): E.passnum[exp.index] = 2
def get_reference_sources(survey, targetwcs, pixscale, bands, tycho_stars=True, gaia_stars=True, large_galaxies=True, star_clusters=True): from legacypipe.survey import GaiaSource from legacypipe.survey import LegacyEllipseWithPriors from tractor import NanoMaggies, RaDecPos from tractor.galaxy import ExpGalaxy from tractor.ellipses import EllipseESoft H, W = targetwcs.shape H, W = int(H), int(W) # How big of a margin to search for bright stars and star clusters -- # this should be based on the maximum radius they are considered to # affect. ref_margin = 0.125 mpix = int(np.ceil(ref_margin * 3600. / pixscale)) marginwcs = targetwcs.get_subimage(-mpix, -mpix, W + 2 * mpix, H + 2 * mpix) refs = [] # Tycho-2 stars if tycho_stars: tycho = read_tycho2(survey, marginwcs) if len(tycho): refs.append(tycho) # Add Gaia stars gaia = None if gaia_stars: from astrometry.libkd.spherematch import match_radec gaia = read_gaia(marginwcs) if gaia is not None: gaia.isbright = (gaia.phot_g_mean_mag < 13.) gaia.ismedium = (gaia.phot_g_mean_mag < 16.) gaia.donotfit = np.zeros(len(gaia), bool) # Handle sources that appear in both Gaia and Tycho-2 by # dropping the entry from Tycho-2. if len(gaia) and len(tycho): # Before matching, apply proper motions to bring them to # the same epoch. We want to use the more-accurate Gaia # proper motions, so rewind Gaia positions to the # approximate epoch of Tycho-2: 1991.5. cosdec = np.cos(np.deg2rad(gaia.dec)) gra = gaia.ra + (1991.5 - gaia.ref_epoch) * gaia.pmra / ( 3600. * 1000.) / cosdec gdec = gaia.dec + (1991.5 - gaia.ref_epoch) * gaia.pmdec / (3600. * 1000.) I, J, _ = match_radec(tycho.ra, tycho.dec, gra, gdec, 1. / 3600., nearest=True) debug('Matched', len(I), 'Tycho-2 stars to Gaia stars.') if len(I): keep = np.ones(len(tycho), bool) keep[I] = False tycho.cut(keep) gaia.isbright[J] = True if gaia is not None and len(gaia) > 0: refs.append(gaia) # Read the catalog of star (open and globular) clusters and add them to the # set of reference stars (with the isbright bit set). if star_clusters: clusters = read_star_clusters(marginwcs) if clusters is not None: debug('Found', len(clusters), 'star clusters nearby') clusters.iscluster = np.ones(len(clusters), bool) refs.append(clusters) # Read large galaxies nearby. if large_galaxies: galaxies = read_large_galaxies(survey, targetwcs) if galaxies is not None: # Resolve possible Gaia-large-galaxy duplicates if gaia and len(gaia): I, J, _ = match_radec(galaxies.ra, galaxies.dec, gaia.ra, gaia.dec, 2. / 3600., nearest=True) print('Matched', len(I), 'large galaxies to Gaia stars.') if len(I): gaia.donotfit[J] = True refs.append(galaxies) refcat = None if len(refs): refs = merge_tables([r for r in refs if r is not None], columns='fillzero') if len(refs) == 0: return None, None refs.radius_pix = np.ceil(refs.radius * 3600. / pixscale).astype(int) ok, xx, yy = targetwcs.radec2pixelxy(refs.ra, refs.dec) # ibx = integer brick coords refs.ibx = np.round(xx - 1.).astype(int) refs.iby = np.round(yy - 1.).astype(int) # cut ones whose position + radius are outside the brick bounds. refs.cut((xx > -refs.radius_pix) * (xx < W + refs.radius_pix) * (yy > -refs.radius_pix) * (yy < H + refs.radius_pix)) # mark ones that are actually inside the brick area. refs.in_bounds = ((refs.ibx >= 0) * (refs.ibx < W) * (refs.iby >= 0) * (refs.iby < H)) for col in [ 'isbright', 'ismedium', 'islargegalaxy', 'iscluster', 'donotfit' ]: if not col in refs.get_columns(): refs.set(col, np.zeros(len(refs), bool)) ## Create Tractor sources from reference stars refcat = [] for g in refs: if g.donotfit or g.iscluster: refcat.append(None) elif g.islargegalaxy: fluxes = dict([(band, NanoMaggies.magToNanomaggies(g.mag)) for band in bands]) assert (np.all(np.isfinite(list(fluxes.values())))) rr = g.radius * 3600. / 0.5 # factor of two accounts for R(25)-->reff pa = 180 - g.pa if not np.isfinite(pa): pa = 0. logr, ee1, ee2 = EllipseESoft.rAbPhiToESoft(rr, g.ba, pa) gal = ExpGalaxy(RaDecPos(g.ra, g.dec), NanoMaggies(order=bands, **fluxes), LegacyEllipseWithPriors(logr, ee1, ee2)) refcat.append(gal) else: # Gaia star -- which we want to create a source for, regardless of # whether it is marked medium | bright (or neither). refcat.append(GaiaSource.from_catalog(g, bands)) for src in refcat: if src: src.is_reference_source = True return refs, refcat
def main(): # I read a DESI DR8 target catalog, cut to ELGs, then took a narrow # r-mag slice around the peak of that distribution; # desi/target/catalogs/dr8/0.31.1/targets/main/resolve/targets-dr8-hp-1,5,11,50,55,60,83,84,86,89,91,98,119,155,158,174,186,187,190.fits') # Then took the median g and z mags # And opened the coadd invvar mags for a random brick in that footprint # (0701p000) to find the median per-pixel invvars. r = 23.0 g = 23.4 z = 22.2 # Selecting EXPs, the peak of the shapeexp_r was ~ 0.75". r_e = 0.75 # Image properties: giv = 77000. riv = 27000. ziv = 8000. # PSF sizes were roughly equal, 1.16, 1.17, 1.19" # -> sigma = 1.9 DECam pixels psf_sigma = 1.9 H, W = 1000, 1000 seed = 42 np.random.seed(seed) ra, dec = 70., 1. brick = BrickDuck(ra, dec, 'custom-0700p010') wcs = wcs_for_brick(brick, W=W, H=H) bands = 'grz' tims = [] for band, iv in zip(bands, [giv, riv, ziv]): img = np.zeros((H, W), np.float32) tiv = np.zeros_like(img) + iv s = psf_sigma**2 psf = GaussianMixturePSF(1., 0., 0., s, s, 0.) twcs = ConstantFitsWcs(wcs) sky = ConstantSky(0.) photocal = LinearPhotoCal(1., band=band) tai = TAITime(None, mjd=55700.) tim = tractor.Image(data=img, invvar=tiv, psf=psf, wcs=twcs, sky=sky, photocal=photocal, name='fake %s' % band, time=tai) tim.skyver = ('1', '1') tim.psfver = ('1', '1') tim.plver = '1' tim.x0 = tim.y0 = 0 tim.subwcs = wcs tim.psfnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma) tim.galnorm = tim.psfnorm tim.propid = '2020A-000' tim.band = band tim.dq = None tim.sig1 = 1. / np.sqrt(iv) tim.psf_sigma = psf_sigma tim.primhdr = fitsio.FITSHDR() tims.append(tim) # Simulated catalog gflux = NanoMaggies.magToNanomaggies(g) rflux = NanoMaggies.magToNanomaggies(r) zflux = NanoMaggies.magToNanomaggies(z) box = 50 CX, CY = np.meshgrid(np.arange(box // 2, W, box), np.arange(box // 2, H, box)) ny, nx = CX.shape BA, PHI = np.meshgrid(np.linspace(0.1, 1.0, nx), np.linspace(0., 180., ny)) cat = [] for cx, cy, ba, phi in zip(CX.ravel(), CY.ravel(), BA.ravel(), PHI.ravel()): #print('x,y %.1f,%.1f, ba %.2f, phi %.2f' % (cx, cy, ba, phi)) r, d = wcs.pixelxy2radec(cx + 1, cy + 1) src = ExpGalaxy(RaDecPos(r, d), NanoMaggies(order=bands, g=gflux, r=rflux, z=zflux), EllipseE.fromRAbPhi(r_e, ba, phi)) cat.append(src) from legacypipe.catalog import prepare_fits_catalog TT = fits_table() TT.bx = CX.ravel() TT.by = CY.ravel() TT.ba = BA.ravel() TT.phi = PHI.ravel() tcat = tractor.Catalog(*cat) T2 = prepare_fits_catalog(tcat, None, TT, bands, save_invvars=False) T2.writeto('sim-cat.fits') tr = Tractor(tims, cat) for band, tim in zip(bands, tims): mod = tr.getModelImage(tim) mod += np.random.standard_normal( size=tim.shape) * 1. / tim.getInvError() fitsio.write('sim-%s.fits' % band, mod, clobber=True) tim.data = mod ccds = fits_table() ccds.filter = np.array([f for f in bands]) ccds.ccd_cuts = np.zeros(len(ccds), np.int16) ccds.imgfn = np.array([tim.name for tim in tims]) ccds.propid = np.array(['2020A-000'] * len(ccds)) ccds.fwhm = np.zeros(len(ccds), np.float32) + psf_sigma * 2.35 ccds.mjd_obs = np.zeros(len(ccds)) ccds.camera = np.array(['fake'] * len(ccds)) survey = FakeLegacySurvey(ccds, tims) import logging verbose = False if verbose == 0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl, format='%(message)s', stream=sys.stdout) # tractor logging is *soooo* chatty logging.getLogger('tractor.engine').setLevel(lvl + 10) run_brick(None, survey, radec=(ra, dec), width=W, height=H, do_calibs=False, gaia_stars=False, large_galaxies=False, tycho_stars=False, forceall=True, outliers=False) #, stages=['image_coadds'])
def get_galaxy_sources(galaxies, bands): from legacypipe.catalog import fits_reverse_typemap from legacypipe.survey import (LegacySersicIndex, LegacyEllipseWithPriors, LogRadius, RexGalaxy) from tractor import NanoMaggies, RaDecPos, PointSource from tractor.ellipses import EllipseE, EllipseESoft from tractor.galaxy import DevGalaxy, ExpGalaxy from tractor.sersic import SersicGalaxy # Factor of HyperLEDA to set the galaxy max radius radius_max_factor = 2. srcs = [None for g in galaxies] # If we have pre-burned galaxies, re-create the Tractor sources for them. I, = np.nonzero(galaxies.preburned) for ii, g in zip(I, galaxies[I]): typ = fits_reverse_typemap[g.type.strip()] pos = RaDecPos(g.ra, g.dec) fluxes = dict([(band, g.get('flux_%s' % band)) for band in bands]) bright = NanoMaggies(order=bands, **fluxes) shape = None # put the Rex branch first, because Rex is a subclass of ExpGalaxy! if issubclass(typ, RexGalaxy): assert (np.isfinite(g.shape_r)) logre = np.log(g.shape_r) shape = LogRadius(logre) # set prior max at 2x SGA radius shape.setMaxLogRadius(logre + np.log(radius_max_factor)) elif issubclass(typ, (DevGalaxy, ExpGalaxy, SersicGalaxy)): assert (np.isfinite(g.shape_r)) assert (np.isfinite(g.shape_e1)) assert (np.isfinite(g.shape_e2)) shape = EllipseE(g.shape_r, g.shape_e1, g.shape_e2) # switch to softened ellipse (better fitting behavior) shape = EllipseESoft.fromEllipseE(shape) # and then to our custom ellipse class logre = shape.logre shape = LegacyEllipseWithPriors(logre, shape.ee1, shape.ee2) assert (np.all(np.isfinite(shape.getParams()))) # set prior max at 2x SGA radius shape.setMaxLogRadius(logre + np.log(radius_max_factor)) if issubclass(typ, PointSource): src = typ(pos, bright) # this catches Rex too elif issubclass(typ, (DevGalaxy, ExpGalaxy)): src = typ(pos, bright, shape) elif issubclass(typ, (SersicGalaxy)): assert (np.isfinite(g.sersic)) sersic = LegacySersicIndex(g.sersic) src = typ(pos, bright, shape, sersic) else: raise RuntimeError('Unknown preburned SGA source type "%s"' % typ) debug('Created', src) assert (np.isfinite(src.getLogPrior())) srcs[ii] = src # SGA parent catalog: 'preburned' is not set # This also can happen in the preburned/ellipse catalog when fitting # fails, or no-grz, etc. I, = np.nonzero(np.logical_not(galaxies.preburned)) for ii, g in zip(I, galaxies[I]): # Initialize each source with an exponential disk-- fluxes = dict([(band, NanoMaggies.magToNanomaggies(g.mag)) for band in bands]) assert (np.all(np.isfinite(list(fluxes.values())))) rr = g.radius * 3600. / 2 # factor of two accounts for R(25)-->reff [arcsec] assert (np.isfinite(rr)) assert (np.isfinite(g.ba)) assert (np.isfinite(g.pa)) ba = g.ba if ba <= 0.0 or ba > 1.0: # Make round! ba = 1.0 logr, ee1, ee2 = EllipseESoft.rAbPhiToESoft( rr, ba, 180 - g.pa) # note the 180 rotation assert (np.isfinite(logr)) assert (np.isfinite(ee1)) assert (np.isfinite(ee2)) shape = LegacyEllipseWithPriors(logr, ee1, ee2) shape.setMaxLogRadius(logr + np.log(radius_max_factor)) src = ExpGalaxy(RaDecPos(g.ra, g.dec), NanoMaggies(order=bands, **fluxes), shape) assert (np.isfinite(src.getLogPrior())) src.needs_initial_flux = True srcs[ii] = src return srcs