def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted =  mi.Factory(mi, True)
        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox(afwImage.PARENT)
            if bbox.contains(afwGeom.PointI(int(xc), int(yc))):
                try:
                    measAlg.subtractPsf(psf, subtracted, xc, yc)
                except:
                    pass
        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        np.sqrt(var.getArray(), var.getArray()) # inplace sqrt
        chi /= var

        if display:
            ds9.mtv(subtracted, title="Subtracted", frame=1)
            ds9.mtv(chi, title="Chi", frame=2)
            xc, yc = exposure.getWidth()//2, exposure.getHeight()//2
            ds9.mtv(psf.computeImage(afwGeom.Point2D(xc, yc)), title="Psf %.1f,%.1f" % (xc, yc), frame=3)

        chi_min, chi_max = np.min(chi.getImage().getArray()),  np.max(chi.getImage().getArray())
        if False:
            print chi_min, chi_max

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(   chi_max,  chi_lim)
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)
        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox(afwImage.PARENT)
            if bbox.contains(afwGeom.PointI(int(xc), int(yc))):
                try:
                    measAlg.subtractPsf(psf, subtracted, xc, yc)
                except:
                    pass
        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        np.sqrt(var.getArray(), var.getArray())  # inplace sqrt
        chi /= var

        if display:
            ds9.mtv(subtracted, title="Subtracted", frame=1)
            ds9.mtv(chi, title="Chi", frame=2)
            xc, yc = exposure.getWidth() // 2, exposure.getHeight() // 2
            ds9.mtv(psf.computeImage(afwGeom.Point2D(xc, yc)),
                    title="Psf %.1f,%.1f" % (xc, yc),
                    frame=3)

        chi_min, chi_max = np.min(chi.getImage().getArray()), np.max(
            chi.getImage().getArray())
        if False:
            print(chi_min, chi_max)

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(chi_max, chi_lim)
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog."""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)

        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox()
            if bbox.contains(lsst.geom.PointI(int(xc), int(yc))):
                measAlg.subtractPsf(psf, subtracted, xc, yc)

        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        np.sqrt(var.getArray(), var.getArray())  # inplace sqrt
        chi /= var

        if display:
            afwDisplay.Display(frame=0).mtv(subtracted, title=self._testMethodName + ": Subtracted")
            afwDisplay.Display(frame=2).mtv(chi, title=self._testMethodName + ": Chi")
            afwDisplay.Display(frame=3).mtv(psf.computeImage(lsst.geom.Point2D(xc, yc)),
                                            title=self._testMethodName + ": Psf")
            afwDisplay.Display(frame=4).mtv(mi, title=self._testMethodName + ": orig")
            kern = psf.getKernel()
            kimg = afwImage.ImageD(kern.getWidth(), kern.getHeight())
            kern.computeImage(kimg, True, xc, yc)
            afwDisplay.Display(frame=5).mtv(kimg, title=self._testMethodName + ": kernel")

        chi_min, chi_max = np.min(chi.getImage().getArray()), np.max(chi.getImage().getArray())
        if False:
            print(chi_min, chi_max)

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(chi_max, chi_lim)
Ejemplo n.º 4
0
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)
        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox(afwImage.PARENT)
            if bbox.contains(afwGeom.PointI(int(xc), int(yc))):
                try:
                    measAlg.subtractPsf(psf, subtracted, xc, yc)
                except:
                    pass
        self.subtracted = subtracted
Ejemplo n.º 5
0
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)
        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox(afwImage.PARENT)
            if bbox.contains(afwGeom.PointI(int(xc), int(yc))):
                try:
                    measAlg.subtractPsf(psf, subtracted, xc, yc)
                except:
                    pass
        self.subtracted = subtracted
Ejemplo n.º 6
0
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog."""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)

        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox()
            if bbox.contains(lsst.geom.PointI(int(xc), int(yc))):
                measAlg.subtractPsf(psf, subtracted, xc, yc)

        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        np.sqrt(var.getArray(), var.getArray())  # inplace sqrt
        chi /= var

        if display:
            afwDisplay.Display(frame=0).mtv(subtracted,
                                            title=self._testMethodName +
                                            ": Subtracted")
            afwDisplay.Display(frame=2).mtv(chi,
                                            title=self._testMethodName +
                                            ": Chi")
            afwDisplay.Display(frame=3).mtv(
                psf.computeImage(lsst.geom.Point2D(xc, yc)),
                title=self._testMethodName + ": Psf")
            afwDisplay.Display(frame=4).mtv(mi,
                                            title=self._testMethodName +
                                            ": orig")
            kern = psf.getKernel()
            kimg = afwImage.ImageD(kern.getWidth(), kern.getHeight())
            kern.computeImage(kimg, True, xc, yc)
            afwDisplay.Display(frame=5).mtv(kimg,
                                            title=self._testMethodName +
                                            ": kernel")

        chi_min, chi_max = np.min(chi.getImage().getArray()), np.max(
            chi.getImage().getArray())
        if False:
            print(chi_min, chi_max)

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(chi_max, chi_lim)
Ejemplo n.º 7
0
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)

        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox()
            if bbox.contains(afwGeom.PointI(int(xc), int(yc))):
                try:
                    measAlg.subtractPsf(psf, subtracted, xc, yc)
                except:
                    pass

        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        numpy.sqrt(var.getArray(), var.getArray())  # inplace sqrt
        chi /= var

        if display:
            ds9.mtv(subtracted, title="Subtracted", frame=1)
            ds9.mtv(chi, title="Chi", frame=2)
            ds9.mtv(psf.computeImage(afwGeom.Point2D(xc, yc)),
                    title="Psf",
                    frame=3)
            ds9.mtv(mi, frame=4, title="orig")
            kern = psf.getKernel()
            kimg = afwImage.ImageD(kern.getWidth(), kern.getHeight())
            kern.computeImage(kimg, True, xc, yc)
            ds9.mtv(kimg, title="kernel", frame=5)

        chi_min, chi_max = numpy.min(chi.getImage().getArray()), numpy.max(
            chi.getImage().getArray())
        if False:
            print chi_min, chi_max

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(chi_max, chi_lim)
Ejemplo n.º 8
0
    def subtractStars(self, exposure, catalog, chi_lim=-1):
        """Subtract the exposure's PSF from all the sources in catalog"""
        mi, psf = exposure.getMaskedImage(), exposure.getPsf()

        subtracted = mi.Factory(mi, True)
        for s in catalog:
            xc, yc = s.getX(), s.getY()
            bbox = subtracted.getBBox(afwImage.PARENT)
            if bbox.contains(geom.PointI(int(xc), int(yc))):
                measAlg.subtractPsf(psf, subtracted, xc, yc)
        chi = subtracted.Factory(subtracted, True)
        var = subtracted.getVariance()
        np.sqrt(var.getArray(), var.getArray())  # inplace sqrt
        chi /= var

        chi_min = np.min(chi.getImage().getArray())
        chi_max = np.max(chi.getImage().getArray())
        print(chi_min, chi_max)

        if chi_lim > 0:
            self.assertGreater(chi_min, -chi_lim)
            self.assertLess(chi_max, chi_lim)
