def get_cfht_catalog(mags=['i'], maglim=27., returnTable=False): from astrometry.util.pyfits_utils import fits_table from tractor import Mags, RaDecPos, PointSource, Images, Catalog from tractor.galaxy import DevGalaxy, ExpGalaxy, CompositeGalaxy, GalaxyShape T = fits_table( '/project/projectdirs/bigboss/data/cs82/W4p1m1_i.V2.7A.swarp.cut.deVexp.fit', hdunum=2) print 'Read', len(T), 'sources' # Cut to ROI (ra0, ra1, dec0, dec1) = radecroi T.ra = T.alpha_j2000 T.dec = T.delta_j2000 T = T[(T.ra > ra0) * (T.ra < ra1) * (T.dec > dec0) * (T.dec < dec1)] print 'Cut to', len(T), 'objects in ROI.' srcs = Catalog() keepi = [] for i, t in enumerate(T): if t.chi2_psf < t.chi2_model and t.mag_psf <= maglim: #print 'PSF' themag = t.mag_psf m = Mags(order=mags, **dict([(k, themag) for k in mags])) srcs.append(PointSource(RaDecPos(t.ra, t.dec), m)) keepi.append(i) continue if t.mag_disk > maglim and t.mag_spheroid > maglim: #print 'Faint' continue keepi.append(i) themag = t.mag_spheroid m_exp = Mags(order=mags, **dict([(k, themag) for k in mags])) themag = t.mag_disk m_dev = Mags(order=mags, **dict([(k, themag) for k in mags])) # SPHEROID_REFF [for Sersic index n= 1] = 1.68 * DISK_SCALE shape_dev = GalaxyShape(t.disk_scale_world * 1.68 * 3600., t.disk_aspect_world, t.disk_theta_world + 90.) shape_exp = GalaxyShape(t.spheroid_reff_world * 3600., t.spheroid_aspect_world, t.spheroid_theta_world + 90.) pos = RaDecPos(t.alphamodel_j2000, t.deltamodel_j2000) if t.mag_disk > maglim and t.mag_spheroid <= maglim: #print 'Exp' srcs.append(ExpGalaxy(pos, m_exp, shape_exp)) continue if t.mag_disk <= maglim and t.mag_spheroid > maglim: #print 'deV' srcs.append(DevGalaxy(pos, m_dev, shape_dev)) continue # exp + deV srcs.append(CompositeGalaxy(pos, m_exp, shape_exp, m_dev, shape_dev)) if returnTable: import numpy as np T.cut(np.array(keepi)) return srcs, T return srcs
def galaxy_norm(self, tim, x=None, y=None): # Galaxy-detection norm from tractor.galaxy import ExpGalaxy from tractor.ellipses import EllipseE from tractor.patch import Patch h,w = tim.shape band = tim.band if x is None: x = w/2. if y is None: y = h/2. pos = tim.wcs.pixelToPosition(x, y) gal = ExpGalaxy(pos, NanoMaggies(**{band:1.}), EllipseE(0.45, 0., 0.)) S = 32 mm = Patch(int(x-S), int(y-S), np.ones((2*S+1, 2*S+1), bool)) galmod = gal.getModelPatch(tim, modelMask=mm).patch galmod = np.maximum(0, galmod) galmod /= galmod.sum() galnorm = np.sqrt(np.sum(galmod**2)) return galnorm
def galaxy_norm(self, tim, x=None, y=None): # Galaxy-detection norm from tractor.galaxy import ExpGalaxy from tractor.ellipses import EllipseE from tractor.patch import Patch h, w = tim.shape band = tim.band if x is None: x = w / 2. if y is None: y = h / 2. pos = tim.wcs.pixelToPosition(x, y) gal = ExpGalaxy(pos, NanoMaggies(**{band: 1.}), EllipseE(0.45, 0., 0.)) S = 32 mm = Patch(int(x - S), int(y - S), np.ones((2 * S + 1, 2 * S + 1), bool)) galmod = gal.getModelPatch(tim, modelMask=mm).patch galmod = np.maximum(0, galmod) galmod /= galmod.sum() galnorm = np.sqrt(np.sum(galmod**2)) return galnorm
def main(): import optparse from astrometry.util.plotutils import PlotSequence from astrometry.util.util import Tan parser = optparse.OptionParser(usage='%prog [options] incat.fits out.fits') parser.add_option('-r', '--ralo', dest='ralo', type=float, help='Minimum RA') parser.add_option('-R', '--rahi', dest='rahi', type=float, help='Maximum RA') parser.add_option('-d', '--declo', dest='declo', type=float, help='Minimum Dec') parser.add_option('-D', '--dechi', dest='dechi', type=float, help='Maximum Dec') parser.add_option('-b', '--band', dest='bands', action='append', type=int, default=[], help='WISE band to photometer (default: 1,2)') parser.add_option('-u', '--unwise', dest='unwise_dir', default='unwise-coadds', help='Directory containing unWISE coadds') parser.add_option('--no-ceres', dest='ceres', action='store_false', default=True, help='Use scipy lsqr rather than Ceres Solver?') parser.add_option('--ceres-block', '-B', dest='ceresblock', type=int, default=8, help='Ceres image block size (default: %default)') parser.add_option('--plots', dest='plots', default=False, action='store_true') parser.add_option('--save-fits', dest='save_fits', default=False, action='store_true') # parser.add_option('--ellipses', action='store_true', # help='Assume catalog shapes are ellipse descriptions (not r,ab,phi)') # parser.add_option('--ra', help='Center RA') # parser.add_option('--dec', help='Center Dec') # parser.add_option('--width', help='Degrees width (in RA*cos(Dec))') # parser.add_option('--height', help='Degrees height (Dec)') opt, args = parser.parse_args() if len(args) != 2: parser.print_help() sys.exit(-1) if len(opt.bands) == 0: opt.bands = [1, 2] # Allow specifying bands like "123" bb = [] for band in opt.bands: for s in str(band): bb.append(int(s)) opt.bands = bb print('Bands', opt.bands) ps = None if opt.plots: ps = PlotSequence('unwise') infn, outfn = args T = fits_table(infn) print('Read', len(T), 'sources from', infn) if opt.declo is not None: T.cut(T.dec >= opt.declo) if opt.dechi is not None: T.cut(T.dec <= opt.dechi) # Let's be a bit smart about RA wrap-around. Compute the 'center' # of the RA points, use the cross product against that to define # inequality (clockwise-of). r = np.deg2rad(T.ra) x = np.mean(np.cos(r)) y = np.mean(np.sin(r)) rr = np.hypot(x, y) x /= rr y /= rr midra = np.rad2deg(np.arctan2(y, x)) midra += 360. * (midra < 0) xx = np.cos(r) yy = np.sin(r) T.cross = x * yy - y * xx minra = T.ra[np.argmin(T.cross)] maxra = T.ra[np.argmax(T.cross)] if opt.ralo is not None: r = np.deg2rad(opt.ralo) xx = np.cos(r) yy = np.sin(r) crosscut = x * yy - y * xx T.cut(T.cross >= crosscut) print('Cut to', len(T), 'with RA >', opt.ralo) if opt.rahi is not None: r = np.deg2rad(opt.rahi) xx = np.cos(r) yy = np.sin(r) crosscut = x * yy - y * xx T.cut(T.cross <= crosscut) print('Cut to', len(T), 'with RA <', opt.rahi) if opt.declo is None: opt.declo = T.dec.min() if opt.dechi is None: opt.dechi = T.dec.max() if opt.ralo is None: opt.ralo = T.ra[np.argmin(T.cross)] if opt.rahi is None: opt.rahi = T.ra[np.argmax(T.cross)] T.delete_column('cross') print('RA range:', opt.ralo, opt.rahi) print('Dec range:', opt.declo, opt.dechi) x = np.mean([np.cos(np.deg2rad(r)) for r in (opt.ralo, opt.rahi)]) y = np.mean([np.sin(np.deg2rad(r)) for r in (opt.ralo, opt.rahi)]) midra = np.rad2deg(np.arctan2(y, x)) midra += 360. * (midra < 0) middec = (opt.declo + opt.dechi) / 2. print('RA,Dec center:', midra, middec) pixscale = 2.75 / 3600. H = (opt.dechi - opt.declo) / pixscale dra = 2. * min(np.abs(midra - opt.ralo), np.abs(midra - opt.rahi)) W = dra * np.cos(np.deg2rad(middec)) / pixscale margin = 5 W = int(W) + margin * 2 H = int(H) + margin * 2 print('W,H', W, H) targetwcs = Tan(midra, middec, (W + 1) / 2., (H + 1) / 2., -pixscale, 0., 0., pixscale, float(W), float(H)) #print('Target WCS:', targetwcs) ra0, dec0 = targetwcs.pixelxy2radec(0.5, 0.5) ra1, dec1 = targetwcs.pixelxy2radec(W + 0.5, H + 0.5) roiradecbox = [ra0, ra1, dec0, dec1] #print('ROI RA,Dec box', roiradecbox) tiles = unwise_tiles_touching_wcs(targetwcs) print('Cut to', len(tiles), 'unWISE tiles') disable_galaxy_cache() cols = T.get_columns() all_ptsrcs = not('type' in cols) if not all_ptsrcs: assert('shapeexp' in cols) assert('shapedev' in cols) assert('fracdev' in cols) wanyband = 'w' print('Creating Tractor catalog...') cat = [] for i, t in enumerate(T): pos = RaDecPos(t.ra, t.dec) flux = NanoMaggies(**{wanyband: 1.}) if all_ptsrcs: cat.append(PointSource(pos, flux)) continue tt = t.type.strip() if tt in ['PTSRC', 'STAR', 'S']: cat.append(PointSource(pos, flux)) elif tt in ['EXP', 'E']: shape = EllipseE(*t.shapeexp) cat.append(ExpGalaxy(pos, flux, shape)) elif tt in ['DEV', 'D']: shape = EllipseE(*t.shapedev) cat.append(DevGalaxy(pos, flux, shape)) elif tt in ['COMP', 'C']: eshape = EllipseE(*t.shapeexp) dshape = EllipseE(*t.shapedev) cat.append(FixedCompositeGalaxy(pos, flux, t.fracdev, eshape, dshape)) else: print('Did not understand row', i, 'of input catalog:') t.about() assert(False) W = unwise_forcedphot(cat, tiles, roiradecbox=roiradecbox, bands=opt.bands, unwise_dir=opt.unwise_dir, use_ceres=opt.ceres, ceres_block=opt.ceresblock, save_fits=opt.save_fits, ps=ps) W.writeto(outfn)
def gim2d_catalog(cat, band): ''' http://irsa.ipac.caltech.edu/data/COSMOS/tables/morphology/cosmos_morph_zurich_colDescriptions.html ''' ''' ACS_MU_CLASS float Type of object. 1 = galaxy 2 = star 3 = spurious ''' ''' ACS_CLEAN float Object useable flag. 0 = do not use this object 1 = use this object ''' ''' FLUX_GIM2D float counts GIM2D total flux R_GIM2D float arcseconds GIM2D psf-convolved half-light radius of object ELL_GIM2D float GIM2D ellipticity = 1-b/a of object PA_GIM2D float degrees GIM2D position angle of object - cw from +y-axis DX_GIM2D float arcseconds x-offset of GIM2D-model center from ACS-coordinate center DY_GIM2D float arcseconds y-offset of GIM2D-model center from ACS-coordinate center SERSIC_N_GIM2D float GIM2D Sersic index R_0P5_GIM2D float arcseconds GIM2D half-light radius of object without PSF convolution TYPE float ZEST Type CLASS 1 = Early type 2 = Disk 3 = Irregular Galaxy 9 = no classification ''' ''' BULG float ZEST "Bulgeness" CLASS - only for Type 2 (disk) galaxies. 0 = bulge dominated galaxy 1,2 = intermediate-bulge galaxies 3 = pure disk galaxy 9 = no classification ''' ''' STELLARITY float Visual Stellarity flag. 0 if ACS_CLASS_STAR<0.6 (object is ASSUMED to be a galaxy; no visual inspection) 0 if ACS_CLASS_STAR>=0.6 AND object visually identified as a galaxy. 1 if ACS_CLASS_STAR>=0.6 AND visually identified as a star. 2 if ACS_CLASS_STAR>=0.8 (object is assumed to be a star and was not visually inspected) 3 if ACS_CLASS_STAR<0.6 but object is visually identified as a star (e.g. saturated star, etc) JUNKFLAG float 0 = good object 1 = spurious ''' print('Classifications:', Counter(cat.type).most_common()) cat.is_galaxy = (cat.stellarity == 0) srcs = [] for t in cat: pos = RaDecPos(t.ra, t.dec) bright = NanoMaggies( **{band: NanoMaggies.magToNanomaggies(t.acs_mag_auto)}) shape = GalaxyShape(t.r_0p5_gim2d, 1. - t.ell_gim2d, 90. + t.pa_gim2d) is_galaxy = (t.is_galaxy * (shape.re >= 0) * (shape.ab <= 1.) * (shape.phi > -999)) if is_galaxy and t.type == 1: # deV src = DevGalaxy(pos, bright, shape) elif is_galaxy and t.type == 2: # exp src = ExpGalaxy(pos, bright, shape) else: src = PointSource(pos, bright) srcs.append(src) return srcs
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 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
def test_psfex(self): if ps is not None: from astrometry.util.plotutils import dimshow import pylab as plt H, W = 100, 100 cx, cy = W / 2., H / 2. pixpsf = self.psf.constantPsfAt(cx, cy) ph, pw = pixpsf.shape xx, yy = np.meshgrid(np.arange(pw), np.arange(ph)) im = pixpsf.img.copy() im /= np.sum(im) cenx, ceny = np.sum(im * xx), np.sum(im * yy) print('Pixpsf centroid:', cenx, ceny) print('shape:', ph, pw) dx, dy = cenx - pw // 2, ceny - ph // 2 print('dx,dy', dx, dy) # gpsf = GaussianMixturePSF.fromStamp(im, N=1) # print('Fit gpsf:', gpsf) # self.assertTrue(np.abs(gpsf.mog.mean[0,0] - dx) < 0.1) # self.assertTrue(np.abs(gpsf.mog.mean[0,1] - dy) < 0.1) # self.assertTrue(np.abs(gpsf.mog.var[0,0,0] - 15.5) < 1.) # self.assertTrue(np.abs(gpsf.mog.var[0,1,1] - 13.5) < 1.) # self.assertTrue(np.abs(gpsf.mog.var[0,1,0] - -1) < 1.) gpsf = GaussianMixturePSF.fromStamp(im, N=2) print('Fit gpsf:', gpsf) print('Params:', ', '.join(['%.1f' % p for p in gpsf.getParams()])) pp = np.array( [0.8, 0.2, 0.1, -0.0, 1.2, 0.2, 7.6, 6.0, -1.0, 51.6, 49.1, -1.3]) self.assertTrue(np.all(np.abs(np.array(gpsf.getParams()) - pp) < 0.1)) tim = Image(data=np.zeros((H, W)), invvar=np.ones((H, W)), psf=self.psf) xx, yy = np.meshgrid(np.arange(W), np.arange(H)) star = PointSource(PixPos(cx, cy), Flux(100.)) gal = ExpGalaxy(PixPos(cx, cy), Flux(100.), EllipseE(1., 0., 0.)) tr1 = Tractor([tim], [star]) tr2 = Tractor([tim], [gal]) disable_galaxy_cache() tim.psf = self.psf mod = tr1.getModelImage(0) mod1 = mod im = mod.copy() im /= im.sum() cenx, ceny = np.sum(im * xx), np.sum(im * yy) print('Star model + PsfEx centroid', cenx, ceny) self.assertTrue(np.abs(cenx - (cx + dx)) < 0.1) self.assertTrue(np.abs(ceny - (cy + dy)) < 0.1) if ps is not None: plt.clf() dimshow(mod) plt.title('Star model, PsfEx') ps.savefig() tim.psf = pixpsf mod = tr1.getModelImage(0) if ps is not None: plt.clf() dimshow(mod) plt.title('Star model, pixpsf') ps.savefig() tim.psf = gpsf mod = tr1.getModelImage(0) mod2 = mod if ps is not None: plt.clf() dimshow(mod) plt.title('Star model, gpsf') plt.colorbar() ps.savefig() plt.clf() dimshow(mod1 - mod2) plt.title('Star model, PsfEx - gpsf') plt.colorbar() ps.savefig() # range ~ -0.15 to +0.25 self.assertTrue(np.all(np.abs(mod1 - mod2) < 0.25)) tim.psf = self.psf mod = tr2.getModelImage(0) mod1 = mod im = mod.copy() im /= im.sum() cenx, ceny = np.sum(im * xx), np.sum(im * yy) print('Gal model + PsfEx centroid', cenx, ceny) self.assertTrue(np.abs(cenx - (cx + dx)) < 0.1) self.assertTrue(np.abs(ceny - (cy + dy)) < 0.1) if ps is not None: plt.clf() dimshow(mod) plt.title('Gal model, PsfEx') ps.savefig() # tim.psf = pixpsf # mod = tr2.getModelImage(0) # plt.clf() # dimshow(mod) # plt.title('Gal model, pixpsf') # ps.savefig() tim.psf = gpsf mod = tr2.getModelImage(0) mod2 = mod # range ~ -0.1 to +0.2 self.assertTrue(np.all(np.abs(mod1 - mod2) < 0.2)) if ps is not None: plt.clf() dimshow(mod) plt.title('Gal model, gpsf') ps.savefig() plt.clf() dimshow(mod1 - mod2) plt.title('Gal model, PsfEx - gpsf') plt.colorbar() ps.savefig()
def add_tractor_sources(obj_cat, sources, w, shape_method='manual'): ''' Add tractor sources to the sources list. Parameters: ---------- obj_cat: astropy Table, objects catalogue. sources: list, to which we will add objects. w: wcs object. shape_method: string, 'manual' or 'decals'. If 'manual', it will adopt the manually measured shapes. If 'decals', it will adopt 'DECaLS' tractor shapes. Returns: -------- sources: list of sources. ''' from tractor import NullWCS, NullPhotoCal, ConstantSky from tractor.galaxy import GalaxyShape, DevGalaxy, ExpGalaxy, CompositeGalaxy from tractor.psf import Flux, PixPos, PointSource, PixelizedPSF, Image, Tractor from tractor.ellipses import EllipseE obj_type = np.array(list(map(lambda st: st.rstrip(' '), obj_cat['type']))) comp_galaxy = obj_cat[obj_type == 'COMP'] dev_galaxy = obj_cat[obj_type == 'DEV'] exp_galaxy = obj_cat[obj_type == 'EXP'] rex_galaxy = obj_cat[obj_type == 'REX'] psf_galaxy = obj_cat[np.logical_or(obj_type =='PSF', obj_type==' ')] if shape_method is 'manual': # Using manually measured shapes if sources is None: sources = [] for obj in obj_cat: pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] if obj['type'].rstrip(' ') == 'COMP': sources.append( CompositeGalaxy( PixPos(pos_x, pos_y), Flux(0.4 * obj['flux']), GalaxyShape(obj['a_arcsec'] * 0.8, 0.9, 90.0 + obj['theta'] * 180.0 / np.pi), Flux(0.6 * obj['flux']), GalaxyShape(obj['a_arcsec'], obj['b_arcsec'] / obj['a_arcsec'], 90.0 + obj['theta'] * 180.0 / np.pi))) elif obj['type'].rstrip(' ') == 'DEV': sources.append( DevGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), GalaxyShape(obj['a_arcsec'], (obj['b_arcsec'] / obj['a_arcsec']), (90.0 + obj['theta'] * 180.0 / np.pi)))) elif obj['type'].rstrip(' ') == 'EXP': sources.append( ExpGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), GalaxyShape(obj['a_arcsec'], (obj['b_arcsec'] / obj['a_arcsec']), (90.0 + obj['theta'] * 180.0 / np.pi)))) elif obj['type'].rstrip(' ') == 'REX': sources.append( ExpGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), GalaxyShape(obj['a_arcsec'], (obj['b_arcsec'] / obj['a_arcsec']), (90.0 + obj['theta'] * 180.0 / np.pi)))) elif obj['type'].rstrip(' ') == 'PSF' or obj['type'].rstrip(' ') == ' ': sources.append(PointSource(PixPos(pos_x, pos_y), Flux(obj['flux']))) print("Now you have %d sources" % len(sources)) elif shape_method is 'decals': ## Using DECaLS shapes if sources is None: sources = [] for obj in comp_galaxy: pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] sources.append( CompositeGalaxy( PixPos(pos_x, pos_y), Flux(0.4 * obj['flux']), EllipseE(obj['shapeexp_r'], obj['shapeexp_e1'], obj['shapeexp_e2']), Flux(0.6 * obj['flux']), EllipseE(obj['shapedev_r'], obj['shapedev_e1'], obj['shapedev_e2']))) for obj in dev_galaxy: pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] sources.append( DevGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), EllipseE(obj['shapedev_r'], obj['shapedev_e1'], -obj['shapedev_e2']))) for obj in exp_galaxy: pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] sources.append( ExpGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), EllipseE(obj['shapeexp_r'], obj['shapeexp_e1'], -obj['shapeexp_e2']))) for obj in rex_galaxy: #if obj['point_source'] > 0.0: # sources.append(PointSource(PixPos(w.wcs_world2pix([[obj['ra'], obj['dec']]],1)[0]), # Flux(obj['flux']))) pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] sources.append( ExpGalaxy( PixPos(pos_x, pos_y), Flux(obj['flux']), EllipseE(obj['shapeexp_r'], obj['shapeexp_e1'], -obj['shapeexp_e2']))) for obj in psf_galaxy: pos_x, pos_y = w.wcs_world2pix([[obj['ra'], obj['dec']]], 1)[0] sources.append(PointSource(PixPos(pos_x, pos_y), Flux(obj['flux']))) print("Now you have %d sources" % len(sources)) else: raise ValueError('Cannot use this shape method') return sources
if __name__ == '__main__': h,w = 100,100 from tractor.galaxy import ExpGalaxy from tractor import Image, GaussianMixturePSF, LinearPhotoCal from tractor import PixPos, Flux, EllipseE, Tractor, ModelMask import pylab as plt # Create a Tractor Image that works in pixel space (WCS not specified). tim = Image(data=np.zeros((h,w)), inverr=np.ones((h,w)), psf=GaussianMixturePSF(1., 0., 0., 3., 3., 0.), photocal=LinearPhotoCal(1.)) # Create a plain Exp galaxy to generate a postage stamp that we'll try to fit with # the MogGalaxy model. gal = ExpGalaxy(PixPos(w//2, h//2), Flux(1000.), EllipseE(10., 0.5, 0.3)) # Get the model tractor = Tractor([tim], [gal]) mod = tractor.getModelImage(0) #mog = gal._getAffineProfile(tim, w//2, h//2) #print('Exp galaxy profile:', str(mog)) # Plot the model plt.clf() plt.imshow(mod, interpolation='nearest', origin='lower') plt.savefig('mod.png') # Set the tractor Image to the Exp model postage stamp -- this is what we'll try to fit. tim.data = mod
# image sensitivity: photocal = LinearPhotoCal(10., band=band) # flat sky sky = ConstantSky(0.) # Create tractor.Image object tim = Image(data, iv, psf=psf, wcs=wcs, sky=sky, photocal=photocal) def brightness(x): return NanoMaggies(**{band: x}) # Create some sources ptsrc = PointSource(RaDecPos(ra, dec), brightness(10.)) gal1 = ExpGalaxy(RaDecPos(ra - 10 * pscale, dec), brightness(50.), GalaxyShape(5., 0.5, 45.)) gal2 = DevGalaxy(RaDecPos(ra + 10 * pscale, dec), brightness(50.), GalaxyShape(5., 0.25, 135.)) gal3 = FixedCompositeGalaxy( RaDecPos(ra, dec + 10 * pscale), brightness(50.), 0.6, # fraction of light in deV component GalaxyShape(5., 0.6, 30.), GalaxyShape(5., 0.3, 45.)) cat = Catalog(ptsrc, gal1, gal2, gal3) # Create Tractor object tr = Tractor([tim], cat)