22 __all__ = [
"DetectionConfig",
"PsfMatchConfig",
"PsfMatchConfigAL",
"PsfMatchConfigDF",
"PsfMatchTask"]
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