Ejemplo n.º 9
0
    def testPsfCandidate(self):

        detector = self.detector

        # make an exposure
        print "Planting"
        psfSigma = 1.5
        exposDist, nGoodDist, expos0, nGood0 = plantSources(self.x0, self.y0,
                                                            self.nx, self.ny,
                                                            self.sky, self.nObj, psfSigma, detector)


        # set the psf
        kwid = 21
        psf = measAlg.SingleGaussianPsf(kwid, kwid, psfSigma)
        exposDist.setPsf(psf)
        exposDist.setDetector(detector)


        # detect
        print "detection"
        sourceList       = self.detectAndMeasure(exposDist)

        # select psf stars
        print "PSF selection"
        psfCandidateList = self.starSelector.run(exposDist, sourceList).psfCandidates

        # determine the PSF
        print "PSF determination"
        metadata = dafBase.PropertyList()
        t0 = time.time()
        psf, cellSet = self.psfDeterminer.determinePsf(exposDist, psfCandidateList, metadata)
        print "... determination time: ", time.time() - t0
        print "PSF kernel width: ", psf.getKernel().getWidth()

        #######################################################################
        # try to subtract off the stars and check the residuals

        imgOrig = exposDist.getMaskedImage().getImage().getArray()
        maxFlux = imgOrig.max()


        ############
        # first try it with no distortion in the psf
        exposDist.setDetector(self.flatDetector)

        print "uncorrected subtraction"
        subImg = afwImage.MaskedImageF(exposDist.getMaskedImage(), True)
        for s in sourceList:
            x, y = s.getX(), s.getY()
            measAlg.subtractPsf(psf, subImg, x, y)

        if display:
            settings = {'scale': 'minmax', 'zoom':"to fit", 'mask':'transparency 80'}
            ds9.mtv(exposDist, frame=1, title="full", settings=settings)
            ds9.mtv(subImg, frame=2, title="subtracted", settings=settings)

        img = subImg.getImage().getArray()
        norm = img/math.sqrt(maxFlux)

        smin0, smax0, srms0 = norm.min(), norm.max(), norm.std()

        print "min:", smin0, "max: ", smax0, "rms: ", srms0


        if False:
            # This section has been disabled as distortion was removed from PsfCandidate and Psf;
            # it will be reintroduced in the future with a different API, at which point this
            # test code should be re-enabled.

            ##############
            # try it with the correct distortion in the psf
            exposDist.setDetector(self.detector)

            print "corrected subtraction"
            subImg = afwImage.MaskedImageF(exposDist.getMaskedImage(), True)
            for s in sourceList:
                x, y = s.getX(), s.getY()
                measAlg.subtractPsf(psf, subImg, x, y)

            if display:
                settings = {'scale': 'minmax', 'zoom':"to fit", 'mask':'transparency 80'}
                ds9.mtv(exposDist, frame=1, title="full", settings=settings)
                ds9.mtv(subImg, frame=2, title="subtracted", settings=settings)

            img = subImg.getImage().getArray()
            norm = img/math.sqrt(maxFlux)

            smin, smax, srms = norm.min(), norm.max(), norm.std()

            # with proper distortion, residuals should be < 4sigma (even for 512x512 pixels)
            print "min:", smin, "max: ", smax, "rms: ", srms

            # the distrib of residuals should be tighter
            self.assertLess(smin0, smin)
            self.assertGreater(smax0, smax)
            self.assertGreater(srms0, srms)
