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 Gaussians in alard-lupton basis",
381 check=
lambda x: x >= 1
383 alardDegGauss = pexConfig.ListField(
385 doc=
"Polynomial order of spatial modification of Gaussian basis functions. " 386 "List length must be `alardNGauss`.",
389 alardSigGauss = pexConfig.ListField(
391 doc=
"Default sigma values in pixels of Gaussian kernel base functions. " 392 "List length must be `alardNGauss`.",
393 default=(0.7, 1.5, 3.0),
395 alardGaussBeta = pexConfig.Field(
397 doc=
"""Multiplier of Gaussian sigmas between adjacent elements of the kernel basis list.""",
399 check=
lambda x: x >= 0.0,
401 alardMinSig = pexConfig.Field(
403 doc=
"""Minimum Sigma (pixels) for Gaussians""",
405 check=
lambda x: x >= 0.25
407 alardDegGaussDeconv = pexConfig.Field(
409 doc=
"""Degree of spatial modification of ALL gaussians in AL basis during deconvolution""",
411 check=
lambda x: x >= 1
413 alardMinSigDeconv = pexConfig.Field(
415 doc=
"Minimum Sigma (pixels) for Gaussians during deconvolution; " 416 "make smaller than alardMinSig as this is only indirectly used",
418 check=
lambda x: x >= 0.25
420 alardNGaussDeconv = pexConfig.Field(
422 doc=
"Number of Gaussians in AL basis during deconvolution",
424 check=
lambda x: x >= 1
429 """The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis""" 432 PsfMatchConfig.setDefaults(self)
439 useRegularization = pexConfig.Field(
441 doc=
"Use regularization to smooth the delta function kernels",
444 regularizationType = pexConfig.ChoiceField(
446 doc=
"Type of regularization.",
447 default=
"centralDifference",
449 "centralDifference":
"Penalize second derivative using 2-D stencil of central finite difference",
450 "forwardDifference":
"Penalize first, second, third derivatives using forward finite differeces" 453 centralRegularizationStencil = pexConfig.ChoiceField(
455 doc=
"Type of stencil to approximate central derivative (for centralDifference only)",
458 5:
"5-point stencil including only adjacent-in-x,y elements",
459 9:
"9-point stencil including diagonal elements" 462 forwardRegularizationOrders = pexConfig.ListField(
464 doc=
"Array showing which order derivatives to penalize (for forwardDifference only)",
466 itemCheck=
lambda x: (x > 0)
and (x < 4)
468 regularizationBorderPenalty = pexConfig.Field(
470 doc=
"Value of the penalty for kernel border pixels",
472 check=
lambda x: x >= 0.0
474 lambdaType = pexConfig.ChoiceField(
476 doc=
"How to choose the value of the regularization strength",
479 "absolute":
"Use lambdaValue as the value of regularization strength",
480 "relative":
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
481 "minimizeBiasedRisk":
"Minimize biased risk estimate",
482 "minimizeUnbiasedRisk":
"Minimize unbiased risk estimate",
485 lambdaValue = pexConfig.Field(
487 doc=
"Value used for absolute determinations of regularization strength",
490 lambdaScaling = pexConfig.Field(
492 doc=
"Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
495 lambdaStepType = pexConfig.ChoiceField(
497 doc=
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 498 use log or linear steps""",
501 "log":
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
502 "linear":
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
505 lambdaMin = pexConfig.Field(
507 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 508 start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
511 lambdaMax = pexConfig.Field(
513 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 514 stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
517 lambdaStep = pexConfig.Field(
519 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 520 step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
526 """Base class for Psf Matching; should not be called directly 530 PsfMatchTask is a base class that implements the core functionality for matching the 531 Psfs of two images using a spatially varying Psf-matching `lsst.afw.math.LinearCombinationKernel`. 532 The Task requires the user to provide an instance of an `lsst.afw.math.SpatialCellSet`, 533 filled with `lsst.ip.diffim.KernelCandidate` instances, and a list of `lsst.afw.math.Kernels` 534 of basis shapes that will be used for the decomposition. If requested, the Task 535 also performs background matching and returns the differential background model as an 536 `lsst.afw.math.Kernel.SpatialFunction`. 541 As a base class, this Task is not directly invoked. However, ``run()`` methods that are 542 implemented on derived classes will make use of the core ``_solve()`` functionality, 543 which defines a sequence of `lsst.afw.math.CandidateVisitor` classes that iterate 544 through the KernelCandidates, first building up a per-candidate solution and then 545 building up a spatial model from the ensemble of candidates. Sigma clipping is 546 performed using the mean and standard deviation of all kernel sums (to reject 547 variable objects), on the per-candidate substamp diffim residuals 548 (to indicate a bad choice of kernel basis shapes for that particular object), 549 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad 550 choice of spatial kernel order, or poor constraints on the spatial model). The 551 ``_diagnostic()`` method logs information on the quality of the spatial fit, and also 552 modifies the Task metadata. 554 .. list-table:: Quantities set in Metadata 559 * - ``spatialConditionNum`` 560 - Condition number of the spatial kernel fit 561 * - ``spatialKernelSum`` 562 - Kernel sum (10^{-0.4 * ``Delta``; zeropoint}) of the spatial Psf-matching kernel 563 * - ``ALBasisNGauss`` 564 - If using sum-of-Gaussian basis, the number of gaussians used 565 * - ``ALBasisDegGauss`` 566 - If using sum-of-Gaussian basis, the deg of spatial variation of the Gaussians 567 * - ``ALBasisSigGauss`` 568 - If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians 570 - If using sum-of-Gaussian basis, the kernel size 571 * - ``NFalsePositivesTotal`` 572 - Total number of diaSources 573 * - ``NFalsePositivesRefAssociated`` 574 - Number of diaSources that associate with the reference catalog 575 * - ``NFalsePositivesRefAssociated`` 576 - Number of diaSources that associate with the source catalog 577 * - ``NFalsePositivesUnassociated`` 578 - Number of diaSources that are orphans 580 - Mean value of substamp diffim quality metrics across all KernelCandidates, 581 for both the per-candidate (LOCAL) and SPATIAL residuals 582 * - ``metric_MEDIAN`` 583 - Median value of substamp diffim quality metrics across all KernelCandidates, 584 for both the per-candidate (LOCAL) and SPATIAL residuals 586 - Standard deviation of substamp diffim quality metrics across all KernelCandidates, 587 for both the per-candidate (LOCAL) and SPATIAL residuals 592 The `lsst.pipe.base.cmdLineTask.CmdLineTask` command line task interface supports a 593 flag -d/--debug to import @b debug.py from your PYTHONPATH. The relevant contents of debug.py 594 for this Task include: 601 di = lsstDebug.getInfo(name) 602 if name == "lsst.ip.diffim.psfMatch": 603 # enable debug output 605 # display mask transparency 606 di.maskTransparency = 80 607 # show all the candidates and residuals 608 di.displayCandidates = True 609 # show kernel basis functions 610 di.displayKernelBasis = False 611 # show kernel realized across the image 612 di.displayKernelMosaic = True 613 # show coefficients of spatial model 614 di.plotKernelSpatialModel = False 615 # show the bad candidates (red) along with good (green) 616 di.showBadCandidates = True 618 lsstDebug.Info = DebugInfo 621 Note that if you want additional logging info, you may add to your scripts: 625 import lsst.log.utils as logUtils 626 logUtils.traceSetAt("ip.diffim", 4) 628 ConfigClass = PsfMatchConfig
629 _DefaultName =
"psfMatch" 632 """Create the psf-matching Task 637 Arguments to be passed to ``lsst.pipe.base.task.Task.__init__`` 639 Keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__`` 643 The initialization sets the Psf-matching kernel configuration using the value of 644 self.config.kernel.active. If the kernel is requested with regularization to moderate 645 the bias/variance tradeoff, currently only used when a delta function kernel basis 646 is provided, it creates a regularization matrix stored as member variable 649 pipeBase.Task.__init__(self, *args, **kwargs)
652 if 'useRegularization' in self.
kConfig:
658 self.
hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePolicy(self.
kConfig))
660 def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
661 """Provide logging diagnostics on quality of spatial kernel fit 665 kernelCellSet : `lsst.afw.math.SpatialCellSet` 666 Cellset that contains the KernelCandidates used in the fitting 667 spatialSolution : `lsst.ip.diffim.SpatialKernelSolution` 668 KernelSolution of best-fit 669 spatialKernel : `lsst.afw.math.LinearCombinationKernel` 670 Best-fit spatial Kernel model 671 spatialBg : `lsst.afw.math.Function2D` 672 Best-fit spatial background model 675 kImage = afwImage.ImageD(spatialKernel.getDimensions())
676 kSum = spatialKernel.computeImage(kImage,
False)
677 self.log.
info(
"Final spatial kernel sum %.3f" % (kSum))
680 conditionNum = spatialSolution.getConditionNumber(
681 getattr(diffimLib.KernelSolution, self.
kConfig.conditionNumberType))
682 self.log.
info(
"Spatial model condition number %.3e" % (conditionNum))
684 if conditionNum < 0.0:
685 self.log.
warn(
"Condition number is negative (%.3e)" % (conditionNum))
686 if conditionNum > self.
kConfig.maxSpatialConditionNumber:
687 self.log.
warn(
"Spatial solution exceeds max condition number (%.3e > %.3e)" % (
688 conditionNum, self.
kConfig.maxSpatialConditionNumber))
690 self.metadata.
set(
"spatialConditionNum", conditionNum)
691 self.metadata.
set(
"spatialKernelSum", kSum)
694 nBasisKernels = spatialKernel.getNBasisKernels()
695 nKernelTerms = spatialKernel.getNSpatialParameters()
696 if nKernelTerms == 0:
700 nBgTerms = spatialBg.getNParameters()
702 if spatialBg.getParameters()[0] == 0.0:
708 for cell
in kernelCellSet.getCellList():
709 for cand
in cell.begin(
False):
711 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
713 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
716 self.log.
info(
"Doing stats of kernel candidates used in the spatial fit.")
720 self.log.
warn(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
723 self.log.
info(
"%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
726 if nGood < nKernelTerms:
727 self.log.
warn(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
728 nGood, nKernelTerms, nBasisKernels))
729 self.log.
warn(
"Consider lowering the spatial order")
730 elif nGood <= 2*nKernelTerms:
731 self.log.
warn(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
732 nGood, nKernelTerms, nBasisKernels))
733 self.log.
warn(
"Consider lowering the spatial order")
735 self.log.
info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
736 nGood, nKernelTerms, nBasisKernels))
739 self.log.
warn(
"Spatial background model underconstrained; %d candidates, %d terms" % (
741 self.log.
warn(
"Consider lowering the spatial order")
742 elif nGood <= 2*nBgTerms:
743 self.log.
warn(
"Spatial background model poorly constrained; %d candidates, %d terms" % (
745 self.log.
warn(
"Consider lowering the spatial order")
747 self.log.
info(
"Spatial background model appears well constrained; %d candidates, %d terms" % (
750 def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
751 """Provide visualization of the inputs and ouputs to the Psf-matching code 755 kernelCellSet : `lsst.afw.math.SpatialCellSet` 756 The SpatialCellSet used in determining the matching kernel and background 757 spatialKernel : `lsst.afw.math.LinearCombinationKernel` 758 Spatially varying Psf-matching kernel 759 spatialBackground : `lsst.afw.math.Function2D` 760 Spatially varying background-matching function 765 displayKernelMosaic =
lsstDebug.Info(__name__).displayKernelMosaic
766 plotKernelSpatialModel =
lsstDebug.Info(__name__).plotKernelSpatialModel
769 if not maskTransparency:
771 afwDisplay.setDefaultMaskTransparency(maskTransparency)
773 if displayCandidates:
774 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
775 frame=lsstDebug.frame,
776 showBadCandidates=showBadCandidates)
778 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
779 frame=lsstDebug.frame,
780 showBadCandidates=showBadCandidates,
783 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
784 frame=lsstDebug.frame,
785 showBadCandidates=showBadCandidates,
789 if displayKernelBasis:
790 diutils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
793 if displayKernelMosaic:
794 diutils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
797 if plotKernelSpatialModel:
798 diutils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
800 def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy):
801 """Create Principal Component basis 803 If a principal component analysis is requested, typically when using a delta function basis, 804 perform the PCA here and return a new basis list containing the new principal components. 808 kernelCellSet : `lsst.afw.math.SpatialCellSet` 809 a SpatialCellSet containing KernelCandidates, from which components are derived 811 the number of stars per cell to visit when doing the PCA 812 policy : `lsst.pex.policy.Policy` 813 input policy controlling the single kernel visitor 818 number of KernelCandidates rejected during PCA loop 819 spatialBasisList : `list` of `lsst.afw.math.kernel.FixedKernel` 820 basis list containing the principal shapes as Kernels 825 If the Eigenvalues sum to zero. 827 nComponents = self.
kConfig.numPrincipalComponents
828 imagePca = diffimLib.KernelPcaD()
829 importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
830 kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
831 if self.
kConfig.subtractMeanForPca:
832 importStarVisitor.subtractMean()
835 eigenValues = imagePca.getEigenValues()
836 pcaBasisList = importStarVisitor.getEigenKernels()
838 eSum = np.sum(eigenValues)
840 raise RuntimeError(
"Eigenvalues sum to zero")
841 for j
in range(len(eigenValues)):
842 log.log(
"TRACE5." + self.log.getName() +
"._solve", log.DEBUG,
843 "Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
845 nToUse =
min(nComponents, len(eigenValues))
847 for j
in range(nToUse):
849 kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
850 pcaBasisList[j].computeImage(kimage,
False)
851 if not (
True in np.isnan(kimage.getArray())):
852 trimBasisList.append(pcaBasisList[j])
855 spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
858 singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, policy)
859 singlekvPca.setSkipBuilt(
False)
860 kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
861 singlekvPca.setSkipBuilt(
True)
862 nRejectedPca = singlekvPca.getNRejected()
864 return nRejectedPca, spatialBasisList
866 def _buildCellSet(self, *args):
867 """Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; 868 override in derived classes""" 872 def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
873 """Solve for the PSF matching kernel 877 kernelCellSet : `lsst.afw.math.SpatialCellSet` 878 a SpatialCellSet to use in determining the matching kernel 879 (typically as provided by _buildCellSet) 880 basisList : `list` of `lsst.afw.math.kernel.FixedKernel` 881 list of Kernels to be used in the decomposition of the spatially varying kernel 882 (typically as provided by makeKernelBasisList) 883 returnOnExcept : `bool`, optional 884 if True then return (None, None) if an error occurs, else raise the exception 888 psfMatchingKernel : `lsst.afw.math.LinearCombinationKernel` 889 Spatially varying Psf-matching kernel 890 backgroundModel : `lsst.afw.math.Function2D` 891 Spatially varying background-matching function 896 If unable to determine PSF matching kernel and ``returnOnExcept==False``. 902 maxSpatialIterations = self.
kConfig.maxSpatialIterations
903 nStarPerCell = self.
kConfig.nStarPerCell
904 usePcaForSpatialKernel = self.
kConfig.usePcaForSpatialKernel
907 policy = pexConfig.makePolicy(self.
kConfig)
909 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy, self.
hMat)
911 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
914 ksv = diffimLib.KernelSumVisitorF(policy)
921 while (thisIteration < maxSpatialIterations):
925 while (nRejectedSkf != 0):
926 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
927 "Building single kernels...")
928 kernelCellSet.visitCandidates(singlekv, nStarPerCell)
929 nRejectedSkf = singlekv.getNRejected()
930 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
931 "Iteration %d, rejected %d candidates due to initial kernel fit",
932 thisIteration, nRejectedSkf)
936 ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
937 kernelCellSet.visitCandidates(ksv, nStarPerCell)
938 ksv.processKsumDistribution()
939 ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
940 kernelCellSet.visitCandidates(ksv, nStarPerCell)
942 nRejectedKsum = ksv.getNRejected()
943 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
944 "Iteration %d, rejected %d candidates due to kernel sum",
945 thisIteration, nRejectedKsum)
948 if nRejectedKsum > 0:
957 if (usePcaForSpatialKernel):
958 log.log(
"TRACE0." + self.log.getName() +
"._solve", log.DEBUG,
959 "Building Pca basis")
961 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
962 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
963 "Iteration %d, rejected %d candidates due to Pca kernel fit",
964 thisIteration, nRejectedPca)
975 if (nRejectedPca > 0):
979 spatialBasisList = basisList
982 regionBBox = kernelCellSet.getBBox()
983 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
984 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
985 spatialkv.solveLinearEquation()
986 log.log(
"TRACE2." + self.log.getName() +
"._solve", log.DEBUG,
987 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
988 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
991 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, policy)
992 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
993 nRejectedSpatial = assesskv.getNRejected()
994 nGoodSpatial = assesskv.getNGood()
995 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
996 "Iteration %d, rejected %d candidates due to spatial kernel fit",
997 thisIteration, nRejectedSpatial)
998 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
999 "%d candidates used in fit", nGoodSpatial)
1002 if nGoodSpatial == 0
and nRejectedSpatial == 0:
1003 raise RuntimeError(
"No kernel candidates for spatial fit")
1005 if nRejectedSpatial == 0:
1013 if (nRejectedSpatial > 0)
and (thisIteration == maxSpatialIterations):
1014 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
"Final spatial fit")
1015 if (usePcaForSpatialKernel):
1016 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
1017 regionBBox = kernelCellSet.getBBox()
1018 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
1019 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1020 spatialkv.solveLinearEquation()
1021 log.log(
"TRACE2." + self.log.getName() +
"._solve", log.DEBUG,
1022 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1023 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1025 spatialSolution = spatialkv.getKernelSolution()
1027 except Exception
as e:
1028 self.log.
error(
"ERROR: Unable to calculate psf matching kernel")
1030 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
str(e))
1034 log.log(
"TRACE0." + self.log.getName() +
"._solve", log.DEBUG,
1035 "Total time to compute the spatial kernel : %.2f s", (t1 - t0))
1038 self.
_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1040 self.
_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1042 return spatialSolution, spatialKernel, spatialBackground
1045 PsfMatch = PsfMatchTask
def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg)
def __init__(self, args, kwargs)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
daf::base::PropertySet * set
def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy)
def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...