23 __all__ = [
"DetectionConfig",
"PsfMatchConfig",
"PsfMatchConfigAL",
"PsfMatchConfigDF",
"PsfMatchTask"]
34 import lsst.pipe.base
as pipeBase
35 from lsst.meas.algorithms
import SubtractBackgroundConfig
36 from .
import utils
as diutils
37 from .
import diffimLib
41 """!Configuration for detecting sources on images for building a PSF-matching kernel 43 Configuration for turning detected lsst.afw.detection.FootPrints into an acceptable 44 (unmasked, high signal-to-noise, not too large or not too small) list of 45 lsst.ip.diffim.KernelSources that are used to build the Psf-matching kernel""" 47 detThreshold = pexConfig.Field(
49 doc=
"Value of footprint detection threshold",
51 check=
lambda x: x >= 3.0
53 detThresholdType = pexConfig.ChoiceField(
55 doc=
"Type of detection threshold",
56 default=
"pixel_stdev",
58 "value":
"Use counts as the detection threshold type",
59 "stdev":
"Use standard deviation of image plane",
60 "variance":
"Use variance of image plane",
61 "pixel_stdev":
"Use stdev derived from variance plane" 64 detOnTemplate = pexConfig.Field(
66 doc=
"""If true run detection on the template (image to convolve); 67 if false run detection on the science image""",
70 badMaskPlanes = pexConfig.ListField(
72 doc=
"""Mask planes that lead to an invalid detection. 73 Options: NO_DATA EDGE SAT BAD CR INTRP""",
74 default=(
"NO_DATA",
"EDGE",
"SAT")
76 fpNpixMin = pexConfig.Field(
78 doc=
"Minimum number of pixels in an acceptable Footprint",
80 check=
lambda x: x >= 5
82 fpNpixMax = pexConfig.Field(
84 doc=
"""Maximum number of pixels in an acceptable Footprint; 85 too big and the subsequent convolutions become unwieldy""",
87 check=
lambda x: x <= 500
89 fpGrowKernelScaling = pexConfig.Field(
91 doc=
"""If config.scaleByFwhm, grow the footprint based on 92 the final kernelSize. Each footprint will be 93 2*fpGrowKernelScaling*kernelSize x 94 2*fpGrowKernelScaling*kernelSize. With the value 95 of 1.0, the remaining pixels in each KernelCandiate 96 after convolution by the basis functions will be 97 equal to the kernel size itself.""",
99 check=
lambda x: x >= 1.0
101 fpGrowPix = pexConfig.Field(
103 doc=
"""Growing radius (in pixels) for each raw detection 104 footprint. The smaller the faster; however the 105 kernel sum does not converge if the stamp is too 106 small; and the kernel is not constrained at all if 107 the stamp is the size of the kernel. The grown stamp 108 is 2 * fpGrowPix pixels larger in each dimension. 109 This is overridden by fpGrowKernelScaling if scaleByFwhm""",
111 check=
lambda x: x >= 10
113 scaleByFwhm = pexConfig.Field(
115 doc=
"Scale fpGrowPix by input Fwhm?",
121 """!Base configuration for Psf-matching 123 The base configuration of the Psf-matching kernel, and of the warping, detection, 124 and background modeling subTasks.""" 126 warpingConfig = pexConfig.ConfigField(
"Config for warping exposures to a common alignment",
127 afwMath.warper.WarperConfig)
128 detectionConfig = pexConfig.ConfigField(
"Controlling the detection of sources for kernel building",
130 afwBackgroundConfig = pexConfig.ConfigField(
"Controlling the Afw background fitting",
131 SubtractBackgroundConfig)
133 useAfwBackground = pexConfig.Field(
135 doc=
"Use afw background subtraction instead of ip_diffim",
138 fitForBackground = pexConfig.Field(
140 doc=
"Include terms (including kernel cross terms) for background in ip_diffim",
143 kernelBasisSet = pexConfig.ChoiceField(
145 doc=
"Type of basis set for PSF matching kernel.",
146 default=
"alard-lupton",
148 "alard-lupton":
"""Alard-Lupton sum-of-gaussians basis set, 149 * The first term has no spatial variation 150 * The kernel sum is conserved 151 * You may want to turn off 'usePcaForSpatialKernel'""",
152 "delta-function":
"""Delta-function kernel basis set, 153 * You may enable the option useRegularization 154 * You should seriously consider usePcaForSpatialKernel, which will also 155 enable kernel sum conservation for the delta function kernels""" 158 kernelSize = pexConfig.Field(
160 doc=
"""Number of rows/columns in the convolution kernel; should be odd-valued. 161 Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
164 scaleByFwhm = pexConfig.Field(
166 doc=
"Scale kernelSize, alardGaussians by input Fwhm",
169 kernelSizeFwhmScaling = pexConfig.Field(
171 doc=
"""How much to scale the kernel size based on the largest AL Sigma""",
173 check=
lambda x: x >= 1.0
175 kernelSizeMin = pexConfig.Field(
177 doc=
"""Minimum Kernel Size""",
180 kernelSizeMax = pexConfig.Field(
182 doc=
"""Maximum Kernel Size""",
185 spatialModelType = pexConfig.ChoiceField(
187 doc=
"Type of spatial functions for kernel and background",
188 default=
"chebyshev1",
190 "chebyshev1":
"Chebyshev polynomial of the first kind",
191 "polynomial":
"Standard x,y polynomial",
194 spatialKernelOrder = pexConfig.Field(
196 doc=
"Spatial order of convolution kernel variation",
198 check=
lambda x: x >= 0
200 spatialBgOrder = pexConfig.Field(
202 doc=
"Spatial order of differential background variation",
204 check=
lambda x: x >= 0
206 sizeCellX = pexConfig.Field(
208 doc=
"Size (rows) in pixels of each SpatialCell for spatial modeling",
210 check=
lambda x: x >= 32
212 sizeCellY = pexConfig.Field(
214 doc=
"Size (columns) in pixels of each SpatialCell for spatial modeling",
216 check=
lambda x: x >= 32
218 nStarPerCell = pexConfig.Field(
220 doc=
"Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
222 check=
lambda x: x >= 1
224 maxSpatialIterations = pexConfig.Field(
226 doc=
"Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
228 check=
lambda x: x >= 1
and x <= 5
230 usePcaForSpatialKernel = pexConfig.Field(
232 doc=
"""Use Pca to reduce the dimensionality of the kernel basis sets. 233 This is particularly useful for delta-function kernels. 234 Functionally, after all Cells have their raw kernels determined, we run 235 a Pca on these Kernels, re-fit the Cells using the eigenKernels and then 236 fit those for spatial variation using the same technique as for Alard-Lupton kernels. 237 If this option is used, the first term will have no spatial variation and the 238 kernel sum will be conserved.""",
241 subtractMeanForPca = pexConfig.Field(
243 doc=
"Subtract off the mean feature before doing the Pca",
246 numPrincipalComponents = pexConfig.Field(
248 doc=
"""Number of principal components to use for Pca basis, including the 249 mean kernel if requested.""",
251 check=
lambda x: x >= 3
253 singleKernelClipping = pexConfig.Field(
255 doc=
"Do sigma clipping on each raw kernel candidate",
258 kernelSumClipping = pexConfig.Field(
260 doc=
"Do sigma clipping on the ensemble of kernel sums",
263 spatialKernelClipping = pexConfig.Field(
265 doc=
"Do sigma clipping after building the spatial model",
268 checkConditionNumber = pexConfig.Field(
270 doc=
"""Test for maximum condition number when inverting a kernel matrix. 271 Anything above maxConditionNumber is not used and the candidate is set as BAD. 272 Also used to truncate inverse matrix in estimateBiasedRisk. However, 273 if you are doing any deconvolution you will want to turn this off, or use 274 a large maxConditionNumber""",
277 badMaskPlanes = pexConfig.ListField(
279 doc=
"""Mask planes to ignore when calculating diffim statistics 280 Options: NO_DATA EDGE SAT BAD CR INTRP""",
281 default=(
"NO_DATA",
"EDGE",
"SAT")
283 candidateResidualMeanMax = pexConfig.Field(
285 doc=
"""Rejects KernelCandidates yielding bad difference image quality. 286 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor. 287 Represents average over pixels of (image/sqrt(variance)).""",
289 check=
lambda x: x >= 0.0
291 candidateResidualStdMax = pexConfig.Field(
293 doc=
"""Rejects KernelCandidates yielding bad difference image quality. 294 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor. 295 Represents stddev over pixels of (image/sqrt(variance)).""",
297 check=
lambda x: x >= 0.0
299 useCoreStats = pexConfig.Field(
301 doc=
"""Use the core of the footprint for the quality statistics, instead of the entire footprint. 302 WARNING: if there is deconvolution we probably will need to turn this off""",
305 candidateCoreRadius = pexConfig.Field(
307 doc=
"""Radius for calculation of stats in 'core' of KernelCandidate diffim. 308 Total number of pixels used will be (2*radius)**2. 309 This is used both for 'core' diffim quality as well as ranking of 310 KernelCandidates by their total flux in this core""",
312 check=
lambda x: x >= 1
314 maxKsumSigma = pexConfig.Field(
316 doc=
"""Maximum allowed sigma for outliers from kernel sum distribution. 317 Used to reject variable objects from the kernel model""",
319 check=
lambda x: x >= 0.0
321 maxConditionNumber = pexConfig.Field(
323 doc=
"Maximum condition number for a well conditioned matrix",
325 check=
lambda x: x >= 0.0
327 conditionNumberType = pexConfig.ChoiceField(
329 doc=
"Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
330 default=
"EIGENVALUE",
332 "SVD":
"Use singular values",
333 "EIGENVALUE":
"Use eigen values (faster)",
336 maxSpatialConditionNumber = pexConfig.Field(
338 doc=
"Maximum condition number for a well conditioned spatial matrix",
340 check=
lambda x: x >= 0.0
342 iterateSingleKernel = pexConfig.Field(
344 doc=
"""Remake KernelCandidate using better variance estimate after first pass? 345 Primarily useful when convolving a single-depth image, otherwise not necessary.""",
348 constantVarianceWeighting = pexConfig.Field(
350 doc=
"""Use constant variance weighting in single kernel fitting? 351 In some cases this is better for bright star residuals.""",
354 calculateKernelUncertainty = pexConfig.Field(
356 doc=
"""Calculate kernel and background uncertainties for each kernel candidate? 357 This comes from the inverse of the covariance matrix. 358 Warning: regularization can cause problems for this step.""",
361 useBicForKernelBasis = pexConfig.Field(
363 doc=
"""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
369 """!The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis""" 372 PsfMatchConfig.setDefaults(self)
376 alardNGauss = pexConfig.Field(
378 doc=
"Number of Gaussians in alard-lupton basis",
380 check=
lambda x: x >= 1
382 alardDegGauss = pexConfig.ListField(
384 doc=
"Polynomial order of spatial modification of Gaussians. Must in number equal alardNGauss",
387 alardSigGauss = pexConfig.ListField(
389 doc=
"""Sigma in pixels of Gaussians (FWHM = 2.35 sigma). Must in number equal alardNGauss""",
390 default=(0.7, 1.5, 3.0),
392 alardGaussBeta = pexConfig.Field(
394 doc=
"""Default scale factor between Gaussian sigmas """,
396 check=
lambda x: x >= 0.0,
398 alardMinSig = pexConfig.Field(
400 doc=
"""Minimum Sigma (pixels) for Gaussians""",
402 check=
lambda x: x >= 0.25
404 alardDegGaussDeconv = pexConfig.Field(
406 doc=
"""Degree of spatial modification of ALL gaussians in AL basis during deconvolution""",
408 check=
lambda x: x >= 1
410 alardMinSigDeconv = pexConfig.Field(
412 doc=
"""Minimum Sigma (pixels) for Gaussians during deconvolution; 413 make smaller than alardMinSig as this is only indirectly used""",
415 check=
lambda x: x >= 0.25
417 alardNGaussDeconv = pexConfig.Field(
419 doc=
"Number of Gaussians in AL basis during deconvolution",
421 check=
lambda x: x >= 1
426 """!The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis""" 429 PsfMatchConfig.setDefaults(self)
436 useRegularization = pexConfig.Field(
438 doc=
"Use regularization to smooth the delta function kernels",
441 regularizationType = pexConfig.ChoiceField(
443 doc=
"Type of regularization.",
444 default=
"centralDifference",
446 "centralDifference":
"Penalize second derivative using 2-D stencil of central finite difference",
447 "forwardDifference":
"Penalize first, second, third derivatives using forward finite differeces" 450 centralRegularizationStencil = pexConfig.ChoiceField(
452 doc=
"Type of stencil to approximate central derivative (for centralDifference only)",
455 5:
"5-point stencil including only adjacent-in-x,y elements",
456 9:
"9-point stencil including diagonal elements" 459 forwardRegularizationOrders = pexConfig.ListField(
461 doc=
"Array showing which order derivatives to penalize (for forwardDifference only)",
463 itemCheck=
lambda x: (x > 0)
and (x < 4)
465 regularizationBorderPenalty = pexConfig.Field(
467 doc=
"Value of the penalty for kernel border pixels",
469 check=
lambda x: x >= 0.0
471 lambdaType = pexConfig.ChoiceField(
473 doc=
"How to choose the value of the regularization strength",
476 "absolute":
"Use lambdaValue as the value of regularization strength",
477 "relative":
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
478 "minimizeBiasedRisk":
"Minimize biased risk estimate",
479 "minimizeUnbiasedRisk":
"Minimize unbiased risk estimate",
482 lambdaValue = pexConfig.Field(
484 doc=
"Value used for absolute determinations of regularization strength",
487 lambdaScaling = pexConfig.Field(
489 doc=
"Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
492 lambdaStepType = pexConfig.ChoiceField(
494 doc=
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 495 use log or linear steps""",
498 "log":
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
499 "linear":
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
502 lambdaMin = pexConfig.Field(
504 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 505 start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
508 lambdaMax = pexConfig.Field(
510 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 511 stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
514 lambdaStep = pexConfig.Field(
516 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk), 517 step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
532 @anchor PsfMatchTask_ 534 @brief Base class for Psf Matching; should not be called directly 536 @section ip_diffim_psfmatch_Contents Contents 538 - @ref ip_diffim_psfmatch_Purpose 539 - @ref ip_diffim_psfmatch_Initialize 540 - @ref ip_diffim_psfmatch_IO 541 - @ref ip_diffim_psfmatch_Config 542 - @ref ip_diffim_psfmatch_Metadata 543 - @ref ip_diffim_psfmatch_Debug 544 - @ref ip_diffim_psfmatch_Example 546 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 548 @section ip_diffim_psfmatch_Purpose Description 550 PsfMatchTask is a base class that implements the core functionality for matching the 551 Psfs of two images using a spatially varying Psf-matching lsst.afw.math.LinearCombinationKernel. 552 The Task requires the user to provide an instance of an lsst.afw.math.SpatialCellSet, 553 filled with lsst.ip.diffim.KernelCandidate instances, and a list of lsst.afw.math.Kernels 554 of basis shapes that will be used for the decomposition. If requested, the Task 555 also performs background matching and returns the differential background model as an 556 lsst.afw.math.Kernel.SpatialFunction. 558 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 560 @ection ip_diffim_psfmatch_Initialize Task initialization 562 @copydoc \_\_init\_\_ 564 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 566 @section ip_diffim_psfmatch_IO Invoking the Task 568 As a base class, this Task is not directly invoked. However, run() methods that are 569 implemented on derived classes will make use of the core _solve() functionality, 570 which defines a sequence of lsst.afw.math.CandidateVisitor classes that iterate 571 through the KernelCandidates, first building up a per-candidate solution and then 572 building up a spatial model from the ensemble of candidates. Sigma clipping is 573 performed using the mean and standard deviation of all kernel sums (to reject 574 variable objects), on the per-candidate substamp diffim residuals 575 (to indicate a bad choice of kernel basis shapes for that particular object), 576 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad 577 choice of spatial kernel order, or poor constraints on the spatial model). The 578 _diagnostic() method logs information on the quality of the spatial fit, and also 579 modifies the Task metadata. 581 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 583 @section ip_diffim_psfmatch_Config Configuration parameters 585 See @ref PsfMatchConfig, @ref PsfMatchConfigAL, @ref PsfMatchConfigDF, and @ref DetectionConfig. 587 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 589 @section ip_diffim_psfmatch_Metadata Quantities set in Metadata 593 <DT> spatialConditionNum <DD> Condition number of the spatial kernel fit; 594 via @link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic @endlink </DD> </DT> 595 <DT> spatialKernelSum <DD> Kernel sum (10^{-0.4 * Δ zeropoint}) of the spatial Psf-matching kernel; 596 via @link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic @endlink </DD> </DT> 598 <DT> ALBasisNGauss <DD> If using sum-of-Gaussian basis, the number of gaussians used; 599 via @link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList 600 generateAlardLuptonBasisList@endlink </DD> </DT> 601 <DT> ALBasisDegGauss <DD> If using sum-of-Gaussian basis, the degree of spatial variation of the Gaussians; 602 via @link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList 603 generateAlardLuptonBasisList@endlink </DD> </DT> 604 <DT> ALBasisSigGauss <DD> If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians; 605 via @link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList 606 generateAlardLuptonBasisList@endlink </DD> </DT> 607 <DT> ALKernelSize <DD> If using sum-of-Gaussian basis, the kernel size; 608 via @link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList 609 generateAlardLuptonBasisList@endlink </DD> </DT> 611 <DT> NFalsePositivesTotal <DD> Total number of diaSources; 612 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 613 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the reference catalog; 614 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 615 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the source catalog; 616 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 617 <DT> NFalsePositivesUnassociated <DD> Number of diaSources that are orphans; 618 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 619 <DT> metric_MEAN <DD> Mean value of substamp diffim quality metrics across all KernelCandidates, 620 for both the per-candidate (LOCAL) and SPATIAL residuals; 621 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 622 <DT> metric_MEDIAN <DD> Median value of substamp diffim quality metrics across all KernelCandidates, 623 for both the per-candidate (LOCAL) and SPATIAL residuals; 624 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 625 <DT> metric_STDEV <DD> Standard deviation of substamp diffim quality metrics across all KernelCandidates, 626 for both the per-candidate (LOCAL) and SPATIAL residuals; 627 via @link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate@endlink </DD> </DT> 631 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 633 @section ip_diffim_psfmatch_Debug Debug variables 636 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 637 flag @c -d/--debug to import @b debug.py from your @c PYTHONPATH. The relevant contents of debug.py 638 for this Task include: 644 di = lsstDebug.getInfo(name) 645 if name == "lsst.ip.diffim.psfMatch": 646 di.display = True # enable debug output 647 di.maskTransparency = 80 # ds9 mask transparency 648 di.displayCandidates = True # show all the candidates and residuals 649 di.displayKernelBasis = False # show kernel basis functions 650 di.displayKernelMosaic = True # show kernel realized across the image 651 di.plotKernelSpatialModel = False # show coefficients of spatial model 652 di.showBadCandidates = True # show the bad candidates (red) along with good (green) 654 lsstDebug.Info = DebugInfo 659 Note that if you want addional logging info, you may add to your scripts: 661 import lsst.log.utils as logUtils 662 logUtils.traceSetAt("ip.diffim", 4) 665 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 667 @section ip_diffim_psfmatch_Example Example code 669 As a base class, there is no example code for PsfMatchTask. 670 However, see @link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask ImagePsfMatchTask@endlink, 671 @link lsst.ip.diffim.snapPsfMatch.SnapPsfMatchTask SnapPsfMatchTask@endlink, and 672 @link lsst.ip.diffim.modelPsfMatch.ModelPsfMatchTask ModelPsfMatchTask@endlink. 674 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 677 ConfigClass = PsfMatchConfig
678 _DefaultName =
"psfMatch" 681 """!Create the psf-matching Task 683 @param *args arguments to be passed to lsst.pipe.base.task.Task.__init__ 684 @param **kwargs keyword arguments to be passed to lsst.pipe.base.task.Task.__init__ 686 The initialization sets the Psf-matching kernel configuration using the value of 687 self.config.kernel.active. If the kernel is requested with regularization to moderate 688 the bias/variance tradeoff, currently only used when a delta function kernel basis 689 is provided, it creates a regularization matrix stored as member variable 692 pipeBase.Task.__init__(self, *args, **kwargs)
696 if 'useRegularization' in self.
kConfig:
702 self.
hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePolicy(self.
kConfig))
704 def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
705 """!Provide logging diagnostics on quality of spatial kernel fit 707 @param kernelCellSet: Cellset that contains the KernelCandidates used in the fitting 708 @param spatialSolution: KernelSolution of best-fit 709 @param spatialKernel: Best-fit spatial Kernel model 710 @param spatialBg: Best-fit spatial background model 714 kImage = afwImage.ImageD(spatialKernel.getDimensions())
715 kSum = spatialKernel.computeImage(kImage,
False)
716 self.log.info(
"Final spatial kernel sum %.3f" % (kSum))
719 conditionNum = spatialSolution.getConditionNumber(
720 getattr(diffimLib.KernelSolution, self.
kConfig.conditionNumberType))
721 self.log.info(
"Spatial model condition number %.3e" % (conditionNum))
723 if conditionNum < 0.0:
724 self.log.warn(
"Condition number is negative (%.3e)" % (conditionNum))
725 if conditionNum > self.
kConfig.maxSpatialConditionNumber:
726 self.log.warn(
"Spatial solution exceeds max condition number (%.3e > %.3e)" % (
727 conditionNum, self.
kConfig.maxSpatialConditionNumber))
729 self.metadata.set(
"spatialConditionNum", conditionNum)
730 self.metadata.set(
"spatialKernelSum", kSum)
733 nBasisKernels = spatialKernel.getNBasisKernels()
734 nKernelTerms = spatialKernel.getNSpatialParameters()
735 if nKernelTerms == 0:
739 nBgTerms = spatialBg.getNParameters()
741 if spatialBg.getParameters()[0] == 0.0:
747 for cell
in kernelCellSet.getCellList():
748 for cand
in cell.begin(
False):
750 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
752 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
755 self.log.info(
"Doing stats of kernel candidates used in the spatial fit.")
759 self.log.warn(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
762 self.log.info(
"%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
765 if nGood < nKernelTerms:
766 self.log.warn(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
767 nGood, nKernelTerms, nBasisKernels))
768 self.log.warn(
"Consider lowering the spatial order")
769 elif nGood <= 2*nKernelTerms:
770 self.log.warn(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
771 nGood, nKernelTerms, nBasisKernels))
772 self.log.warn(
"Consider lowering the spatial order")
774 self.log.info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
775 nGood, nKernelTerms, nBasisKernels))
778 self.log.warn(
"Spatial background model underconstrained; %d candidates, %d terms" % (
780 self.log.warn(
"Consider lowering the spatial order")
781 elif nGood <= 2*nBgTerms:
782 self.log.warn(
"Spatial background model poorly constrained; %d candidates, %d terms" % (
784 self.log.warn(
"Consider lowering the spatial order")
786 self.log.info(
"Spatial background model appears well constrained; %d candidates, %d terms" % (
789 def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
790 """!Provide visualization of the inputs and ouputs to the Psf-matching code 792 @param kernelCellSet: the SpatialCellSet used in determining the matching kernel and background 793 @param spatialKernel: spatially varying Psf-matching kernel 794 @param spatialBackground: spatially varying background-matching function 800 displayKernelMosaic =
lsstDebug.Info(__name__).displayKernelMosaic
801 plotKernelSpatialModel =
lsstDebug.Info(__name__).plotKernelSpatialModel
804 if not maskTransparency:
806 ds9.setMaskTransparency(maskTransparency)
808 if displayCandidates:
809 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
810 frame=lsstDebug.frame,
811 showBadCandidates=showBadCandidates)
813 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
814 frame=lsstDebug.frame,
815 showBadCandidates=showBadCandidates,
818 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
819 frame=lsstDebug.frame,
820 showBadCandidates=showBadCandidates,
824 if displayKernelBasis:
825 diutils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
828 if displayKernelMosaic:
829 diutils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
832 if plotKernelSpatialModel:
833 diutils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
835 def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy):
836 """!Create Principal Component basis 838 If a principal component analysis is requested, typically when using a delta function basis, 839 perform the PCA here and return a new basis list containing the new principal components. 841 @param kernelCellSet: a SpatialCellSet containing KernelCandidates, from which components are derived 842 @param nStarPerCell: the number of stars per cell to visit when doing the PCA 843 @param policy: input policy controlling the single kernel visitor 846 - nRejectedPca: number of KernelCandidates rejected during PCA loop 847 - spatialBasisList: basis list containing the principal shapes as Kernels 850 nComponents = self.
kConfig.numPrincipalComponents
851 imagePca = diffimLib.KernelPcaD()
852 importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
853 kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
854 if self.
kConfig.subtractMeanForPca:
855 importStarVisitor.subtractMean()
858 eigenValues = imagePca.getEigenValues()
859 pcaBasisList = importStarVisitor.getEigenKernels()
861 eSum = np.sum(eigenValues)
863 raise RuntimeError(
"Eigenvalues sum to zero")
864 for j
in range(len(eigenValues)):
865 log.log(
"TRACE5." + self.log.getName() +
"._solve", log.DEBUG,
866 "Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
868 nToUse = min(nComponents, len(eigenValues))
870 for j
in range(nToUse):
872 kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
873 pcaBasisList[j].computeImage(kimage,
False)
874 if not (
True in np.isnan(kimage.getArray())):
875 trimBasisList.append(pcaBasisList[j])
878 spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
881 singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, policy)
882 singlekvPca.setSkipBuilt(
False)
883 kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
884 singlekvPca.setSkipBuilt(
True)
885 nRejectedPca = singlekvPca.getNRejected()
887 return nRejectedPca, spatialBasisList
889 def _buildCellSet(self, *args):
890 """!Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; 891 override in derived classes""" 895 def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
896 """!Solve for the PSF matching kernel 898 @param kernelCellSet: a SpatialCellSet to use in determining the matching kernel 899 (typically as provided by _buildCellSet) 900 @param basisList: list of Kernels to be used in the decomposition of the spatially varying kernel 901 (typically as provided by makeKernelBasisList) 902 @param returnOnExcept: if True then return (None, None) if an error occurs, else raise the exception 905 - psfMatchingKernel: PSF matching kernel 906 - backgroundModel: differential background model 908 Raise Exception if unable to determine PSF matching kernel and returnOnExcept False 914 maxSpatialIterations = self.
kConfig.maxSpatialIterations
915 nStarPerCell = self.
kConfig.nStarPerCell
916 usePcaForSpatialKernel = self.
kConfig.usePcaForSpatialKernel
919 policy = pexConfig.makePolicy(self.
kConfig)
921 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy, self.
hMat)
923 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
926 ksv = diffimLib.KernelSumVisitorF(policy)
933 while (thisIteration < maxSpatialIterations):
937 while (nRejectedSkf != 0):
938 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
939 "Building single kernels...")
940 kernelCellSet.visitCandidates(singlekv, nStarPerCell)
941 nRejectedSkf = singlekv.getNRejected()
942 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
943 "Iteration %d, rejected %d candidates due to initial kernel fit",
944 thisIteration, nRejectedSkf)
948 ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
949 kernelCellSet.visitCandidates(ksv, nStarPerCell)
950 ksv.processKsumDistribution()
951 ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
952 kernelCellSet.visitCandidates(ksv, nStarPerCell)
954 nRejectedKsum = ksv.getNRejected()
955 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
956 "Iteration %d, rejected %d candidates due to kernel sum",
957 thisIteration, nRejectedKsum)
960 if nRejectedKsum > 0:
969 if (usePcaForSpatialKernel):
970 log.log(
"TRACE0." + self.log.getName() +
"._solve", log.DEBUG,
971 "Building Pca basis")
973 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
974 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
975 "Iteration %d, rejected %d candidates due to Pca kernel fit",
976 thisIteration, nRejectedPca)
987 if (nRejectedPca > 0):
991 spatialBasisList = basisList
994 regionBBox = kernelCellSet.getBBox()
995 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
996 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
997 spatialkv.solveLinearEquation()
998 log.log(
"TRACE2." + self.log.getName() +
"._solve", log.DEBUG,
999 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1000 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1003 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, policy)
1004 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1005 nRejectedSpatial = assesskv.getNRejected()
1006 nGoodSpatial = assesskv.getNGood()
1007 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
1008 "Iteration %d, rejected %d candidates due to spatial kernel fit",
1009 thisIteration, nRejectedSpatial)
1010 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
1011 "%d candidates used in fit", nGoodSpatial)
1014 if nGoodSpatial == 0
and nRejectedSpatial == 0:
1015 raise RuntimeError(
"No kernel candidates for spatial fit")
1017 if nRejectedSpatial == 0:
1025 if (nRejectedSpatial > 0)
and (thisIteration == maxSpatialIterations):
1026 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG,
"Final spatial fit")
1027 if (usePcaForSpatialKernel):
1028 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, policy)
1029 regionBBox = kernelCellSet.getBBox()
1030 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
1031 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1032 spatialkv.solveLinearEquation()
1033 log.log(
"TRACE2." + self.log.getName() +
"._solve", log.DEBUG,
1034 "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1035 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1037 spatialSolution = spatialkv.getKernelSolution()
1039 except Exception
as e:
1040 self.log.error(
"ERROR: Unable to calculate psf matching kernel")
1042 log.log(
"TRACE1." + self.log.getName() +
"._solve", log.DEBUG, str(e))
1046 log.log(
"TRACE0." + self.log.getName() +
"._solve", log.DEBUG,
1047 "Total time to compute the spatial kernel : %.2f s", (t1 - t0))
1050 self.
_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1052 self.
_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1054 return spatialSolution, spatialKernel, spatialBackground
1057 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 _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg)
Provide logging diagnostics on quality of spatial kernel fit.
def __init__(self, args, kwargs)
Create the psf-matching Task.
def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy)
Create Principal Component basis.
def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground)
Provide visualization of the inputs and ouputs to the Psf-matching code.