def galex_tractor_image(tile, band, galex_dir, radecbox, bandname): from tractor import (NanoMaggies, Image, LinearPhotoCal, ConstantFitsWcs, ConstantSky) assert(band in ['n','f']) #nicegbands = ['NUV', 'FUV'] #zps = dict(n=20.08, f=18.82) #zp = zps[band] imfn = os.path.join(galex_dir, tile.tilename.strip(), '%s-%sd-intbgsub.fits.gz' % (tile.visitname.strip(), band)) gwcs = Tan(*[float(f) for f in [tile.crval1, tile.crval2, tile.crpix1, tile.crpix2, tile.cdelt1, 0., 0., tile.cdelt2, 3840., 3840.]]) (r0,r1,d0,d1) = radecbox H,W = gwcs.shape ok,xx,yy = gwcs.radec2pixelxy([r0,r0,r1,r1], [d0,d1,d1,d0]) #print('GALEX WCS pixel positions of RA,Dec box:', xx, yy) if np.any(np.logical_not(ok)): return None x0 = np.clip(np.floor(xx-1).astype(int).min(), 0, W-1) x1 = np.clip(np.ceil (xx-1).astype(int).max(), 0, W) if x1-x0 <= 1: return None y0 = np.clip(np.floor(yy-1).astype(int).min(), 0, H-1) y1 = np.clip(np.ceil (yy-1).astype(int).max(), 0, H) if y1-y0 <= 1: return None debug('Reading GALEX subimage x0,y0', x0,y0, 'size', x1-x0, y1-y0) gwcs = gwcs.get_subimage(x0, y0, x1 - x0, y1 - y0) twcs = ConstantFitsWcs(gwcs) roislice = (slice(y0, y1), slice(x0, x1)) fitsimg = fitsio.FITS(imfn)[0] hdr = fitsimg.read_header() img = fitsimg[roislice] inverr = np.ones_like(img) inverr[img == 0.] = 0. zp = tile.get('%s_zpmag' % band) photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=bandname) tsky = ConstantSky(0.) name = 'GALEX ' + hdr['OBJECT'] + ' ' + band psfimg = galex_psf(band, galex_dir) tpsf = PixelizedPSF(psfimg) tim = Image(data=img, inverr=inverr, psf=tpsf, wcs=twcs, sky=tsky, photocal=photocal, name=name) tim.roi = [x0,x1,y0,y1] return tim
tractors = [] stars.x0 = stars.ix - margin stars.y0 = stars.iy - margin plt.clf() for i,s in enumerate(stars): #x0,y0 = s.ix - margin, s.iy - margin x0,y0 = s.x0, s.y0 slc = (slice(y0, s.iy + margin+1), slice(x0, s.ix + margin+1)) subpsf = tim.psf.constantPsfAt(s.ix, s.iy) subtim = Image(data=tim.data[slc], inverr=tim.inverr[slc], psf=subpsf, photocal=tim.photocal, name=tim.name + '/star %i' % i) flux = NanoMaggies.magToNanomaggies(s.mag) print 'Flux:', flux flux = max(flux, 1.) src = PointSource(PixPos(s.xx - x0, s.yy - y0), NanoMaggies(**{tim.band: flux})) tr = Tractor([subtim], [src]) mod = tr.getModelImage(0) #mod = src.getModelImage(tsubtim) tractors.append(tr) resids.append((subtim.data - mod) * (subtim.inverr > 0)) chis.append((subtim.data - mod) * subtim.inverr)
def main(): # Where are the data? datadir = os.path.join(os.path.dirname(__file__), 'data-decam') name = 'decam-520206-S16' imagefn = os.path.join(datadir, '%s-image-sub.fits' % name) invvarfn = os.path.join(datadir, '%s-invvar-sub.fits' % name) psfexfn = os.path.join(datadir, '%s-psfex.fits' % name) catfn = os.path.join(datadir, 'tractor-1816p325-sub.fits') # Read the image and inverse-variance maps. image = fitsio.read(imagefn) invvar = fitsio.read(invvarfn) # The DECam inverse-variance maps are unfortunately corrupted # by fpack, causing zeros to become negative. Fix those. invvar[invvar < np.median(invvar) * 0.1] = 0. H, W = image.shape print('Subimage size:', image.shape) # For the PSF model, we need to know what subimage region this is: subimage_offset = (35, 1465) # We also need the calibrated zeropoint. zeropoint = 24.7787 # What filter was this image taken in? (z) prim_header = fitsio.read_header(imagefn) band = prim_header['FILTER'].strip()[0] print('Band:', band) # These DECam images were calibrated so that the zeropoints need # an exposure-time factor, so add that in. exptime = prim_header['EXPTIME'] zeropoint += 2.5 * np.log10(exptime) # Read the PsfEx model file psf = PixelizedPsfEx(psfexfn) # Instantiate a constant pixelized PSF at the image center # (of the subimage) x0, y0 = subimage_offset psf = psf.constantPsfAt(x0 + W / 2., y0 + H / 2.) # Load the WCS model from the header # We convert from the RA---TPV type to RA---SIP header = fitsio.read_header(imagefn, ext=1) wcsobj = wcs_pv2sip_hdr(header, stepsize=10) # We'll just use a rough sky estimate... skyval = np.median(image) # Create the Tractor Image (tim). tim = Image(data=image, invvar=invvar, psf=psf, wcs=ConstantFitsWcs(wcsobj), sky=ConstantSky(skyval), photocal=LinearPhotoCal( NanoMaggies.zeropointToScale(zeropoint), band=band)) # Read the official DECaLS DR3 catalog -- it has only two sources in this subimage. catalog = fits_table(catfn) print('Read', len(catalog), 'sources') print('Source types:', catalog.type) # Create Tractor sources corresponding to these two catalog # entries. # In DECaLS, the "SIMP" type is a round Exponential galaxy with a # fixed 0.45" radius, but we'll treat it as a general Exp galaxy. sources = [] for c in catalog: # Create a "position" object given the catalog RA,Dec position = RaDecPos(c.ra, c.dec) # Create a "brightness" object; in the catalog, the fluxes are # stored in a [ugrizY] array, so pull out the right index band_index = 'ugrizY'.index(band) flux = c.decam_flux[band_index] brightness = NanoMaggies(**{band: flux}) # Depending on the source classification in the catalog, pull # out different fields for the galaxy shape, and for the # galaxy type. The DECaLS catalogs, conveniently, store # galaxy shapes as (radius, e1, e2) ellipses. if c.type.strip() == 'DEV': shape = EllipseE(c.shapedev_r, c.shapedev_e1, c.shapedev_e2) galclass = DevGalaxy elif c.type.strip() == 'SIMP': shape = EllipseE(c.shapeexp_r, c.shapeexp_e1, c.shapeexp_e2) galclass = ExpGalaxy else: assert (False) # Create the tractor galaxy object source = galclass(position, brightness, shape) print('Created', source) sources.append(source) # Create the Tractor object -- a list of tractor Images and a list of tractor sources. tractor = Tractor([tim], sources) # Render the initial model image. print('Getting initial model...') mod = tractor.getModelImage(0) make_plot(tim, mod, 'Initial Scene', 'mod0.png') # Instantiate a new source at the location of the unmodelled peak. print('Adding new source...') # Find the peak very naively... ipeak = np.argmax((image - mod) * tim.inverr) iy, ix = np.unravel_index(ipeak, tim.shape) print('Residual peak at', ix, iy) # Compute the RA,Dec location of the peak... radec = tim.getWcs().pixelToPosition(ix, iy) print('RA,Dec', radec) # Try modelling it as a point source. # We'll initialize the brightness arbitrarily to 1 nanomaggy (= mag 22.5) brightness = NanoMaggies(**{band: 1.}) source = PointSource(radec, brightness) # Add it to the catalog! tractor.catalog.append(source) # Render the new model image with this source added. mod = tractor.getModelImage(0) make_plot(tim, mod, 'New Source (Before Fit)', 'mod1.png') print('Fitting new source...') # Now we're going to fit for the properties of the new source we # added. # We don't want to fit for any of the image calibration properties: tractor.freezeParam('images') # And we don't (yet) want to fit the existing sources. The new # source is index number 2, so freeze everything else in the catalog. tractor.catalog.freezeAllBut(2) print('Fitting parameters:') tractor.printThawedParams() # Do the actual optimization: tractor.optimize_loop() mod = tractor.getModelImage(0) make_plot(tim, mod, 'New Source Fit', 'mod2.png') print('Fitting sources simultaneously...') # Now let's unfreeze all the sources and fit them simultaneously. tractor.catalog.thawAllParams() tractor.printThawedParams() tractor.optimize_loop() mod = tractor.getModelImage(0) make_plot(tim, mod, 'Simultaneous Fit', 'mod3.png')
def scan_dchisq(seeing, target_dchisq, ps, e1=0.): pixscale = 0.262 psfsigma = seeing / pixscale / 2.35 print('PSF sigma:', psfsigma, 'pixels') psf = GaussianMixturePSF(1., 0., 0., psfsigma**2, psfsigma**2, 0.) sig1 = 0.01 psfnorm = 1./(2. * np.sqrt(np.pi) * psfsigma) detsig1 = sig1 / psfnorm sz = 50 cd = pixscale / 3600. wcs = Tan(0., 0., float(sz/2), float(sz/2), -cd, 0., 0., cd, float(sz), float(sz)) band = 'r' tim = Image(data=np.zeros((sz,sz)), inverr=np.ones((sz,sz)) / sig1, psf=psf, wcs = ConstantFitsWcs(wcs), photocal = LinearPhotoCal(1., band=band)) re_vals = np.logspace(-1., 0., 50) all_runs = [] mods = [] for i,re in enumerate(re_vals): true_src = ExpGalaxy(RaDecPos(0., 0.), NanoMaggies(**{band: 1.}), EllipseE(re, e1, 0.)) print('True source:', true_src) tr = Tractor([tim], [true_src]) tr.freezeParams('images') true_mod = tr.getModelImage(0) dchisq_none = np.sum((true_mod * tim.inverr)**2) scale = np.sqrt(target_dchisq / dchisq_none) true_src.brightness.setParams([scale]) true_mod = tr.getModelImage(0) dchisq_none = np.sum((true_mod * tim.inverr)**2) mods.append(true_mod) tim.data = true_mod exp_src = true_src.copy() psf_src = PointSource(true_src.pos.copy(), true_src.brightness.copy()) simp_src = SimpleGalaxy(true_src.pos.copy(), true_src.brightness.copy()) dchisqs = [] #for src in [psf_src, simp_src, exp_src]: for src in [psf_src, simp_src]: src.freezeParam('pos') #print('Fitting source:', src) #src.printThawedParams() tr.catalog[0] = src tr.optimize_loop() #print('Fitted:', src) mod = tr.getModelImage(0) dchisqs.append(dchisq_none - np.sum(((true_mod - mod) * tim.inverr)**2)) #print('dchisq:', dchisqs[-1]) dchisqs.append(dchisq_none) all_runs.append([re,] + dchisqs) all_runs = np.array(all_runs) re = all_runs[:,0] dchi_psf = all_runs[:,1] dchi_simp = all_runs[:,2] dchi_exp = all_runs[:,3] dchi_ps = np.maximum(dchi_psf, dchi_simp) dchi_cut1 = dchi_ps + 3+9 dchi_cut2 = dchi_ps + dchi_psf * 0.02 dchi_cut3 = dchi_ps + dchi_psf * 0.008 plt.clf() plt.plot(re, dchi_psf, 'k-', label='PSF') plt.plot(re, dchi_simp, 'b-', label='SIMP') plt.plot(re, dchi_exp, 'r-', label='EXP') plt.plot(re, dchi_cut2, 'm--', alpha=0.5, lw=2, label='Cut: 2%') plt.plot(re, dchi_cut3, 'm:', alpha=0.5, lw=2, label='Cut: 0.08%') plt.plot(re, dchi_cut1, 'm-', alpha=0.5, lw=2, label='Cut: 12') plt.xlabel('True r_e (arcsec)') plt.ylabel('dchisq') #plt.legend(loc='lower left') plt.legend(loc='upper right') tt = 'Seeing = %g arcsec, S/N ~ %i' % (seeing, int(np.round(np.sqrt(target_dchisq)))) if e1 != 0.: tt += ', Ellipticity %g' % e1 plt.title(tt) plt.ylim(0.90 * target_dchisq, 1.05 * target_dchisq) # aspect = 1.2 # ax = plt.axis() # dre = (ax[1]-ax[0]) / 20 / aspect # dchi = (ax[3]-ax[2]) / 20 # I = np.linspace(0, len(re_vals)-1, 8).astype(int) # for mod,re in [(mods[i], re_vals[i]) for i in I]: # print('extent:', [re-dre, re+dre, ax[2], ax[2]+dchi]) # plt.imshow(mod, interpolation='nearest', origin='lower', aspect='auto', # extent=[re-dre, re+dre, ax[2], ax[2]+dchi], cmap='gray') # plt.axis(ax) ps.savefig()
Nother= np.zeros_like(Nexp) np.random.seed(42) sz = 50 cd = pixscale / 3600. wcs = Tan(0., 0., float(sz/2), float(sz/2), -cd, 0., 0., cd, float(sz), float(sz)) band = 'r' #all_dchisqs = [] all_runs = [] tim = Image(data=np.zeros((sz,sz)), inverr=np.ones((sz,sz)) / sig1, psf=psf, wcs = ConstantFitsWcs(wcs), photocal = LinearPhotoCal(1., band=band)) for i,sn in enumerate(sn_vals): for j,re in enumerate(re_vals): ## HACK -- this is the flux required for a PSF to be ## detected at target S/N... adjust for galaxy? flux = sn * detsig1 # Create round EXP galaxy #PixPos(sz/2, sz/2), true_src = ExpGalaxy(RaDecPos(0., 0.), NanoMaggies(**{band: flux}), EllipseE(re, 0., 0.)) tr = Tractor([tim], [true_src]) tr.freezeParams('images')
def rbmain(): travis = 'travis' in sys.argv extra_args = [ '--old-calibs-ok', #'--verbose', ] if travis: extra_args.extend( ['--no-wise-ceres', '--no-gaia', '--no-large-galaxies']) if 'ceres' in sys.argv: surveydir = os.path.join(os.path.dirname(__file__), 'testcase3') main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--ceres', '--survey-dir', surveydir, '--outdir', 'out-testcase3-ceres', '--no-depth-cut' ]) sys.exit(0) # demo RexGalaxy, with plots if False: from legacypipe.survey import RexGalaxy from tractor import NanoMaggies, PixPos from tractor import Image, GaussianMixturePSF, LinearPhotoCal from legacypipe.survey import LogRadius rex = RexGalaxy(PixPos(1., 2.), NanoMaggies(r=3.), LogRadius(0.)) print('Rex:', rex) print('Rex params:', rex.getParams()) print('Rex nparams:', rex.numberOfParams()) H, W = 100, 100 tim = Image(data=np.zeros((H, W), np.float32), inverr=np.ones((H, W), np.float32), psf=GaussianMixturePSF(1., 0., 0., 4., 4., 0.), photocal=LinearPhotoCal(1., band='r')) derivs = rex.getParamDerivatives(tim) print('Derivs:', len(derivs)) print('Rex params:', rex.getParamNames()) import pylab as plt from astrometry.util.plotutils import PlotSequence ps = PlotSequence('rex') for d, nm in zip(derivs, rex.getParamNames()): plt.clf() plt.imshow(d.patch, interpolation='nearest', origin='lower') plt.title('Derivative %s' % nm) ps.savefig() sys.exit(0) # Test RexGalaxy surveydir = os.path.join(os.path.dirname(__file__), 'testcase6') outdir = 'out-testcase6-rex' main(args=[ '--brick', '1102p240', '--zoom', '500', '600', '650', '750', '--force-all', '--no-write', '--no-wise', #'--rex', #'--plots', '--survey-dir', surveydir, '--outdir', outdir ] + extra_args) fn = os.path.join(outdir, 'tractor', '110', 'tractor-1102p240.fits') assert (os.path.exists(fn)) T = fits_table(fn) assert (len(T) == 2) print('Types:', T.type) # Since there is a Tycho-2 star in the blob, forced to be PSF. assert (T.type[0] == 'PSF ') cmd = ( '(cd %s && sha256sum -c %s)' % (outdir, os.path.join('tractor', '110', 'brick-1102p240.sha256sum'))) print(cmd) rtn = os.system(cmd) assert (rtn == 0) # Test with a Tycho-2 star in the blob. surveydir = os.path.join(os.path.dirname(__file__), 'testcase6') outdir = 'out-testcase6' main(args=[ '--brick', '1102p240', '--zoom', '500', '600', '650', '750', '--force-all', '--no-write', '--no-wise', '--survey-dir', surveydir, '--outdir', outdir ] + extra_args) fn = os.path.join(outdir, 'tractor', '110', 'tractor-1102p240.fits') assert (os.path.exists(fn)) T = fits_table(fn) assert (len(T) == 2) print('Types:', T.type) # Since there is a Tycho-2 star in the blob, forced to be PSF. assert (T.type[0] == 'PSF ') # Test that we can run splinesky calib if required... from legacypipe.decam import DecamImage DecamImage.splinesky_boxsize = 128 surveydir = os.path.join(os.path.dirname(__file__), 'testcase4') outdir = 'out-testcase4' fn = os.path.join(surveydir, 'calib', 'decam', 'splinesky', '00431', '00431608', 'decam-00431608-N3.fits') if os.path.exists(fn): os.unlink(fn) main(args=[ '--brick', '1867p255', '--zoom', '2050', '2300', '1150', '1400', '--force-all', '--no-write', '--coadd-bw', '--unwise-dir', os.path.join(surveydir, 'images', 'unwise'), '--unwise-tr-dir', os.path.join(surveydir, 'images', 'unwise-tr'), '--blob-image', '--no-hybrid-psf', '--survey-dir', surveydir, '--outdir', outdir ] + extra_args + ['-v']) print('Checking for calib file', fn) assert (os.path.exists(fn)) # Wrap-around, hybrid PSF surveydir = os.path.join(os.path.dirname(__file__), 'testcase8') outdir = 'out-testcase8' main(args=[ '--brick', '1209p050', '--zoom', '720', '1095', '3220', '3500', '--force-all', '--no-write', '--no-wise', #'--plots', '--survey-dir', surveydir, '--outdir', outdir ] + extra_args) # Test with a Tycho-2 star + another saturated star in the blob. surveydir = os.path.join(os.path.dirname(__file__), 'testcase7') outdir = 'out-testcase7' # remove --no-gaia my_extra_args = [a for a in extra_args if a != '--no-gaia'] os.environ['GAIA_CAT_DIR'] = os.path.join(surveydir, 'gaia-dr2') os.environ['GAIA_CAT_VER'] = '2' main(args=[ '--brick', '1102p240', '--zoom', '250', '350', '1550', '1650', '--force-all', '--no-write', '--no-wise', #'--plots', '--survey-dir', surveydir, '--outdir', outdir ] + my_extra_args) del os.environ['GAIA_CAT_DIR'] del os.environ['GAIA_CAT_VER'] fn = os.path.join(outdir, 'tractor', '110', 'tractor-1102p240.fits') assert (os.path.exists(fn)) T = fits_table(fn) assert (len(T) == 4) # Check skipping blobs outside the brick's unique area. # (this now doesn't detect any sources at all, reasonably) # surveydir = os.path.join(os.path.dirname(__file__), 'testcase5') # outdir = 'out-testcase5' # # fn = os.path.join(outdir, 'tractor', '186', 'tractor-1867p255.fits') # if os.path.exists(fn): # os.unlink(fn) # # main(args=['--brick', '1867p255', '--zoom', '0', '150', '0', '150', # '--force-all', '--no-write', '--coadd-bw', # '--survey-dir', surveydir, # '--early-coadds', # '--outdir', outdir] + extra_args) # # assert(os.path.exists(fn)) # T = fits_table(fn) # assert(len(T) == 1) # Custom RA,Dec; blob ra,dec. surveydir = os.path.join(os.path.dirname(__file__), 'testcase4') outdir = 'out-testcase4b' # Catalog written with one entry (--blobradec) fn = os.path.join(outdir, 'tractor', 'cus', 'tractor-custom-186743p25461.fits') if os.path.exists(fn): os.unlink(fn) main(args=[ '--radec', '186.743965', '25.461788', '--width', '250', '--height', '250', '--force-all', '--no-write', '--no-wise', '--blobradec', '186.740369', '25.453855', '--survey-dir', surveydir, '--outdir', outdir ] + extra_args) assert (os.path.exists(fn)) T = fits_table(fn) assert (len(T) == 1) surveydir = os.path.join(os.path.dirname(__file__), 'testcase3') outdir = 'out-testcase3' checkpoint_fn = os.path.join(outdir, 'checkpoint.pickle') if os.path.exists(checkpoint_fn): os.unlink(checkpoint_fn) main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--survey-dir', surveydir, '--outdir', outdir, '--checkpoint', checkpoint_fn, '--checkpoint-period', '1', '--threads', '2' ] + extra_args) # Read catalog into Tractor sources to test read_fits_catalog from legacypipe.catalog import read_fits_catalog from legacypipe.survey import LegacySurveyData, GaiaSource from tractor.galaxy import DevGalaxy from tractor import PointSource survey = LegacySurveyData(survey_dir=outdir) fn = survey.find_file('tractor', brick='2447p120') print('Checking', fn) T = fits_table(fn) cat = read_fits_catalog(T, fluxPrefix='') print('Read catalog:', cat) assert (len(cat) == 2) src = cat[0] assert (type(src) == DevGalaxy) assert (np.abs(src.pos.ra - 244.77973) < 0.00001) assert (np.abs(src.pos.dec - 12.07233) < 0.00001) src = cat[1] print('Source', src) assert (type(src) in [PointSource, GaiaSource]) assert (np.abs(src.pos.ra - 244.77830) < 0.00001) assert (np.abs(src.pos.dec - 12.07250) < 0.00001) # DevGalaxy(pos=RaDecPos[244.77975494973529, 12.072348111713127], brightness=NanoMaggies: g=19.2, r=17.9, z=17.1, shape=re=2.09234, e1=-0.198453, e2=0.023652, # PointSource(RaDecPos[244.77833280764278, 12.072521274981987], NanoMaggies: g=25, r=23, z=21.7) # Check that we can run again, using that checkpoint file. main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--survey-dir', surveydir, '--outdir', outdir, '--checkpoint', checkpoint_fn, '--checkpoint-period', '1', '--threads', '2' ] + extra_args) # Assert...... something? # Test --checkpoint without --threads main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--survey-dir', surveydir, '--outdir', outdir, '--checkpoint', checkpoint_fn, '--checkpoint-period', '1' ] + extra_args) # MzLS + BASS data # surveydir2 = os.path.join(os.path.dirname(__file__), 'mzlsbass') # main(args=['--brick', '3521p002', '--zoom', '2400', '2450', '1200', '1250', # '--no-wise', '--force-all', '--no-write', # '--survey-dir', surveydir2, # '--outdir', 'out-mzlsbass']) # From Kaylan's Bootes pre-DR4 run # surveydir2 = os.path.join(os.path.dirname(__file__), 'mzlsbass3') # main(args=['--brick', '2173p350', '--zoom', '100', '200', '100', '200', # '--no-wise', '--force-all', '--no-write', # '--survey-dir', surveydir2, # '--outdir', 'out-mzlsbass3'] + extra_args) # With plots! main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--survey-dir', surveydir, '--outdir', 'out-testcase3', '--plots', '--nblobs', '1' ] + extra_args) # Decals Image Simulations # Uncomment WHEN galsim build for Travis #os.environ["DECALS_SIM_DIR"]= os.path.join(os.path.dirname(__file__),'image_sims') #brick= '2447p120' #sim_main(args=['--brick', brick, '-n', '2', '-o', 'STAR', \ # '-ic', '1', '--rmag-range', '18', '26', '--threads', '1',\ # '--zoom', '1020', '1070', '2775', '2815']) # Check if correct files written out #rt_dir= os.path.join(os.getenv('DECALS_SIM_DIR'),brick,'star','001') #assert( os.path.exists(os.path.join(rt_dir,'../','metacat-'+brick+'-star.fits')) ) #for fn in ['tractor-%s-star-01.fits' % brick,'simcat-%s-star-01.fits' % brick]: # assert( os.path.exists(os.path.join(rt_dir,fn)) ) #for fn in ['image','model','resid','simscoadd']: # assert( os.path.exists(os.path.join(rt_dir,'qa-'+brick+'-star-'+fn+'-01.jpg')) ) if not travis: # With ceres main(args=[ '--brick', '2447p120', '--zoom', '1020', '1070', '2775', '2815', '--no-wise', '--force-all', '--no-write', '--ceres', '--survey-dir', surveydir, '--outdir', 'out-testcase3-ceres' ] + extra_args)
photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=band) tsky = ConstantSky(0.) # HACK -- circular Gaussian PSF of fixed size... # in arcsec #fwhms = dict(NUV=6.0, FUV=6.0) # -> sigma in pixels #sig = fwhms[band] / 2.35 / twcs.pixel_scale() sig = 6.0 / 2.35 / twcs.pixel_scale() tpsf = NCircularGaussianPSF([sig], [1.]) tim = Image(data=timg, inverr=tie, psf=tpsf, wcs=twcs, sky=tsky, photocal=photocal, name='GALEX ' + band + brick.brickname) tractor = Tractor([tim], srcs) mod = tractor.getModelImage(0) print('Tractor image', tim.name) plt.clf() plt.imshow(timg, interpolation='nearest', origin='lower') ps.savefig() print('Tractor model', tim.name) plt.clf() plt.imshow(mod, interpolation='nearest', origin='lower') ps.savefig()
def galex_coadds(onegal, galaxy=None, radius_mosaic=30, radius_mask=None, pixscale=1.5, ref_pixscale=0.262, output_dir=None, galex_dir=None, log=None, centrals=True, verbose=False): '''Generate custom GALEX cutouts. radius_mosaic and radius_mask in arcsec pixscale: GALEX pixel scale in arcsec/pixel. ''' import fitsio import matplotlib.pyplot as plt from astrometry.libkd.spherematch import match_radec from astrometry.util.resample import resample_with_wcs, OverlapError from tractor import (Tractor, NanoMaggies, Image, LinearPhotoCal, NCircularGaussianPSF, ConstantFitsWcs, ConstantSky) from legacypipe.survey import imsave_jpeg from legacypipe.catalog import read_fits_catalog if galaxy is None: galaxy = 'galaxy' if galex_dir is None: galex_dir = os.environ.get('GALEX_DIR') if output_dir is None: output_dir = '.' if radius_mask is None: radius_mask = radius_mosaic radius_search = 5.0 # [arcsec] else: radius_search = radius_mask W = H = np.ceil(2 * radius_mosaic / pixscale).astype('int') # [pixels] targetwcs = Tan(onegal['RA'], onegal['DEC'], (W + 1) / 2.0, (H + 1) / 2.0, -pixscale / 3600.0, 0.0, 0.0, pixscale / 3600.0, float(W), float(H)) # Read the custom Tractor catalog tractorfile = os.path.join(output_dir, '{}-tractor.fits'.format(galaxy)) if not os.path.isfile(tractorfile): print('Missing Tractor catalog {}'.format(tractorfile)) return 0 cat = fits_table(tractorfile) print('Read {} sources from {}'.format(len(cat), tractorfile), flush=True, file=log) keep = np.ones(len(cat)).astype(bool) if centrals: # Find the large central galaxy and mask out (ignore) all the models # which are within its elliptical mask. # This algorithm will have to change for mosaics not centered on large # galaxies, e.g., in galaxy groups. m1, m2, d12 = match_radec(cat.ra, cat.dec, onegal['RA'], onegal['DEC'], radius_search / 3600.0, nearest=False) if len(m1) == 0: print('No central galaxies found at the central coordinates!', flush=True, file=log) else: pixfactor = ref_pixscale / pixscale # shift the optical Tractor positions for mm in m1: morphtype = cat.type[mm].strip() if morphtype == 'EXP' or morphtype == 'COMP': e1, e2, r50 = cat.shapeexp_e1[mm], cat.shapeexp_e2[ mm], cat.shapeexp_r[mm] # [arcsec] elif morphtype == 'DEV' or morphtype == 'COMP': e1, e2, r50 = cat.shapedev_e1[mm], cat.shapedev_e2[ mm], cat.shapedev_r[mm] # [arcsec] else: r50 = None if r50: majoraxis = r50 * 5 / pixscale # [pixels] ba, phi = SGA.misc.convert_tractor_e1e2(e1, e2) these = SGA.misc.ellipse_mask(W / 2, W / 2, majoraxis, ba * majoraxis, np.radians(phi), cat.bx * pixfactor, cat.by * pixfactor) if np.sum(these) > 0: #keep[these] = False pass print('Hack!') keep[mm] = False #srcs = read_fits_catalog(cat) #_srcs = np.array(srcs)[~keep].tolist() #mod = SGA.misc.srcs2image(_srcs, ConstantFitsWcs(targetwcs), psf_sigma=3.0) #import matplotlib.pyplot as plt ##plt.imshow(mod, origin='lower') ; plt.savefig('junk.png') #plt.imshow(np.log10(mod), origin='lower') ; plt.savefig('junk.png') #pdb.set_trace() srcs = read_fits_catalog(cat) for src in srcs: src.freezeAllBut('brightness') #srcs_nocentral = np.array(srcs)[keep].tolist() # Find all overlapping GALEX tiles and then read the tims. galex_tiles = _read_galex_tiles(targetwcs, galex_dir, log=log, verbose=verbose) gbands = ['n', 'f'] nicegbands = ['NUV', 'FUV'] zps = dict(n=20.08, f=18.82) coimgs, comods, coresids, coimgs_central, comods_nocentral = [], [], [], [], [] for niceband, band in zip(nicegbands, gbands): J = np.flatnonzero(galex_tiles.get('has_' + band)) print(len(J), 'GALEX tiles have coverage in band', band) coimg = np.zeros((H, W), np.float32) comod = np.zeros((H, W), np.float32) cowt = np.zeros((H, W), np.float32) comod_nocentral = np.zeros((H, W), np.float32) for src in srcs: src.setBrightness(NanoMaggies(**{band: 1})) for j in J: brick = galex_tiles[j] fn = os.path.join( galex_dir, brick.tilename.strip(), '%s-%sd-intbgsub.fits.gz' % (brick.brickname, band)) #print(fn) gwcs = Tan(*[ float(f) for f in [ brick.crval1, brick.crval2, brick.crpix1, brick.crpix2, brick.cdelt1, 0., 0., brick.cdelt2, 3840., 3840. ] ]) img = fitsio.read(fn) #print('Read', img.shape) try: Yo, Xo, Yi, Xi, nil = resample_with_wcs(targetwcs, gwcs, [], 3) except OverlapError: continue K = np.flatnonzero(img[Yi, Xi] != 0.) if len(K) == 0: continue Yo, Xo, Yi, Xi = Yo[K], Xo[K], Yi[K], Xi[K] wt = brick.get(band + 'exptime') coimg[Yo, Xo] += wt * img[Yi, Xi] cowt[Yo, Xo] += wt x0, x1, y0, y1 = min(Xi), max(Xi), min(Yi), max(Yi) subwcs = gwcs.get_subimage(x0, y0, x1 - x0 + 1, y1 - y0 + 1) twcs = ConstantFitsWcs(subwcs) timg = img[y0:y1 + 1, x0:x1 + 1] tie = np.ones_like(timg) ## HACK! #hdr = fitsio.read_header(fn) #zp = hdr[''] zp = zps[band] photocal = LinearPhotoCal(NanoMaggies.zeropointToScale(zp), band=band) tsky = ConstantSky(0.0) # HACK -- circular Gaussian PSF of fixed size... # in arcsec #fwhms = dict(NUV=6.0, FUV=6.0) # -> sigma in pixels #sig = fwhms[band] / 2.35 / twcs.pixel_scale() sig = 6.0 / np.sqrt(8 * np.log(2)) / twcs.pixel_scale() tpsf = NCircularGaussianPSF([sig], [1.]) tim = Image(data=timg, inverr=tie, psf=tpsf, wcs=twcs, sky=tsky, photocal=photocal, name='GALEX ' + band + brick.brickname) ## Build the model image with and without the central galaxy model. tractor = Tractor([tim], srcs) mod = tractor.getModelImage(0) tractor.freezeParam('images') tractor.optimize_forced_photometry(priors=False, shared_params=False) mod = tractor.getModelImage(0) srcs_nocentral = np.array(srcs)[keep].tolist() #srcs_nocentral = np.array(srcs)[nocentral].tolist() tractor_nocentral = Tractor([tim], srcs_nocentral) mod_nocentral = tractor_nocentral.getModelImage(0) comod[Yo, Xo] += wt * mod[Yi - y0, Xi - x0] comod_nocentral[Yo, Xo] += wt * mod_nocentral[Yi - y0, Xi - x0] coimg /= np.maximum(cowt, 1e-18) comod /= np.maximum(cowt, 1e-18) comod_nocentral /= np.maximum(cowt, 1e-18) coresid = coimg - comod # Subtract the model image which excludes the central (comod_nocentral) # from the data (coimg) to isolate the light of the central # (coimg_central). coimg_central = coimg - comod_nocentral coimgs.append(coimg) comods.append(comod) coresids.append(coresid) comods_nocentral.append(comod_nocentral) coimgs_central.append(coimg_central) # Write out the final images with and without the central, making sure # to apply the zeropoint to go from counts/s to AB nanomaggies. # https://asd.gsfc.nasa.gov/archive/galex/FAQ/counts_background.html for thisimg, imtype in zip((coimg, comod, comod_nocentral), ('image', 'model', 'model-nocentral')): fitsfile = os.path.join( output_dir, '{}-{}-{}.fits'.format(galaxy, imtype, niceband)) if verbose: print('Writing {}'.format(fitsfile)) fitsio.write(fitsfile, thisimg * 10**(-0.4 * (zp - 22.5)), clobber=True) # Build a color mosaic (but note that the images here are in units of # background-subtracted counts/s). #_galex_rgb = _galex_rgb_moustakas #_galex_rgb = _galex_rgb_dstn _galex_rgb = _galex_rgb_official for imgs, imtype in zip( (coimgs, comods, coresids, comods_nocentral, coimgs_central), ('image', 'model', 'resid', 'model-nocentral', 'image-central')): rgb = _galex_rgb(imgs) jpgfile = os.path.join(output_dir, '{}-{}-FUVNUV.jpg'.format(galaxy, imtype)) if verbose: print('Writing {}'.format(jpgfile)) imsave_jpeg(jpgfile, rgb, origin='lower') return 1
if False: from legacypipe.survey import RexGalaxy from tractor import RaDecPos, NanoMaggies, PixPos from tractor import ScalarParam from tractor import Image, GaussianMixturePSF, LinearPhotoCal from legacypipe.survey import LogRadius rex = RexGalaxy( PixPos(1., 2.), NanoMaggies(r=3.), LogRadius(0.)) print('Rex:', rex) print('Rex params:', rex.getParams()) print('Rex nparams:', rex.numberOfParams()) H,W = 100,100 tim = Image(data=np.zeros((H,W), np.float32), inverr=np.ones((H,W), np.float32), psf=GaussianMixturePSF(1., 0., 0., 4., 4., 0.), photocal=LinearPhotoCal(1., band='r')) derivs = rex.getParamDerivatives(tim) print('Derivs:', len(derivs)) print('Rex params:', rex.getParamNames()) import pylab as plt from astrometry.util.plotutils import PlotSequence ps = PlotSequence('rex') for d,nm in zip(derivs, rex.getParamNames()): plt.clf() plt.imshow(d.patch, interpolation='nearest', origin='lower') plt.title('Derivative %s' % nm) ps.savefig()
def get_unwise_tractor_image(basedir, tile, band, bandname=None, masked=True, **kwargs): ''' masked: read "-m" images, or "-u"? bandname: PhotoCal band name to use: default: "w%i" % band ''' if bandname is None: bandname = 'w%i' % band mu = 'm' if masked else 'u' # Allow multiple colon-separated unwise-coadd directories. basedirs = basedir.split(':') foundFiles = False for basedir in basedirs: thisdir = get_unwise_tile_dir(basedir, tile) base = os.path.join(thisdir, 'unwise-%s-w%i-' % (tile, band)) imfn = base + 'img-%s.fits' % mu ivfn = base + 'invvar-%s.fits.gz' % mu # ppfn = base + 'std-%s.fits.gz' % mu nifn = base + 'n-%s.fits.gz' % mu nufn = base + 'n-u.fits.gz' if not os.path.exists(imfn): print('Does not exist:', imfn) continue print('Reading', imfn) wcs = Tan(imfn) twcs = ConstantFitsWcs(wcs) F = fitsio.FITS(imfn) img = F[0] hdr = img.read_header() H, W = img.get_info()['dims'] H, W = int(H), int(W) roi = interpret_roi(twcs, (H, W), **kwargs) if roi is None: # No overlap with ROI return None # interpret_roi returns None or a tuple; drop the second element in the tuple. roi, nil = roi (x0, x1, y0, y1) = roi wcs = wcs.get_subimage(x0, y0, x1 - x0, y1 - y0) twcs = ConstantFitsWcs(wcs) roislice = (slice(y0, y1), slice(x0, x1)) img = img[roislice] if not os.path.exists(ivfn) and os.path.exists( ivfn.replace('.fits.gz', '.fits')): ivfn = ivfn.replace('.fits.gz', '.fits') if not os.path.exists(nifn) and os.path.exists( nifn.replace('.fits.gz', '.fits')): nifn = nifn.replace('.fits.gz', '.fits') if not os.path.exists(nufn) and os.path.exists( nufn.replace('.fits.gz', '.fits')): nufn = nufn.replace('.fits.gz', '.fits') if not (os.path.exists(ivfn) and os.path.exists(nifn) and os.path.exists(nufn)): print('Files do not exist:', ivfn, nifn, nufn) continue foundFiles = True break if not foundFiles: raise IOError('unWISE files not found in ' + str(basedirs) + ' for tile ' + tile) print('Reading', ivfn) invvar = fitsio.FITS(ivfn)[0][roislice] if band == 4: # due to upsampling, effective invvar is smaller (the pixels # are correlated) invvar *= 0.25 # print 'Reading', ppfn #pp = fitsio.FITS(ppfn)[0][roislice] print('Reading', nifn) nims = fitsio.FITS(nifn)[0][roislice] if nufn == nifn: nuims = nims else: print('Reading', nufn) nuims = fitsio.FITS(nufn)[0][roislice] # print 'Median # ims:', np.median(nims) good = (nims > 0) invvar[np.logical_not(good)] = 0. sig1 = 1. / np.sqrt(np.median(invvar[good])) # Load the average PSF model (generated by wise_psf.py) psffn = os.path.join(os.path.dirname(__file__), 'wise-psf-avg.fits') print('Reading', psffn) P = fits_table(psffn, hdu=band) psf = GaussianMixturePSF(P.amp, P.mean, P.var) sky = 0. tsky = ConstantSky(sky) # if opt.errfrac > 0: # nz = (iv > 0) # iv2 = np.zeros_like(invvar) # iv2[nz] = 1./(1./invvar[nz] + (img[nz] * opt.errfrac)**2) # print 'Increasing error estimate by', opt.errfrac, 'of image flux' # invvar = iv2 tim = Image(data=img, invvar=invvar, psf=psf, wcs=twcs, sky=tsky, photocal=LinearPhotoCal(1., band=bandname), name='unWISE %s W%i' % (tile, band)) tim.sig1 = sig1 tim.roi = roi tim.nims = nims tim.nuims = nuims tim.hdr = hdr if 'MJDMIN' in hdr and 'MJDMAX' in hdr: from tractor.tractortime import TAITime tim.mjdmin = hdr['MJDMIN'] tim.mjdmax = hdr['MJDMAX'] tim.time = TAITime(None, mjd=(tim.mjdmin + tim.mjdmax) / 2.) return tim
'''Set step sizes when taking derivatives of the parameters of the mixture of Gaussians.''' return [0.01]*self.K*2 #################### (end of second way) 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
def make_depth_cut(survey, ccds, bands, targetrd, brick, W, H, pixscale, plots, ps, splinesky, gaussPsf, pixPsf, normalizePsf, do_calibs, gitver, targetwcs, old_calibs_ok, get_depth_maps=False, margin=0.5, use_approx_wcs=False): if plots: import pylab as plt # Add some margin to our DESI depth requirements target_depth_map = dict(g=24.0 + margin, r=23.4 + margin, z=22.5 + margin) # List extra (redundant) target percentiles so that increasing the depth at # any of these percentiles causes the image to be kept. target_percentiles = np.array(list(range(2, 10)) + list(range(10, 30, 5)) + list(range(30, 101, 10))) target_ddepths = np.zeros(len(target_percentiles), np.float32) target_ddepths[target_percentiles < 10] = -0.3 target_ddepths[target_percentiles < 5] = -0.6 #print('Target percentiles:', target_percentiles) #print('Target ddepths:', target_ddepths) cH,cW = H//10, W//10 coarsewcs = targetwcs.scale(0.1) coarsewcs.imagew = cW coarsewcs.imageh = cH # Unique pixels in this brick (U: cH x cW boolean) U = find_unique_pixels(coarsewcs, cW, cH, None, brick.ra1, brick.ra2, brick.dec1, brick.dec2) pixscale = 3600. * np.sqrt(np.abs(ccds.cd1_1*ccds.cd2_2 - ccds.cd1_2*ccds.cd2_1)) seeing = ccds.fwhm * pixscale # Compute the rectangle in *coarsewcs* covered by each CCD slices = [] overlapping_ccds = np.zeros(len(ccds), bool) for i,ccd in enumerate(ccds): wcs = survey.get_approx_wcs(ccd) hh,ww = wcs.shape rr,dd = wcs.pixelxy2radec([1,ww,ww,1], [1,1,hh,hh]) ok,xx,yy = coarsewcs.radec2pixelxy(rr, dd) y0 = int(np.round(np.clip(yy.min(), 0, cH-1))) y1 = int(np.round(np.clip(yy.max(), 0, cH-1))) x0 = int(np.round(np.clip(xx.min(), 0, cW-1))) x1 = int(np.round(np.clip(xx.max(), 0, cW-1))) if y0 == y1 or x0 == x1: slices.append(None) continue # Check whether this CCD overlaps the unique area of this brick... if not np.any(U[y0:y1+1, x0:x1+1]): info('No overlap with unique area for CCD', ccd.expnum, ccd.ccdname) slices.append(None) continue overlapping_ccds[i] = True slices.append((slice(y0, y1+1), slice(x0, x1+1))) keep_ccds = np.zeros(len(ccds), bool) depthmaps = [] for band in bands: # scalar target_depth = target_depth_map[band] # vector target_depths = target_depth + target_ddepths depthiv = np.zeros((cH,cW), np.float32) depthmap = np.zeros_like(depthiv) depthvalue = np.zeros_like(depthiv) last_pcts = np.zeros_like(target_depths) # indices of CCDs we still want to look at in the current band b_inds = np.where(ccds.filter == band)[0] info(len(b_inds), 'CCDs in', band, 'band') if len(b_inds) == 0: continue b_inds = np.array([i for i in b_inds if slices[i] is not None]) info(len(b_inds), 'CCDs in', band, 'band overlap target') if len(b_inds) == 0: continue # CCDs that we will try before searching for good ones -- CCDs # from the same exposure number as CCDs we have chosen to # take. try_ccds = set() # Try DECaLS data first! Idecals = np.where(ccds.propid[b_inds] == '2014B-0404')[0] if len(Idecals): try_ccds.update(b_inds[Idecals]) debug('Added', len(try_ccds), 'DECaLS CCDs to try-list') plot_vals = [] if plots: plt.clf() for i in b_inds: sy,sx = slices[i] x0,x1 = sx.start, sx.stop y0,y1 = sy.start, sy.stop plt.plot([x0,x0,x1,x1,x0], [y0,y1,y1,y0,y0], 'b-', alpha=0.5) plt.title('CCDs overlapping brick: %i in %s band' % (len(b_inds), band)) ps.savefig() nccds = np.zeros((cH,cW), np.int16) plt.clf() for i in b_inds: nccds[slices[i]] += 1 plt.imshow(nccds, interpolation='nearest', origin='lower', vmin=0) plt.colorbar() plt.title('CCDs overlapping brick: %i in %s band (%i / %i / %i)' % (len(b_inds), band, nccds.min(), np.median(nccds), nccds.max())) ps.savefig() #continue while len(b_inds): if len(try_ccds) == 0: # Choose the next CCD to look at in this band. # A rough point-source depth proxy would be: # metric = np.sqrt(ccds.extime[b_inds]) / seeing[b_inds] # If we want to put more weight on choosing good-seeing images, we could do: #metric = np.sqrt(ccds.exptime[b_inds]) / seeing[b_inds]**2 # depth would be ~ 1 / (sig1 * seeing); we privilege good seeing here. metric = 1. / (ccds.sig1[b_inds] * seeing[b_inds]**2) # This metric is *BIG* for *GOOD* ccds! # Here, we try explicitly to include CCDs that cover # pixels that are still shallow by the largest amount # for the largest number of percentiles of interest; # note that pixels with no coverage get depth 0, so # score high in this metric. # # The value is the depth still required to hit the # target, summed over percentiles of interest # (for pixels unique to this brick) depthvalue[:,:] = 0. active = (last_pcts < target_depths) for d in target_depths[active]: depthvalue += U * np.maximum(0, d - depthmap) ccdvalue = np.zeros(len(b_inds), np.float32) for j,i in enumerate(b_inds): #ccdvalue[j] = np.sum(depthvalue[slices[i]]) # mean -- we want the most bang for the buck per pixel? ccdvalue[j] = np.mean(depthvalue[slices[i]]) metric *= ccdvalue # *ibest* is an index into b_inds ibest = np.argmax(metric) # *iccd* is an index into ccds. iccd = b_inds[ibest] ccd = ccds[iccd] debug('Chose best CCD: seeing', seeing[iccd], 'exptime', ccds.exptime[iccd], 'with value', ccdvalue[ibest]) else: iccd = try_ccds.pop() ccd = ccds[iccd] debug('Popping CCD from use_ccds list') # remove *iccd* from b_inds b_inds = b_inds[b_inds != iccd] im = survey.get_image_object(ccd) debug('Band', im.band, 'expnum', im.expnum, 'exptime', im.exptime, 'seeing', im.fwhm*im.pixscale, 'arcsec, propid', im.propid) im.check_for_cached_files(survey) debug(im) if do_calibs: kwa = dict(git_version=gitver, old_calibs_ok=old_calibs_ok) if gaussPsf: kwa.update(psfex=False) if splinesky: kwa.update(splinesky=True) im.run_calibs(**kwa) if use_approx_wcs: debug('Using approximate (TAN) WCS') wcs = survey.get_approx_wcs(ccd) else: debug('Reading WCS from', im.imgfn, 'HDU', im.hdu) wcs = im.get_wcs() x0,x1,y0,y1,slc = im.get_image_extent(wcs=wcs, radecpoly=targetrd) if x0==x1 or y0==y1: debug('No actual overlap') continue wcs = wcs.get_subimage(int(x0), int(y0), int(x1-x0), int(y1-y0)) if 'galnorm_mean' in ccds.get_columns(): galnorm = ccd.galnorm_mean debug('Using galnorm_mean from CCDs table:', galnorm) else: psf = im.read_psf_model(x0, y0, gaussPsf=gaussPsf, pixPsf=pixPsf, normalizePsf=normalizePsf) psf = psf.constantPsfAt((x1-x0)//2, (y1-y0)//2) # create a fake tim to compute galnorm from tractor import PixPos, Flux, ModelMask, Image, NullWCS from legacypipe.survey import SimpleGalaxy h,w = 50,50 gal = SimpleGalaxy(PixPos(w//2,h//2), Flux(1.)) tim = Image(data=np.zeros((h,w), np.float32), psf=psf, wcs=NullWCS(pixscale=im.pixscale)) mm = ModelMask(0, 0, w, h) galmod = gal.getModelPatch(tim, modelMask=mm).patch galmod = np.maximum(0, galmod) galmod /= galmod.sum() galnorm = np.sqrt(np.sum(galmod**2)) detiv = 1. / (im.sig1 / galnorm)**2 galdepth = -2.5 * (np.log10(5. * im.sig1 / galnorm) - 9.) debug('Galnorm:', galnorm, 'sig1:', im.sig1, 'galdepth', galdepth) # Add this image the the depth map... from astrometry.util.resample import resample_with_wcs, OverlapError try: Yo,Xo,_,_,_ = resample_with_wcs(coarsewcs, wcs) debug(len(Yo), 'of', (cW*cH), 'pixels covered by this image') except OverlapError: debug('No overlap') continue depthiv[Yo,Xo] += detiv # compute the new depth map & percentiles (including the proposed new CCD) depthmap[:,:] = 0. depthmap[depthiv > 0] = 22.5 - 2.5*np.log10(5./np.sqrt(depthiv[depthiv > 0])) depthpcts = np.percentile(depthmap[U], target_percentiles) for i,(p,d,t) in enumerate(zip(target_percentiles, depthpcts, target_depths)): info(' pct % 3i, prev %5.2f -> %5.2f vs target %5.2f %s' % (p, last_pcts[i], d, t, ('ok' if d >= t else ''))) keep = False # Did we increase the depth of any target percentile that did not already exceed its target depth? if np.any((depthpcts > last_pcts) * (last_pcts < target_depths)): keep = True # Add any other CCDs from this same expnum to the try_ccds list. # (before making the plot) I = np.where(ccd.expnum == ccds.expnum[b_inds])[0] try_ccds.update(b_inds[I]) debug('Adding', len(I), 'CCDs with the same expnum to try_ccds list') if plots: cc = '1' if keep else '0' xx = [Xo.min(), Xo.min(), Xo.max(), Xo.max(), Xo.min()] yy = [Yo.min(), Yo.max(), Yo.max(), Yo.min(), Yo.min()] plot_vals.append(((xx,yy,cc),(last_pcts,depthpcts,keep),im.ccdname)) if plots and ( (len(try_ccds) == 0) or np.all(depthpcts >= target_depths)): plt.clf() plt.subplot2grid((2,2),(0,0)) plt.imshow(depthvalue, interpolation='nearest', origin='lower', vmin=0) plt.xticks([]); plt.yticks([]) plt.colorbar() plt.title('heuristic value') plt.subplot2grid((2,2),(0,1)) plt.imshow(depthmap, interpolation='nearest', origin='lower', vmin=target_depth - 2, vmax=target_depth + 0.5) ax = plt.axis() for (xx,yy,cc) in [p[0] for p in plot_vals]: plt.plot(xx,yy, '-', color=cc, lw=3) plt.axis(ax) plt.xticks([]); plt.yticks([]) plt.colorbar() plt.title('depth map') plt.subplot2grid((2,2),(1,0), colspan=2) ax = plt.gca() plt.plot(target_percentiles, target_depths, 'ro', label='Target') plt.plot(target_percentiles, target_depths, 'r-') for (lp,dp,k) in [p[1] for p in plot_vals]: plt.plot(target_percentiles, lp, 'k-', label='Previous percentiles') for (lp,dp,k) in [p[1] for p in plot_vals]: cc = 'b' if k else 'r' plt.plot(target_percentiles, dp, '-', color=cc, label='Depth percentiles') ccdnames = ','.join([p[2] for p in plot_vals]) plot_vals = [] plt.ylim(target_depth - 2, target_depth + 0.5) plt.xscale('log') plt.xlabel('Percentile') plt.ylabel('Depth') plt.title('depth percentiles') plt.suptitle('%s %i-%s, exptime %.0f, seeing %.2f, band %s' % (im.camera, im.expnum, ccdnames, im.exptime, im.pixscale * im.fwhm, band)) ps.savefig() if keep: info('Keeping this exposure') else: info('Not keeping this exposure') depthiv[Yo,Xo] -= detiv continue keep_ccds[iccd] = True last_pcts = depthpcts if np.all(depthpcts >= target_depths): info('Reached all target depth percentiles for band', band) break if get_depth_maps: if np.any(depthiv > 0): depthmap[:,:] = 0. depthmap[depthiv > 0] = 22.5 -2.5*np.log10(5./np.sqrt(depthiv[depthiv > 0])) depthmap[np.logical_not(U)] = np.nan depthmaps.append((band, depthmap.copy())) if plots: I = np.where(ccds.filter == band)[0] plt.clf() plt.plot(seeing[I], ccds.exptime[I], 'k.') # which CCDs from this band are we keeping? kept, = np.nonzero(keep_ccds) if len(kept): kept = kept[ccds.filter[kept] == band] plt.plot(seeing[kept], ccds.exptime[kept], 'ro') plt.xlabel('Seeing (arcsec)') plt.ylabel('Exptime (sec)') plt.title('CCDs kept for band %s' % band) plt.ylim(0, np.max(ccds.exptime[I]) * 1.1) ps.savefig() if get_depth_maps: return (keep_ccds, overlapping_ccds, depthmaps) return keep_ccds, overlapping_ccds
wcs = ConstantFitsWcs( Tan(ra, dec, (1. + W) / 2., (1. + H) / 2., pscale, 0., 0., pscale, W, H)) # an arbitrary name for this image's bandpass; if we used Flux # rather than NanoMaggies as the brightness class we wouldn't need this # (but in SDSS we'll be using multi-band NanoMaggies) band = 'r' # 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),