22 __all__ = [
"DetectionConfig",
"PsfMatchConfig",
"PsfMatchConfigAL",
"PsfMatchConfigDF",
"PsfMatchTask"]
35 from lsst.utils.timer
import timeMethod
36 from .
import utils
as diutils
37 from .
import diffimLib
41 """Configuration for detecting sources on images for building a
44 Configuration for turning detected lsst.afw.detection.FootPrints into an
45 acceptable (unmasked, high signal-to-noise, not too large or not too small)
46 list of `lsst.ip.diffim.KernelSources` that are used to build the
47 Psf-matching kernel"""
49 detThreshold = pexConfig.Field(
51 doc=
"Value of footprint detection threshold",
53 check=
lambda x: x >= 3.0
55 detThresholdType = pexConfig.ChoiceField(
57 doc=
"Type of detection threshold",
58 default=
"pixel_stdev",
60 "value":
"Use counts as the detection threshold type",
61 "stdev":
"Use standard deviation of image plane",
62 "variance":
"Use variance of image plane",
63 "pixel_stdev":
"Use stdev derived from variance plane"
66 detOnTemplate = pexConfig.Field(
68 doc=
"""If true run detection on the template (image to convolve);
69 if false run detection on the science image""",
72 badMaskPlanes = pexConfig.ListField(
74 doc=
"""Mask planes that lead to an invalid detection.
75 Options: NO_DATA EDGE SAT BAD CR INTRP""",
76 default=(
"NO_DATA",
"EDGE",
"SAT")
78 fpNpixMin = pexConfig.Field(
80 doc=
"Minimum number of pixels in an acceptable Footprint",
82 check=
lambda x: x >= 5
84 fpNpixMax = pexConfig.Field(
86 doc=
"""Maximum number of pixels in an acceptable Footprint;
87 too big and the subsequent convolutions become unwieldy""",
89 check=
lambda x: x <= 500
91 fpGrowKernelScaling = pexConfig.Field(
93 doc=
"""If config.scaleByFwhm, grow the footprint based on
94 the final kernelSize. Each footprint will be
95 2*fpGrowKernelScaling*kernelSize x
96 2*fpGrowKernelScaling*kernelSize. With the value
97 of 1.0, the remaining pixels in each KernelCandiate
98 after convolution by the basis functions will be
99 equal to the kernel size itself.""",
101 check=
lambda x: x >= 1.0
103 fpGrowPix = pexConfig.Field(
105 doc=
"""Growing radius (in pixels) for each raw detection
106 footprint. The smaller the faster; however the
107 kernel sum does not converge if the stamp is too
108 small; and the kernel is not constrained at all if
109 the stamp is the size of the kernel. The grown stamp
110 is 2 * fpGrowPix pixels larger in each dimension.
111 This is overridden by fpGrowKernelScaling if scaleByFwhm""",
113 check=
lambda x: x >= 10
115 scaleByFwhm = pexConfig.Field(
117 doc=
"Scale fpGrowPix by input Fwhm?",
123 """Base configuration for Psf-matching
125 The base configuration of the Psf-matching kernel, and of the warping, detection,
126 and background modeling subTasks."""
128 warpingConfig = pexConfig.ConfigField(
"Config for warping exposures to a common alignment",
130 detectionConfig = pexConfig.ConfigField(
"Controlling the detection of sources for kernel building",
132 afwBackgroundConfig = pexConfig.ConfigField(
"Controlling the Afw background fitting",
133 SubtractBackgroundConfig)
135 useAfwBackground = pexConfig.Field(
137 doc=
"Use afw background subtraction instead of ip_diffim",
140 fitForBackground = pexConfig.Field(
142 doc=
"Include terms (including kernel cross terms) for background in ip_diffim",
145 kernelBasisSet = pexConfig.ChoiceField(
147 doc=
"Type of basis set for PSF matching kernel.",
148 default=
"alard-lupton",
150 "alard-lupton":
"""Alard-Lupton sum-of-gaussians basis set,
151 * The first term has no spatial variation
152 * The kernel sum is conserved
153 * You may want to turn off 'usePcaForSpatialKernel'""",
154 "delta-function":
"""Delta-function kernel basis set,
155 * You may enable the option useRegularization
156 * You should seriously consider usePcaForSpatialKernel, which will also
157 enable kernel sum conservation for the delta function kernels"""
160 kernelSize = pexConfig.Field(
162 doc=
"""Number of rows/columns in the convolution kernel; should be odd-valued.
163 Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
166 scaleByFwhm = pexConfig.Field(
168 doc=
"Scale kernelSize, alardGaussians by input Fwhm",
171 kernelSizeFwhmScaling = pexConfig.Field(
173 doc=
"Multiplier of the largest AL Gaussian basis sigma to get the kernel bbox (pixel) size.",
175 check=
lambda x: x >= 1.0
177 kernelSizeMin = pexConfig.Field(
179 doc=
"Minimum kernel bbox (pixel) size.",
182 kernelSizeMax = pexConfig.Field(
184 doc=
"Maximum kernel bbox (pixel) size.",
187 spatialModelType = pexConfig.ChoiceField(
189 doc=
"Type of spatial functions for kernel and background",
190 default=
"chebyshev1",
192 "chebyshev1":
"Chebyshev polynomial of the first kind",
193 "polynomial":
"Standard x,y polynomial",
196 spatialKernelOrder = pexConfig.Field(
198 doc=
"Spatial order of convolution kernel variation",
200 check=
lambda x: x >= 0
202 spatialBgOrder = pexConfig.Field(
204 doc=
"Spatial order of differential background variation",
206 check=
lambda x: x >= 0
208 sizeCellX = pexConfig.Field(
210 doc=
"Size (rows) in pixels of each SpatialCell for spatial modeling",
212 check=
lambda x: x >= 32
214 sizeCellY = pexConfig.Field(
216 doc=
"Size (columns) in pixels of each SpatialCell for spatial modeling",
218 check=
lambda x: x >= 32
220 nStarPerCell = pexConfig.Field(
222 doc=
"Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
224 check=
lambda x: x >= 1
226 maxSpatialIterations = pexConfig.Field(
228 doc=
"Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
230 check=
lambda x: x >= 1
and x <= 5
232 usePcaForSpatialKernel = pexConfig.Field(
234 doc=
"""Use Pca to reduce the dimensionality of the kernel basis sets.
235 This is particularly useful for delta-function kernels.
236 Functionally, after all Cells have their raw kernels determined, we run
237 a Pca on these Kernels, re-fit the Cells using the eigenKernels and then
238 fit those for spatial variation using the same technique as for Alard-Lupton kernels.
239 If this option is used, the first term will have no spatial variation and the
240 kernel sum will be conserved.""",
243 subtractMeanForPca = pexConfig.Field(
245 doc=
"Subtract off the mean feature before doing the Pca",
248 numPrincipalComponents = pexConfig.Field(
250 doc=
"""Number of principal components to use for Pca basis, including the
251 mean kernel if requested.""",
253 check=
lambda x: x >= 3
255 singleKernelClipping = pexConfig.Field(
257 doc=
"Do sigma clipping on each raw kernel candidate",
260 kernelSumClipping = pexConfig.Field(
262 doc=
"Do sigma clipping on the ensemble of kernel sums",
265 spatialKernelClipping = pexConfig.Field(
267 doc=
"Do sigma clipping after building the spatial model",
270 checkConditionNumber = pexConfig.Field(
272 doc=
"""Test for maximum condition number when inverting a kernel matrix.
273 Anything above maxConditionNumber is not used and the candidate is set as BAD.
274 Also used to truncate inverse matrix in estimateBiasedRisk. However,
275 if you are doing any deconvolution you will want to turn this off, or use
276 a large maxConditionNumber""",
279 badMaskPlanes = pexConfig.ListField(
281 doc=
"""Mask planes to ignore when calculating diffim statistics
282 Options: NO_DATA EDGE SAT BAD CR INTRP""",
283 default=(
"NO_DATA",
"EDGE",
"SAT")
285 candidateResidualMeanMax = pexConfig.Field(
287 doc=
"""Rejects KernelCandidates yielding bad difference image quality.
288 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
289 Represents average over pixels of (image/sqrt(variance)).""",
291 check=
lambda x: x >= 0.0
293 candidateResidualStdMax = pexConfig.Field(
295 doc=
"""Rejects KernelCandidates yielding bad difference image quality.
296 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
297 Represents stddev over pixels of (image/sqrt(variance)).""",
299 check=
lambda x: x >= 0.0
301 useCoreStats = pexConfig.Field(
303 doc=
"""Use the core of the footprint for the quality statistics, instead of the entire footprint.
304 WARNING: if there is deconvolution we probably will need to turn this off""",
307 candidateCoreRadius = pexConfig.Field(
309 doc=
"""Radius for calculation of stats in 'core' of KernelCandidate diffim.
310 Total number of pixels used will be (2*radius)**2.
311 This is used both for 'core' diffim quality as well as ranking of
312 KernelCandidates by their total flux in this core""",
314 check=
lambda x: x >= 1
316 maxKsumSigma = pexConfig.Field(
318 doc=
"""Maximum allowed sigma for outliers from kernel sum distribution.
319 Used to reject variable objects from the kernel model""",
321 check=
lambda x: x >= 0.0
323 maxConditionNumber = pexConfig.Field(
325 doc=
"Maximum condition number for a well conditioned matrix",
327 check=
lambda x: x >= 0.0
329 conditionNumberType = pexConfig.ChoiceField(
331 doc=
"Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
332 default=
"EIGENVALUE",
334 "SVD":
"Use singular values",
335 "EIGENVALUE":
"Use eigen values (faster)",
338 maxSpatialConditionNumber = pexConfig.Field(
340 doc=
"Maximum condition number for a well conditioned spatial matrix",
342 check=
lambda x: x >= 0.0
344 iterateSingleKernel = pexConfig.Field(
346 doc=
"""Remake KernelCandidate using better variance estimate after first pass?
347 Primarily useful when convolving a single-depth image, otherwise not necessary.""",
350 constantVarianceWeighting = pexConfig.Field(
352 doc=
"""Use constant variance weighting in single kernel fitting?
353 In some cases this is better for bright star residuals.""",
356 calculateKernelUncertainty = pexConfig.Field(
358 doc=
"""Calculate kernel and background uncertainties for each kernel candidate?
359 This comes from the inverse of the covariance matrix.
360 Warning: regularization can cause problems for this step.""",
363 useBicForKernelBasis = pexConfig.Field(
365 doc=
"""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
371 """The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis"""
374 PsfMatchConfig.setDefaults(self)
378 alardNGauss = pexConfig.Field(
380 doc=
"Number of base Gaussians in alard-lupton kernel basis function generation.",
382 check=
lambda x: x >= 1
384 alardDegGauss = pexConfig.ListField(
386 doc=
"Polynomial order of spatial modification of base Gaussians. "
387 "List length must be `alardNGauss`.",
390 alardSigGauss = pexConfig.ListField(
392 doc=
"Default sigma values in pixels of base Gaussians. "
393 "List length must be `alardNGauss`.",
394 default=(0.7, 1.5, 3.0),
396 alardGaussBeta = pexConfig.Field(
398 doc=
"Used if `scaleByFwhm==True`, scaling multiplier of base "
399 "Gaussian sigmas for automated sigma determination",
401 check=
lambda x: x >= 0.0,
403 alardMinSig = pexConfig.Field(
405 doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians",
407 check=
lambda x: x >= 0.25
409 alardDegGaussDeconv = pexConfig.Field(
411 doc=
"Used if `scaleByFwhm==True`, degree of spatial modification of ALL base Gaussians "
412 "in AL basis during deconvolution",
414 check=
lambda x: x >= 1
416 alardMinSigDeconv = pexConfig.Field(
418 doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians during deconvolution; "
419 "make smaller than `alardMinSig` as this is only indirectly used",
421 check=
lambda x: x >= 0.25
423 alardNGaussDeconv = pexConfig.Field(
425 doc=
"Used if `scaleByFwhm==True`, number of base Gaussians in AL basis during deconvolution",
427 check=
lambda x: x >= 1
432 """The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis"""
435 PsfMatchConfig.setDefaults(self)
442 useRegularization = pexConfig.Field(
444 doc=
"Use regularization to smooth the delta function kernels",
447 regularizationType = pexConfig.ChoiceField(
449 doc=
"Type of regularization.",
450 default=
"centralDifference",
452 "centralDifference":
"Penalize second derivative using 2-D stencil of central finite difference",
453 "forwardDifference":
"Penalize first, second, third derivatives using forward finite differeces"
456 centralRegularizationStencil = pexConfig.ChoiceField(
458 doc=
"Type of stencil to approximate central derivative (for centralDifference only)",
461 5:
"5-point stencil including only adjacent-in-x,y elements",
462 9:
"9-point stencil including diagonal elements"
465 forwardRegularizationOrders = pexConfig.ListField(
467 doc=
"Array showing which order derivatives to penalize (for forwardDifference only)",
469 itemCheck=
lambda x: (x > 0)
and (x < 4)
471 regularizationBorderPenalty = pexConfig.Field(
473 doc=
"Value of the penalty for kernel border pixels",
475 check=
lambda x: x >= 0.0
477 lambdaType = pexConfig.ChoiceField(
479 doc=
"How to choose the value of the regularization strength",
482 "absolute":
"Use lambdaValue as the value of regularization strength",
483 "relative":
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
484 "minimizeBiasedRisk":
"Minimize biased risk estimate",
485 "minimizeUnbiasedRisk":
"Minimize unbiased risk estimate",
488 lambdaValue = pexConfig.Field(
490 doc=
"Value used for absolute determinations of regularization strength",
493 lambdaScaling = pexConfig.Field(
495 doc=
"Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
498 lambdaStepType = pexConfig.ChoiceField(
500 doc=
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
501 use log or linear steps""",
504 "log":
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
505 "linear":
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
508 lambdaMin = pexConfig.Field(
510 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
511 start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
514 lambdaMax = pexConfig.Field(
516 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
517 stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
520 lambdaStep = pexConfig.Field(
522 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
523 step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
529 """Base class for Psf Matching; should not be called directly
533 PsfMatchTask is a base class that implements the core functionality for matching the
534 Psfs of two images using a spatially varying Psf-matching `lsst.afw.math.LinearCombinationKernel`.
535 The Task requires the user to provide an instance of an `lsst.afw.math.SpatialCellSet`,
536 filled with `lsst.ip.diffim.KernelCandidate` instances, and a list of `lsst.afw.math.Kernels`
537 of basis shapes that will be used for the decomposition. If requested, the Task
538 also performs background matching and returns the differential background model as an
539 `lsst.afw.math.Kernel.SpatialFunction`.
541 **Invoking the Task**
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
593 The `lsst.pipe.base.cmdLineTask.CmdLineTask` command line task interface supports a
594 flag -d/--debug to import @b debug.py from your PYTHONPATH. The relevant contents of debug.py
595 for this Task include:
602 di = lsstDebug.getInfo(name)
603 if name == "lsst.ip.diffim.psfMatch":
604 # enable debug output
606 # display mask transparency
607 di.maskTransparency = 80
608 # show all the candidates and residuals
609 di.displayCandidates = True
610 # show kernel basis functions
611 di.displayKernelBasis = False
612 # show kernel realized across the image
613 di.displayKernelMosaic = True
614 # show coefficients of spatial model
615 di.plotKernelSpatialModel = False
616 # show fixed and spatial coefficients and coefficient histograms
617 di.plotKernelCoefficients = True
618 # show the bad candidates (red) along with good (green)
619 di.showBadCandidates = True
621 lsstDebug.Info = DebugInfo
624 Note that if you want additional logging info, you may add to your scripts:
628 import lsst.log.utils as logUtils
629 logUtils.traceSetAt("lsst.ip.diffim", 4)
631 ConfigClass = PsfMatchConfig
632 _DefaultName =
"psfMatch"
635 """Create the psf-matching Task
640 Arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
642 Keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
646 The initialization sets the Psf-matching kernel configuration using the value of
647 self.config.kernel.active. If the kernel is requested with regularization to moderate
648 the bias/variance tradeoff, currently only used when a delta function kernel basis
649 is provided, it creates a regularization matrix stored as member variable
652 pipeBase.Task.__init__(self, *args, **kwargs)
653 self.
kConfigkConfig = self.config.kernel.active
655 if 'useRegularization' in self.
kConfigkConfig:
661 self.
hMathMat = diffimLib.makeRegularizationMatrix(pexConfig.makePropertySet(self.
kConfigkConfig))
663 def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
664 """Provide logging diagnostics on quality of spatial kernel fit
668 kernelCellSet : `lsst.afw.math.SpatialCellSet`
669 Cellset that contains the KernelCandidates used in the fitting
670 spatialSolution : `lsst.ip.diffim.SpatialKernelSolution`
671 KernelSolution of best-fit
672 spatialKernel : `lsst.afw.math.LinearCombinationKernel`
673 Best-fit spatial Kernel model
674 spatialBg : `lsst.afw.math.Function2D`
675 Best-fit spatial background model
678 kImage = afwImage.ImageD(spatialKernel.getDimensions())
679 kSum = spatialKernel.computeImage(kImage,
False)
680 self.log.
info(
"Final spatial kernel sum %.3f", kSum)
683 conditionNum = spatialSolution.getConditionNumber(
684 getattr(diffimLib.KernelSolution, self.
kConfigkConfig.conditionNumberType))
685 self.log.
info(
"Spatial model condition number %.3e", conditionNum)
687 if conditionNum < 0.0:
688 self.log.
warning(
"Condition number is negative (%.3e)", conditionNum)
689 if conditionNum > self.
kConfigkConfig.maxSpatialConditionNumber:
690 self.log.
warning(
"Spatial solution exceeds max condition number (%.3e > %.3e)",
691 conditionNum, self.
kConfigkConfig.maxSpatialConditionNumber)
693 self.metadata[
"spatialConditionNum"] = conditionNum
694 self.metadata[
"spatialKernelSum"] = kSum
697 nBasisKernels = spatialKernel.getNBasisKernels()
698 nKernelTerms = spatialKernel.getNSpatialParameters()
699 if nKernelTerms == 0:
703 nBgTerms = spatialBg.getNParameters()
705 if spatialBg.getParameters()[0] == 0.0:
711 for cell
in kernelCellSet.getCellList():
712 for cand
in cell.begin(
False):
714 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
716 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
719 self.log.
info(
"Doing stats of kernel candidates used in the spatial fit.")
723 self.log.
warning(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used",
726 self.log.
info(
"%d candidates total, %d rejected, %d used", nTot, nBad, nGood)
729 if nGood < nKernelTerms:
730 self.log.
warning(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases",
731 nGood, nKernelTerms, nBasisKernels)
732 self.log.
warning(
"Consider lowering the spatial order")
733 elif nGood <= 2*nKernelTerms:
734 self.log.
warning(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases",
735 nGood, nKernelTerms, nBasisKernels)
736 self.log.
warning(
"Consider lowering the spatial order")
738 self.log.
info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases",
739 nGood, nKernelTerms, nBasisKernels)
742 self.log.
warning(
"Spatial background model underconstrained; %d candidates, %d terms",
744 self.log.
warning(
"Consider lowering the spatial order")
745 elif nGood <= 2*nBgTerms:
746 self.log.
warning(
"Spatial background model poorly constrained; %d candidates, %d terms",
748 self.log.
warning(
"Consider lowering the spatial order")
750 self.log.
info(
"Spatial background model appears well constrained; %d candidates, %d terms",
753 def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
754 """Provide visualization of the inputs and ouputs to the Psf-matching code
758 kernelCellSet : `lsst.afw.math.SpatialCellSet`
759 The SpatialCellSet used in determining the matching kernel and background
760 spatialKernel : `lsst.afw.math.LinearCombinationKernel`
761 Spatially varying Psf-matching kernel
762 spatialBackground : `lsst.afw.math.Function2D`
763 Spatially varying background-matching function
768 displayKernelMosaic =
lsstDebug.Info(__name__).displayKernelMosaic
769 plotKernelSpatialModel =
lsstDebug.Info(__name__).plotKernelSpatialModel
770 plotKernelCoefficients =
lsstDebug.Info(__name__).plotKernelCoefficients
773 if not maskTransparency:
775 afwDisplay.setDefaultMaskTransparency(maskTransparency)
777 if displayCandidates:
778 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
779 frame=lsstDebug.frame,
780 showBadCandidates=showBadCandidates)
782 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
783 frame=lsstDebug.frame,
784 showBadCandidates=showBadCandidates,
787 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
788 frame=lsstDebug.frame,
789 showBadCandidates=showBadCandidates,
793 if displayKernelBasis:
794 diutils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
797 if displayKernelMosaic:
798 diutils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
801 if plotKernelSpatialModel:
802 diutils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
804 if plotKernelCoefficients:
805 diutils.plotKernelCoefficients(spatialKernel, kernelCellSet)
807 def _createPcaBasis(self, kernelCellSet, nStarPerCell, ps):
808 """Create Principal Component basis
810 If a principal component analysis is requested, typically when using a delta function basis,
811 perform the PCA here and return a new basis list containing the new principal components.
815 kernelCellSet : `lsst.afw.math.SpatialCellSet`
816 a SpatialCellSet containing KernelCandidates, from which components are derived
818 the number of stars per cell to visit when doing the PCA
819 ps : `lsst.daf.base.PropertySet`
820 input property set controlling the single kernel visitor
825 number of KernelCandidates rejected during PCA loop
826 spatialBasisList : `list` of `lsst.afw.math.kernel.FixedKernel`
827 basis list containing the principal shapes as Kernels
832 If the Eigenvalues sum to zero.
834 nComponents = self.
kConfigkConfig.numPrincipalComponents
835 imagePca = diffimLib.KernelPcaD()
836 importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
837 kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
838 if self.
kConfigkConfig.subtractMeanForPca:
839 importStarVisitor.subtractMean()
842 eigenValues = imagePca.getEigenValues()
843 pcaBasisList = importStarVisitor.getEigenKernels()
845 eSum = np.sum(eigenValues)
847 raise RuntimeError(
"Eigenvalues sum to zero")
848 for j
in range(len(eigenValues)):
849 log.log(
"TRACE5." + self.log.name +
"._solve", log.DEBUG,
850 "Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
852 nToUse =
min(nComponents, len(eigenValues))
854 for j
in range(nToUse):
856 kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
857 pcaBasisList[j].computeImage(kimage,
False)
858 if not (
True in np.isnan(kimage.getArray())):
859 trimBasisList.append(pcaBasisList[j])
862 spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
865 singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, ps)
866 singlekvPca.setSkipBuilt(
False)
867 kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
868 singlekvPca.setSkipBuilt(
True)
869 nRejectedPca = singlekvPca.getNRejected()
871 return nRejectedPca, spatialBasisList
873 def _buildCellSet(self, *args):
874 """Fill a SpatialCellSet with KernelCandidates for the Psf-matching process;
875 override in derived classes"""
879 def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
880 """Solve for the PSF matching kernel
884 kernelCellSet : `lsst.afw.math.SpatialCellSet`
885 a SpatialCellSet to use in determining the matching kernel
886 (typically as provided by _buildCellSet)
887 basisList : `list` of `lsst.afw.math.kernel.FixedKernel`
888 list of Kernels to be used in the decomposition of the spatially varying kernel
889 (typically as provided by makeKernelBasisList)
890 returnOnExcept : `bool`, optional
891 if True then return (None, None) if an error occurs, else raise the exception
895 psfMatchingKernel : `lsst.afw.math.LinearCombinationKernel`
896 Spatially varying Psf-matching kernel
897 backgroundModel : `lsst.afw.math.Function2D`
898 Spatially varying background-matching function
903 If unable to determine PSF matching kernel and ``returnOnExcept==False``.
909 maxSpatialIterations = self.
kConfigkConfig.maxSpatialIterations
910 nStarPerCell = self.
kConfigkConfig.nStarPerCell
911 usePcaForSpatialKernel = self.
kConfigkConfig.usePcaForSpatialKernel
914 ps = pexConfig.makePropertySet(self.
kConfigkConfig)
916 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps, self.
hMathMat)
918 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps)
921 ksv = diffimLib.KernelSumVisitorF(ps)
928 while (thisIteration < maxSpatialIterations):
932 while (nRejectedSkf != 0):
933 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
934 "Building single kernels...")
935 kernelCellSet.visitCandidates(singlekv, nStarPerCell)
936 nRejectedSkf = singlekv.getNRejected()
937 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
938 "Iteration %d, rejected %d candidates due to initial kernel fit",
939 thisIteration, nRejectedSkf)
943 ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
944 kernelCellSet.visitCandidates(ksv, nStarPerCell)
945 ksv.processKsumDistribution()
946 ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
947 kernelCellSet.visitCandidates(ksv, nStarPerCell)
949 nRejectedKsum = ksv.getNRejected()
950 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
951 "Iteration %d, rejected %d candidates due to kernel sum",
952 thisIteration, nRejectedKsum)
955 if nRejectedKsum > 0:
964 if (usePcaForSpatialKernel):
965 log.log(
"TRACE0." + self.log.name +
"._solve", log.DEBUG,
966 "Building Pca basis")
968 nRejectedPca, spatialBasisList = self.
_createPcaBasis_createPcaBasis(kernelCellSet, nStarPerCell, ps)
969 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
970 "Iteration %d, rejected %d candidates due to Pca kernel fit",
971 thisIteration, nRejectedPca)
982 if (nRejectedPca > 0):
986 spatialBasisList = basisList
989 regionBBox = kernelCellSet.getBBox()
990 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
991 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
992 spatialkv.solveLinearEquation()
993 log.log(
"TRACE2." + self.log.name +
"._solve", log.DEBUG,
994 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
995 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
998 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, ps)
999 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1000 nRejectedSpatial = assesskv.getNRejected()
1001 nGoodSpatial = assesskv.getNGood()
1002 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
1003 "Iteration %d, rejected %d candidates due to spatial kernel fit",
1004 thisIteration, nRejectedSpatial)
1005 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
1006 "%d candidates used in fit", nGoodSpatial)
1009 if nGoodSpatial == 0
and nRejectedSpatial == 0:
1010 raise RuntimeError(
"No kernel candidates for spatial fit")
1012 if nRejectedSpatial == 0:
1020 if (nRejectedSpatial > 0)
and (thisIteration == maxSpatialIterations):
1021 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
"Final spatial fit")
1022 if (usePcaForSpatialKernel):
1023 nRejectedPca, spatialBasisList = self.
_createPcaBasis_createPcaBasis(kernelCellSet, nStarPerCell, ps)
1024 regionBBox = kernelCellSet.getBBox()
1025 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
1026 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1027 spatialkv.solveLinearEquation()
1028 log.log(
"TRACE2." + self.log.name +
"._solve", log.DEBUG,
1029 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1030 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1032 spatialSolution = spatialkv.getKernelSolution()
1034 except Exception
as e:
1035 self.log.
error(
"ERROR: Unable to calculate psf matching kernel")
1037 log.log(
"TRACE1." + self.log.name +
"._solve", log.DEBUG,
"%s", e)
1041 log.log(
"TRACE0." + self.log.name +
"._solve", log.DEBUG,
1042 "Total time to compute the spatial kernel : %.2f s", (t1 - t0))
1045 self.
_displayDebug_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1047 self.
_diagnostic_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1049 return spatialSolution, spatialKernel, spatialBackground
1052 PsfMatch = PsfMatchTask
def __init__(self, *args, **kwargs)
def _createPcaBasis(self, kernelCellSet, nStarPerCell, ps)
def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground)
def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.