def psf_residuals(expnum, ccdname, stampsize=35, nstar=30, magrange=(13, 17), verbose=0, splinesky=False): # Set the debugging level. if verbose == 0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl, format='%(message)s', stream=sys.stdout) pngprefix = 'qapsf-{}-{}'.format(expnum, ccdname) # Gather all the info we need about this CCD. decals = Decals() ccd = decals.find_ccds(expnum=expnum, ccdname=ccdname)[0] band = ccd.filter ps1band = dict(g=0, r=1, i=2, z=3, Y=4) print('Band {}'.format(band)) #scales = dict(g=0.0066, r=0.01, z=0.025) #vmin, vmax = np.arcsinh(-1), np.arcsinh(100) #print(scales[band]) im = decals.get_image_object(ccd) iminfo = im.get_image_info() H, W = iminfo['dims'] wcs = im.get_wcs() # Choose a uniformly selected subset of PS1 stars on this CCD. ps1 = ps1cat(ccdwcs=wcs) cat = ps1.get_stars(band=band, magrange=magrange) rand = np.random.RandomState(seed=expnum * ccd.ccdnum) these = rand.choice(len(cat) - 1, nstar, replace=False) #these = rand.random_integers(0,len(cat)-1,nstar) cat = cat[these] cat = cat[np.argsort(cat.median[:, ps1band[band]])] # sort by magnitude #print(cat.nmag_ok) get_tim_kwargs = dict(const2psf=True, splinesky=splinesky) # Make a QAplot of the positions of all the stars. tim = im.get_tractor_image(**get_tim_kwargs) img = tim.getImage() #img = tim.getImage()/scales[band] fig = plt.figure(figsize=(5, 10)) ax = fig.gca() ax.get_xaxis().get_major_formatter().set_useOffset(False) #ax.imshow(np.arcsinh(img),cmap='gray',interpolation='nearest', # origin='lower',vmin=vmax,vmax=vmax) ax.imshow(img, **tim.ima) ax.axis('off') ax.set_title('{}: {}/{} AM={:.2f} Seeing={:.3f}"'.format( band, expnum, ccdname, ccd.airmass, ccd.seeing)) for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ax.text(xpos, ypos, '{:2d}'.format(istar + 1), color='red', horizontalalignment='left') circ = plt.Circle((xpos, ypos), radius=30, color='g', fill=False, lw=1) ax.add_patch(circ) #radec = wcs.radec_bounds() #ax.scatter(cat.ra,cat.dec) #ax.set_xlim([radec[1],radec[0]])#*[1.0002,0.9998]) #ax.set_ylim([radec[2],radec[3]])#*[0.985,1.015]) #ax.set_xlabel('$RA\ (deg)$',fontsize=18) #ax.set_ylabel('$Dec\ (deg)$',fontsize=18) fig.savefig(pngprefix + '-ccd.png', bbox_inches='tight') # Initialize the many-stamp QAplot ncols = 3 nrows = np.ceil(nstar / ncols).astype('int') inchperstamp = 2.0 fig = plt.figure(figsize=(inchperstamp * 3 * ncols, inchperstamp * nrows)) irow = 0 icol = 0 for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) mag = ps1star.median[ps1band[band]] # r-band ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ix, iy = int(xpos), int(ypos) # create a little tractor Image object around the star slc = (slice(max(iy - stampsize, 0), min(iy + stampsize + 1, H)), slice(max(ix - stampsize, 0), min(ix + stampsize + 1, W))) # The PSF model 'const2Psf' is the one used in DR1: a 2-component # Gaussian fit to PsfEx instantiated in the image center. tim = im.get_tractor_image(slc=slc, **get_tim_kwargs) stamp = tim.getImage() ivarstamp = tim.getInvvar() # Initialize a tractor PointSource from PS1 measurements flux = NanoMaggies.magToNanomaggies(mag) star = PointSource(RaDecPos(ra, dec), NanoMaggies(**{band: flux})) # Fit just the source RA,Dec,flux. tractor = Tractor([tim], [star]) tractor.freezeParam('images') print('2-component MOG:', tim.psf) tractor.printThawedParams() for step in range(50): dlnp, X, alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_mog = tractor.getModelImage(0) chi2_mog = -2.0 * tractor.getLogLikelihood() mag_mog = NanoMaggies.nanomaggiesToMag(star.brightness)[0] # Now change the PSF model to a pixelized PSF model from PsfEx instantiated # at this place in the image. psf = PixelizedPsfEx(im.psffn) tim.psf = psf.constantPsfAt(xpos, ypos) #print('PSF model:', tim.psf) #tractor.printThawedParams() for step in range(50): dlnp, X, alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_psfex = tractor.getModelImage(0) chi2_psfex = -2.0 * tractor.getLogLikelihood() mag_psfex = NanoMaggies.nanomaggiesToMag(star.brightness)[0] #mn, mx = np.percentile((stamp-model_psfex)[ivarstamp>0],[1,95]) sig = np.std((stamp - model_psfex)[ivarstamp > 0]) mn, mx = [-2.0 * sig, 5 * sig] # Generate a QAplot. if (istar > 0) and (istar % (ncols) == 0): irow = irow + 1 icol = 3 * istar - 3 * ncols * irow #print(istar, irow, icol, icol+1, icol+2) ax1 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol), aspect='equal') ax1.axis('off') #ax1.imshow(stamp, **tim.ima) ax1.imshow(stamp, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax1.text(0.1, 0.9, '{:2d}'.format(istar + 1), color='white', horizontalalignment='left', verticalalignment='top', transform=ax1.transAxes) ax2 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol + 1), aspect='equal') ax2.axis('off') #ax2.imshow(stamp-model_mog, **tim.ima) ax2.imshow(stamp - model_mog, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax2.text(0.1, 0.9, 'MoG', color='white', horizontalalignment='left', verticalalignment='top', transform=ax2.transAxes) ax2.text(0.08, 0.08, '{:.3f}'.format(mag_mog), color='white', horizontalalignment='left', verticalalignment='bottom', transform=ax2.transAxes) #ax2.set_title('{:.3f}, {:.2f}'.format(mag_psfex,chi2_psfex),fontsize=14) #ax2.set_title('{:.3f}, $\chi^{2}$={:.2f}'.format(mag_psfex,chi2_psfex)) ax3 = plt.subplot2grid((nrows, 3 * ncols), (irow, icol + 2), aspect='equal') ax3.axis('off') #ax3.imshow(stamp-model_psfex, **tim.ima) ax3.imshow(stamp - model_psfex, cmap='gray', interpolation='nearest', origin='lower', vmin=mn, vmax=mx) ax3.text(0.1, 0.9, 'PSFEx', color='white', horizontalalignment='left', verticalalignment='top', transform=ax3.transAxes) ax3.text(0.08, 0.08, '{:.3f}'.format(mag_psfex), color='white', horizontalalignment='left', verticalalignment='bottom', transform=ax3.transAxes) if istar == (nstar - 1): break fig.savefig(pngprefix + '-stargrid.png', bbox_inches='tight')
import pylab as plt import numpy as np from astrometry.util.plotutils import * from legacyanalysis.ps1cat import ps1cat from legacypipe.common import Decals from tractor import Image, PointSource, PixPos, NanoMaggies, Tractor ps = PlotSequence('rewcs') expnum, ccdname = 431109, 'N14' cat = ps1cat(expnum=expnum, ccdname=ccdname) stars = cat.get_stars() print len(stars), 'stars' decals = Decals() ccd = decals.find_ccds(expnum=expnum,ccdname=ccdname)[0] im = decals.get_image_object(ccd) wcs = im.get_wcs() tim = im.get_tractor_image(pixPsf=True, splinesky=True) margin = 15 ok,stars.xx,stars.yy = wcs.radec2pixelxy(stars.ra, stars.dec) stars.xx -= 1. stars.yy -= 1. W,H = wcs.get_width(), wcs.get_height() stars.ix = np.round(stars.xx).astype(int) stars.iy = np.round(stars.yy).astype(int) stars.cut((stars.ix >= margin) * (stars.ix < (W-margin)) * (stars.iy >= margin) * (stars.iy < (H-margin)))
def read_forcedphot_ccds(ccds, survey): ccds.mdiff = np.zeros(len(ccds)) ccds.mscatter = np.zeros(len(ccds)) Nap = 8 ccds.apdiff = np.zeros((len(ccds), Nap)) ccds.apscatter = np.zeros((len(ccds), Nap)) ccds.nforced = np.zeros(len(ccds), np.int16) ccds.nunmasked = np.zeros(len(ccds), np.int16) ccds.nmatched = np.zeros(len(ccds), np.int16) ccds.nps1 = np.zeros(len(ccds), np.int16) brickcache = {} FF = [] for iccd, ccd in enumerate(ccds): print('CCD', iccd, 'of', len(ccds)) F = fits_table(ccd.path) print(len(F), 'sources in', ccd.path) ccds.nforced[iccd] = len(F) # arr, have to match with brick sources to get RA,Dec. F.ra = np.zeros(len(F)) F.dec = np.zeros(len(F)) F.masked = np.zeros(len(F), bool) maglo, maghi = 14., 21. maxdmag = 1. F.mag = -2.5 * (np.log10(F.flux) - 9) F.cut((F.flux > 0) * (F.mag > maglo - maxdmag) * (F.mag < maghi + maxdmag)) print(len(F), 'sources between', (maglo - maxdmag), 'and', (maghi + maxdmag), 'mag') im = survey.get_image_object(ccd) print('Reading DQ image for', im) dq = im.read_dq() H, W = dq.shape ix = np.clip(np.round(F.x), 0, W - 1).astype(int) iy = np.clip(np.round(F.y), 0, H - 1).astype(int) F.mask = dq[iy, ix] print(np.sum(F.mask != 0), 'sources are masked') for brickname in np.unique(F.brickname): if not brickname in brickcache: brickcache[brickname] = fits_table( survey.find_file('tractor', brick=brickname)) T = brickcache[brickname] idtoindex = np.zeros(T.objid.max() + 1, int) - 1 idtoindex[T.objid] = np.arange(len(T)) I = np.flatnonzero(F.brickname == brickname) J = idtoindex[F.objid[I]] assert (np.all(J >= 0)) F.ra[I] = T.ra[J] F.dec[I] = T.dec[J] F.masked[I] = (T.decam_anymask[J, :].max(axis=1) > 0) #F.cut(F.masked == False) #print(len(F), 'not masked') print(np.sum(F.masked), 'masked in ANYMASK') ccds.nunmasked[iccd] = len(F) wcs = Tan(*[ float(x) for x in [ ccd.crval1, ccd.crval2, ccd.crpix1, ccd.crpix2, ccd.cd1_1, ccd.cd1_2, ccd.cd2_1, ccd.cd2_2, ccd.width, ccd.height ] ]) ps1 = ps1cat(ccdwcs=wcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') ccds.nps1[iccd] = len(stars) # Now cut to just *stars* with good colors stars.gicolor = stars.median[:, 0] - stars.median[:, 2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') stars.cut(np.minimum(stars.stdev[:, 1], stars.stdev[:, 2]) < 0.05) print(len(stars), 'PS1 stars with min stdev(r,i) < 0.05') I, J, d = match_radec(F.ra, F.dec, stars.ra, stars.dec, 1. / 3600.) print(len(I), 'matches') band = ccd.filter colorterm = ps1_to_decam(stars.median[J], band) F.cut(I) F.psmag = stars.median[J, ps1.ps1band[band]] + colorterm K = np.flatnonzero((F.psmag > maglo) * (F.psmag < maghi)) print(len(K), 'with mag', maglo, 'to', maghi) F.cut(K) K = np.flatnonzero(np.abs(F.mag - F.psmag) < maxdmag) print(len(K), 'with good mag matches (<', maxdmag, 'mag difference)') ccds.nmatched[iccd] = len(K) if len(K) == 0: continue F.cut(K) ccds.mdiff[iccd] = np.median(F.mag - F.psmag) ccds.mscatter[iccd] = (np.percentile(F.mag - F.psmag, 84) - np.percentile(F.mag - F.psmag, 16)) / 2. for i in range(Nap): apmag = -2.5 * (np.log10(F.apflux[:, i]) - 9) ccds.apdiff[iccd, i] = np.median(apmag - F.psmag) ccds.apscatter[iccd, i] = (np.percentile(apmag - F.psmag, 84) - np.percentile(apmag - F.psmag, 16)) / 2. #F.about() for c in [ 'apflux_ivar', 'brickid', 'flux_ivar', 'mjd', 'objid', 'fracflux', 'rchi2', 'x', 'y' ]: F.delete_column(c) F.expnum = np.zeros(len(F), np.int32) + ccd.expnum F.ccdname = np.array([ccd.ccdname] * len(F)) F.iforced = np.zeros(len(F), np.int32) + iccd FF.append(F) FF = merge_tables(FF) return FF bricks = survey.get_bricks_readonly() bricks = bricks[(bricks.ra > ralo) * (bricks.ra < rahi) * (bricks.dec > declo) * (bricks.dec < dechi)] print(len(bricks), 'bricks') I, = np.nonzero([ os.path.exists(survey.find_file('tractor', brick=b.brickname)) for b in bricks ]) print(len(I), 'bricks with catalogs') bricks.cut(I) for band in bands: bricks.set('diff_%s' % band, np.zeros(len(bricks), np.float32)) bricks.set('psfsize_%s' % band, np.zeros(len(bricks), np.float32)) diffs = dict([(b, []) for b in bands]) for ibrick, b in enumerate(bricks): fn = survey.find_file('tractor', brick=b.brickname) T = fits_table(fn) print(len(T), 'sources in', b.brickname) brickwcs = wcs_for_brick(b) ps1 = ps1cat(ccdwcs=brickwcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') # Now cut to just *stars* with good colors stars.gicolor = stars.median[:, 0] - stars.median[:, 2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') I, J, d = match_radec(T.ra, T.dec, stars.ra, stars.dec, 1. / 3600.) print(len(I), 'matches') for band in bands: bricks.get('psfsize_%s' % band)[ibrick] = np.median( T.decam_psfsize[:, survey.index_of_band(band)]) colorterm = ps1_to_decam(stars.median[J], band) psmag = stars.median[J, ps1.ps1band[band]] psmag += colorterm decflux = T.decam_flux[I, survey.index_of_band(band)] decmag = -2.5 * (np.log10(decflux) - 9) #K = np.flatnonzero((psmag > 14) * (psmag < 24)) #print(len(K), 'with mag 14 to 24') K = np.flatnonzero((psmag > 14) * (psmag < 21)) print(len(K), 'with mag 14 to 21') decmag = decmag[K] psmag = psmag[K] K = np.flatnonzero(np.abs(decmag - psmag) < 1) print(len(K), 'with good mag matches (< 1 mag difference)') decmag = decmag[K] psmag = psmag[K] if False and ibrick == 0: plt.clf() #plt.plot(psmag, decmag, 'b.') plt.plot(psmag, decmag - psmag, 'b.') plt.xlabel('PS1 mag') plt.xlabel('DECam - PS1 mag') plt.title('PS1 matches for %s band, brick %s' % (band, b.brickname)) ps.savefig() mdiff = np.median(decmag - psmag) diffs[band].append(mdiff) print('Median difference:', mdiff) bricks.get('diff_%s' % band)[ibrick] = mdiff for band in bands: d = diffs[band] plt.clf() plt.hist(d, bins=20, range=(-0.02, 0.02), histtype='step') plt.xlabel('Median mag difference per brick') plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) ps.savefig() print('Median differences in', band, 'band:', np.median(d)) if False: plt.clf() plt.hist(diffs['g'], bins=20, range=(-0.02, 0.02), histtype='step', color='g') plt.hist(diffs['r'], bins=20, range=(-0.02, 0.02), histtype='step', color='r') plt.hist(diffs['z'], bins=20, range=(-0.02, 0.02), histtype='step', color='m') plt.xlabel('Median mag difference per brick') plt.title('DR3 EDR PS1 vs DECaLS') ps.savefig() rr, dd = np.meshgrid(np.linspace(ralo, rahi, 400), np.linspace(declo, dechi, 400)) I, J, d = match_radec(rr.ravel(), dd.ravel(), bricks.ra, bricks.dec, 0.18, nearest=True) print(len(I), 'matches') for band in bands: plt.clf() dmag = np.zeros_like(rr) - 1. dmag.ravel()[I] = bricks.get('diff_%s' % band)[J] plt.imshow(dmag, interpolation='nearest', origin='lower', vmin=-0.01, vmax=0.01, cmap='hot', extent=(ralo, rahi, declo, dechi)) plt.colorbar() plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis([ralo, rahi, declo, dechi]) ps.savefig() plt.clf() # reuse 'dmag' map... dmag = np.zeros_like(rr) dmag.ravel()[I] = bricks.get('psfsize_%s' % band)[J] plt.imshow(dmag, interpolation='nearest', origin='lower', cmap='hot', extent=(ralo, rahi, declo, dechi)) plt.colorbar() plt.title('DR3 EDR: DECaLS PSF size: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis([ralo, rahi, declo, dechi]) ps.savefig() if False: for band in bands: plt.clf() plt.scatter(bricks.ra, bricks.dec, c=bricks.get('diff_%s' % band), vmin=-0.01, vmax=0.01, edgecolors='face', s=200) plt.colorbar() plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis('scaled') plt.axis([ralo, rahi, declo, dechi]) ps.savefig() plt.clf() plt.plot(bricks.psfsize_g, bricks.diff_g, 'g.') plt.plot(bricks.psfsize_r, bricks.diff_r, 'r.') plt.plot(bricks.psfsize_z, bricks.diff_z, 'm.') plt.xlabel('PSF size (arcsec)') plt.ylabel('DECaLS PSF - PS1 (mag)') plt.title('DR3 EDR') ps.savefig()
def star_profiles(ps): # Run an example CCD, 292604-N4, with fairly large difference vs PS1. # python -c "from astrometry.util.fits import *; T = merge_tables([fits_table('/project/projectdirs/desiproc/dr3/tractor/244/tractor-244%s.fits' % b) for b in ['2p065','4p065', '7p065']]); T.writeto('tst-cat.fits')" # python legacypipe/forced_photom_decam.py --save-data tst-data.fits --save-model tst-model.fits 292604 N4 tst-cat.fits tst-phot.fits # -> tst-{model,data,phot}.fits datafn = 'tst-data.fits' modfn = 'tst-model.fits' photfn = 'tst-phot.fits' catfn = 'tst-cat.fits' img = fitsio.read(datafn) mod = fitsio.read(modfn) phot = fits_table(photfn) cat = fits_table(catfn) print(len(phot), 'forced-photometry results') margin = 25 phot.cut((phot.x > 0 + margin) * (phot.x < 2046 - margin) * (phot.y > 0 + margin) * (phot.y < 4096 - margin)) print(len(phot), 'in bounds') cmap = dict([((b, o), i) for i, (b, o) in enumerate(zip(cat.brickname, cat.objid))]) I = np.array( [cmap.get((b, o), -1) for b, o in zip(phot.brickname, phot.objid)]) print(np.sum(I >= 0), 'forced-phot matched cat') phot.type = cat.type[I] wcs = Sip(datafn) phot.ra, phot.dec = wcs.pixelxy2radec(phot.x + 1, phot.y + 1) phot.cut(np.argsort(phot.flux)) phot.sn = phot.flux * np.sqrt(phot.flux_ivar) phot.cut(phot.sn > 5) print(len(phot), 'with S/N > 5') ps1 = ps1cat(ccdwcs=wcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') # Now cut to just *stars* with good colors stars.gicolor = stars.median[:, 0] - stars.median[:, 2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') stars.cut(np.minimum(stars.stdev[:, 1], stars.stdev[:, 2]) < 0.05) print(len(stars), 'PS1 stars with min stdev(r,i) < 0.05') I, J, d = match_radec(phot.ra, phot.dec, stars.ra, stars.dec, 1. / 3600.) print(len(I), 'matches') plt.clf() ha = dict(histtype='step', bins=20, range=(0, 100), normed=True) plt.hist(phot.flux, color='b', **ha) plt.hist(phot.flux[I], color='r', **ha) ps.savefig() plt.clf() plt.hist(phot.flux * np.sqrt(phot.flux_ivar), bins=100, range=(-10, 50)) plt.xlabel('Flux S/N') ps.savefig() K = np.argsort(phot.flux[I]) I = I[K] J = J[K] ix = np.round(phot.x).astype(int) iy = np.round(phot.y).astype(int) sz = 10 P = np.flatnonzero(phot.type == 'PSF ') print(len(P), 'PSFs') imed = len(P) / 2 i1 = int(len(P) * 0.75) i2 = int(len(P) * 0.25) N = 401 allmods = [] allimgs = [] for II, tt in [ #(I[:len(I)/2], 'faint matches to PS1'), #(I[len(I)/2:], 'bright matches to PS1'), #(P[i2: i2+N], '25th pct PSFs'), #(P[imed: imed+N], 'median PSFs'), #(P[i1: i1+N], '75th pct PSFs'), #(P[-25:], 'brightest PSFs'), (P[i2:imed], '2nd quartile of PSFs'), (P[imed:i1], '3rd quartile of PSFs'), #(P[:len(P)/2], 'faint half of PSFs'), #(P[len(P)/2:], 'bright half of PSFs'), ]: imgs = [] mods = [] shimgs = [] shmods = [] imgsum = modsum = 0 #plt.clf() for i in II: from astrometry.util.util import lanczos_shift_image dy = phot.y[i] - iy[i] dx = phot.x[i] - ix[i] sub = img[iy[i] - sz:iy[i] + sz + 1, ix[i] - sz:ix[i] + sz + 1] shimg = lanczos_shift_image(sub, -dx, -dy) sub = mod[iy[i] - sz:iy[i] + sz + 1, ix[i] - sz:ix[i] + sz + 1] shmod = lanczos_shift_image(sub, -dx, -dy) iyslice = img[iy[i], ix[i] - sz:ix[i] + sz + 1] myslice = mod[iy[i], ix[i] - sz:ix[i] + sz + 1] ixslice = img[iy[i] - sz:iy[i] + sz + 1, ix[i]] mxslice = mod[iy[i] - sz:iy[i] + sz + 1, ix[i]] mx = iyslice.max() # plt.plot(iyslice/mx, 'b-', alpha=0.1) # plt.plot(myslice/mx, 'r-', alpha=0.1) # plt.plot(ixslice/mx, 'b-', alpha=0.1) # plt.plot(mxslice/mx, 'r-', alpha=0.1) siyslice = shimg[sz, :] sixslice = shimg[:, sz] smyslice = shmod[sz, :] smxslice = shmod[:, sz] shimgs.append(siyslice / mx) shimgs.append(sixslice / mx) shmods.append(smyslice / mx) shmods.append(smxslice / mx) imgs.append(iyslice / mx) imgs.append(ixslice / mx) mods.append(myslice / mx) mods.append(mxslice / mx) imgsum = imgsum + ixslice + iyslice modsum = modsum + mxslice + myslice # plt.ylim(-0.1, 1.1) # plt.title(tt) # ps.savefig() mimg = np.median(np.array(imgs), axis=0) mmod = np.median(np.array(mods), axis=0) mshim = np.median(np.array(shimgs), axis=0) mshmo = np.median(np.array(shmods), axis=0) allmods.append(mshmo) allimgs.append(mshim) plt.clf() # plt.plot(mimg, 'b-') # plt.plot(mmod, 'r-') plt.plot(mshim, 'g-') plt.plot(mshmo, 'm-') plt.ylim(-0.1, 1.1) plt.title(tt + ': median; sums %.3f/%.3f' % (np.sum(mimg), np.sum(mmod))) ps.savefig() # plt.clf() # mx = imgsum.max() # plt.plot(imgsum/mx, 'b-') # plt.plot(modsum/mx, 'r-') # plt.ylim(-0.1, 1.1) # plt.title(tt + ': sum') # ps.savefig() plt.clf() plt.plot((mimg + 0.01) / (mmod + 0.01), 'k-') plt.plot((imgsum / mx + 0.01) / (modsum / mx + 0.01), 'g-') plt.plot((mshim + 0.01) / (mshmo + 0.01), 'm-') plt.ylabel('(img + 0.01) / (mod + 0.01)') plt.title(tt) ps.savefig() iq2, iq3 = allimgs mq2, mq3 = allmods plt.clf() plt.plot(iq2, 'r-') plt.plot(mq2, 'm-') plt.plot(iq3, 'b-') plt.plot(mq3, 'g-') plt.title('Q2 vs Q3') ps.savefig()
def star_profiles(ps): # Run an example CCD, 292604-N4, with fairly large difference vs PS1. # python -c "from astrometry.util.fits import *; T = merge_tables([fits_table('/project/projectdirs/desiproc/dr3/tractor/244/tractor-244%s.fits' % b) for b in ['2p065','4p065', '7p065']]); T.writeto('tst-cat.fits')" # python legacypipe/forced_photom_decam.py --save-data tst-data.fits --save-model tst-model.fits 292604 N4 tst-cat.fits tst-phot.fits # -> tst-{model,data,phot}.fits datafn = 'tst-data.fits' modfn = 'tst-model.fits' photfn = 'tst-phot.fits' catfn = 'tst-cat.fits' img = fitsio.read(datafn) mod = fitsio.read(modfn) phot = fits_table(photfn) cat = fits_table(catfn) print(len(phot), 'forced-photometry results') margin = 25 phot.cut((phot.x > 0+margin) * (phot.x < 2046-margin) * (phot.y > 0+margin) * (phot.y < 4096-margin)) print(len(phot), 'in bounds') cmap = dict([((b,o),i) for i,(b,o) in enumerate(zip(cat.brickname, cat.objid))]) I = np.array([cmap.get((b,o), -1) for b,o in zip(phot.brickname, phot.objid)]) print(np.sum(I >= 0), 'forced-phot matched cat') phot.type = cat.type[I] wcs = Sip(datafn) phot.ra,phot.dec = wcs.pixelxy2radec(phot.x+1, phot.y+1) phot.cut(np.argsort(phot.flux)) phot.sn = phot.flux * np.sqrt(phot.flux_ivar) phot.cut(phot.sn > 5) print(len(phot), 'with S/N > 5') ps1 = ps1cat(ccdwcs=wcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') # Now cut to just *stars* with good colors stars.gicolor = stars.median[:,0] - stars.median[:,2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') stars.cut(np.minimum(stars.stdev[:,1], stars.stdev[:,2]) < 0.05) print(len(stars), 'PS1 stars with min stdev(r,i) < 0.05') I,J,d = match_radec(phot.ra, phot.dec, stars.ra, stars.dec, 1./3600.) print(len(I), 'matches') plt.clf() ha=dict(histtype='step', bins=20, range=(0,100), normed=True) plt.hist(phot.flux, color='b', **ha) plt.hist(phot.flux[I], color='r', **ha) ps.savefig() plt.clf() plt.hist(phot.flux * np.sqrt(phot.flux_ivar), bins=100, range=(-10, 50)) plt.xlabel('Flux S/N') ps.savefig() K = np.argsort(phot.flux[I]) I = I[K] J = J[K] ix = np.round(phot.x).astype(int) iy = np.round(phot.y).astype(int) sz = 10 P = np.flatnonzero(phot.type == 'PSF ') print(len(P), 'PSFs') imed = len(P)/2 i1 = int(len(P) * 0.75) i2 = int(len(P) * 0.25) N = 401 allmods = [] allimgs = [] for II,tt in [#(I[:len(I)/2], 'faint matches to PS1'), #(I[len(I)/2:], 'bright matches to PS1'), #(P[i2: i2+N], '25th pct PSFs'), #(P[imed: imed+N], 'median PSFs'), #(P[i1: i1+N], '75th pct PSFs'), #(P[-25:], 'brightest PSFs'), (P[i2:imed], '2nd quartile of PSFs'), (P[imed:i1], '3rd quartile of PSFs'), #(P[:len(P)/2], 'faint half of PSFs'), #(P[len(P)/2:], 'bright half of PSFs'), ]: imgs = [] mods = [] shimgs = [] shmods = [] imgsum = modsum = 0 #plt.clf() for i in II: from astrometry.util.util import lanczos_shift_image dy = phot.y[i] - iy[i] dx = phot.x[i] - ix[i] sub = img[iy[i]-sz : iy[i]+sz+1, ix[i]-sz : ix[i]+sz+1] shimg = lanczos_shift_image(sub, -dx, -dy) sub = mod[iy[i]-sz : iy[i]+sz+1, ix[i]-sz : ix[i]+sz+1] shmod = lanczos_shift_image(sub, -dx, -dy) iyslice = img[iy[i], ix[i]-sz : ix[i]+sz+1] myslice = mod[iy[i], ix[i]-sz : ix[i]+sz+1] ixslice = img[iy[i]-sz : iy[i]+sz+1, ix[i]] mxslice = mod[iy[i]-sz : iy[i]+sz+1, ix[i]] mx = iyslice.max() # plt.plot(iyslice/mx, 'b-', alpha=0.1) # plt.plot(myslice/mx, 'r-', alpha=0.1) # plt.plot(ixslice/mx, 'b-', alpha=0.1) # plt.plot(mxslice/mx, 'r-', alpha=0.1) siyslice = shimg[sz, :] sixslice = shimg[:, sz] smyslice = shmod[sz, :] smxslice = shmod[:, sz] shimgs.append(siyslice/mx) shimgs.append(sixslice/mx) shmods.append(smyslice/mx) shmods.append(smxslice/mx) imgs.append(iyslice/mx) imgs.append(ixslice/mx) mods.append(myslice/mx) mods.append(mxslice/mx) imgsum = imgsum + ixslice + iyslice modsum = modsum + mxslice + myslice # plt.ylim(-0.1, 1.1) # plt.title(tt) # ps.savefig() mimg = np.median(np.array(imgs), axis=0) mmod = np.median(np.array(mods), axis=0) mshim = np.median(np.array(shimgs), axis=0) mshmo = np.median(np.array(shmods), axis=0) allmods.append(mshmo) allimgs.append(mshim) plt.clf() # plt.plot(mimg, 'b-') # plt.plot(mmod, 'r-') plt.plot(mshim, 'g-') plt.plot(mshmo, 'm-') plt.ylim(-0.1, 1.1) plt.title(tt + ': median; sums %.3f/%.3f' % (np.sum(mimg), np.sum(mmod))) ps.savefig() # plt.clf() # mx = imgsum.max() # plt.plot(imgsum/mx, 'b-') # plt.plot(modsum/mx, 'r-') # plt.ylim(-0.1, 1.1) # plt.title(tt + ': sum') # ps.savefig() plt.clf() plt.plot((mimg + 0.01) / (mmod + 0.01), 'k-') plt.plot((imgsum/mx + 0.01) / (modsum/mx + 0.01), 'g-') plt.plot((mshim + 0.01) / (mshmo + 0.01), 'm-') plt.ylabel('(img + 0.01) / (mod + 0.01)') plt.title(tt) ps.savefig() iq2,iq3 = allimgs mq2,mq3 = allmods plt.clf() plt.plot(iq2, 'r-') plt.plot(mq2, 'm-') plt.plot(iq3, 'b-') plt.plot(mq3, 'g-') plt.title('Q2 vs Q3') ps.savefig()
def read_forcedphot_ccds(ccds, survey): ccds.mdiff = np.zeros(len(ccds)) ccds.mscatter = np.zeros(len(ccds)) Nap = 8 ccds.apdiff = np.zeros((len(ccds), Nap)) ccds.apscatter = np.zeros((len(ccds), Nap)) ccds.nforced = np.zeros(len(ccds), np.int16) ccds.nunmasked = np.zeros(len(ccds), np.int16) ccds.nmatched = np.zeros(len(ccds), np.int16) ccds.nps1 = np.zeros(len(ccds), np.int16) brickcache = {} FF = [] for iccd,ccd in enumerate(ccds): print('CCD', iccd, 'of', len(ccds)) F = fits_table(ccd.path) print(len(F), 'sources in', ccd.path) ccds.nforced[iccd] = len(F) # arr, have to match with brick sources to get RA,Dec. F.ra = np.zeros(len(F)) F.dec = np.zeros(len(F)) F.masked = np.zeros(len(F), bool) maglo,maghi = 14.,21. maxdmag = 1. F.mag = -2.5 * (np.log10(F.flux) - 9) F.cut((F.flux > 0) * (F.mag > maglo-maxdmag) * (F.mag < maghi+maxdmag)) print(len(F), 'sources between', (maglo-maxdmag), 'and', (maghi+maxdmag), 'mag') im = survey.get_image_object(ccd) print('Reading DQ image for', im) dq = im.read_dq() H,W = dq.shape ix = np.clip(np.round(F.x), 0, W-1).astype(int) iy = np.clip(np.round(F.y), 0, H-1).astype(int) F.mask = dq[iy,ix] print(np.sum(F.mask != 0), 'sources are masked') for brickname in np.unique(F.brickname): if not brickname in brickcache: brickcache[brickname] = fits_table(survey.find_file('tractor', brick=brickname)) T = brickcache[brickname] idtoindex = np.zeros(T.objid.max()+1, int) - 1 idtoindex[T.objid] = np.arange(len(T)) I = np.flatnonzero(F.brickname == brickname) J = idtoindex[F.objid[I]] assert(np.all(J >= 0)) F.ra [I] = T.ra [J] F.dec[I] = T.dec[J] F.masked[I] = (T.decam_anymask[J,:].max(axis=1) > 0) #F.cut(F.masked == False) #print(len(F), 'not masked') print(np.sum(F.masked), 'masked in ANYMASK') ccds.nunmasked[iccd] = len(F) wcs = Tan(*[float(x) for x in [ccd.crval1, ccd.crval2, ccd.crpix1, ccd.crpix2, ccd.cd1_1, ccd.cd1_2, ccd.cd2_1, ccd.cd2_2, ccd.width, ccd.height]]) ps1 = ps1cat(ccdwcs=wcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') ccds.nps1[iccd] = len(stars) # Now cut to just *stars* with good colors stars.gicolor = stars.median[:,0] - stars.median[:,2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') stars.cut(np.minimum(stars.stdev[:,1], stars.stdev[:,2]) < 0.05) print(len(stars), 'PS1 stars with min stdev(r,i) < 0.05') I,J,d = match_radec(F.ra, F.dec, stars.ra, stars.dec, 1./3600.) print(len(I), 'matches') band = ccd.filter colorterm = ps1_to_decam(stars.median[J], band) F.cut(I) F.psmag = stars.median[J, ps1.ps1band[band]] + colorterm K = np.flatnonzero((F.psmag > maglo) * (F.psmag < maghi)) print(len(K), 'with mag', maglo, 'to', maghi) F.cut(K) K = np.flatnonzero(np.abs(F.mag - F.psmag) < maxdmag) print(len(K), 'with good mag matches (<', maxdmag, 'mag difference)') ccds.nmatched[iccd] = len(K) if len(K) == 0: continue F.cut(K) ccds.mdiff[iccd] = np.median(F.mag - F.psmag) ccds.mscatter[iccd] = (np.percentile(F.mag - F.psmag, 84) - np.percentile(F.mag - F.psmag, 16))/2. for i in range(Nap): apmag = -2.5 * (np.log10(F.apflux[:, i]) - 9) ccds.apdiff[iccd,i] = np.median(apmag - F.psmag) ccds.apscatter[iccd,i] = (np.percentile(apmag - F.psmag, 84) - np.percentile(apmag - F.psmag, 16))/2. #F.about() for c in ['apflux_ivar', 'brickid', 'flux_ivar', 'mjd', 'objid', 'fracflux', 'rchi2', 'x','y']: F.delete_column(c) F.expnum = np.zeros(len(F), np.int32) + ccd.expnum F.ccdname = np.array([ccd.ccdname] * len(F)) F.iforced = np.zeros(len(F), np.int32) + iccd FF.append(F) FF = merge_tables(FF) return FF bricks = survey.get_bricks_readonly() bricks = bricks[(bricks.ra > ralo) * (bricks.ra < rahi) * (bricks.dec > declo) * (bricks.dec < dechi)] print(len(bricks), 'bricks') I, = np.nonzero([os.path.exists(survey.find_file('tractor', brick=b.brickname)) for b in bricks]) print(len(I), 'bricks with catalogs') bricks.cut(I) for band in bands: bricks.set('diff_%s' % band, np.zeros(len(bricks), np.float32)) bricks.set('psfsize_%s' % band, np.zeros(len(bricks), np.float32)) diffs = dict([(b,[]) for b in bands]) for ibrick,b in enumerate(bricks): fn = survey.find_file('tractor', brick=b.brickname) T = fits_table(fn) print(len(T), 'sources in', b.brickname) brickwcs = wcs_for_brick(b) ps1 = ps1cat(ccdwcs=brickwcs) stars = ps1.get_stars() print(len(stars), 'PS1 sources') # Now cut to just *stars* with good colors stars.gicolor = stars.median[:,0] - stars.median[:,2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) print(len(stars), 'PS1 stars with good colors') I,J,d = match_radec(T.ra, T.dec, stars.ra, stars.dec, 1./3600.) print(len(I), 'matches') for band in bands: bricks.get('psfsize_%s' % band)[ibrick] = np.median( T.decam_psfsize[:, survey.index_of_band(band)]) colorterm = ps1_to_decam(stars.median[J], band) psmag = stars.median[J, ps1.ps1band[band]] psmag += colorterm decflux = T.decam_flux[I, survey.index_of_band(band)] decmag = -2.5 * (np.log10(decflux) - 9) #K = np.flatnonzero((psmag > 14) * (psmag < 24)) #print(len(K), 'with mag 14 to 24') K = np.flatnonzero((psmag > 14) * (psmag < 21)) print(len(K), 'with mag 14 to 21') decmag = decmag[K] psmag = psmag [K] K = np.flatnonzero(np.abs(decmag - psmag) < 1) print(len(K), 'with good mag matches (< 1 mag difference)') decmag = decmag[K] psmag = psmag [K] if False and ibrick == 0: plt.clf() #plt.plot(psmag, decmag, 'b.') plt.plot(psmag, decmag - psmag, 'b.') plt.xlabel('PS1 mag') plt.xlabel('DECam - PS1 mag') plt.title('PS1 matches for %s band, brick %s' % (band, b.brickname)) ps.savefig() mdiff = np.median(decmag - psmag) diffs[band].append(mdiff) print('Median difference:', mdiff) bricks.get('diff_%s' % band)[ibrick] = mdiff for band in bands: d = diffs[band] plt.clf() plt.hist(d, bins=20, range=(-0.02, 0.02), histtype='step') plt.xlabel('Median mag difference per brick') plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) ps.savefig() print('Median differences in', band, 'band:', np.median(d)) if False: plt.clf() plt.hist(diffs['g'], bins=20, range=(-0.02, 0.02), histtype='step', color='g') plt.hist(diffs['r'], bins=20, range=(-0.02, 0.02), histtype='step', color='r') plt.hist(diffs['z'], bins=20, range=(-0.02, 0.02), histtype='step', color='m') plt.xlabel('Median mag difference per brick') plt.title('DR3 EDR PS1 vs DECaLS') ps.savefig() rr,dd = np.meshgrid(np.linspace(ralo,rahi, 400), np.linspace(declo,dechi, 400)) I,J,d = match_radec(rr.ravel(), dd.ravel(), bricks.ra, bricks.dec, 0.18, nearest=True) print(len(I), 'matches') for band in bands: plt.clf() dmag = np.zeros_like(rr) - 1. dmag.ravel()[I] = bricks.get('diff_%s' % band)[J] plt.imshow(dmag, interpolation='nearest', origin='lower', vmin=-0.01, vmax=0.01, cmap='hot', extent=(ralo,rahi,declo,dechi)) plt.colorbar() plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis([ralo,rahi,declo,dechi]) ps.savefig() plt.clf() # reuse 'dmag' map... dmag = np.zeros_like(rr) dmag.ravel()[I] = bricks.get('psfsize_%s' % band)[J] plt.imshow(dmag, interpolation='nearest', origin='lower', cmap='hot', extent=(ralo,rahi,declo,dechi)) plt.colorbar() plt.title('DR3 EDR: DECaLS PSF size: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis([ralo,rahi,declo,dechi]) ps.savefig() if False: for band in bands: plt.clf() plt.scatter(bricks.ra, bricks.dec, c=bricks.get('diff_%s' % band), vmin=-0.01, vmax=0.01, edgecolors='face', s=200) plt.colorbar() plt.title('DR3 EDR PS1 vs DECaLS: %s band' % band) plt.xlabel('RA (deg)') plt.ylabel('Dec (deg)') plt.axis('scaled') plt.axis([ralo,rahi,declo,dechi]) ps.savefig() plt.clf() plt.plot(bricks.psfsize_g, bricks.diff_g, 'g.') plt.plot(bricks.psfsize_r, bricks.diff_r, 'r.') plt.plot(bricks.psfsize_z, bricks.diff_z, 'm.') plt.xlabel('PSF size (arcsec)') plt.ylabel('DECaLS PSF - PS1 (mag)') plt.title('DR3 EDR') ps.savefig()
def psf_residuals(expnum,ccdname,stampsize=35,nstar=30, magrange=(13,17),verbose=0): # Set the debugging level. if verbose==0: lvl = logging.INFO else: lvl = logging.DEBUG logging.basicConfig(level=lvl,format='%(message)s',stream=sys.stdout) pngprefix = 'qapsf-{}-{}'.format(expnum,ccdname) # Gather all the info we need about this CCD. decals = Decals() ccd = decals.find_ccds(expnum=expnum,ccdname=ccdname)[0] band = ccd.filter ps1band = dict(g=0,r=1,i=2,z=3,Y=4) print('Band {}'.format(band)) #scales = dict(g=0.0066, r=0.01, z=0.025) #vmin, vmax = np.arcsinh(-1), np.arcsinh(100) #print(scales[band]) im = DecamImage(decals,ccd) iminfo = im.get_image_info() H,W = iminfo['dims'] wcs = im.get_wcs() # Choose a uniformly selected subset of PS1 stars on this CCD. ps1 = ps1cat(ccdwcs=wcs) cat = ps1.get_stars(band=band,magrange=magrange) rand = np.random.RandomState(seed=expnum*ccd.ccdnum) these = rand.choice(len(cat)-1,nstar,replace=False) #these = rand.random_integers(0,len(cat)-1,nstar) cat = cat[these] cat = cat[np.argsort(cat.median[:,ps1band[band]])] # sort by magnitude #print(cat.nmag_ok) # Make a QAplot of the positions of all the stars. tim = im.get_tractor_image(const2psf=True) img = tim.getImage() #img = tim.getImage()/scales[band] fig = plt.figure(figsize=(5,10)) ax = fig.gca() ax.get_xaxis().get_major_formatter().set_useOffset(False) #ax.imshow(np.arcsinh(img),cmap='gray',interpolation='nearest', # origin='lower',vmin=vmax,vmax=vmax) ax.imshow(img, **tim.ima) ax.axis('off') ax.set_title('{}: {}/{} AM={:.2f} Seeing={:.3f}"'. format(band,expnum,ccdname,ccd.airmass,ccd.seeing)) for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ax.text(xpos,ypos,'{:2d}'.format(istar+1),color='red', horizontalalignment='left') circ = plt.Circle((xpos,ypos),radius=30,color='g',fill=False,lw=1) ax.add_patch(circ) #radec = wcs.radec_bounds() #ax.scatter(cat.ra,cat.dec) #ax.set_xlim([radec[1],radec[0]])#*[1.0002,0.9998]) #ax.set_ylim([radec[2],radec[3]])#*[0.985,1.015]) #ax.set_xlabel('$RA\ (deg)$',fontsize=18) #ax.set_ylabel('$Dec\ (deg)$',fontsize=18) fig.savefig(pngprefix+'-ccd.png',bbox_inches='tight') # Initialize the many-stamp QAplot ncols = 3 nrows = np.ceil(nstar/ncols).astype('int') inchperstamp = 2.0 fig = plt.figure(figsize=(inchperstamp*3*ncols,inchperstamp*nrows)) irow = 0 icol = 0 for istar, ps1star in enumerate(cat): ra, dec = (ps1star.ra, ps1star.dec) mag = ps1star.median[ps1band[band]] # r-band ok, xpos, ypos = wcs.radec2pixelxy(ra, dec) ix,iy = int(xpos), int(ypos) # create a little tractor Image object around the star slc = (slice(max(iy-stampsize, 0), min(iy+stampsize+1, H)), slice(max(ix-stampsize, 0), min(ix+stampsize+1, W))) # The PSF model 'const2Psf' is the one used in DR1: a 2-component # Gaussian fit to PsfEx instantiated in the image center. tim = im.get_tractor_image(slc=slc, const2psf=True) stamp = tim.getImage() ivarstamp = tim.getInvvar() # Initialize a tractor PointSource from PS1 measurements flux = NanoMaggies.magToNanomaggies(mag) star = PointSource(RaDecPos(ra,dec), NanoMaggies(**{band: flux})) # Fit just the source RA,Dec,flux. tractor = Tractor([tim], [star]) tractor.freezeParam('images') print('2-component MOG:', tim.psf) tractor.printThawedParams() for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_mog = tractor.getModelImage(0) chi2_mog = -2.0*tractor.getLogLikelihood() mag_mog = NanoMaggies.nanomaggiesToMag(star.brightness)[0] # Now change the PSF model to a pixelized PSF model from PsfEx instantiated # at this place in the image. psf = PixelizedPsfEx(im.psffn) tim.psf = psf.constantPsfAt(xpos, ypos) #print('PSF model:', tim.psf) #tractor.printThawedParams() for step in range(50): dlnp,X,alpha = tractor.optimize() if dlnp < 0.1: break print('Fit:', star) model_psfex = tractor.getModelImage(0) chi2_psfex = -2.0*tractor.getLogLikelihood() mag_psfex = NanoMaggies.nanomaggiesToMag(star.brightness)[0] #mn, mx = np.percentile((stamp-model_psfex)[ivarstamp>0],[1,95]) sig = np.std((stamp-model_psfex)[ivarstamp>0]) mn, mx = [-2.0*sig,5*sig] # Generate a QAplot. if (istar>0) and (istar%(ncols)==0): irow = irow+1 icol = 3*istar - 3*ncols*irow #print(istar, irow, icol, icol+1, icol+2) ax1 = plt.subplot2grid((nrows,3*ncols), (irow,icol), aspect='equal') ax1.axis('off') #ax1.imshow(stamp, **tim.ima) ax1.imshow(stamp,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax1.text(0.1,0.9,'{:2d}'.format(istar+1),color='white', horizontalalignment='left',verticalalignment='top', transform=ax1.transAxes) ax2 = plt.subplot2grid((nrows,3*ncols), (irow,icol+1), aspect='equal') ax2.axis('off') #ax2.imshow(stamp-model_mog, **tim.ima) ax2.imshow(stamp-model_mog,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax2.text(0.1,0.9,'MoG',color='white', horizontalalignment='left',verticalalignment='top', transform=ax2.transAxes) ax2.text(0.08,0.08,'{:.3f}'.format(mag_mog),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax2.transAxes) #ax2.set_title('{:.3f}, {:.2f}'.format(mag_psfex,chi2_psfex),fontsize=14) #ax2.set_title('{:.3f}, $\chi^{2}$={:.2f}'.format(mag_psfex,chi2_psfex)) ax3 = plt.subplot2grid((nrows,3*ncols), (irow,icol+2), aspect='equal') ax3.axis('off') #ax3.imshow(stamp-model_psfex, **tim.ima) ax3.imshow(stamp-model_psfex,cmap='gray',interpolation='nearest', origin='lower',vmin=mn,vmax=mx) ax3.text(0.1,0.9,'PSFEx',color='white', horizontalalignment='left',verticalalignment='top', transform=ax3.transAxes) ax3.text(0.08,0.08,'{:.3f}'.format(mag_psfex),color='white', horizontalalignment='left',verticalalignment='bottom', transform=ax3.transAxes) if istar==(nstar-1): break fig.savefig(pngprefix+'-stargrid.png',bbox_inches='tight')
def run(self, ps=None, focus=False, momentsize=5, n_fwhm=100): import pylab as plt from astrometry.util.plotutils import dimshow, plothist from legacyanalysis.ps1cat import ps1cat import photutils import tractor fn = self.fn ext = self.ext pixsc = self.pixscale F = fitsio.FITS(fn) primhdr = F[0].read_header() self.primhdr = primhdr img, hdr = self.read_raw(F, ext) self.hdr = hdr # pre sky-sub mn, mx = np.percentile(img.ravel(), [25, 98]) self.imgkwa = dict(vmin=mn, vmax=mx, cmap='gray') if self.debug and ps is not None: plt.clf() dimshow(img, **self.imgkwa) plt.title('Raw image') ps.savefig() M = 200 plt.clf() plt.subplot(2, 2, 1) dimshow(img[-M:, :M], ticks=False, **self.imgkwa) plt.subplot(2, 2, 2) dimshow(img[-M:, -M:], ticks=False, **self.imgkwa) plt.subplot(2, 2, 3) dimshow(img[:M, :M], ticks=False, **self.imgkwa) plt.subplot(2, 2, 4) dimshow(img[:M, -M:], ticks=False, **self.imgkwa) plt.suptitle('Raw image corners') ps.savefig() img, trim_x0, trim_y0 = self.trim_edges(img) fullH, fullW = img.shape if self.debug and ps is not None: plt.clf() dimshow(img, **self.imgkwa) plt.title('Trimmed image') ps.savefig() M = 200 plt.clf() plt.subplot(2, 2, 1) dimshow(img[-M:, :M], ticks=False, **self.imgkwa) plt.subplot(2, 2, 2) dimshow(img[-M:, -M:], ticks=False, **self.imgkwa) plt.subplot(2, 2, 3) dimshow(img[:M, :M], ticks=False, **self.imgkwa) plt.subplot(2, 2, 4) dimshow(img[:M, -M:], ticks=False, **self.imgkwa) plt.suptitle('Trimmed corners') ps.savefig() band = self.get_band(primhdr) exptime = primhdr['EXPTIME'] airmass = primhdr['AIRMASS'] print('Band', band, 'Exptime', exptime, 'Airmass', airmass) zp0 = self.nom.zeropoint(band, ext=self.ext) sky0 = self.nom.sky(band) kx = self.nom.fiducial_exptime(band).k_co # Find the sky value and noise level sky, sig1 = self.get_sky_and_sigma(img) sky1 = np.median(sky) skybr = -2.5 * np.log10(sky1 / pixsc / pixsc / exptime) + zp0 print('Sky brightness: %8.3f mag/arcsec^2' % skybr) print('Fiducial: %8.3f mag/arcsec^2' % sky0) img -= sky self.remove_sky_gradients(img) # Post sky-sub mn, mx = np.percentile(img.ravel(), [25, 98]) self.imgkwa = dict(vmin=mn, vmax=mx, cmap='gray') if ps is not None: plt.clf() dimshow(img, **self.imgkwa) plt.title('Sky-sub image: %s-%s' % (os.path.basename(fn).replace( '.fits', '').replace('.fz', ''), ext)) plt.colorbar() ps.savefig() # Read WCS header and compute boresight wcs = self.get_wcs(hdr) ra_ccd, dec_ccd = wcs.pixelxy2radec((fullW + 1) / 2., (fullH + 1) / 2.) # Detect stars psfsig = self.nominal_fwhm / 2.35 detsn = self.detection_map(img, sig1, psfsig, ps) slices = self.detect_sources(detsn, self.det_thresh, ps) print(len(slices), 'sources detected') if len(slices) < 20: slices = self.detect_sources(detsn, 10., ps) print(len(slices), 'sources detected') ndetected = len(slices) camera = primhdr.get('INSTRUME', '').strip().lower() # -> "decam" / "mosaic3" meas = dict(band=band, airmass=airmass, skybright=skybr, pixscale=pixsc, primhdr=primhdr, hdr=hdr, wcs=wcs, ra_ccd=ra_ccd, dec_ccd=dec_ccd, extension=ext, camera=camera, ndetected=ndetected) if ndetected == 0: print('NO SOURCES DETECTED') return meas xx, yy = [], [] fx, fy = [], [] mx2, my2, mxy = [], [], [] wmx2, wmy2, wmxy = [], [], [] # "Peak" region to centroid P = momentsize H, W = img.shape for i, slc in enumerate(slices): y0 = slc[0].start x0 = slc[1].start subimg = detsn[slc] imax = np.argmax(subimg) y, x = np.unravel_index(imax, subimg.shape) if (x0 + x) < P or (x0 + x) > W - 1 - P or (y0 + y) < P or ( y0 + y) > H - 1 - P: #print('Skipping edge peak', x0+x, y0+y) continue xx.append(x0 + x) yy.append(y0 + y) pkarea = detsn[y0 + y - P:y0 + y + P + 1, x0 + x - P:x0 + x + P + 1] from scipy.ndimage.measurements import center_of_mass cy, cx = center_of_mass(pkarea) #print('Center of mass', cx,cy) fx.append(x0 + x - P + cx) fy.append(y0 + y - P + cy) #print('x,y', x0+x, y0+y, 'vs centroid', x0+x-P+cx, y0+y-P+cy) ### HACK -- measure source ellipticity # go back to the image (not detection map) #subimg = img[slc] subimg = img[y0 + y - P:y0 + y + P + 1, x0 + x - P:x0 + x + P + 1].copy() subimg /= subimg.sum() ph, pw = subimg.shape px, py = np.meshgrid(np.arange(pw), np.arange(ph)) mx2.append(np.sum(subimg * (px - cx)**2)) my2.append(np.sum(subimg * (py - cy)**2)) mxy.append(np.sum(subimg * (px - cx) * (py - cy))) # Gaussian windowed version s = 1. wimg = subimg * np.exp(-0.5 * ((px - cx)**2 + (py - cy)**2) / s**2) wimg /= np.sum(wimg) wmx2.append(np.sum(wimg * (px - cx)**2)) wmy2.append(np.sum(wimg * (py - cy)**2)) wmxy.append(np.sum(wimg * (px - cx) * (py - cy))) mx2 = np.array(mx2) my2 = np.array(my2) mxy = np.array(mxy) wmx2 = np.array(wmx2) wmy2 = np.array(wmy2) wmxy = np.array(wmxy) # semi-major/minor axes and position angle theta = np.rad2deg(np.arctan2(2 * mxy, mx2 - my2) / 2.) theta = np.abs(theta) * np.sign(mxy) s = np.sqrt(((mx2 - my2) / 2.)**2 + mxy**2) a = np.sqrt((mx2 + my2) / 2. + s) b = np.sqrt((mx2 + my2) / 2. - s) ell = 1. - b / a wtheta = np.rad2deg(np.arctan2(2 * wmxy, wmx2 - wmy2) / 2.) wtheta = np.abs(wtheta) * np.sign(wmxy) ws = np.sqrt(((wmx2 - wmy2) / 2.)**2 + wmxy**2) wa = np.sqrt((wmx2 + wmy2) / 2. + ws) wb = np.sqrt((wmx2 + wmy2) / 2. - ws) well = 1. - wb / wa fx = np.array(fx) fy = np.array(fy) xx = np.array(xx) yy = np.array(yy) if ps is not None: plt.clf() dimshow(detsn, vmin=-3, vmax=50, cmap='gray') ax = plt.axis() plt.plot(fx, fy, 'go', mec='g', mfc='none', ms=10) plt.colorbar() plt.title('Detected sources') plt.axis(ax) ps.savefig() # show centroids too # plt.plot(xx, yy, 'go', mec='g', mfc='none', ms=8) # plt.axis(ax) # ps.savefig() # if ps is not None: # plt.clf() # plt.subplot(2,1,1) # mx = np.percentile(np.append(mx2,my2), 99) # ha = dict(histtype='step', range=(0,mx), bins=50) # plt.hist(mx2, color='b', label='mx2', **ha) # plt.hist(my2, color='r', label='my2', **ha) # plt.hist(mxy, color='g', label='mxy', **ha) # plt.legend() # plt.xlim(0,mx) # plt.subplot(2,1,2) # mx = np.percentile(np.append(wmx2,wmy2), 99) # ha = dict(histtype='step', range=(0,mx), bins=50, lw=3, alpha=0.3) # plt.hist(wmx2, color='b', label='wx2', **ha) # plt.hist(wmy2, color='r', label='wy2', **ha) # plt.hist(wmxy, color='g', label='wxy', **ha) # plt.legend() # plt.xlim(0,mx) # plt.suptitle('Source moments') # ps.savefig() # # #mx = np.percentile(np.abs(np.append(mxy,wmxy)), 99) # plt.clf() # plt.subplot(2,1,1) # ha = dict(histtype='step', range=(0,1), bins=50) # plt.hist(ell, color='g', label='ell', **ha) # plt.hist(well, color='g', lw=3, alpha=0.3, label='windowed ell', **ha) # plt.legend() # plt.subplot(2,1,2) # ha = dict(histtype='step', range=(-90,90), bins=50) # plt.hist(theta, color='g', label='theta', **ha) # plt.hist(wtheta, color='g', lw=3, alpha=0.3, # label='windowed theta', **ha) # plt.xlim(-90,90) # plt.legend() # plt.suptitle('Source ellipticities & angles') # ps.savefig() # Cut down to stars whose centroids are within 1 pixel of their peaks... #keep = (np.hypot(fx - xx, fy - yy) < 2) #print(sum(keep), 'of', len(keep), 'stars have centroids within 2 of peaks') #print('mean dx', np.mean(fx-xx), 'dy', np.mean(fy-yy), 'pixels') #assert(float(sum(keep)) / len(keep) > 0.9) #fx = fx[keep] #fy = fy[keep] apxy = np.vstack((fx, fy)).T ap = [] aprad_pix = self.aprad / pixsc aper = photutils.CircularAperture(apxy, aprad_pix) p = photutils.aperture_photometry(img, aper) apflux = p.field('aperture_sum') # Manual aperture photometry to get clipped means in sky annulus sky_inner_r, sky_outer_r = [r / pixsc for r in self.skyrad] sky = [] for xi, yi in zip(fx, fy): ix = int(np.round(xi)) iy = int(np.round(yi)) skyR = int(np.ceil(sky_outer_r)) xlo = max(0, ix - skyR) xhi = min(W, ix + skyR + 1) ylo = max(0, iy - skyR) yhi = min(H, iy + skyR + 1) xx, yy = np.meshgrid(np.arange(xlo, xhi), np.arange(ylo, yhi)) r2 = (xx - xi)**2 + (yy - yi)**2 inannulus = ((r2 >= sky_inner_r**2) * (r2 < sky_outer_r**2)) skypix = img[ylo:yhi, xlo:xhi][inannulus] #print('ylo,yhi, xlo,xhi', ylo,yhi, xlo,xhi, 'img subshape', img[ylo:yhi, xlo:xhi].shape, 'inann shape', inannulus.shape) s, nil = sensible_sigmaclip(skypix) sky.append(s) sky = np.array(sky) apflux2 = apflux - sky * (np.pi * aprad_pix**2) good = (apflux2 > 0) * (apflux > 0) apflux = apflux[good] apflux2 = apflux2[good] fx = fx[good] fy = fy[good] # Read in the PS1 catalog, and keep those within 0.25 deg of CCD center # and those with main sequence colors pscat = ps1cat(ccdwcs=wcs) stars = pscat.get_stars() #print('Got PS1 stars:', len(stars)) # we add the color term later ps1band = ps1cat.ps1band[band] stars.mag = stars.median[:, ps1band] ok, px, py = wcs.radec2pixelxy(stars.ra, stars.dec) px -= 1 py -= 1 if ps is not None: #kwa = dict(vmin=-3*sig1, vmax=50*sig1, cmap='gray') # Add to the 'detected sources' plot # mn,mx = np.percentile(img.ravel(), [50,99]) # kwa = dict(vmin=mn, vmax=mx, cmap='gray') # plt.clf() # dimshow(img, **kwa) ax = plt.axis() #plt.plot(fx, fy, 'go', mec='g', mfc='none', ms=10) K = np.argsort(stars.mag) plt.plot(px[K[:10]] - trim_x0, py[K[:10]] - trim_y0, 'o', mec='m', mfc='none', ms=12, mew=2) plt.plot(px[K[10:]] - trim_x0, py[K[10:]] - trim_y0, 'o', mec='m', mfc='none', ms=8) plt.axis(ax) plt.title('PS1 stars') #plt.colorbar() ps.savefig() # we trimmed the image before running detection; re-add that margin fullx = fx + trim_x0 fully = fy + trim_y0 # Match PS1 to our detections, find offset radius = self.maxshift / pixsc I, J, dx, dy = self.match_ps1_stars(px, py, fullx, fully, radius, stars) print(len(I), 'spatial matches with large radius', self.maxshift, 'arcsec,', radius, 'pix') bins = 2 * int(np.ceil(radius)) #print('Histogramming with', bins, 'bins') histo, xe, ye = np.histogram2d(dx, dy, bins=bins, range=((-radius, radius), (-radius, radius))) # smooth histogram before finding peak -- fuzzy matching from scipy.ndimage.filters import gaussian_filter histo = gaussian_filter(histo, 1.) histo = histo.T mx = np.argmax(histo) my, mx = np.unravel_index(mx, histo.shape) shiftx = (xe[mx] + xe[mx + 1]) / 2. shifty = (ye[my] + ye[my + 1]) / 2. if ps is not None: plt.clf() plothist(dx, dy, range=((-radius, radius), (-radius, radius))) plt.xlabel('dx (pixels)') plt.ylabel('dy (pixels)') plt.title('Offsets to PS1 stars') ax = plt.axis() plt.axhline(0, color='b') plt.axvline(0, color='b') plt.plot(shiftx, shifty, 'o', mec='m', mfc='none', ms=15, mew=3) plt.axis(ax) ps.savefig() # Refine with smaller search radius radius2 = 3. / pixsc I, J, dx, dy = self.match_ps1_stars(px, py, fullx + shiftx, fully + shifty, radius2, stars) print(len(J), 'matches to PS1 with small radius', 3, 'arcsec') shiftx2 = np.median(dx) shifty2 = np.median(dy) #print('Stage-1 shift', shiftx, shifty) #print('Stage-2 shift', shiftx2, shifty2) sx = shiftx + shiftx2 sy = shifty + shifty2 print('Astrometric shift (%.0f, %.0f) pixels' % (sx, sy)) if self.debug and ps is not None: plt.clf() plothist(dx, dy, range=((-radius2, radius2), (-radius2, radius2))) plt.xlabel('dx (pixels)') plt.ylabel('dy (pixels)') plt.title('Offsets to PS1 stars') ax = plt.axis() plt.axhline(0, color='b') plt.axvline(0, color='b') plt.plot(shiftx2, shifty2, 'o', mec='m', mfc='none', ms=15, mew=3) plt.axis(ax) ps.savefig() if ps is not None: mn, mx = np.percentile(img.ravel(), [50, 99]) kwa2 = dict(vmin=mn, vmax=mx, cmap='gray') plt.clf() dimshow(img, **kwa2) ax = plt.axis() plt.plot(fx[J], fy[J], 'go', mec='g', mfc='none', ms=10, mew=2) plt.plot(px[I] - sx - trim_x0, py[I] - sy - trim_y0, 'm+', ms=10, mew=2) plt.axis(ax) plt.title('Matched PS1 stars') plt.colorbar() ps.savefig() plt.clf() dimshow(img, **kwa2) ax = plt.axis() plt.plot(fx[J], fy[J], 'go', mec='g', mfc='none', ms=10, mew=2) K = np.argsort(stars.mag) plt.plot(px[K[:10]] - sx - trim_x0, py[K[:10]] - sy - trim_y0, 'o', mec='m', mfc='none', ms=12, mew=2) plt.plot(px[K[10:]] - sx - trim_x0, py[K[10:]] - sy - trim_y0, 'o', mec='m', mfc='none', ms=8, mew=2) plt.axis(ax) plt.title('All PS1 stars') plt.colorbar() ps.savefig() # Now cut to just *stars* with good colors stars.gicolor = stars.median[:, 0] - stars.median[:, 2] keep = (stars.gicolor > 0.4) * (stars.gicolor < 2.7) stars.cut(keep) if len(stars) == 0: print('No overlap or too few stars in PS1') return None px = px[keep] py = py[keep] # Re-match I, J, dx, dy = self.match_ps1_stars(px, py, fullx + sx, fully + sy, radius2, stars) print('Cut to', len(stars), 'PS1 stars with good colors; matched', len(I)) nmatched = len(I) meas.update(dx=sx, dy=sy, nmatched=nmatched) if focus: meas.update(img=img, hdr=hdr, primhdr=primhdr, fx=fx, fy=fy, px=px - trim_x0 - sx, py=py - trim_y0 - sy, sig1=sig1, stars=stars, moments=(mx2, my2, mxy, theta, a, b, ell), wmoments=(wmx2, wmy2, wmxy, wtheta, wa, wb, well), apflux=apflux, apflux2=apflux2) return meas #print('Mean astrometric shift (arcsec): delta-ra=', -np.mean(dy)*0.263, 'delta-dec=', np.mean(dx)*0.263) # Compute photometric offset compared to PS1 # as the PS1 minus observed mags colorterm = self.colorterm_ps1_to_observed(stars.median, band) stars.mag += colorterm ps1mag = stars.mag[I] if False and ps is not None: plt.clf() plt.semilogy(ps1mag, apflux2[J], 'b.') plt.xlabel('PS1 mag') plt.ylabel('DECam ap flux (with sky sub)') ps.savefig() plt.clf() plt.semilogy(ps1mag, apflux[J], 'b.') plt.xlabel('PS1 mag') plt.ylabel('DECam ap flux (no sky sub)') ps.savefig() apmag2 = -2.5 * np.log10(apflux2) + zp0 + 2.5 * np.log10(exptime) apmag = -2.5 * np.log10(apflux) + zp0 + 2.5 * np.log10(exptime) if ps is not None: plt.clf() plt.plot(ps1mag, apmag[J], 'b.', label='No sky sub') plt.plot(ps1mag, apmag2[J], 'r.', label='Sky sub') # ax = plt.axis() # mn = min(ax[0], ax[2]) # mx = max(ax[1], ax[3]) # plt.plot([mn,mx], [mn,mx], 'k-', alpha=0.1) # plt.axis(ax) plt.xlabel('PS1 mag') plt.ylabel('DECam ap mag') plt.legend(loc='upper left') plt.title('Zeropoint') ps.savefig() dm = ps1mag - apmag[J] dmag, dsig = sensible_sigmaclip(dm, nsigma=2.5) print('Mag offset: %8.3f' % dmag) print('Scatter: %8.3f' % dsig) if not np.isfinite(dmag) or not np.isfinite(dsig): print('FAILED TO GET ZEROPOINT!') meas.update(zp=None) return meas from scipy.stats import sigmaclip goodpix, lo, hi = sigmaclip(dm, low=3, high=3) dmagmed = np.median(goodpix) print(len(goodpix), 'stars used for zeropoint median') print('Using median zeropoint:') zp_med = zp0 + dmagmed trans_med = 10.**(-0.4 * (zp0 - zp_med - kx * (airmass - 1.))) print('Zeropoint %6.3f' % zp_med) print('Transparency: %.3f' % trans_med) dm = ps1mag - apmag2[J] dmag2, dsig2 = sensible_sigmaclip(dm, nsigma=2.5) #print('Sky-sub mag offset', dmag2) #print('Scatter', dsig2) if ps is not None: plt.clf() plt.plot(ps1mag, apmag[J] + dmag - ps1mag, 'b.', label='No sky sub') plt.plot(ps1mag, apmag2[J] + dmag2 - ps1mag, 'r.', label='Sky sub') plt.xlabel('PS1 mag') plt.ylabel('DECam ap mag - PS1 mag') plt.legend(loc='upper left') plt.ylim(-0.25, 0.25) plt.axhline(0, color='k', alpha=0.25) plt.title('Zeropoint') ps.savefig() zp_obs = zp0 + dmag transparency = 10.**(-0.4 * (zp0 - zp_obs - kx * (airmass - 1.))) meas.update(zp=zp_obs, transparency=transparency) print('Zeropoint %6.3f' % zp_obs) print('Fiducial %6.3f' % zp0) print('Transparency: %.3f' % transparency) # print('Using sky-subtracted values:') # zp_sky = zp0 + dmag2 # trans_sky = 10.**(-0.4 * (zp0 - zp_sky - kx * (airmass - 1.))) # print('Zeropoint %6.3f' % zp_sky) # print('Transparency: %.3f' % trans_sky) fwhms = [] psf_r = 15 if n_fwhm not in [0, None]: Jf = J[:n_fwhm] for i, (xi, yi, fluxi) in enumerate(zip(fx[Jf], fy[Jf], apflux[Jf])): #print('Fitting source', i, 'of', len(Jf)) ix = int(np.round(xi)) iy = int(np.round(yi)) xlo = max(0, ix - psf_r) xhi = min(W, ix + psf_r + 1) ylo = max(0, iy - psf_r) yhi = min(H, iy + psf_r + 1) xx, yy = np.meshgrid(np.arange(xlo, xhi), np.arange(ylo, yhi)) r2 = (xx - xi)**2 + (yy - yi)**2 keep = (r2 < psf_r**2) pix = img[ylo:yhi, xlo:xhi].copy() ie = np.zeros_like(pix) ie[keep] = 1. / sig1 #print('fitting source at', ix,iy) #print('number of active pixels:', np.sum(ie > 0), 'shape', ie.shape) psf = tractor.NCircularGaussianPSF([4.], [1.]) tim = tractor.Image(data=pix, inverr=ie, psf=psf) src = tractor.PointSource(tractor.PixPos(xi - xlo, yi - ylo), tractor.Flux(fluxi)) tr = tractor.Tractor([tim], [src]) #print('Posterior before prior:', tr.getLogProb()) src.pos.addGaussianPrior('x', 0., 1.) #print('Posterior after prior:', tr.getLogProb()) doplot = (i < 5) * (ps is not None) if doplot: mod0 = tr.getModelImage(0) tim.freezeAllBut('psf') psf.freezeAllBut('sigmas') # print('Optimizing params:') # tr.printThawedParams() #print('Parameter step sizes:', tr.getStepSizes()) optargs = dict(priors=False, shared_params=False) for step in range(50): dlnp, x, alpha = tr.optimize(**optargs) #print('dlnp', dlnp) #print('src', src) #print('psf', psf) if dlnp == 0: break # Now fit only the PSF size tr.freezeParam('catalog') # print('Optimizing params:') # tr.printThawedParams() for step in range(50): dlnp, x, alpha = tr.optimize(**optargs) #print('dlnp', dlnp) #print('src', src) #print('psf', psf) if dlnp == 0: break fwhms.append(psf.sigmas[0] * 2.35 * pixsc) if doplot: mod1 = tr.getModelImage(0) chi1 = tr.getChiImage(0) plt.clf() plt.subplot(2, 2, 1) plt.title('Image') dimshow(pix, **self.imgkwa) plt.subplot(2, 2, 2) plt.title('Initial model') dimshow(mod0, **self.imgkwa) plt.subplot(2, 2, 3) plt.title('Final model') dimshow(mod1, **self.imgkwa) plt.subplot(2, 2, 4) plt.title('Final chi') dimshow(chi1, vmin=-10, vmax=10) plt.suptitle('PSF fit') ps.savefig() fwhms = np.array(fwhms) fwhm = np.median(fwhms) print('Median FWHM: %.3f' % np.median(fwhms)) meas.update(seeing=fwhm) if False and ps is not None: lo, hi = np.percentile(fwhms, [5, 95]) lo -= 0.1 hi += 0.1 plt.clf() plt.hist(fwhms, 25, range=(lo, hi), histtype='step', color='b') plt.xlabel('FWHM (arcsec)') ps.savefig() if ps is not None: plt.clf() for i, (xi, yi) in enumerate(zip(fx[J], fy[J])[:50]): ix = int(np.round(xi)) iy = int(np.round(yi)) xlo = max(0, ix - psf_r) xhi = min(W, ix + psf_r + 1) ylo = max(0, iy - psf_r) yhi = min(H, iy + psf_r + 1) pix = img[ylo:yhi, xlo:xhi] slc = pix[iy - ylo, :].copy() slc /= np.sum(slc) p1 = plt.plot(slc, 'b-', alpha=0.2) slc = pix[:, ix - xlo].copy() slc /= np.sum(slc) p2 = plt.plot(slc, 'r-', alpha=0.2) ph, pw = pix.shape cx, cy = pw / 2, ph / 2 if i == 0: xx = np.linspace(0, pw, 300) dx = xx[1] - xx[0] sig = fwhm / pixsc / 2.35 yy = np.exp(-0.5 * (xx - cx)**2 / sig**2) # * np.sum(pix) yy /= (np.sum(yy) * dx) p3 = plt.plot(xx, yy, 'k-', zorder=20) #plt.ylim(-0.2, 1.0) plt.legend([p1[0], p2[0], p3[0]], ['image slice (y)', 'image slice (x)', 'fit']) plt.title('PSF fit') ps.savefig() return meas
if __name__ == '__main__': expnum, ccdname = 292604, 'N4' survey = LegacySurveyData(output_dir='onechip-civ') ccds = survey.find_ccds(expnum=expnum, ccdname=ccdname) print('Found', len(ccds), 'CCD') # HACK -- cut to JUST that ccd. survey.ccds = ccds ccd = ccds[0] im = survey.get_image_object(ccd) wcs = survey.get_approx_wcs(ccd) rc,dc = wcs.radec_center() # Read PS-1 catalog to find out which blobs to process. ps1 = ps1cat(ccdwcs=wcs) stars = ps1.get_stars() print('Read', len(stars), 'PS1 stars') brickname = ('custom-%06i%s%05i' % (int(1000*rc), 'm' if dc < 0 else 'p', int(1000*np.abs(dc)))) outfn = survey.find_file('tractor', brick=brickname, output=True) print('Output catalog:', outfn) if not os.path.exists(outfn): run_brick(None, survey, radec=(rc,dc), width=ccd.height, height=ccd.width, # CCDs are rotated bands=im.band, wise=False, blobradec=zip(stars.ra, stars.dec),