Ejemplo n.º 10
0
    def testGetPcaKernel(self):
        """Convert our cellSet to a LinearCombinationKernel"""

        nEigenComponents = 2
        spatialOrder = 1
        kernelSize = 21
        nStarPerCell = 2
        nStarPerCellSpatialFit = 2
        tolerance = 1e-5

        if display:
            ds9.mtv(self.mi, frame=0)
            #
            # Show the candidates we're using
            #
            for cell in self.cellSet.getCellList():
                i = 0
                for cand in cell:
                    i += 1
                    source = cand.getSource()

                    xc, yc = source.getXAstrom() - self.mi.getX0(
                    ), source.getYAstrom() - self.mi.getY0()
                    if i <= nStarPerCell:
                        ds9.dot("o", xc, yc, ctype=ds9.GREEN)
                    else:
                        ds9.dot("o", xc, yc, ctype=ds9.YELLOW)

        pair = algorithms.createKernelFromPsfCandidates(
            self.cellSet, self.exposure.getDimensions(),
            self.exposure.getXY0(), nEigenComponents, spatialOrder, kernelSize,
            nStarPerCell)

        kernel, eigenValues = pair[0], pair[1]
        del pair

        print("lambda", " ".join(["%g" % l for l in eigenValues]))

        pair = algorithms.fitSpatialKernelFromPsfCandidates(
            kernel, self.cellSet, nStarPerCellSpatialFit, tolerance)
        status, chi2 = pair[0], pair[1]
        del pair
        print("Spatial fit: %s chi^2 = %.2g" % (status, chi2))

        psf = roundTripPsf(5, algorithms.PcaPsf(kernel))  # Hurrah!

        self.assertIsNotNone(psf.getKernel())

        self.checkTablePersistence(psf)

        if display:
            # print psf.getKernel().toString()

            eImages = []
            for k in psf.getKernel().getKernelList():
                im = afwImage.ImageD(k.getDimensions())
                k.computeImage(im, False)
                eImages.append(im)

            mos = displayUtils.Mosaic()
            frame = 3
            ds9.mtv(mos.makeMosaic(eImages), frame=frame)
            ds9.dot("Eigen Images", 0, 0, frame=frame)
            #
            # Make a mosaic of PSF candidates
            #
            stamps = []
            stampInfo = []

            for cell in self.cellSet.getCellList():
                for cand in cell:
                    s = cand.getSource()

                    im = cand.getMaskedImage()

                    stamps.append(im)
                    stampInfo.append("[%d 0x%x]" %
                                     (s.getId(), s.getFlagForDetection()))

                    mos = displayUtils.Mosaic()
            frame = 1
            ds9.mtv(mos.makeMosaic(stamps), frame=frame, lowOrderBits=True)
            for i in range(len(stampInfo)):
                ds9.dot(stampInfo[i],
                        mos.getBBox(i).getX0(),
                        mos.getBBox(i).getY0(),
                        frame=frame,
                        ctype=ds9.RED)

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5) * self.mi.getWidth() / nx)
                        y = int((iy + 0.5) * self.mi.getHeight() / ny)

                        im = psf.getImage(x, y)
                        psfImages.append(im.Factory(im, True))
                        labels.append("PSF(%d,%d)" % (int(x), int(y)))

                        if True:
                            print((x, y, "PSF parameters:",
                                   psf.getKernel().getKernelParameters()))
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20), (60, 210), (20, 210)]:

                    im = psf.computeImage(afwGeom.PointD(x, y))
                    psfImages.append(im.Factory(im, True))
                    labels.append("PSF(%d,%d)" % (int(x), int(y)))

                    if True:
                        print(x, y, "PSF parameters:",
                              psf.getKernel().getKernelParameters())

            frame = 2
            mos.makeMosaic(psfImages, frame=frame, mode=nx)
            mos.drawLabels(labels, frame=frame)

        if display:

            ds9.mtv(self.mi, frame=0)

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5) * self.mi.getWidth() / nx)
                        y = int((iy + 0.5) * self.mi.getHeight() / ny)

                        algorithms.subtractPsf(psf, self.mi, x, y)
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20), (60, 210), (20, 210)]:

                    if False:  # Test subtraction with non-centered psfs
                        x += 0.5
                        y -= 0.5

                    #algorithms.subtractPsf(psf, self.mi, x, y)

            ds9.mtv(self.mi, frame=1)
