22 __all__ = [
"DetectionConfig", 
"PsfMatchConfig", 
"PsfMatchConfigAL", 
"PsfMatchConfigDF", 
"PsfMatchTask"]
 
   29 import lsst.pex.config 
as pexConfig
 
   35 from . 
import utils 
as diutils
 
   36 from . 
import diffimLib
 
   40     """Configuration for detecting sources on images for building a 
   43     Configuration for turning detected lsst.afw.detection.FootPrints into an 
   44     acceptable (unmasked, high signal-to-noise, not too large or not too small) 
   45     list of `lsst.ip.diffim.KernelSources` that are used to build the 
   46     Psf-matching kernel""" 
   48     detThreshold = pexConfig.Field(
 
   50         doc=
"Value of footprint detection threshold",
 
   52         check=
lambda x: x >= 3.0
 
   54     detThresholdType = pexConfig.ChoiceField(
 
   56         doc=
"Type of detection threshold",
 
   57         default=
"pixel_stdev",
 
   59             "value": 
"Use counts as the detection threshold type",
 
   60             "stdev": 
"Use standard deviation of image plane",
 
   61             "variance": 
"Use variance of image plane",
 
   62             "pixel_stdev": 
"Use stdev derived from variance plane" 
   65     detOnTemplate = pexConfig.Field(
 
   67         doc=
"""If true run detection on the template (image to convolve); 
   68                  if false run detection on the science image""",
 
   71     badMaskPlanes = pexConfig.ListField(
 
   73         doc=
"""Mask planes that lead to an invalid detection. 
   74                  Options: NO_DATA EDGE SAT BAD CR INTRP""",
 
   75         default=(
"NO_DATA", 
"EDGE", 
"SAT")
 
   77     fpNpixMin = pexConfig.Field(
 
   79         doc=
"Minimum number of pixels in an acceptable Footprint",
 
   81         check=
lambda x: x >= 5
 
   83     fpNpixMax = pexConfig.Field(
 
   85         doc=
"""Maximum number of pixels in an acceptable Footprint; 
   86                  too big and the subsequent convolutions become unwieldy""",
 
   88         check=
lambda x: x <= 500
 
   90     fpGrowKernelScaling = pexConfig.Field(
 
   92         doc=
"""If config.scaleByFwhm, grow the footprint based on 
   93                  the final kernelSize.  Each footprint will be 
   94                  2*fpGrowKernelScaling*kernelSize x 
   95                  2*fpGrowKernelScaling*kernelSize.  With the value 
   96                  of 1.0, the remaining pixels in each KernelCandiate 
   97                  after convolution by the basis functions will be 
   98                  equal to the kernel size itself.""",
 
  100         check=
lambda x: x >= 1.0
 
  102     fpGrowPix = pexConfig.Field(
 
  104         doc=
"""Growing radius (in pixels) for each raw detection 
  105                  footprint.  The smaller the faster; however the 
  106                  kernel sum does not converge if the stamp is too 
  107                  small; and the kernel is not constrained at all if 
  108                  the stamp is the size of the kernel.  The grown stamp 
  109                  is 2 * fpGrowPix pixels larger in each dimension. 
  110                  This is overridden by fpGrowKernelScaling if scaleByFwhm""",
 
  112         check=
lambda x: x >= 10
 
  114     scaleByFwhm = pexConfig.Field(
 
  116         doc=
"Scale fpGrowPix by input Fwhm?",
 
  122     """Base configuration for Psf-matching 
  124     The base configuration of the Psf-matching kernel, and of the warping, detection, 
  125     and background modeling subTasks.""" 
  127     warpingConfig = pexConfig.ConfigField(
"Config for warping exposures to a common alignment",
 
  128                                           afwMath.warper.WarperConfig)
 
  129     detectionConfig = pexConfig.ConfigField(
"Controlling the detection of sources for kernel building",
 
  131     afwBackgroundConfig = pexConfig.ConfigField(
"Controlling the Afw background fitting",
 
  132                                                 SubtractBackgroundConfig)
 
  134     useAfwBackground = pexConfig.Field(
 
  136         doc=
"Use afw background subtraction instead of ip_diffim",
 
  139     fitForBackground = pexConfig.Field(
 
  141         doc=
"Include terms (including kernel cross terms) for background in ip_diffim",
 
  144     kernelBasisSet = pexConfig.ChoiceField(
 
  146         doc=
"Type of basis set for PSF matching kernel.",
 
  147         default=
"alard-lupton",
 
  149             "alard-lupton": 
"""Alard-Lupton sum-of-gaussians basis set, 
  150                            * The first term has no spatial variation 
  151                            * The kernel sum is conserved 
  152                            * You may want to turn off 'usePcaForSpatialKernel'""",
 
  153             "delta-function": 
"""Delta-function kernel basis set, 
  154                            * You may enable the option useRegularization 
  155                            * You should seriously consider usePcaForSpatialKernel, which will also 
  156                              enable kernel sum conservation for the delta function kernels""" 
  159     kernelSize = pexConfig.Field(
 
  161         doc=
"""Number of rows/columns in the convolution kernel; should be odd-valued. 
  162                  Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
 
  165     scaleByFwhm = pexConfig.Field(
 
  167         doc=
"Scale kernelSize, alardGaussians by input Fwhm",
 
  170     kernelSizeFwhmScaling = pexConfig.Field(
 
  172         doc=
"Multiplier of the largest AL Gaussian basis sigma to get the kernel bbox (pixel) size.",
 
  174         check=
lambda x: x >= 1.0
 
  176     kernelSizeMin = pexConfig.Field(
 
  178         doc=
"Minimum kernel bbox (pixel) size.",
 
  181     kernelSizeMax = pexConfig.Field(
 
  183         doc=
"Maximum kernel bbox (pixel) size.",
 
  186     spatialModelType = pexConfig.ChoiceField(
 
  188         doc=
"Type of spatial functions for kernel and background",
 
  189         default=
"chebyshev1",
 
  191             "chebyshev1": 
"Chebyshev polynomial of the first kind",
 
  192             "polynomial": 
"Standard x,y polynomial",
 
  195     spatialKernelOrder = pexConfig.Field(
 
  197         doc=
"Spatial order of convolution kernel variation",
 
  199         check=
lambda x: x >= 0
 
  201     spatialBgOrder = pexConfig.Field(
 
  203         doc=
"Spatial order of differential background variation",
 
  205         check=
lambda x: x >= 0
 
  207     sizeCellX = pexConfig.Field(
 
  209         doc=
"Size (rows) in pixels of each SpatialCell for spatial modeling",
 
  211         check=
lambda x: x >= 32
 
  213     sizeCellY = pexConfig.Field(
 
  215         doc=
"Size (columns) in pixels of each SpatialCell for spatial modeling",
 
  217         check=
lambda x: x >= 32
 
  219     nStarPerCell = pexConfig.Field(
 
  221         doc=
"Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
 
  223         check=
lambda x: x >= 1
 
  225     maxSpatialIterations = pexConfig.Field(
 
  227         doc=
"Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
 
  229         check=
lambda x: x >= 1 
and x <= 5
 
  231     usePcaForSpatialKernel = pexConfig.Field(
 
  233         doc=
"""Use Pca to reduce the dimensionality of the kernel basis sets. 
  234                  This is particularly useful for delta-function kernels. 
  235                  Functionally, after all Cells have their raw kernels determined, we run 
  236                  a Pca on these Kernels, re-fit the Cells using the eigenKernels and then 
  237                  fit those for spatial variation using the same technique as for Alard-Lupton kernels. 
  238                  If this option is used, the first term will have no spatial variation and the 
  239                  kernel sum will be conserved.""",
 
  242     subtractMeanForPca = pexConfig.Field(
 
  244         doc=
"Subtract off the mean feature before doing the Pca",
 
  247     numPrincipalComponents = pexConfig.Field(
 
  249         doc=
"""Number of principal components to use for Pca basis, including the 
  250                  mean kernel if requested.""",
 
  252         check=
lambda x: x >= 3
 
  254     singleKernelClipping = pexConfig.Field(
 
  256         doc=
"Do sigma clipping on each raw kernel candidate",
 
  259     kernelSumClipping = pexConfig.Field(
 
  261         doc=
"Do sigma clipping on the ensemble of kernel sums",
 
  264     spatialKernelClipping = pexConfig.Field(
 
  266         doc=
"Do sigma clipping after building the spatial model",
 
  269     checkConditionNumber = pexConfig.Field(
 
  271         doc=
"""Test for maximum condition number when inverting a kernel matrix. 
  272                  Anything above maxConditionNumber is not used and the candidate is set as BAD. 
  273                  Also used to truncate inverse matrix in estimateBiasedRisk.  However, 
  274                  if you are doing any deconvolution you will want to turn this off, or use 
  275                  a large maxConditionNumber""",
 
  278     badMaskPlanes = pexConfig.ListField(
 
  280         doc=
"""Mask planes to ignore when calculating diffim statistics 
  281                  Options: NO_DATA EDGE SAT BAD CR INTRP""",
 
  282         default=(
"NO_DATA", 
"EDGE", 
"SAT")
 
  284     candidateResidualMeanMax = pexConfig.Field(
 
  286         doc=
"""Rejects KernelCandidates yielding bad difference image quality. 
  287                  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor. 
  288                  Represents average over pixels of (image/sqrt(variance)).""",
 
  290         check=
lambda x: x >= 0.0
 
  292     candidateResidualStdMax = pexConfig.Field(
 
  294         doc=
"""Rejects KernelCandidates yielding bad difference image quality. 
  295                  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor. 
  296                  Represents stddev over pixels of (image/sqrt(variance)).""",
 
  298         check=
lambda x: x >= 0.0
 
  300     useCoreStats = pexConfig.Field(
 
  302         doc=
"""Use the core of the footprint for the quality statistics, instead of the entire footprint. 
  303                  WARNING: if there is deconvolution we probably will need to turn this off""",
 
  306     candidateCoreRadius = pexConfig.Field(
 
  308         doc=
"""Radius for calculation of stats in 'core' of KernelCandidate diffim. 
  309                  Total number of pixels used will be (2*radius)**2. 
  310                  This is used both for 'core' diffim quality as well as ranking of 
  311                  KernelCandidates by their total flux in this core""",
 
  313         check=
lambda x: x >= 1
 
  315     maxKsumSigma = pexConfig.Field(
 
  317         doc=
"""Maximum allowed sigma for outliers from kernel sum distribution. 
  318                  Used to reject variable objects from the kernel model""",
 
  320         check=
lambda x: x >= 0.0
 
  322     maxConditionNumber = pexConfig.Field(
 
  324         doc=
"Maximum condition number for a well conditioned matrix",
 
  326         check=
lambda x: x >= 0.0
 
  328     conditionNumberType = pexConfig.ChoiceField(
 
  330         doc=
"Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
 
  331         default=
"EIGENVALUE",
 
  333             "SVD": 
"Use singular values",
 
  334             "EIGENVALUE": 
"Use eigen values (faster)",
 
  337     maxSpatialConditionNumber = pexConfig.Field(
 
  339         doc=
"Maximum condition number for a well conditioned spatial matrix",
 
  341         check=
lambda x: x >= 0.0
 
  343     iterateSingleKernel = pexConfig.Field(
 
  345         doc=
"""Remake KernelCandidate using better variance estimate after first pass? 
  346                  Primarily useful when convolving a single-depth image, otherwise not necessary.""",
 
  349     constantVarianceWeighting = pexConfig.Field(
 
  351         doc=
"""Use constant variance weighting in single kernel fitting? 
  352                  In some cases this is better for bright star residuals.""",
 
  355     calculateKernelUncertainty = pexConfig.Field(
 
  357         doc=
"""Calculate kernel and background uncertainties for each kernel candidate? 
  358                  This comes from the inverse of the covariance matrix. 
  359                  Warning: regularization can cause problems for this step.""",
 
  362     useBicForKernelBasis = pexConfig.Field(
 
  364         doc=
"""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
 
  370     """The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis""" 
  373         PsfMatchConfig.setDefaults(self)
 
  377     alardNGauss = pexConfig.Field(
 
  379         doc=
"Number of base Gaussians in alard-lupton kernel basis function generation.",
 
  381         check=
lambda x: x >= 1
 
  383     alardDegGauss = pexConfig.ListField(
 
  385         doc=
"Polynomial order of spatial modification of base Gaussians. " 
  386             "List length must be `alardNGauss`.",
 
  389     alardSigGauss = pexConfig.ListField(
 
  391         doc=
"Default sigma values in pixels of base Gaussians. " 
  392             "List length must be `alardNGauss`.",
 
  393         default=(0.7, 1.5, 3.0),
 
  395     alardGaussBeta = pexConfig.Field(
 
  397         doc=
"Used if `scaleByFwhm==True`, scaling multiplier of base " 
  398             "Gaussian sigmas for automated sigma determination",
 
  400         check=
lambda x: x >= 0.0,
 
  402     alardMinSig = pexConfig.Field(
 
  404         doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians",
 
  406         check=
lambda x: x >= 0.25
 
  408     alardDegGaussDeconv = pexConfig.Field(
 
  410         doc=
"Used if `scaleByFwhm==True`, degree of spatial modification of ALL base Gaussians " 
  411             "in AL basis during deconvolution",
 
  413         check=
lambda x: x >= 1
 
  415     alardMinSigDeconv = pexConfig.Field(
 
  417         doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians during deconvolution; " 
  418             "make smaller than `alardMinSig` as this is only indirectly used",
 
  420         check=
lambda x: x >= 0.25
 
  422     alardNGaussDeconv = pexConfig.Field(
 
  424         doc=
"Used if `scaleByFwhm==True`, number of base Gaussians in AL basis during deconvolution",
 
  426         check=
lambda x: x >= 1
 
  431     """The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis""" 
  434         PsfMatchConfig.setDefaults(self)
 
  441     useRegularization = pexConfig.Field(
 
  443         doc=
"Use regularization to smooth the delta function kernels",
 
  446     regularizationType = pexConfig.ChoiceField(
 
  448         doc=
"Type of regularization.",
 
  449         default=
"centralDifference",
 
  451             "centralDifference": 
"Penalize second derivative using 2-D stencil of central finite difference",
 
  452             "forwardDifference": 
"Penalize first, second, third derivatives using forward finite differeces" 
  455     centralRegularizationStencil = pexConfig.ChoiceField(
 
  457         doc=
"Type of stencil to approximate central derivative (for centralDifference only)",
 
  460             5: 
"5-point stencil including only adjacent-in-x,y elements",
 
  461             9: 
"9-point stencil including diagonal elements" 
  464     forwardRegularizationOrders = pexConfig.ListField(
 
  466         doc=
"Array showing which order derivatives to penalize (for forwardDifference only)",
 
  468         itemCheck=
lambda x: (x > 0) 
and (x < 4)
 
  470     regularizationBorderPenalty = pexConfig.Field(
 
  472         doc=
"Value of the penalty for kernel border pixels",
 
  474         check=
lambda x: x >= 0.0
 
  476     lambdaType = pexConfig.ChoiceField(
 
  478         doc=
"How to choose the value of the regularization strength",
 
  481             "absolute": 
"Use lambdaValue as the value of regularization strength",
 
  482             "relative": 
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
 
  483             "minimizeBiasedRisk": 
"Minimize biased risk estimate",
 
  484             "minimizeUnbiasedRisk": 
"Minimize unbiased risk estimate",
 
  487     lambdaValue = pexConfig.Field(
 
  489         doc=
"Value used for absolute determinations of regularization strength",
 
  492     lambdaScaling = pexConfig.Field(
 
  494         doc=
"Fraction of the default lambda strength (N.R. 18.5.8) to use.  1e-4 or 1e-5",
 
  497     lambdaStepType = pexConfig.ChoiceField(
 
  499         doc=
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 
  500                  use log or linear steps""",
 
  503             "log": 
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
 
  504             "linear": 
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
 
  507     lambdaMin = pexConfig.Field(
 
  509         doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 
  510                  start at this value.  If lambdaStepType = log:linear, suggest -1:0.1""",
 
  513     lambdaMax = pexConfig.Field(
 
  515         doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 
  516                  stop at this value.  If lambdaStepType = log:linear, suggest 2:100""",
 
  519     lambdaStep = pexConfig.Field(
 
  521         doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 
  522                  step in these increments.  If lambdaStepType = log:linear, suggest 0.1:0.1""",
 
  528     """Base class for Psf Matching; should not be called directly 
  532     PsfMatchTask is a base class that implements the core functionality for matching the 
  533     Psfs of two images using a spatially varying Psf-matching `lsst.afw.math.LinearCombinationKernel`. 
  534     The Task requires the user to provide an instance of an `lsst.afw.math.SpatialCellSet`, 
  535     filled with `lsst.ip.diffim.KernelCandidate` instances, and a list of `lsst.afw.math.Kernels` 
  536     of basis shapes that will be used for the decomposition.  If requested, the Task 
  537     also performs background matching and returns the differential background model as an 
  538     `lsst.afw.math.Kernel.SpatialFunction`. 
  543     As a base class, this Task is not directly invoked.  However, ``run()`` methods that are 
  544     implemented on derived classes will make use of the core ``_solve()`` functionality, 
  545     which defines a sequence of `lsst.afw.math.CandidateVisitor` classes that iterate 
  546     through the KernelCandidates, first building up a per-candidate solution and then 
  547     building up a spatial model from the ensemble of candidates.  Sigma clipping is 
  548     performed using the mean and standard deviation of all kernel sums (to reject 
  549     variable objects), on the per-candidate substamp diffim residuals 
  550     (to indicate a bad choice of kernel basis shapes for that particular object), 
  551     and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad 
  552     choice of spatial kernel order, or poor constraints on the spatial model).  The 
  553     ``_diagnostic()`` method logs information on the quality of the spatial fit, and also 
  554     modifies the Task metadata. 
  556     .. list-table:: Quantities set in Metadata 
  561        * - ``spatialConditionNum`` 
  562          - Condition number of the spatial kernel fit 
  563        * - ``spatialKernelSum`` 
  564          - Kernel sum (10^{-0.4 * ``Delta``; zeropoint}) of the spatial Psf-matching kernel 
  565        * - ``ALBasisNGauss`` 
  566          - If using sum-of-Gaussian basis, the number of gaussians used 
  567        * - ``ALBasisDegGauss`` 
  568          - If using sum-of-Gaussian basis, the deg of spatial variation of the Gaussians 
  569        * - ``ALBasisSigGauss`` 
  570          - If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians 
  572          - If using sum-of-Gaussian basis, the kernel size 
  573        * - ``NFalsePositivesTotal`` 
  574          - Total number of diaSources 
  575        * - ``NFalsePositivesRefAssociated`` 
  576          - Number of diaSources that associate with the reference catalog 
  577        * - ``NFalsePositivesRefAssociated`` 
  578          - Number of diaSources that associate with the source catalog 
  579        * - ``NFalsePositivesUnassociated`` 
  580          - Number of diaSources that are orphans 
  582          - Mean value of substamp diffim quality metrics across all KernelCandidates, 
  583            for both the per-candidate (LOCAL) and SPATIAL residuals 
  584        * - ``metric_MEDIAN`` 
  585          - Median value of substamp diffim quality metrics across all KernelCandidates, 
  586            for both the per-candidate (LOCAL) and SPATIAL residuals 
  588          - Standard deviation of substamp diffim quality metrics across all KernelCandidates, 
  589            for both the per-candidate (LOCAL) and SPATIAL residuals 
  594     The `lsst.pipe.base.cmdLineTask.CmdLineTask` command line task interface supports a 
  595     flag -d/--debug to import @b debug.py from your PYTHONPATH.  The relevant contents of debug.py 
  596     for this Task include: 
  603             di = lsstDebug.getInfo(name) 
  604             if name == "lsst.ip.diffim.psfMatch": 
  605                 # enable debug output 
  607                 # display mask transparency 
  608                 di.maskTransparency = 80 
  609                 # show all the candidates and residuals 
  610                 di.displayCandidates = True 
  611                 # show kernel basis functions 
  612                 di.displayKernelBasis = False 
  613                 # show kernel realized across the image 
  614                 di.displayKernelMosaic = True 
  615                 # show coefficients of spatial model 
  616                 di.plotKernelSpatialModel = False 
  617                 # show fixed and spatial coefficients and coefficient histograms 
  618                 di.plotKernelCoefficients = True 
  619                 # show the bad candidates (red) along with good (green) 
  620                 di.showBadCandidates = True 
  622         lsstDebug.Info = DebugInfo 
  625     Note that if you want additional logging info, you may add to your scripts: 
  629         import lsst.log.utils as logUtils 
  630         logUtils.traceSetAt("ip.diffim", 4) 
  632     ConfigClass = PsfMatchConfig
 
  633     _DefaultName = 
"psfMatch" 
  636         """Create the psf-matching Task 
  641             Arguments to be passed to ``lsst.pipe.base.task.Task.__init__`` 
  643             Keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__`` 
  647         The initialization sets the Psf-matching kernel configuration using the value of 
  648         self.config.kernel.active.  If the kernel is requested with regularization to moderate 
  649         the bias/variance tradeoff, currently only used when a delta function kernel basis 
  650         is provided, it creates a regularization matrix stored as member variable 
  653         pipeBase.Task.__init__(self, *args, **kwargs)
 
  656         if 'useRegularization' in self.
kConfig:
 
  662             self.
hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePropertySet(self.
kConfig))
 
  664     def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
 
  665         """Provide logging diagnostics on quality of spatial kernel fit 
  669         kernelCellSet : `lsst.afw.math.SpatialCellSet` 
  670             Cellset that contains the KernelCandidates used in the fitting 
  671         spatialSolution : `lsst.ip.diffim.SpatialKernelSolution` 
  672             KernelSolution of best-fit 
  673         spatialKernel : `lsst.afw.math.LinearCombinationKernel` 
  674             Best-fit spatial Kernel model 
  675         spatialBg : `lsst.afw.math.Function2D` 
  676             Best-fit spatial background model 
  679         kImage = afwImage.ImageD(spatialKernel.getDimensions())
 
  680         kSum = spatialKernel.computeImage(kImage, 
False)
 
  681         self.log.
info(
"Final spatial kernel sum %.3f" % (kSum))
 
  684         conditionNum = spatialSolution.getConditionNumber(
 
  685             getattr(diffimLib.KernelSolution, self.
kConfig.conditionNumberType))
 
  686         self.log.
info(
"Spatial model condition number %.3e" % (conditionNum))
 
  688         if conditionNum < 0.0:
 
  689             self.log.
warn(
"Condition number is negative (%.3e)" % (conditionNum))
 
  690         if conditionNum > self.
kConfig.maxSpatialConditionNumber:
 
  691             self.log.
warn(
"Spatial solution exceeds max condition number (%.3e > %.3e)" % (
 
  692                 conditionNum, self.
kConfig.maxSpatialConditionNumber))
 
  694         self.metadata.
set(
"spatialConditionNum", conditionNum)
 
  695         self.metadata.
set(
"spatialKernelSum", kSum)
 
  698         nBasisKernels = spatialKernel.getNBasisKernels()
 
  699         nKernelTerms = spatialKernel.getNSpatialParameters()
 
  700         if nKernelTerms == 0:  
 
  704         nBgTerms = spatialBg.getNParameters()
 
  706             if spatialBg.getParameters()[0] == 0.0:
 
  712         for cell 
in kernelCellSet.getCellList():
 
  713             for cand 
in cell.begin(
False):  
 
  715                 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
 
  717                 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
 
  720         self.log.
info(
"Doing stats of kernel candidates used in the spatial fit.")
 
  724             self.log.
warn(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
 
  727             self.log.
info(
"%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
 
  730         if nGood < nKernelTerms:
 
  731             self.log.
warn(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
 
  732                 nGood, nKernelTerms, nBasisKernels))
 
  733             self.log.
warn(
"Consider lowering the spatial order")
 
  734         elif nGood <= 2*nKernelTerms:
 
  735             self.log.
warn(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
 
  736                 nGood, nKernelTerms, nBasisKernels))
 
  737             self.log.
warn(
"Consider lowering the spatial order")
 
  739             self.log.
info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
 
  740                 nGood, nKernelTerms, nBasisKernels))
 
  743             self.log.
warn(
"Spatial background model underconstrained; %d candidates, %d terms" % (
 
  745             self.log.
warn(
"Consider lowering the spatial order")
 
  746         elif nGood <= 2*nBgTerms:
 
  747             self.log.
warn(
"Spatial background model poorly constrained; %d candidates, %d terms" % (
 
  749             self.log.
warn(
"Consider lowering the spatial order")
 
  751             self.log.
info(
"Spatial background model appears well constrained; %d candidates, %d terms" % (
 
  754     def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
 
  755         """Provide visualization of the inputs and ouputs to the Psf-matching code 
  759         kernelCellSet : `lsst.afw.math.SpatialCellSet` 
  760             The SpatialCellSet used in determining the matching kernel and background 
  761         spatialKernel : `lsst.afw.math.LinearCombinationKernel` 
  762             Spatially varying Psf-matching kernel 
  763         spatialBackground : `lsst.afw.math.Function2D` 
  764             Spatially varying background-matching function 
  769         displayKernelMosaic = 
lsstDebug.Info(__name__).displayKernelMosaic
 
  770         plotKernelSpatialModel = 
lsstDebug.Info(__name__).plotKernelSpatialModel
 
  771         plotKernelCoefficients = 
lsstDebug.Info(__name__).plotKernelCoefficients
 
  774         if not maskTransparency:
 
  776         afwDisplay.setDefaultMaskTransparency(maskTransparency)
 
  778         if displayCandidates:
 
  779             diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
 
  780                                          frame=lsstDebug.frame,
 
  781                                          showBadCandidates=showBadCandidates)
 
  783             diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
 
  784                                          frame=lsstDebug.frame,
 
  785                                          showBadCandidates=showBadCandidates,
 
  788             diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
 
  789                                          frame=lsstDebug.frame,
 
  790                                          showBadCandidates=showBadCandidates,
 
  794         if displayKernelBasis:
 
  795             diutils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
 
  798         if displayKernelMosaic:
 
  799             diutils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
 
  802         if plotKernelSpatialModel:
 
  803             diutils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
 
  805         if plotKernelCoefficients:
 
  806             diutils.plotKernelCoefficients(spatialKernel, kernelCellSet)
 
  808     def _createPcaBasis(self, kernelCellSet, nStarPerCell, ps):
 
  809         """Create Principal Component basis 
  811         If a principal component analysis is requested, typically when using a delta function basis, 
  812         perform the PCA here and return a new basis list containing the new principal components. 
  816         kernelCellSet : `lsst.afw.math.SpatialCellSet` 
  817             a SpatialCellSet containing KernelCandidates, from which components are derived 
  819             the number of stars per cell to visit when doing the PCA 
  820         ps : `lsst.daf.base.PropertySet` 
  821             input property set controlling the single kernel visitor 
  826             number of KernelCandidates rejected during PCA loop 
  827         spatialBasisList : `list` of `lsst.afw.math.kernel.FixedKernel` 
  828             basis list containing the principal shapes as Kernels 
  833             If the Eigenvalues sum to zero. 
  835         nComponents = self.
kConfig.numPrincipalComponents
 
  836         imagePca = diffimLib.KernelPcaD()
 
  837         importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
 
  838         kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
 
  839         if self.
kConfig.subtractMeanForPca:
 
  840             importStarVisitor.subtractMean()
 
  843         eigenValues = imagePca.getEigenValues()
 
  844         pcaBasisList = importStarVisitor.getEigenKernels()
 
  846         eSum = np.sum(eigenValues)
 
  848             raise RuntimeError(
"Eigenvalues sum to zero")
 
  849         for j 
in range(len(eigenValues)):
 
  850             log.log(
"TRACE5." + self.log.getName() + 
"._solve", log.DEBUG,
 
  851                     "Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
 
  853         nToUse = 
min(nComponents, len(eigenValues))
 
  855         for j 
in range(nToUse):
 
  857             kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
 
  858             pcaBasisList[j].computeImage(kimage, 
False)
 
  859             if not (
True in np.isnan(kimage.getArray())):
 
  860                 trimBasisList.append(pcaBasisList[j])
 
  863         spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
 
  866         singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, ps)
 
  867         singlekvPca.setSkipBuilt(
False)
 
  868         kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
 
  869         singlekvPca.setSkipBuilt(
True)
 
  870         nRejectedPca = singlekvPca.getNRejected()
 
  872         return nRejectedPca, spatialBasisList
 
  874     def _buildCellSet(self, *args):
 
  875         """Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; 
  876         override in derived classes""" 
  880     def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
 
  881         """Solve for the PSF matching kernel 
  885         kernelCellSet : `lsst.afw.math.SpatialCellSet` 
  886             a SpatialCellSet to use in determining the matching kernel 
  887              (typically as provided by _buildCellSet) 
  888         basisList : `list` of `lsst.afw.math.kernel.FixedKernel` 
  889             list of Kernels to be used in the decomposition of the spatially varying kernel 
  890             (typically as provided by makeKernelBasisList) 
  891         returnOnExcept : `bool`, optional 
  892             if True then return (None, None) if an error occurs, else raise the exception 
  896         psfMatchingKernel : `lsst.afw.math.LinearCombinationKernel` 
  897             Spatially varying Psf-matching kernel 
  898         backgroundModel : `lsst.afw.math.Function2D` 
  899             Spatially varying background-matching function 
  904             If unable to determine PSF matching kernel and ``returnOnExcept==False``. 
  910         maxSpatialIterations = self.
kConfig.maxSpatialIterations
 
  911         nStarPerCell = self.
kConfig.nStarPerCell
 
  912         usePcaForSpatialKernel = self.
kConfig.usePcaForSpatialKernel
 
  915         ps = pexConfig.makePropertySet(self.
kConfig)
 
  917             singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps, self.
hMat)
 
  919             singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps)
 
  922         ksv = diffimLib.KernelSumVisitorF(ps)
 
  929             while (thisIteration < maxSpatialIterations):
 
  933                 while (nRejectedSkf != 0):
 
  934                     log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
  935                             "Building single kernels...")
 
  936                     kernelCellSet.visitCandidates(singlekv, nStarPerCell)
 
  937                     nRejectedSkf = singlekv.getNRejected()
 
  938                     log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
  939                             "Iteration %d, rejected %d candidates due to initial kernel fit",
 
  940                             thisIteration, nRejectedSkf)
 
  944                 ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
 
  945                 kernelCellSet.visitCandidates(ksv, nStarPerCell)
 
  946                 ksv.processKsumDistribution()
 
  947                 ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
 
  948                 kernelCellSet.visitCandidates(ksv, nStarPerCell)
 
  950                 nRejectedKsum = ksv.getNRejected()
 
  951                 log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
  952                         "Iteration %d, rejected %d candidates due to kernel sum",
 
  953                         thisIteration, nRejectedKsum)
 
  956                 if nRejectedKsum > 0:
 
  965                 if (usePcaForSpatialKernel):
 
  966                     log.log(
"TRACE0." + self.log.getName() + 
"._solve", log.DEBUG,
 
  967                             "Building Pca basis")
 
  969                     nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, ps)
 
  970                     log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
  971                             "Iteration %d, rejected %d candidates due to Pca kernel fit",
 
  972                             thisIteration, nRejectedPca)
 
  983                     if (nRejectedPca > 0):
 
  987                     spatialBasisList = basisList
 
  990                 regionBBox = kernelCellSet.getBBox()
 
  991                 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
 
  992                 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
 
  993                 spatialkv.solveLinearEquation()
 
  994                 log.log(
"TRACE2." + self.log.getName() + 
"._solve", log.DEBUG,
 
  995                         "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
 
  996                 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
 
  999                 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, ps)
 
 1000                 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
 
 1001                 nRejectedSpatial = assesskv.getNRejected()
 
 1002                 nGoodSpatial = assesskv.getNGood()
 
 1003                 log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
 1004                         "Iteration %d, rejected %d candidates due to spatial kernel fit",
 
 1005                         thisIteration, nRejectedSpatial)
 
 1006                 log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG,
 
 1007                         "%d candidates used in fit", nGoodSpatial)
 
 1010                 if nGoodSpatial == 0 
and nRejectedSpatial == 0:
 
 1011                     raise RuntimeError(
"No kernel candidates for spatial fit")
 
 1013                 if nRejectedSpatial == 0:
 
 1021             if (nRejectedSpatial > 0) 
and (thisIteration == maxSpatialIterations):
 
 1022                 log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG, 
"Final spatial fit")
 
 1023                 if (usePcaForSpatialKernel):
 
 1024                     nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, ps)
 
 1025                 regionBBox = kernelCellSet.getBBox()
 
 1026                 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
 
 1027                 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
 
 1028                 spatialkv.solveLinearEquation()
 
 1029                 log.log(
"TRACE2." + self.log.getName() + 
"._solve", log.DEBUG,
 
 1030                         "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
 
 1031                 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
 
 1033             spatialSolution = spatialkv.getKernelSolution()
 
 1035         except Exception 
as e:
 
 1036             self.log.
error(
"ERROR: Unable to calculate psf matching kernel")
 
 1038             log.log(
"TRACE1." + self.log.getName() + 
"._solve", log.DEBUG, str(e))
 
 1042         log.log(
"TRACE0." + self.log.getName() + 
"._solve", log.DEBUG,
 
 1043                 "Total time to compute the spatial kernel : %.2f s", (t1 - t0))
 
 1046             self.
_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
 
 1048         self.
_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
 
 1050         return spatialSolution, spatialKernel, spatialBackground
 
 1053 PsfMatch = PsfMatchTask