def testLogLevel(self): """Test --loglevel""" for logLevel in ("trace", "debug", "Info", "WARN", "eRRoR", "fatal"): namespace = self.ap.parse_args( config=self.config, args=[DataPath, "--loglevel", logLevel], ) intLevel = getattr(namespace.log, logLevel.upper()) print("testing logLevel=%r" % (logLevel,)) self.assertEqual(namespace.log.getEffectiveLevel(), intLevel) self.assertFalse(hasattr(namespace, "loglevel")) bazLevel = "TRACE" namespace = self.ap.parse_args( config=self.config, args=[DataPath, "--loglevel", logLevel, "foo.bar=%s" % (logLevel,), "baz=INFO", "baz=%s" % bazLevel, # test that later values override earlier values ], ) self.assertEqual(namespace.log.getEffectiveLevel(), intLevel) self.assertEqual(getLogger("foo.bar").getEffectiveLevel(), intLevel) self.assertEqual(getLogger("baz").getEffectiveLevel(), getattr(getLogger(), bazLevel)) with self.assertRaises(SystemExit): self.ap.parse_args(config=self.config, args=[DataPath, "--loglevel", "1234"], ) with self.assertRaises(SystemExit): self.ap.parse_args(config=self.config, args=[DataPath, "--loglevel", "INVALID_LEVEL"], )
def __init__(self): # Set up defaults to send to deblender # Always deblend as Psf self.psfChisqCut1 = self.psfChisqCut2 = self.psfChisqCut2b = np.inf self.log = getLogger('lsst.ip.diffim.DipoleDeblender') self.sigma2fwhm = 2. * np.sqrt(2. * np.log(2.))
def getTaskLogger(name=None, logger=None): """Get a logger compatible with LSST usage. Parameters ---------- name : `str`, optional Name of the logger. Root logger if `None`. logger : `logging.Logger` If given the logger is converted to the relevant logger class. If ``name`` is given the logger is assumed to be a child of the supplied logger. Returns ------- logger : `lsst.utils.logging.LsstLogAdapter` The relevant logger. Notes ----- A `logging.LoggerAdapter` is used since it is easier to provide a more uniform interface than when using `logging.setLoggerClass`. An adapter can be wrapped around the root logger and the `~logging.setLoggerClass` will return the logger first given that name even if the name was used before the `Task` was created. """ return getLogger(name=name, logger=logger)
def testLogCommands(self): """Check that all the log commands work.""" root = getLogger() with self.assertLogs(level=root.TRACE) as cm: root.trace("Trace") root.debug("Debug") root.verbose("Verbose") root.info("Info") root.warning("Warning") root.fatal("Fatal") root.critical("Critical") root.error("Error") self.assertEqual(len(cm.records), 8) # Check that each record has an explicit level name rather than # "Level N" and comes from this file (and not the logging.py). for record in cm.records: self.assertRegex(record.levelname, "^[A-Z]+$") self.assertEqual(record.filename, "test_logging.py") with self.assertLogs(level=root.DEBUG) as cm: # Should only issue the INFO message. with root.temporary_log_level(root.INFO): root.info("Info") root.debug("Debug") self.assertEqual(len(cm.records), 1) child = root.getChild("child") self.assertEqual(child.getEffectiveLevel(), root.getEffectiveLevel()) child.setLevel(root.DEBUG) self.assertNotEqual(child.getEffectiveLevel(), root.getEffectiveLevel())
def testLogLevels(self): """Check that the new log levels look reasonable.""" root = getLogger() self.assertEqual(root.DEBUG, logging.DEBUG) self.assertGreater(root.VERBOSE, logging.DEBUG) self.assertLess(root.VERBOSE, logging.INFO) self.assertLess(root.TRACE, logging.DEBUG)
def testTraceSetAt(self): log_name = "lsst.afw" trace_set_at(log_name, 2) trace2_log = getLogger(f"TRACE2.{log_name}") trace3_log = getLogger(f"TRACE3.{log_name}") self.assertEqual(trace2_log.getEffectiveLevel(), logging.DEBUG) self.assertEqual(trace3_log.getEffectiveLevel(), logging.INFO) # Check that child loggers are affected. log_name = "lsst.daf" child3_log = getLogger("TRACE3.lsst.daf") child2_log = getLogger("TRACE2.lsst.daf") self.assertEqual(child3_log.getEffectiveLevel(), logging.WARNING) self.assertEqual(child2_log.getEffectiveLevel(), logging.WARNING) trace_set_at("lsst", 2) self.assertEqual(child3_log.getEffectiveLevel(), logging.INFO) self.assertEqual(child2_log.getEffectiveLevel(), logging.DEBUG) # Also check the root logger. trace_set_at("", 3) self.assertEqual(trace3_log.getEffectiveLevel(), logging.INFO) self.assertEqual(getLogger("TRACE3.test").getEffectiveLevel(), logging.DEBUG)
def test_periodic(self): logger = getLogger("test.periodicity") periodic = PeriodicLogger(logger) # First message will not be issued. periodic.log("Message") self.assertEqual(periodic.num_issued, 0) # Create a new periodic logger with no delay. # Every message should be issued. periodic = PeriodicLogger(logger, interval=0.0) with self.assertLogs(logger.name, level=logger.VERBOSE) as cm: periodic.log("Message") periodic.log("Message %d", 1) self.assertEqual(len(cm.output), 2) self.assertEqual(periodic.num_issued, 2) self.assertEqual(cm.output[0], f"VERBOSE:{logger.name}:Message") self.assertEqual(cm.output[1], f"VERBOSE:{logger.name}:Message 1") self.assertEqual(cm.records[0].filename, "test_logging.py", str(cm.records[0])) # Create a new periodic logger with small delay. # One message should be issued. periodic = PeriodicLogger(logger, interval=0.2, level=logger.INFO) with self.assertLogs(logger.name, level=logger.INFO) as cm: periodic.log("Message") time.sleep(0.5) issued = periodic.log("Message %d", 1) self.assertTrue(issued) issued = periodic.log("Message %d", 2) self.assertFalse(issued) self.assertEqual(periodic.num_issued, 1) self.assertEqual(cm.output[0], f"INFO:{logger.name}:Message 1") # Again with a standard python Logger. pylog = logging.getLogger("python.logger") periodic = PeriodicLogger(pylog, interval=0.0, level=logging.DEBUG) with self.assertLogs(pylog.name, level=logging.DEBUG) as cm: periodic.log("Message") self.assertEqual(cm.records[0].filename, "test_logging.py", str(cm.records[0]))
def backgroundSubtract(config, maskedImages): """Subtract the background from masked images. Parameters ---------- config : TODO: DM-17458 TODO: DM-17458 maskedImages : `list` of `lsst.afw.image.MaskedImage` TODO: DM-17458 Returns ------- TODO: DM-17458 TODO: DM-17458 """ backgrounds = [] t0 = time.time() algorithm = config.algorithm binsize = config.binSize undersample = config.undersampleStyle bctrl = afwMath.BackgroundControl(algorithm) bctrl.setUndersampleStyle(undersample) for maskedImage in maskedImages: bctrl.setNxSample(maskedImage.getWidth() // binsize + 1) bctrl.setNySample(maskedImage.getHeight() // binsize + 1) image = maskedImage.getImage() backobj = afwMath.makeBackground(image, bctrl) image -= backobj.getImageF() backgrounds.append(backobj.getImageF()) del backobj t1 = time.time() logger = getLogger("lsst.ip.diffim.backgroundSubtract") logger.debug("Total time for background subtraction : %.2f s", (t1 - t0)) return backgrounds
import sys import unittest import lsst.utils.tests as tests import lsst.utils import lsst.afw.display as afwDisplay import lsst.afw.geom as afwGeom import lsst.afw.image as afwImage import lsst.afw.math as afwMath import lsst.ip.diffim as ipDiffim import lsst.ip.diffim.diffimTools as diffimTools import lsst.utils.logging as logUtils import lsst.pex.config as pexConfig logUtils.trace_set_at("lsst.ip.diffim", 6) logger = logUtils.getLogger("lsst.ip.diffim.compareLambdaTypes") logger.setLevel(logging.DEBUG) display = True writefits = False # This one compares DeltaFunction kernels of different types; iterate lambdaVal for different strengths CFHTTORUN = 'cal-53535-i-797722_1' class DiffimTestCases(unittest.TestCase): # D = I - (K.x.T + bg) def setUp(self, CFHT=True): lambdaValue = 1.0
def generateAlardLuptonBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, basisSigmaGauss=None, metadata=None): """Generate an Alard-Lupton kernel basis list based upon the Config and the input FWHM of the science and template images. Parameters ---------- config : `lsst.ip.diffim.PsfMatchConfigAL` Configuration object for the Alard-Lupton algorithm. targetFwhmPix : `float`, optional Fwhm width (pixel) of the template exposure characteristic psf. This is the _target_ that will be matched to the science exposure. referenceFwhmPix : `float`, optional Fwhm width (pixel) of the science exposure characteristic psf. basisDegGauss : `list` of `int`, optional Polynomial degree of each Gaussian (sigma) basis. If None, defaults to `config.alardDegGauss`. basisSigmaGauss : `list` of `int`, optional Sigmas of each Gaussian basis. If None, defaults to `config.alardSigGauss`. metadata : `lsst.daf.base.PropertySet`, optional If specified, object to collect metadata fields about the kernel basis list. Returns ------- basisList : `list` of `lsst.afw.math.kernel.FixedKernel` List of basis kernels. For each degree value ``n`` in ``config.basisDegGauss`` (n+2)(n+1)/2 kernels are generated and appended to the list in the order of the polynomial parameter number. See `lsst.afw.math.polynomialFunction2D` documentation for more details. Raises ------ RuntimeError - if ``config.kernelBasisSet`` is not equal to "alard-lupton" ValueError - if ``config.kernelSize`` is even - if the number of Gaussians and the number of given sigma values are not equal or - if the number of Gaussians and the number of given polynomial degree values are not equal Notes ----- The polynomial functions (``f``) are always evaluated in the -1.0, +1.0 range in both x, y directions, edge to edge, with ``f(0,0)`` evaluated at the kernel center pixel, ``f(-1.0,-1.0)`` at the kernel ``(0,0)`` pixel. They are not scaled by the sigmas of the Gaussians. Base Gaussian widths (sigmas in pixels) of the kernels are determined as: - If not all fwhm parameters are provided or ``config.scaleByFwhm==False`` then ``basisSigmaGauss`` is used. If ``basisSigmaGauss`` is not provided, then ``config.alardSigGauss`` is used. In both cases, the length of sigmas must be equal to ``config.alardNGauss``. - If ``targetFwhmPix<referenceFwhmPix`` (normal convolution): First sigma ``Sig_K`` is determined to satisfy: ``Sig_reference**2 = Sig_target**2 + Sig_K**2``. If it's larger than ``config.alardMinSig * config.alardGaussBeta``, make it the second kernel. Else make it the smallest kernel, unless only 1 kernel is asked for. - If ``referenceFwhmPix < targetFwhmPix`` (deconvolution): Define the progression of Gaussians using a method to derive a deconvolution sum-of-Gaussians from it's convolution counterpart. [1]_ Only use 3 since the algorithm assumes 3 components. **Metadata fields** ALBasisNGauss : `int` The number of base Gaussians in the AL basis functions. ALBasisDegGauss : `list` of `int` Polynomial order of spatial modification of the base Gaussian functions. ALBasisSigGauss : `list` of `float` Sigmas in pixels of the base Gaussians. ALKernelSize : `int` Kernel stamp size is (ALKernelSize pix, ALKernelSize pix). ALBasisMode : `str`, either of ``config``, ``convolution``, ``deconvolution`` Indicates whether the config file values, the convolution or deconvolution algorithm was used to determine the base Gaussian sigmas and the kernel stamp size. References ---------- .. [1] Ulmer, W.: Inverse problem of linear combinations of Gaussian convolution kernels (deconvolution) and some applications to proton/photon dosimetry and image processing. http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40 """ if config.kernelBasisSet != "alard-lupton": raise RuntimeError("Cannot generate %s basis within generateAlardLuptonBasisList" % config.kernelBasisSet) kernelSize = config.kernelSize fwhmScaling = config.kernelSizeFwhmScaling basisNGauss = config.alardNGauss basisGaussBeta = config.alardGaussBeta basisMinSigma = config.alardMinSig if basisDegGauss is None: basisDegGauss = config.alardDegGauss if basisSigmaGauss is None: basisSigmaGauss = config.alardSigGauss if len(basisDegGauss) != basisNGauss: raise ValueError("len(basisDegGauss) != basisNGauss : %d vs %d" % (len(basisDegGauss), basisNGauss)) if len(basisSigmaGauss) != basisNGauss: raise ValueError("len(basisSigmaGauss) != basisNGauss : %d vs %d" % (len(basisSigmaGauss), basisNGauss)) if (kernelSize % 2) != 1: raise ValueError("Only odd-sized Alard-Lupton bases allowed") logger = getLogger("lsst.ip.diffim.generateAlardLuptonBasisList") if (targetFwhmPix is None) or (referenceFwhmPix is None) or (not config.scaleByFwhm): logger.info("PSF sigmas are not available or scaling by fwhm disabled, " "falling back to config values") if metadata is not None: metadata.add("ALBasisNGauss", basisNGauss) metadata.add("ALBasisDegGauss", basisDegGauss) metadata.add("ALBasisSigGauss", basisSigmaGauss) metadata.add("ALKernelSize", kernelSize) metadata.add("ALBasisMode", "config") return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss) targetSigma = targetFwhmPix / sigma2fwhm referenceSigma = referenceFwhmPix / sigma2fwhm logger.debug("Generating matching bases for sigma %.2f pix -> %.2f pix", targetSigma, referenceSigma) # Modify the size of Alard Lupton kernels based upon the images FWHM # # Note the operation is : template.x.kernel = science # # Assuming the template and science image Psfs are Gaussians with # the Fwhm above, Fwhm_T **2 + Fwhm_K **2 = Fwhm_S **2 # if targetSigma == referenceSigma: # Leave defaults as-is logger.debug("Target and reference psf fwhms are equal, falling back to config values") basisMode = "config" elif referenceSigma > targetSigma: # Normal convolution # First Gaussian has the sigma that comes from the convolution # of two Gaussians : Sig_S**2 = Sig_T**2 + Sig_K**2 # # If it's larger than basisMinSigma * basisGaussBeta, make it the # second kernel. Else make it the smallest kernel. Unless # only 1 kernel is asked for. logger.debug("Reference psf fwhm is the greater, normal convolution mode") basisMode = "convolution" kernelSigma = np.sqrt(referenceSigma**2 - targetSigma**2) if kernelSigma < basisMinSigma: kernelSigma = basisMinSigma basisSigmaGauss = [] if basisNGauss == 1: basisSigmaGauss.append(kernelSigma) nAppended = 1 else: if (kernelSigma/basisGaussBeta) > basisMinSigma: basisSigmaGauss.append(kernelSigma/basisGaussBeta) basisSigmaGauss.append(kernelSigma) nAppended = 2 else: basisSigmaGauss.append(kernelSigma) nAppended = 1 # Any other Gaussians above basisNGauss=1 come from a scaling # relationship: Sig_i+1 / Sig_i = basisGaussBeta for i in range(nAppended, basisNGauss): basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta) kernelSize = int(fwhmScaling * basisSigmaGauss[-1]) kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin)) else: # Deconvolution; Define the progression of Gaussians using a # method to derive a deconvolution sum-of-Gaussians from it's # convolution counterpart. Only use 3 since the algorithm # assumes 3 components. # # http://iopscience.iop.org/0266-5611/26/8/085002 Equation 40 # Use specializations for deconvolution logger.debug("Target psf fwhm is the greater, deconvolution mode") basisMode = "deconvolution" basisNGauss = config.alardNGaussDeconv basisMinSigma = config.alardMinSigDeconv kernelSigma = np.sqrt(targetSigma**2 - referenceSigma**2) if kernelSigma < basisMinSigma: kernelSigma = basisMinSigma basisSigmaGauss = [] if (kernelSigma/basisGaussBeta) > basisMinSigma: basisSigmaGauss.append(kernelSigma/basisGaussBeta) basisSigmaGauss.append(kernelSigma) nAppended = 2 else: basisSigmaGauss.append(kernelSigma) nAppended = 1 for i in range(nAppended, basisNGauss): basisSigmaGauss.append(basisSigmaGauss[-1]*basisGaussBeta) kernelSize = int(fwhmScaling * basisSigmaGauss[-1]) kernelSize += 0 if kernelSize%2 else 1 # Make sure it's odd kernelSize = min(config.kernelSizeMax, max(kernelSize, config.kernelSizeMin)) # Now build a deconvolution set from these sigmas sig0 = basisSigmaGauss[0] sig1 = basisSigmaGauss[1] sig2 = basisSigmaGauss[2] basisSigmaGauss = [] for n in range(1, 3): for j in range(n): sigma2jn = (n - j)*sig1**2 sigma2jn += j * sig2**2 sigma2jn -= (n + 1)*sig0**2 sigmajn = np.sqrt(sigma2jn) basisSigmaGauss.append(sigmajn) basisSigmaGauss.sort() basisNGauss = len(basisSigmaGauss) basisDegGauss = [config.alardDegGaussDeconv for x in basisSigmaGauss] if metadata is not None: metadata.add("ALBasisNGauss", basisNGauss) metadata.add("ALBasisDegGauss", basisDegGauss) metadata.add("ALBasisSigGauss", basisSigmaGauss) metadata.add("ALKernelSize", kernelSize) metadata.add("ALBasisMode", basisMode) logger.debug("basisSigmaGauss: %s basisDegGauss: %s", ','.join(['{:.1f}'.format(v) for v in basisSigmaGauss]), ','.join(['{:d}'.format(v) for v in basisDegGauss])) return diffimLib.makeAlardLuptonBasisList(kernelSize//2, basisNGauss, basisSigmaGauss, basisDegGauss)
import lsst.utils import lsst.afw.display as afwDisplay import lsst.afw.image as afwImage import lsst.afw.math as afwMath import lsst.ip.diffim as ipDiffim import lsst.ip.diffim.diffimTools as diffimTools import lsst.utils.logging as logUtils import lsst.pex.config as pexConfig afwDisplay.setDefaultMaskTransparency(75) verbosity = 2 logUtils.trace_set_at("lsst.ip.diffim", verbosity) logger = logUtils.getLogger("lsst.ip.diffim.JackknifeResampleKernel") display = False writefits = False try: defDataDir = lsst.utils.getPackageDir('afwdata') except Exception: defDataDir = None if defDataDir: defTemplatePath = os.path.join(defDataDir, "DC3a-Sim", "sci", "v5-e0", "v5-e0-c011-a00.sci.fits") defSciencePath = os.path.join(defDataDir, "DC3a-Sim", "sci", "v26-e0", "v26-e0-c011-a00.sci.fits")
from __future__ import annotations """Module defining a butler like object specialized to a specific quantum. """ __all__ = ("ButlerQuantumContext", ) from typing import Any, List, Sequence, Union from lsst.daf.butler import Butler, DatasetRef, Quantum from lsst.utils.introspection import get_full_type_name from lsst.utils.logging import PeriodicLogger, getLogger from .connections import DeferredDatasetRef, InputQuantizedConnection, OutputQuantizedConnection from .struct import Struct _LOG = getLogger(__name__) class ButlerQuantumContext: """A Butler-like class specialized for a single quantum A ButlerQuantumContext class wraps a standard butler interface and specializes it to the context of a given quantum. What this means in practice is that the only gets and puts that this class allows are DatasetRefs that are contained in the quantum. In the future this class will also be used to record provenance on what was actually get and put. This is in contrast to what the preflight expects to be get and put by looking at the graph before execution.
import os import unittest import lsst.utils.tests import lsst.utils import lsst.afw.image as afwImage import lsst.afw.math as afwMath import lsst.geom as geom import lsst.utils.logging as logUtils import lsst.meas.algorithms as measAlg import lsst.ip.diffim as ipDiffim import lsst.ip.diffim.diffimTools as diffimTools verbosity = 4 logUtils.trace_set_at("lsst.ip.diffim", verbosity) logUtils.getLogger('lsst.psfMatch').setLevel(logging.INFO) display = False # known input images try: defDataDir = lsst.utils.getPackageDir('afwdata') except Exception: defDataDir = None class DiffimTestCases(lsst.utils.tests.TestCase): # D = I - (K.x.T + bg) def setUp(self):
def plotPixelResiduals(exposure, warpedTemplateExposure, diffExposure, kernelCellSet, kernel, background, testSources, config, origVariance=False, nptsFull=1e6, keepPlots=True, titleFs=14): """Plot diffim residuals for LOCAL and SPATIAL models. """ candidateResids = [] spatialResids = [] nonfitResids = [] for cell in kernelCellSet.getCellList(): for cand in cell.begin(True): # only look at good ones # Be sure if not (cand.getStatus() == afwMath.SpatialCellCandidate.GOOD): continue diffim = cand.getDifferenceImage(diffimLib.KernelCandidateF.ORIG) orig = cand.getScienceMaskedImage() ski = afwImage.ImageD(kernel.getDimensions()) kernel.computeImage(ski, False, int(cand.getXCenter()), int(cand.getYCenter())) sk = afwMath.FixedKernel(ski) sbg = background(int(cand.getXCenter()), int(cand.getYCenter())) sdiffim = cand.getDifferenceImage(sk, sbg) # trim edgs due to convolution bbox = kernel.shrinkBBox(diffim.getBBox()) tdiffim = diffim.Factory(diffim, bbox) torig = orig.Factory(orig, bbox) tsdiffim = sdiffim.Factory(sdiffim, bbox) if origVariance: candidateResids.append( np.ravel(tdiffim.getImage().getArray() / np.sqrt(torig.getVariance().getArray()))) spatialResids.append( np.ravel(tsdiffim.getImage().getArray() / np.sqrt(torig.getVariance().getArray()))) else: candidateResids.append( np.ravel(tdiffim.getImage().getArray() / np.sqrt(tdiffim.getVariance().getArray()))) spatialResids.append( np.ravel(tsdiffim.getImage().getArray() / np.sqrt(tsdiffim.getVariance().getArray()))) fullIm = diffExposure.getMaskedImage().getImage().getArray() fullMask = diffExposure.getMaskedImage().getMask().getArray() if origVariance: fullVar = exposure.getMaskedImage().getVariance().getArray() else: fullVar = diffExposure.getMaskedImage().getVariance().getArray() bitmaskBad = 0 bitmaskBad |= afwImage.Mask.getPlaneBitMask('NO_DATA') bitmaskBad |= afwImage.Mask.getPlaneBitMask('SAT') idx = np.where((fullMask & bitmaskBad) == 0) stride = int(len(idx[0]) // nptsFull) sidx = idx[0][::stride], idx[1][::stride] allResids = fullIm[sidx] / np.sqrt(fullVar[sidx]) testFootprints = diffimTools.sourceToFootprintList( testSources, warpedTemplateExposure, exposure, config, getLogger(__name__).getChild("plotPixelResiduals")) for fp in testFootprints: subexp = diffExposure.Factory(diffExposure, fp["footprint"].getBBox()) subim = subexp.getMaskedImage().getImage() if origVariance: subvar = afwImage.ExposureF( exposure, fp["footprint"].getBBox()).getMaskedImage().getVariance() else: subvar = subexp.getMaskedImage().getVariance() nonfitResids.append( np.ravel(subim.getArray() / np.sqrt(subvar.getArray()))) candidateResids = np.ravel(np.array(candidateResids)) spatialResids = np.ravel(np.array(spatialResids)) nonfitResids = np.ravel(np.array(nonfitResids)) try: import pylab from matplotlib.font_manager import FontProperties except ImportError as e: print("Unable to import pylab: %s" % e) return fig = pylab.figure() fig.clf() try: fig.canvas._tkcanvas._root().lift( ) # == Tk's raise, but raise is a python reserved word except Exception: # protect against API changes pass if origVariance: fig.suptitle("Diffim residuals: Normalized by sqrt(input variance)", fontsize=titleFs) else: fig.suptitle("Diffim residuals: Normalized by sqrt(diffim variance)", fontsize=titleFs) sp1 = pylab.subplot(221) sp2 = pylab.subplot(222, sharex=sp1, sharey=sp1) sp3 = pylab.subplot(223, sharex=sp1, sharey=sp1) sp4 = pylab.subplot(224, sharex=sp1, sharey=sp1) xs = np.arange(-5, 5.05, 0.1) ys = 1. / np.sqrt(2 * np.pi) * np.exp(-0.5 * xs**2) sp1.hist(candidateResids, bins=xs, normed=True, alpha=0.5, label="N(%.2f, %.2f)" % (np.mean(candidateResids), np.var(candidateResids))) sp1.plot(xs, ys, "r-", lw=2, label="N(0,1)") sp1.set_title("Candidates: basis fit", fontsize=titleFs - 2) sp1.legend(loc=1, fancybox=True, shadow=True, prop=FontProperties(size=titleFs - 6)) sp2.hist(spatialResids, bins=xs, normed=True, alpha=0.5, label="N(%.2f, %.2f)" % (np.mean(spatialResids), np.var(spatialResids))) sp2.plot(xs, ys, "r-", lw=2, label="N(0,1)") sp2.set_title("Candidates: spatial fit", fontsize=titleFs - 2) sp2.legend(loc=1, fancybox=True, shadow=True, prop=FontProperties(size=titleFs - 6)) sp3.hist(nonfitResids, bins=xs, normed=True, alpha=0.5, label="N(%.2f, %.2f)" % (np.mean(nonfitResids), np.var(nonfitResids))) sp3.plot(xs, ys, "r-", lw=2, label="N(0,1)") sp3.set_title("Control sample: spatial fit", fontsize=titleFs - 2) sp3.legend(loc=1, fancybox=True, shadow=True, prop=FontProperties(size=titleFs - 6)) sp4.hist(allResids, bins=xs, normed=True, alpha=0.5, label="N(%.2f, %.2f)" % (np.mean(allResids), np.var(allResids))) sp4.plot(xs, ys, "r-", lw=2, label="N(0,1)") sp4.set_title("Full image (subsampled)", fontsize=titleFs - 2) sp4.legend(loc=1, fancybox=True, shadow=True, prop=FontProperties(size=titleFs - 6)) pylab.setp(sp1.get_xticklabels() + sp1.get_yticklabels(), fontsize=titleFs - 4) pylab.setp(sp2.get_xticklabels() + sp2.get_yticklabels(), fontsize=titleFs - 4) pylab.setp(sp3.get_xticklabels() + sp3.get_yticklabels(), fontsize=titleFs - 4) pylab.setp(sp4.get_xticklabels() + sp4.get_yticklabels(), fontsize=titleFs - 4) sp1.set_xlim(-5, 5) sp1.set_ylim(0, 0.5) fig.show() global keptPlots if keepPlots and not keptPlots: # Keep plots open when done def show(): print("%s: Please close plots when done." % __name__) try: pylab.show() except Exception: pass print("Plots closed, exiting...") import atexit atexit.register(show) keptPlots = True