Ejemplo n.º 11
0
    def testGetPcaKernel(self):
        """Convert our cellSet to a LinearCombinationKernel"""

        nEigenComponents = 2
        spatialOrder  =    1
        kernelSize =      21
        nStarPerCell =     2
        nStarPerCellSpatialFit = 2
        tolerance =     1e-5

        if display:
            ds9.mtv(self.mi, frame=0)
            #
            # Show the candidates we're using
            #
            for cell in self.cellSet.getCellList():
                i = 0
                for cand in cell:
                    i += 1
                    source = algorithms.cast_PsfCandidateF(cand).getSource()
                    
                    xc, yc = source.getXAstrom() - self.mi.getX0(), source.getYAstrom() - self.mi.getY0()
                    if i <= nStarPerCell:
                        ds9.dot("o", xc, yc, ctype=ds9.GREEN)
                    else:
                        ds9.dot("o", xc, yc, ctype=ds9.YELLOW)

        pair = algorithms.createKernelFromPsfCandidates(self.cellSet, self.exposure.getDimensions(),
                                                        self.exposure.getXY0(), nEigenComponents, spatialOrder,
                                                        kernelSize, nStarPerCell)

        kernel, eigenValues = pair[0], pair[1]; del pair

        print "lambda", " ".join(["%g" % l for l in eigenValues])

        pair = algorithms.fitSpatialKernelFromPsfCandidates(kernel, self.cellSet, nStarPerCellSpatialFit, tolerance)
        status, chi2 = pair[0], pair[1]; del pair
        print "Spatial fit: %s chi^2 = %.2g" % (status, chi2)

        psf = algorithms.PcaPsf.swigConvert(roundTripPsf(5, algorithms.PcaPsf(kernel))) # Hurrah!

        self.assertTrue(afwMath.cast_AnalyticKernel(psf.getKernel()) is None)
        self.assertTrue(afwMath.cast_LinearCombinationKernel(psf.getKernel()) is not None)

        self.checkTablePersistence(psf)

        if display:
            #print psf.getKernel().toString()

            eImages = []
            for k in afwMath.cast_LinearCombinationKernel(psf.getKernel()).getKernelList():
                im = afwImage.ImageD(k.getDimensions())
                k.computeImage(im, False)
                eImages.append(im)

            mos = displayUtils.Mosaic()
            frame = 3
            ds9.mtv(mos.makeMosaic(eImages), frame=frame)
            ds9.dot("Eigen Images", 0, 0, frame=frame)
            #
            # Make a mosaic of PSF candidates
            #
            stamps = []
            stampInfo = []

            for cell in self.cellSet.getCellList():
                for cand in cell:
                    #
                    # Swig doesn't know that we inherited from SpatialCellMaskedImageCandidate;  all
                    # it knows is that we have a SpatialCellCandidate, and SpatialCellCandidates
                    # don't know about getMaskedImage;  so cast the pointer to PsfCandidate
                    #
                    cand = algorithms.cast_PsfCandidateF(cand)
                    s = cand.getSource()

                    im = cand.getMaskedImage()

                    stamps.append(im)
                    stampInfo.append("[%d 0x%x]" % (s.getId(), s.getFlagForDetection()))
        
                    mos = displayUtils.Mosaic()
            frame = 1
            ds9.mtv(mos.makeMosaic(stamps), frame=frame, lowOrderBits=True)
            for i in range(len(stampInfo)):
                ds9.dot(stampInfo[i], mos.getBBox(i).getX0(), mos.getBBox(i).getY0(), frame=frame, ctype=ds9.RED)

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5)*self.mi.getWidth()/nx)
                        y = int((iy + 0.5)*self.mi.getHeight()/ny)

                        im = psf.getImage(x, y)
                        psfImages.append(im.Factory(im, True))
                        labels.append("PSF(%d,%d)" % (int(x), int(y)))

                        if True:
                            print x, y, "PSF parameters:", psf.getKernel().getKernelParameters()
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20), 
                             (60, 210), (20, 210)]:

                    im = psf.computeImage(afwGeom.PointD(x, y))
                    psfImages.append(im.Factory(im, True))
                    labels.append("PSF(%d,%d)" % (int(x), int(y)))
                    
                    if True:
                        print x, y, "PSF parameters:", psf.getKernel().getKernelParameters()
                    
            frame = 2
            mos.makeMosaic(psfImages, frame=frame, mode=nx)
            mos.drawLabels(labels, frame=frame)

        if display:
            
            ds9.mtv(self.mi, frame=0)

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5)*self.mi.getWidth()/nx)
                        y = int((iy + 0.5)*self.mi.getHeight()/ny)

                        algorithms.subtractPsf(psf, self.mi, x, y)
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20), 
                             (60, 210), (20, 210)]:
                        
                    if False:               # Test subtraction with non-centered psfs
                        x += 0.5; y -= 0.5

                    #algorithms.subtractPsf(psf, self.mi, x, y)

            ds9.mtv(self.mi, frame=1)
