31 from .
import utils
as diUtils
32 from .
import diffimLib
35 """!Configuration for detecting sources on images for building a PSF-matching kernel
37 Configuration for turning detected lsst.afw.detection.FootPrints into an acceptable
38 (unmasked, high signal-to-noise, not too large or not too small) list of
39 lsst.ip.diffim.KernelSources that are used to build the Psf-matching kernel"""
41 detThreshold = pexConfig.Field(
43 doc =
"Value of footprint detection threshold",
45 check =
lambda x : x >= 3.0
47 detThresholdType = pexConfig.ChoiceField(
49 doc =
"Type of detection threshold",
50 default =
"pixel_stdev",
52 "value" :
"Use counts as the detection threshold type",
53 "stdev" :
"Use standard deviation of image plane",
54 "variance" :
"Use variance of image plane",
55 "pixel_stdev" :
"Use stdev derived from variance plane"
58 detOnTemplate = pexConfig.Field(
60 doc =
"""If true run detection on the template (image to convolve);
61 if false run detection on the science image""",
64 badMaskPlanes = pexConfig.ListField(
66 doc =
"""Mask planes that lead to an invalid detection.
67 Options: NO_DATA EDGE SAT BAD CR INTRP""",
68 default = (
"NO_DATA",
"EDGE",
"SAT")
70 fpNpixMin = pexConfig.Field(
72 doc =
"Minimum number of pixels in an acceptable Footprint",
74 check =
lambda x : x >= 5
76 fpNpixMax = pexConfig.Field(
78 doc =
"""Maximum number of pixels in an acceptable Footprint;
79 too big and the subsequent convolutions become unwieldy""",
81 check =
lambda x : x <= 500
83 fpGrowKernelScaling = pexConfig.Field(
85 doc =
"""If config.scaleByFwhm, grow the footprint based on
86 the final kernelSize. Each footprint will be
87 2*fpGrowKernelScaling*kernelSize x
88 2*fpGrowKernelScaling*kernelSize. With the value
89 of 1.0, the remaining pixels in each KernelCandiate
90 after convolution by the basis functions will be
91 eqaul to the kernel size iteslf.""",
93 check =
lambda x : x >= 1.0
95 fpGrowPix = pexConfig.Field(
97 doc =
"""Growing radius (in pixels) for each raw detection
98 footprint. The smaller the faster; however the
99 kernel sum does not converge if the stamp is too
100 small; and the kernel is not constrained at all if
101 the stamp is the size of the kernel. The grown stamp
102 is 2 * fpGrowPix pixels larger in each dimension.
103 This is overridden by fpGrowKernelScaling if scaleByFwhm""",
105 check =
lambda x : x >= 10
107 scaleByFwhm = pexConfig.Field(
109 doc =
"Scale fpGrowPix by input Fwhm?",
115 """!Base configuration for Psf-matching
117 The base configuration of the Psf-matching kernel, and of the warping, detection,
118 and background modeling subTasks."""
120 warpingConfig = pexConfig.ConfigField(
"Config for warping exposures to a common alignment",
121 afwMath.warper.WarperConfig)
122 detectionConfig = pexConfig.ConfigField(
"Controlling the detection of sources for kernel building",
124 afwBackgroundConfig = pexConfig.ConfigField(
"Controlling the Afw background fitting",
125 SubtractBackgroundConfig)
127 useAfwBackground = pexConfig.Field(
129 doc =
"Use afw background subtraction instead of ip_diffim",
132 fitForBackground = pexConfig.Field(
134 doc =
"Include terms (including kernel cross terms) for background in ip_diffim",
137 kernelBasisSet = pexConfig.ChoiceField(
139 doc =
"Type of basis set for PSF matching kernel.",
140 default =
"alard-lupton",
142 "alard-lupton" :
"""Alard-Lupton sum-of-gaussians basis set,
143 * The first term has no spatial variation
144 * The kernel sum is conserved
145 * You may want to turn off 'usePcaForSpatialKernel'""",
146 "delta-function" :
"""Delta-function kernel basis set,
147 * You may enable the option useRegularization
148 * You should seriously consider usePcaForSpatialKernel, which will also
149 enable kernel sum conservation for the delta function kernels"""
152 kernelSize = pexConfig.Field(
154 doc =
"""Number of rows/columns in the convolution kernel; should be odd-valued.
155 Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
158 scaleByFwhm = pexConfig.Field(
160 doc =
"Scale kernelSize, alardGaussians by input Fwhm",
163 kernelSizeFwhmScaling = pexConfig.Field(
165 doc =
"""How much to scale the kernel size based on the largest AL Sigma""",
167 check =
lambda x : x >= 1.0
169 kernelSizeMin = pexConfig.Field(
171 doc =
"""Minimum Kernel Size""",
174 kernelSizeMax = pexConfig.Field(
176 doc =
"""Maximum Kernel Size""",
179 spatialModelType = pexConfig.ChoiceField(
181 doc =
"Type of spatial functions for kernel and background",
182 default =
"chebyshev1",
184 "chebyshev1" :
"Chebyshev polynomial of the first kind",
185 "polynomial" :
"Standard x,y polynomial",
188 spatialKernelOrder = pexConfig.Field(
190 doc =
"Spatial order of convolution kernel variation",
192 check =
lambda x : x >= 0
194 spatialBgOrder = pexConfig.Field(
196 doc =
"Spatial order of differential background variation",
198 check =
lambda x : x >= 0
200 sizeCellX = pexConfig.Field(
202 doc =
"Size (rows) in pixels of each SpatialCell for spatial modeling",
204 check =
lambda x : x >= 32
206 sizeCellY = pexConfig.Field(
208 doc =
"Size (columns) in pixels of each SpatialCell for spatial modeling",
210 check =
lambda x : x >= 32
212 nStarPerCell = pexConfig.Field(
214 doc =
"Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
216 check =
lambda x : x >= 1
218 maxSpatialIterations = pexConfig.Field(
220 doc =
"Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
222 check =
lambda x : x >= 1
and x <= 5
224 usePcaForSpatialKernel = pexConfig.Field(
226 doc =
"""Use Pca to reduce the dimensionality of the kernel basis sets.
227 This is particularly useful for delta-function kernels.
228 Functionally, after all Cells have their raw kernels determined, we run
229 a Pca on these Kernels, re-fit the Cells using the eigenKernels and then
230 fit those for spatial variation using the same technique as for Alard-Lupton kernels.
231 If this option is used, the first term will have no spatial variation and the
232 kernel sum will be conserved.""",
235 subtractMeanForPca = pexConfig.Field(
237 doc =
"Subtract off the mean feature before doing the Pca",
240 numPrincipalComponents = pexConfig.Field(
242 doc =
"""Number of principal components to use for Pca basis, including the
243 mean kernel if requested.""",
245 check =
lambda x : x >= 3
247 singleKernelClipping = pexConfig.Field(
249 doc =
"Do sigma clipping on each raw kernel candidate",
252 kernelSumClipping = pexConfig.Field(
254 doc =
"Do sigma clipping on the ensemble of kernel sums",
257 spatialKernelClipping = pexConfig.Field(
259 doc =
"Do sigma clipping after building the spatial model",
262 checkConditionNumber = pexConfig.Field(
264 doc =
"""Test for maximum condition number when inverting a kernel matrix.
265 Anything above maxConditionNumber is not used and the candidate is set as BAD.
266 Also used to truncate inverse matrix in estimateBiasedRisk. However,
267 if you are doing any deconvolution you will want to turn this off, or use
268 a large maxConditionNumber""",
271 badMaskPlanes = pexConfig.ListField(
273 doc =
"""Mask planes to ignore when calculating diffim statistics
274 Options: NO_DATA EDGE SAT BAD CR INTRP""",
275 default = (
"NO_DATA",
"EDGE",
"SAT")
277 candidateResidualMeanMax = pexConfig.Field(
279 doc =
"""Rejects KernelCandidates yielding bad difference image quality.
280 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
281 Represents average over pixels of (image/sqrt(variance)).""",
283 check =
lambda x : x >= 0.0
285 candidateResidualStdMax = pexConfig.Field(
287 doc =
"""Rejects KernelCandidates yielding bad difference image quality.
288 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
289 Represents stddev over pixels of (image/sqrt(variance)).""",
291 check =
lambda x : x >= 0.0
293 useCoreStats = pexConfig.Field(
295 doc =
"""Use the core of the footprint for the quality statistics, instead of the entire footprint.
296 WARNING: if there is deconvolution we probably will need to turn this off""",
299 candidateCoreRadius = pexConfig.Field(
301 doc =
"""Radius for calculation of stats in 'core' of KernelCandidate diffim.
302 Total number of pixels used will be (2*radius)**2.
303 This is used both for 'core' diffim quality as well as ranking of
304 KernelCandidates by their total flux in this core""",
306 check =
lambda x : x >= 1
308 maxKsumSigma = pexConfig.Field(
310 doc =
"""Maximum allowed sigma for outliers from kernel sum distribution.
311 Used to reject variable objects from the kernel model""",
313 check =
lambda x : x >= 0.0
315 maxConditionNumber = pexConfig.Field(
317 doc =
"Maximum condition number for a well conditioned matrix",
319 check =
lambda x : x >= 0.0
321 conditionNumberType = pexConfig.ChoiceField(
323 doc =
"Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
324 default =
"EIGENVALUE",
326 "SVD" :
"Use singular values",
327 "EIGENVALUE" :
"Use eigen values (faster)",
330 maxSpatialConditionNumber = pexConfig.Field(
332 doc =
"Maximum condition number for a well conditioned spatial matrix",
334 check =
lambda x : x >= 0.0
336 iterateSingleKernel = pexConfig.Field(
338 doc =
"""Remake KernelCandidate using better variance estimate after first pass?
339 Primarily useful when convolving a single-depth image, otherwise not necessary.""",
342 constantVarianceWeighting = pexConfig.Field(
344 doc =
"""Use constant variance weighting in single kernel fitting?
345 In some cases this is better for bright star residuals.""",
348 calculateKernelUncertainty = pexConfig.Field(
350 doc =
"""Calculate kernel and background uncertainties for each kernel candidate?
351 This comes from the inverse of the covariance matrix.
352 Warning: regularization can cause problems for this step.""",
355 useBicForKernelBasis = pexConfig.Field(
357 doc =
"""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
363 """!The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis"""
366 PsfMatchConfig.setDefaults(self)
370 alardNGauss = pexConfig.Field(
372 doc =
"Number of Gaussians in alard-lupton basis",
374 check =
lambda x : x >= 1
376 alardDegGauss = pexConfig.ListField(
378 doc =
"Polynomial order of spatial modification of Gaussians. Must in number equal alardNGauss",
381 alardSigGauss = pexConfig.ListField(
383 doc =
"""Sigma in pixels of Gaussians (FWHM = 2.35 sigma). Must in number equal alardNGauss""",
384 default = (0.7, 1.5, 3.0),
386 alardGaussBeta = pexConfig.Field(
388 doc =
"""Default scale factor between Gaussian sigmas """,
390 check =
lambda x: x >= 0.0,
392 alardMinSig = pexConfig.Field(
394 doc =
"""Minimum Sigma (pixels) for Gaussians""",
396 check =
lambda x : x >= 0.25
398 alardDegGaussDeconv = pexConfig.Field(
400 doc =
"""Degree of spatial modification of ALL gaussians in AL basis during deconvolution""",
402 check =
lambda x : x >= 1
404 alardMinSigDeconv = pexConfig.Field(
406 doc =
"""Minimum Sigma (pixels) for Gaussians during deconvolution;
407 make smaller than alardMinSig as this is only indirectly used""",
409 check =
lambda x : x >= 0.25
411 alardNGaussDeconv = pexConfig.Field(
413 doc =
"Number of Gaussians in AL basis during deconvolution",
415 check =
lambda x : x >= 1
421 """!The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis"""
424 PsfMatchConfig.setDefaults(self)
431 useRegularization = pexConfig.Field(
433 doc =
"Use regularization to smooth the delta function kernels",
436 regularizationType = pexConfig.ChoiceField(
438 doc =
"Type of regularization.",
439 default =
"centralDifference",
441 "centralDifference":
"Penalize second derivative using 2-D stencil of central finite difference",
442 "forwardDifference":
"Penalize first, second, third derivatives using forward finite differeces"
445 centralRegularizationStencil = pexConfig.ChoiceField(
447 doc =
"Type of stencil to approximate central derivative (for centralDifference only)",
450 5 :
"5-point stencil including only adjacent-in-x,y elements",
451 9 :
"9-point stencil including diagonal elements"
454 forwardRegularizationOrders = pexConfig.ListField(
456 doc =
"Array showing which order derivatives to penalize (for forwardDifference only)",
458 itemCheck =
lambda x: (x > 0)
and (x < 4)
460 regularizationBorderPenalty = pexConfig.Field(
462 doc =
"Value of the penalty for kernel border pixels",
464 check =
lambda x : x >= 0.0
466 lambdaType = pexConfig.ChoiceField(
468 doc =
"How to choose the value of the regularization strength",
469 default =
"absolute",
471 "absolute" :
"Use lambdaValue as the value of regularization strength",
472 "relative" :
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
473 "minimizeBiasedRisk" :
"Minimize biased risk estimate",
474 "minimizeUnbiasedRisk" :
"Minimize unbiased risk estimate",
477 lambdaValue = pexConfig.Field(
479 doc =
"Value used for absolute determinations of regularization strength",
482 lambdaScaling = pexConfig.Field(
484 doc =
"Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
487 lambdaStepType = pexConfig.ChoiceField(
489 doc =
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
490 use log or linear steps""",
493 "log" :
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
494 "linear" :
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
497 lambdaMin = pexConfig.Field(
499 doc =
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
500 start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
503 lambdaMax = pexConfig.Field(
505 doc =
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
506 stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
509 lambdaStep = pexConfig.Field(
511 doc =
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
512 step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
527 \anchor PsfMatchTask_
529 \brief Base class for Psf Matching; should not be called directly
531 \section ip_diffim_psfmatch_Contents Contents
533 - \ref ip_diffim_psfmatch_Purpose
534 - \ref ip_diffim_psfmatch_Initialize
535 - \ref ip_diffim_psfmatch_IO
536 - \ref ip_diffim_psfmatch_Config
537 - \ref ip_diffim_psfmatch_Metadata
538 - \ref ip_diffim_psfmatch_Debug
539 - \ref ip_diffim_psfmatch_Example
541 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
543 \section ip_diffim_psfmatch_Purpose Description
545 PsfMatchTask is a base class that implements the core functionality for matching the
546 Psfs of two images using a spatially varying Psf-matching lsst.afw.math.LinearCombinationKernel.
547 The Task requires the user to provide an instance of an lsst.afw.math.SpatialCellSet,
548 filled with lsst.ip.diffim.KernelCandidate instances, and an lsst.afw.math.KernelList
549 of basis shapes that will be used for the decomposition. If requested, the Task
550 also performs background matching and returns the differential background model as an
551 lsst.afw.math.Kernel.SpatialFunction.
553 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
555 \section ip_diffim_psfmatch_Initialize Task initialization
557 \copydoc \_\_init\_\_
559 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
561 \section ip_diffim_psfmatch_IO Invoking the Task
563 As a base class, this Task is not directly invoked. However, run() methods that are
564 implemented on derived classes will make use of the core _solve() functionality,
565 which defines a sequence of lsst.afw.math.CandidateVisitor classes that iterate
566 through the KernelCandidates, first building up a per-candidate solution and then
567 building up a spatial model from the ensemble of candidates. Sigma clipping is
568 performed using the mean and standard deviation of all kernel sums (to reject
569 variable objects), on the per-candidate substamp diffim residuals
570 (to indicate a bad choice of kernel basis shapes for that particular object),
571 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad
572 choice of spatial kernel order, or poor constraints on the spatial model). The
573 _diagnostic() method logs information on the quality of the spatial fit, and also
574 modifies the Task metadata.
576 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
578 \section ip_diffim_psfmatch_Config Configuration parameters
580 See \ref PsfMatchConfig, \ref PsfMatchConfigAL, \ref PsfMatchConfigDF, and \ref DetectionConfig.
582 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
584 \section ip_diffim_psfmatch_Metadata Quantities set in Metadata
588 <DT> spatialConditionNum <DD> Condition number of the spatial kernel fit;
589 via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
590 <DT> spatialKernelSum <DD> Kernel sum (10^{-0.4 * Δ zeropoint}) of the spatial Psf-matching kernel;
591 via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
593 <DT> ALBasisNGauss <DD> If using sum-of-Gaussian basis, the number of gaussians used;
594 via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
595 generateAlardLuptonBasisList\endlink </DD> </DT>
596 <DT> ALBasisDegGauss <DD> If using sum-of-Gaussian basis, the degree of spatial variation of the Gaussians;
597 via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
598 generateAlardLuptonBasisList\endlink </DD> </DT>
599 <DT> ALBasisSigGauss <DD> If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians;
600 via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
601 generateAlardLuptonBasisList\endlink </DD> </DT>
602 <DT> ALKernelSize <DD> If using sum-of-Gaussian basis, the kernel size;
603 via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
604 generateAlardLuptonBasisList\endlink </DD> </DT>
606 <DT> NFalsePositivesTotal <DD> Total number of diaSources;
607 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
608 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the reference catalog;
609 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
610 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the source catalog;
611 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
612 <DT> NFalsePositivesUnassociated <DD> Number of diaSources that are orphans;
613 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
614 <DT> metric_MEAN <DD> Mean value of substamp diffim quality metrics across all KernelCandidates,
615 for both the per-candidate (LOCAL) and SPATIAL residuals;
616 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
617 <DT> metric_MEDIAN <DD> Median value of substamp diffim quality metrics across all KernelCandidates,
618 for both the per-candidate (LOCAL) and SPATIAL residuals;
619 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
620 <DT> metric_STDEV <DD> Standard deviation of substamp diffim quality metrics across all KernelCandidates,
621 for both the per-candidate (LOCAL) and SPATIAL residuals;
622 via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
626 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
628 \section ip_diffim_psfmatch_Debug Debug variables
631 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
632 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py
633 for this Task include:
639 di = lsstDebug.getInfo(name)
640 if name == "lsst.ip.diffim.psfMatch":
641 di.display = True # enable debug output
642 di.maskTransparency = 80 # ds9 mask transparency
643 di.displayCandidates = True # show all the candidates and residuals
644 di.displayKernelBasis = False # show kernel basis functions
645 di.displayKernelMosaic = True # show kernel realized across the image
646 di.plotKernelSpatialModel = False # show coefficients of spatial model
647 di.showBadCandidates = True # show the bad candidates (red) along with good (green)
649 lsstDebug.Info = DebugInfo
654 Note that if you want addional logging info, you may add to your scripts:
656 import lsst.pex.logging as pexLog
657 pexLog.Trace_setVerbosity('lsst.ip.diffim', 5)
660 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
662 \section ip_diffim_psfmatch_Example Example code
664 As a base class, there is no example code for PsfMatchTask.
665 However, see \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask ImagePsfMatchTask\endlink,
666 \link lsst.ip.diffim.snapPsfMatch.SnapPsfMatchTask SnapPsfMatchTask\endlink, and
667 \link lsst.ip.diffim.modelPsfMatch.ModelPsfMatchTask ModelPsfMatchTask\endlink.
669 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
672 ConfigClass = PsfMatchConfig
673 _DefaultName =
"psfMatch"
676 """!Create the psf-matching Task
678 \param *args arguments to be passed to lsst.pipe.base.task.Task.__init__
679 \param **kwargs keyword arguments to be passed to lsst.pipe.base.task.Task.__init__
681 The initialization sets the Psf-matching kernel configuration using the value of
682 self.config.kernel.active. If the kernel is requested with regularization to moderate
683 the bias/variance tradeoff, currently only used when a delta function kernel basis
684 is provided, it creates a regularization matrix stored as member variable
687 pipeBase.Task.__init__(self, *args, **kwargs)
691 if 'useRegularization' in self.kConfig.keys():
697 self.
hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePolicy(self.
kConfig))
699 def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
700 """!Provide logging diagnostics on quality of spatial kernel fit
702 @param kernelCellSet: Cellset that contains the KernelCandidates used in the fitting
703 @param spatialSolution: KernelSolution of best-fit
704 @param spatialKernel: Best-fit spatial Kernel model
705 @param spatialBg: Best-fit spatial background model
709 kImage = afwImage.ImageD(spatialKernel.getDimensions())
710 kSum = spatialKernel.computeImage(kImage,
False)
711 self.log.info(
"Final spatial kernel sum %.3f" % (kSum))
714 conditionNum = spatialSolution.getConditionNumber(
715 getattr(diffimLib.KernelSolution, self.kConfig.conditionNumberType))
716 self.log.info(
"Spatial model condition number %.3e" % (conditionNum))
718 if conditionNum < 0.0:
719 self.log.warn(
"Condition number is negative (%.3e)" % (conditionNum))
720 if conditionNum > self.kConfig.maxSpatialConditionNumber:
721 self.log.warn(
"Spatial solution exceeds max condition number (%.3e > %.3e)" % (
722 conditionNum, self.kConfig.maxSpatialConditionNumber))
724 self.metadata.set(
"spatialConditionNum", conditionNum)
725 self.metadata.set(
"spatialKernelSum", kSum)
728 nBasisKernels = spatialKernel.getNBasisKernels()
729 nKernelTerms = spatialKernel.getNSpatialParameters()
730 if nKernelTerms == 0:
734 nBgTerms = spatialBg.getNParameters()
736 if spatialBg.getParameters()[0] == 0.0:
742 for cell
in kernelCellSet.getCellList():
743 for cand
in cell.begin(
False):
744 cand = diffimLib.cast_KernelCandidateF(cand)
746 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
748 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
751 self.log.info(
"Doing stats of kernel candidates used in the spatial fit.")
755 self.log.warn(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
758 self.log.info(
"%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
761 if nGood < nKernelTerms:
762 self.log.warn(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
763 nGood, nKernelTerms, nBasisKernels))
764 self.log.warn(
"Consider lowering the spatial order")
765 elif nGood <= 2*nKernelTerms:
766 self.log.warn(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
767 nGood, nKernelTerms, nBasisKernels))
768 self.log.warn(
"Consider lowering the spatial order")
770 self.log.info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
771 nGood, nKernelTerms, nBasisKernels))
774 self.log.warn(
"Spatial background model underconstrained; %d candidates, %d terms" % (
776 self.log.warn(
"Consider lowering the spatial order")
777 elif nGood <= 2*nBgTerms:
778 self.log.warn(
"Spatial background model poorly constrained; %d candidates, %d terms" % (
780 self.log.warn(
"Consider lowering the spatial order")
782 self.log.info(
"Spatial background model appears well constrained; %d candidates, %d terms" % (
786 """!Provide visualization of the inputs and ouputs to the Psf-matching code
788 @param kernelCellSet: the SpatialCellSet used in determining the matching kernel and background
789 @param spatialKernel: spatially varying Psf-matching kernel
790 @param spatialBackground: spatially varying background-matching function
796 displayKernelMosaic =
lsstDebug.Info(__name__).displayKernelMosaic
797 plotKernelSpatialModel =
lsstDebug.Info(__name__).plotKernelSpatialModel
800 if not maskTransparency:
802 ds9.setMaskTransparency(maskTransparency)
804 if displayCandidates:
805 diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
806 frame=lsstDebug.frame,
807 showBadCandidates=showBadCandidates)
809 diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
810 frame=lsstDebug.frame,
811 showBadCandidates=showBadCandidates,
814 diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
815 frame=lsstDebug.frame,
816 showBadCandidates=showBadCandidates,
820 if displayKernelBasis:
821 diUtils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
824 if displayKernelMosaic:
825 diUtils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
828 if plotKernelSpatialModel:
829 diUtils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
833 """!Create Principal Component basis
835 If a principal component analysis is requested, typically when using a delta function basis,
836 perform the PCA here and return a new basis list containing the new principal components.
838 @param kernelCellSet: a SpatialCellSet containing KernelCandidates, from which components are derived
839 @param nStarPerCell: the number of stars per cell to visit when doing the PCA
840 @param policy: input policy controlling the single kernel visitor
843 - nRejectedPca: number of KernelCandidates rejected during PCA loop
844 - spatialBasisList: basis list containing the principal shapes as Kernels
847 nComponents = self.kConfig.numPrincipalComponents
848 imagePca = diffimLib.KernelPcaD()
849 importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
850 kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
851 if self.kConfig.subtractMeanForPca:
852 importStarVisitor.subtractMean()
855 eigenValues = imagePca.getEigenValues()
856 pcaBasisList = importStarVisitor.getEigenKernels()
858 eSum = num.sum(eigenValues)
860 raise RuntimeError(
"Eigenvalues sum to zero")
861 for j
in range(len(eigenValues)):
863 "Eigenvalue %d : %f (%f)" % (j, eigenValues[j], eigenValues[j]/eSum))
865 nToUse = min(nComponents, len(eigenValues))
867 for j
in range(nToUse):
869 kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
870 pcaBasisList[j].computeImage(kimage,
False)
871 if not (
True in num.isnan(kimage.getArray())):
872 trimBasisList.push_back(pcaBasisList[j])
875 spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
878 singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, policy)
879 singlekvPca.setSkipBuilt(
False)
880 kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
881 singlekvPca.setSkipBuilt(
True)
882 nRejectedPca = singlekvPca.getNRejected()
884 return nRejectedPca, spatialBasisList
888 """!Fill a SpatialCellSet with KernelCandidates for the Psf-matching process;
889 override in derived classes"""
893 def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
894 """!Solve for the PSF matching kernel
896 @param kernelCellSet: a SpatialCellSet to use in determining the matching kernel
897 (typically as provided by _buildCellSet)
898 @param basisList: list of Kernels to be used in the decomposition of the spatially varying kernel
899 (typically as provided by makeKernelBasisList)
900 @param returnOnExcept: if True then return (None, None) if an error occurs, else raise the exception
903 - psfMatchingKernel: PSF matching kernel
904 - backgroundModel: differential background model
906 Raise Exception if unable to determine PSF matching kernel and returnOnExcept False
912 maxSpatialIterations = self.kConfig.maxSpatialIterations
913 nStarPerCell = self.kConfig.nStarPerCell
914 usePcaForSpatialKernel = self.kConfig.usePcaForSpatialKernel
917 policy = pexConfig.makePolicy(self.
kConfig)
919 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy, self.
hMat)
921 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
924 ksv = diffimLib.KernelSumVisitorF(policy)
931 while (thisIteration < maxSpatialIterations):
935 while (nRejectedSkf != 0):
937 "Building single kernels...")
938 kernelCellSet.visitCandidates(singlekv, nStarPerCell)
939 nRejectedSkf = singlekv.getNRejected()
941 "Iteration %d, rejected %d candidates due to initial kernel fit" %
942 (thisIteration, nRejectedSkf))
946 ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
947 kernelCellSet.visitCandidates(ksv, nStarPerCell)
948 ksv.processKsumDistribution()
949 ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
950 kernelCellSet.visitCandidates(ksv, nStarPerCell)
952 nRejectedKsum = ksv.getNRejected()
954 "Iteration %d, rejected %d candidates due to kernel sum" %
955 (thisIteration, nRejectedKsum))
959 if nRejectedKsum > 0:
968 if (usePcaForSpatialKernel):
969 pexLog.Trace(self.log.getName()+
"._solve", 1,
"Building Pca basis")
971 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
973 "Iteration %d, rejected %d candidates due to Pca kernel fit" % (
974 thisIteration, nRejectedPca))
985 if (nRejectedPca > 0):
989 spatialBasisList = basisList
992 regionBBox = kernelCellSet.getBBox()
993 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
994 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
995 spatialkv.solveLinearEquation()
997 "Spatial kernel built with %d candidates" % (spatialkv.getNCandidates()))
998 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1001 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, policy)
1002 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1003 nRejectedSpatial = assesskv.getNRejected()
1004 nGoodSpatial = assesskv.getNGood()
1006 "Iteration %d, rejected %d candidates due to spatial kernel fit" % (
1007 thisIteration, nRejectedSpatial))
1009 "%d candidates used in fit" % (nGoodSpatial))
1012 if nGoodSpatial == 0
and nRejectedSpatial == 0:
1013 raise RuntimeError(
"No kernel candidates for spatial fit")
1015 if nRejectedSpatial == 0:
1023 if (nRejectedSpatial > 0)
and (thisIteration == maxSpatialIterations):
1024 pexLog.Trace(self.log.getName()+
"._solve", 2,
"Final spatial fit")
1025 if (usePcaForSpatialKernel):
1026 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
1027 regionBBox = kernelCellSet.getBBox()
1028 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
1029 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1030 spatialkv.solveLinearEquation()
1032 "Spatial kernel built with %d candidates" % (spatialkv.getNCandidates()))
1033 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1035 spatialSolution = spatialkv.getKernelSolution()
1037 except Exception
as e:
1038 pexLog.Trace(self.log.getName()+
"._solve", 1,
"ERROR: Unable to calculate psf matching kernel")
1044 "Total time to compute the spatial kernel : %.2f s" % (t1 - t0))
1047 self.
_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1049 self.
_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1051 return spatialSolution, spatialKernel, spatialBackground
1053 PsfMatch=PsfMatchTask
Configuration for detecting sources on images for building a PSF-matching kernel. ...
The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis.
Base configuration for Psf-matching.
The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis.
Base class for Psf Matching; should not be called directly.
def _displayDebug
Provide visualization of the inputs and ouputs to the Psf-matching code.
def _diagnostic
Provide logging diagnostics on quality of spatial kernel fit.
limited backward compatibility to the DC2 run-time trace facilities
def __init__
Create the psf-matching Task.
def _buildCellSet
Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; override in derived classes...
def _createPcaBasis
Create Principal Component basis.
tuple usePcaForSpatialKernel
def _solve
Solve for the PSF matching kernel.
std::vector< boost::shared_ptr< Kernel > > KernelList
tuple useBicForKernelBasis