def switch_to_soft_ellipses(cat): from tractor.galaxy import DevGalaxy, ExpGalaxy, FixedCompositeGalaxy from tractor.ellipses import EllipseESoft for src in cat: if isinstance(src, (DevGalaxy, ExpGalaxy)): src.shape = EllipseESoft.fromEllipseE(src.shape) elif isinstance(src, FixedCompositeGalaxy): src.shapeDev = EllipseESoft.fromEllipseE(src.shapeDev) src.shapeExp = EllipseESoft.fromEllipseE(src.shapeExp)
def __init__(self, *args, **kwargs): super(EllipseWithPriors, self).__init__(*args, **kwargs) if self.ellipsePriors is None: ellipsePriors = _GaussianPriors(None) ellipsePriors.add('ee1', 0., self.ellipticityStd, param=EllipseESoft(1.,0.,0.)) ellipsePriors.add('ee2', 0., self.ellipticityStd, param=EllipseESoft(1.,0.,0.)) self.__class__.ellipsePriors = ellipsePriors self.gpriors = self.ellipsePriors self.uppers[0] = 5.
def fromStamp(stamp, N=3, P0=None, approx=1e-6, damp=0.): ''' optional P0 = (list of floats): initial parameter guess. (parameters of a GaussianMixtureEllipsePSF) ''' from tractor.ellipses import EllipseESoft w = np.ones(N) / float(N) mu = np.zeros((N, 2)) ell = [EllipseESoft(np.log(2 * r), 0., 0.) for r in range(1, N + 1)] psf = GaussianMixtureEllipsePSF(w, mu, ell) if P0 is not None: psf.setAllParams(P0) tim = Image(data=stamp, invvar=1e6 * np.ones_like(stamp), psf=psf) H, W = stamp.shape src = PointSource(PixPos(W // 2, H // 2), Flux(1.)) tr = Tractor([tim], [src]) tr.freezeParam('catalog') tim.freezeAllBut('psf') # print 'Fitting:' # tr.printThawedParams() tim.modelMinval = approx alphas = [0.1, 0.3, 1.0] for step in range(50): dlnp, X, alpha = tr.optimize(shared_params=False, alphas=alphas, damp=damp) # print 'dlnp', dlnp, 'alpha', alpha # print 'X', X if dlnp < 1e-6: break # print 'psf', psf return psf
def __init__(self, *args): ''' args = (amp, mean, ell) or args = (a0,a1,..., mx0,my0,mx1,my1,..., logr0,ee1-0,ee2-0,logr1,ee1-2,...) amp: np array (size K) of Gaussian amplitudes mean: np array (size K,2) of means ell: list (length K) of EllipseESoft objects ''' if len(args) == 3: amp, mean, ell = args else: from tractor.ellipses import EllipseESoft assert (len(args) % 6 == 0) K = len(args) // 6 amp = np.array(args[:K]) mean = np.array(args[K:3 * K]).reshape((K, 2)) args = args[3 * K:] ell = [EllipseESoft(*args[3 * k:3 * (k + 1)]) for k in range(K)] K = len(amp) var = np.zeros((K, 2, 2)) for k in range(K): var[k, :, :] = self.ellipseToVariance(ell[k]) self.ellipses = [e.copy() for e in ell] super(GaussianMixtureEllipsePSF, self).__init__(amp, mean, var) self.stepsizes = [0.001] * K + [0.001] * (K * 2) + [0.001] * (K * 3)
def __init__(self, *args, **kwargs): super(EllipseWithPriors, self).__init__(*args, **kwargs) if self.ellipsePriors is None: ellipsePriors = _GaussianPriors(None) ellipsePriors.add('ee1', 0., self.ellipticityStd, param=EllipseESoft(1., 0., 0.)) ellipsePriors.add('ee2', 0., self.ellipticityStd, param=EllipseESoft(1., 0., 0.)) self.__class__.ellipsePriors = ellipsePriors self.gpriors = self.ellipsePriors # MAGIC -- 30" default max r_e! # SEE ALSO survey.py : class(LogRadius)! self.uppers[0] = np.log(30.)
def switch_to_soft_ellipses(cat): ''' Converts our softened-ellipticity EllipseESoft parameters into normal EllipseE ellipses. *cat*: an iterable of tractor Sources, which will be modified in-place. ''' from tractor.galaxy import DevGalaxy, ExpGalaxy, FixedCompositeGalaxy from tractor.ellipses import EllipseESoft for src in cat: if isinstance(src, (DevGalaxy, ExpGalaxy)): src.shape = EllipseESoft.fromEllipseE(src.shape) elif isinstance(src, FixedCompositeGalaxy): src.shapeDev = EllipseESoft.fromEllipseE(src.shapeDev) src.shapeExp = EllipseESoft.fromEllipseE(src.shapeExp)
def fromRAbPhi(cls, r, ba, phi): logr, ee1, ee2 = EllipseESoft.rAbPhiToESoft(r, ba, phi) return cls(logr, ee1, ee2)
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