Ejemplo n.º 12
0
    def testGetPcaKernel(self):
        """Convert our cellSet to a LinearCombinationKernel"""

        nEigenComponents = 2
        spatialOrder = 1
        kernelSize = 21
        nStarPerCell = 2
        nStarPerCellSpatialFit = 2
        tolerance = 1e-5

        if display:
            disp = afwDisplay.Display(frame=0)
            disp.mtv(self.mi, title=self._testMethodName + ": image")
            #
            # Show the candidates we're using
            #
            for cell in self.cellSet.getCellList():
                i = 0
                for cand in cell:
                    i += 1
                    source = cand.getSource()
                    xc, yc = source.getX() - self.mi.getX0(), source.getY() - self.mi.getY0()
                    if i <= nStarPerCell:
                        disp.dot("o", xc, yc, ctype=afwDisplay.GREEN)
                    else:
                        disp.dot("o", xc, yc, ctype=afwDisplay.YELLOW)

        pair = algorithms.createKernelFromPsfCandidates(self.cellSet, self.exposure.getDimensions(),
                                                        self.exposure.getXY0(), nEigenComponents,
                                                        spatialOrder, kernelSize, nStarPerCell)

        kernel, eigenValues = pair[0], pair[1]
        del pair

        print("lambda", " ".join(["%g" % l for l in eigenValues]))

        pair = algorithms.fitSpatialKernelFromPsfCandidates(kernel, self.cellSet, nStarPerCellSpatialFit,
                                                            tolerance)
        status, chi2 = pair[0], pair[1]
        del pair
        print("Spatial fit: %s chi^2 = %.2g" % (status, chi2))

        psf = roundTripPsf(5, algorithms.PcaPsf(kernel))  # Hurrah!

        self.assertIsNotNone(psf.getKernel())

        self.checkTablePersistence(psf)

        if display:
            # print psf.getKernel().toString()

            eImages = []
            for k in psf.getKernel().getKernelList():
                im = afwImage.ImageD(k.getDimensions())
                k.computeImage(im, False)
                eImages.append(im)

            mos = afwDisplay.utils.Mosaic()
            disp = afwDisplay.Display(frame=3)
            disp.mtv(mos.makeMosaic(eImages), title=self._testMethodName + ": mosaic")
            disp.dot("Eigen Images", 0, 0)
            #
            # Make a mosaic of PSF candidates
            #
            stamps = []
            stampInfo = []

            for cell in self.cellSet.getCellList():
                for cand in cell:
                    s = cand.getSource()
                    im = cand.getMaskedImage()

                    stamps.append(im)
                    stampInfo.append("[%d 0x%x]" % (s.getId(), s["base_PixelFlags_flag"]))

            mos = afwDisplay.utils.Mosaic()
            disp = afwDisplay.Display(frame=1)
            disp.mtv(mos.makeMosaic(stamps), title=self._testMethodName + ": PSF candidates")
            for i in range(len(stampInfo)):
                disp.dot(stampInfo[i], mos.getBBox(i).getMinX(), mos.getBBox(i).getMinY(),
                         ctype=afwDisplay.RED)

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5)*self.mi.getWidth()/nx)
                        y = int((iy + 0.5)*self.mi.getHeight()/ny)

                        im = psf.getImage(x, y)
                        psfImages.append(im.Factory(im, True))
                        labels.append("PSF(%d,%d)" % (int(x), int(y)))

                        if True:
                            print((x, y, "PSF parameters:", psf.getKernel().getKernelParameters()))
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20),
                             (60, 210), (20, 210)]:

                    im = psf.computeImage(lsst.geom.PointD(x, y))
                    psfImages.append(im.Factory(im, True))
                    labels.append("PSF(%d,%d)" % (int(x), int(y)))

                    if True:
                        print(x, y, "PSF parameters:", psf.getKernel().getKernelParameters())
            mos = afwDisplay.utils.Mosaic()
            disp = afwDisplay.Display(frame=2)
            mos.makeMosaic(psfImages, display=disp, mode=nx)
            mos.drawLabels(labels, display=disp)

        if display:
            disp = afwDisplay.Display(frame=0)
            disp.mtv(self.mi, title=self._testMethodName + ": image")

            psfImages = []
            labels = []
            if False:
                nx, ny = 3, 4
                for iy in range(ny):
                    for ix in range(nx):
                        x = int((ix + 0.5)*self.mi.getWidth()/nx)
                        y = int((iy + 0.5)*self.mi.getHeight()/ny)

                        algorithms.subtractPsf(psf, self.mi, x, y)
            else:
                nx, ny = 2, 2
                for x, y in [(20, 20), (60, 20),
                             (60, 210), (20, 210)]:

                    if False:               # Test subtraction with non-centered psfs
                        x += 0.5
                        y -= 0.5

                    # algorithms.subtractPsf(psf, self.mi, x, y)

            afwDisplay.Display(frame=1).mtv(self.mi, title=self._testMethodName + ": image")