22__all__ = [
"DetectionConfig",
"PsfMatchConfig",
"PsfMatchConfigAL",
"PsfMatchConfigDF",
"PsfMatchTask"]
35from lsst.utils.logging
import getTraceLogger
36from lsst.utils.timer
import timeMethod
37from .
import utils
as diutils
38from .
import diffimLib
42 """Configuration for detecting sources on images for building a
45 Configuration for turning detected lsst.afw.detection.FootPrints into an
46 acceptable (unmasked, high signal-to-noise,
not too large
or not too small)
47 list of `lsst.ip.diffim.KernelSources` that are used to build the
48 Psf-matching kernel
"""
50 detThreshold = pexConfig.Field(
52 doc="Value of footprint detection threshold",
54 check=
lambda x: x >= 3.0
56 detThresholdType = pexConfig.ChoiceField(
58 doc=
"Type of detection threshold",
59 default=
"pixel_stdev",
61 "value":
"Use counts as the detection threshold type",
62 "stdev":
"Use standard deviation of image plane",
63 "variance":
"Use variance of image plane",
64 "pixel_stdev":
"Use stdev derived from variance plane"
67 detOnTemplate = pexConfig.Field(
69 doc=
"""If true run detection on the template (image to convolve);
70 if false run detection on the science image
""",
73 badMaskPlanes = pexConfig.ListField(
75 doc=
"""Mask planes that lead to an invalid detection.
76 Options: NO_DATA EDGE SAT BAD CR INTRP""",
77 default=("NO_DATA",
"EDGE",
"SAT")
79 fpNpixMin = pexConfig.Field(
81 doc=
"Minimum number of pixels in an acceptable Footprint",
83 check=
lambda x: x >= 5
85 fpNpixMax = pexConfig.Field(
87 doc=
"""Maximum number of pixels in an acceptable Footprint;
88 too big and the subsequent convolutions become unwieldy
""",
90 check=lambda x: x <= 500
92 fpGrowKernelScaling = pexConfig.Field(
94 doc=
"""If config.scaleByFwhm, grow the footprint based on
95 the final kernelSize. Each footprint will be
96 2*fpGrowKernelScaling*kernelSize x
97 2*fpGrowKernelScaling*kernelSize. With the value
98 of 1.0, the remaining pixels in each KernelCandiate
99 after convolution by the basis functions will be
100 equal to the kernel size itself.
""",
102 check=lambda x: x >= 1.0
104 fpGrowPix = pexConfig.Field(
106 doc=
"""Growing radius (in pixels) for each raw detection
107 footprint. The smaller the faster; however the
108 kernel sum does not converge
if the stamp
is too
109 small;
and the kernel
is not constrained at all
if
110 the stamp
is the size of the kernel. The grown stamp
111 is 2 * fpGrowPix pixels larger
in each dimension.
112 This
is overridden by fpGrowKernelScaling
if scaleByFwhm
""",
114 check=lambda x: x >= 10
116 scaleByFwhm = pexConfig.Field(
118 doc=
"Scale fpGrowPix by input Fwhm?",
124 """Base configuration for Psf-matching
126 The base configuration of the Psf-matching kernel, and of the warping, detection,
127 and background modeling subTasks.
"""
129 warpingConfig = pexConfig.ConfigField("Config for warping exposures to a common alignment",
131 detectionConfig = pexConfig.ConfigField(
"Controlling the detection of sources for kernel building",
133 afwBackgroundConfig = pexConfig.ConfigField(
"Controlling the Afw background fitting",
134 SubtractBackgroundConfig)
136 useAfwBackground = pexConfig.Field(
138 doc=
"Use afw background subtraction instead of ip_diffim",
141 fitForBackground = pexConfig.Field(
143 doc=
"Include terms (including kernel cross terms) for background in ip_diffim",
146 kernelBasisSet = pexConfig.ChoiceField(
148 doc=
"Type of basis set for PSF matching kernel.",
149 default=
"alard-lupton",
151 "alard-lupton":
"""Alard-Lupton sum-of-gaussians basis set,
152 * The first term has no spatial variation
153 * The kernel sum is conserved
154 * You may want to turn off
'usePcaForSpatialKernel'""",
155 "delta-function":
"""Delta-function kernel basis set,
156 * You may enable the option useRegularization
157 * You should seriously consider usePcaForSpatialKernel, which will also
158 enable kernel sum conservation for the delta function kernels
"""
161 kernelSize = pexConfig.Field(
163 doc="""Number of rows/columns in the convolution kernel; should be odd-valued.
164 Modified by kernelSizeFwhmScaling if scaleByFwhm = true
""",
167 scaleByFwhm = pexConfig.Field(
169 doc="Scale kernelSize, alardGaussians by input Fwhm",
172 kernelSizeFwhmScaling = pexConfig.Field(
174 doc=
"Multiplier of the largest AL Gaussian basis sigma to get the kernel bbox (pixel) size.",
176 check=
lambda x: x >= 1.0
178 kernelSizeMin = pexConfig.Field(
180 doc=
"Minimum kernel bbox (pixel) size.",
183 kernelSizeMax = pexConfig.Field(
185 doc=
"Maximum kernel bbox (pixel) size.",
188 spatialModelType = pexConfig.ChoiceField(
190 doc=
"Type of spatial functions for kernel and background",
191 default=
"chebyshev1",
193 "chebyshev1":
"Chebyshev polynomial of the first kind",
194 "polynomial":
"Standard x,y polynomial",
197 spatialKernelOrder = pexConfig.Field(
199 doc=
"Spatial order of convolution kernel variation",
201 check=
lambda x: x >= 0
203 spatialBgOrder = pexConfig.Field(
205 doc=
"Spatial order of differential background variation",
207 check=
lambda x: x >= 0
209 sizeCellX = pexConfig.Field(
211 doc=
"Size (rows) in pixels of each SpatialCell for spatial modeling",
213 check=
lambda x: x >= 32
215 sizeCellY = pexConfig.Field(
217 doc=
"Size (columns) in pixels of each SpatialCell for spatial modeling",
219 check=
lambda x: x >= 32
221 nStarPerCell = pexConfig.Field(
223 doc=
"Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
225 check=
lambda x: x >= 1
227 maxSpatialIterations = pexConfig.Field(
229 doc=
"Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
231 check=
lambda x: x >= 1
and x <= 5
233 usePcaForSpatialKernel = pexConfig.Field(
235 doc=
"""Use Pca to reduce the dimensionality of the kernel basis sets.
236 This is particularly useful
for delta-function kernels.
237 Functionally, after all Cells have their raw kernels determined, we run
238 a Pca on these Kernels, re-fit the Cells using the eigenKernels
and then
239 fit those
for spatial variation using the same technique
as for Alard-Lupton kernels.
240 If this option
is used, the first term will have no spatial variation
and the
241 kernel sum will be conserved.
""",
244 subtractMeanForPca = pexConfig.Field(
246 doc=
"Subtract off the mean feature before doing the Pca",
249 numPrincipalComponents = pexConfig.Field(
251 doc=
"""Number of principal components to use for Pca basis, including the
252 mean kernel if requested.
""",
254 check=lambda x: x >= 3
256 singleKernelClipping = pexConfig.Field(
258 doc=
"Do sigma clipping on each raw kernel candidate",
261 kernelSumClipping = pexConfig.Field(
263 doc=
"Do sigma clipping on the ensemble of kernel sums",
266 spatialKernelClipping = pexConfig.Field(
268 doc=
"Do sigma clipping after building the spatial model",
271 checkConditionNumber = pexConfig.Field(
273 doc=
"""Test for maximum condition number when inverting a kernel matrix.
274 Anything above maxConditionNumber is not used
and the candidate
is set
as BAD.
275 Also used to truncate inverse matrix
in estimateBiasedRisk. However,
276 if you are doing any deconvolution you will want to turn this off,
or use
277 a large maxConditionNumber
""",
280 badMaskPlanes = pexConfig.ListField(
282 doc=
"""Mask planes to ignore when calculating diffim statistics
283 Options: NO_DATA EDGE SAT BAD CR INTRP""",
284 default=("NO_DATA",
"EDGE",
"SAT")
286 candidateResidualMeanMax = pexConfig.Field(
288 doc=
"""Rejects KernelCandidates yielding bad difference image quality.
289 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
290 Represents average over pixels of (image/sqrt(variance)).""",
292 check=lambda x: x >= 0.0
294 candidateResidualStdMax = pexConfig.Field(
296 doc=
"""Rejects KernelCandidates yielding bad difference image quality.
297 Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
298 Represents stddev over pixels of (image/sqrt(variance)).""",
300 check=lambda x: x >= 0.0
302 useCoreStats = pexConfig.Field(
304 doc=
"""Use the core of the footprint for the quality statistics, instead of the entire footprint.
305 WARNING: if there
is deconvolution we probably will need to turn this off
""",
308 candidateCoreRadius = pexConfig.Field(
310 doc=
"""Radius for calculation of stats in 'core' of KernelCandidate diffim.
311 Total number of pixels used will be (2*radius)**2.
312 This is used both
for 'core' diffim quality
as well
as ranking of
313 KernelCandidates by their total flux
in this core
""",
315 check=lambda x: x >= 1
317 maxKsumSigma = pexConfig.Field(
319 doc=
"""Maximum allowed sigma for outliers from kernel sum distribution.
320 Used to reject variable objects from the kernel model
""",
322 check=lambda x: x >= 0.0
324 maxConditionNumber = pexConfig.Field(
326 doc=
"Maximum condition number for a well conditioned matrix",
328 check=
lambda x: x >= 0.0
330 conditionNumberType = pexConfig.ChoiceField(
332 doc=
"Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
333 default=
"EIGENVALUE",
335 "SVD":
"Use singular values",
336 "EIGENVALUE":
"Use eigen values (faster)",
339 maxSpatialConditionNumber = pexConfig.Field(
341 doc=
"Maximum condition number for a well conditioned spatial matrix",
343 check=
lambda x: x >= 0.0
345 iterateSingleKernel = pexConfig.Field(
347 doc=
"""Remake KernelCandidate using better variance estimate after first pass?
348 Primarily useful when convolving a single-depth image, otherwise not necessary.
""",
351 constantVarianceWeighting = pexConfig.Field(
353 doc=
"""Use constant variance weighting in single kernel fitting?
354 In some cases this is better
for bright star residuals.
""",
357 calculateKernelUncertainty = pexConfig.Field(
359 doc=
"""Calculate kernel and background uncertainties for each kernel candidate?
360 This comes from the inverse of the covariance matrix.
361 Warning: regularization can cause problems
for this step.
""",
364 useBicForKernelBasis = pexConfig.Field(
366 doc=
"""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
372 """The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis"""
375 PsfMatchConfig.setDefaults(self)
379 alardNGauss = pexConfig.Field(
381 doc=
"Number of base Gaussians in alard-lupton kernel basis function generation.",
383 check=
lambda x: x >= 1
385 alardDegGauss = pexConfig.ListField(
387 doc=
"Polynomial order of spatial modification of base Gaussians. "
388 "List length must be `alardNGauss`.",
391 alardSigGauss = pexConfig.ListField(
393 doc=
"Default sigma values in pixels of base Gaussians. "
394 "List length must be `alardNGauss`.",
395 default=(0.7, 1.5, 3.0),
397 alardGaussBeta = pexConfig.Field(
399 doc=
"Used if `scaleByFwhm==True`, scaling multiplier of base "
400 "Gaussian sigmas for automated sigma determination",
402 check=
lambda x: x >= 0.0,
404 alardMinSig = pexConfig.Field(
406 doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians",
408 check=
lambda x: x >= 0.25
410 alardDegGaussDeconv = pexConfig.Field(
412 doc=
"Used if `scaleByFwhm==True`, degree of spatial modification of ALL base Gaussians "
413 "in AL basis during deconvolution",
415 check=
lambda x: x >= 1
417 alardMinSigDeconv = pexConfig.Field(
419 doc=
"Used if `scaleByFwhm==True`, minimum sigma (pixels) for base Gaussians during deconvolution; "
420 "make smaller than `alardMinSig` as this is only indirectly used",
422 check=
lambda x: x >= 0.25
424 alardNGaussDeconv = pexConfig.Field(
426 doc=
"Used if `scaleByFwhm==True`, number of base Gaussians in AL basis during deconvolution",
428 check=
lambda x: x >= 1
433 """The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis"""
436 PsfMatchConfig.setDefaults(self)
443 useRegularization = pexConfig.Field(
445 doc=
"Use regularization to smooth the delta function kernels",
448 regularizationType = pexConfig.ChoiceField(
450 doc=
"Type of regularization.",
451 default=
"centralDifference",
453 "centralDifference":
"Penalize second derivative using 2-D stencil of central finite difference",
454 "forwardDifference":
"Penalize first, second, third derivatives using forward finite differeces"
457 centralRegularizationStencil = pexConfig.ChoiceField(
459 doc=
"Type of stencil to approximate central derivative (for centralDifference only)",
462 5:
"5-point stencil including only adjacent-in-x,y elements",
463 9:
"9-point stencil including diagonal elements"
466 forwardRegularizationOrders = pexConfig.ListField(
468 doc=
"Array showing which order derivatives to penalize (for forwardDifference only)",
470 itemCheck=
lambda x: (x > 0)
and (x < 4)
472 regularizationBorderPenalty = pexConfig.Field(
474 doc=
"Value of the penalty for kernel border pixels",
476 check=
lambda x: x >= 0.0
478 lambdaType = pexConfig.ChoiceField(
480 doc=
"How to choose the value of the regularization strength",
483 "absolute":
"Use lambdaValue as the value of regularization strength",
484 "relative":
"Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
485 "minimizeBiasedRisk":
"Minimize biased risk estimate",
486 "minimizeUnbiasedRisk":
"Minimize unbiased risk estimate",
489 lambdaValue = pexConfig.Field(
491 doc=
"Value used for absolute determinations of regularization strength",
494 lambdaScaling = pexConfig.Field(
496 doc=
"Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
499 lambdaStepType = pexConfig.ChoiceField(
501 doc=
"""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
502 use log or linear steps
""",
505 "log":
"Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
506 "linear":
"Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
509 lambdaMin = pexConfig.Field(
511 doc=
"""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
512 start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
515 lambdaMax = pexConfig.Field(
517 doc="""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
518 stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
521 lambdaStep = pexConfig.Field(
523 doc="""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
524 step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1
""",
530 """Base class for Psf Matching; should not be called directly
534 PsfMatchTask is a base
class that implements the core functionality for matching the
538 of basis shapes that will be used
for the decomposition. If requested, the Task
539 also performs background matching
and returns the differential background model
as an
542 **Invoking the Task**
544 As a base
class, this Task
is not directly invoked. However, ``run()`` methods that are
545 implemented on derived classes will make use of the core ``
_solve()`` functionality,
547 through the KernelCandidates, first building up a per-candidate solution
and then
548 building up a spatial model
from the ensemble of candidates. Sigma clipping
is
549 performed using the mean
and standard deviation of all kernel sums (to reject
550 variable objects), on the per-candidate substamp diffim residuals
551 (to indicate a bad choice of kernel basis shapes
for that particular object),
552 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad
553 choice of spatial kernel order,
or poor constraints on the spatial model). The
554 ``
_diagnostic()`` method logs information on the quality of the spatial fit,
and also
555 modifies the Task metadata.
557 .. list-table:: Quantities set
in Metadata
562 * - ``spatialConditionNum``
563 - Condition number of the spatial kernel fit
564 * - ``spatialKernelSum``
565 - Kernel sum (10^{-0.4 * ``Delta``; zeropoint}) of the spatial Psf-matching kernel
566 * - ``ALBasisNGauss``
567 - If using sum-of-Gaussian basis, the number of gaussians used
568 * - ``ALBasisDegGauss``
569 - If using sum-of-Gaussian basis, the deg of spatial variation of the Gaussians
570 * - ``ALBasisSigGauss``
571 - If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians
573 - If using sum-of-Gaussian basis, the kernel size
574 * - ``NFalsePositivesTotal``
575 - Total number of diaSources
576 * - ``NFalsePositivesRefAssociated``
577 - Number of diaSources that associate
with the reference catalog
578 * - ``NFalsePositivesRefAssociated``
579 - Number of diaSources that associate
with the source catalog
580 * - ``NFalsePositivesUnassociated``
581 - Number of diaSources that are orphans
583 - Mean value of substamp diffim quality metrics across all KernelCandidates,
584 for both the per-candidate (LOCAL)
and SPATIAL residuals
585 * - ``metric_MEDIAN``
586 - Median value of substamp diffim quality metrics across all KernelCandidates,
587 for both the per-candidate (LOCAL)
and SPATIAL residuals
589 - Standard deviation of substamp diffim quality metrics across all KernelCandidates,
590 for both the per-candidate (LOCAL)
and SPATIAL residuals
594 The ``pipetask`` command line interface supports a
595 flag --debug to
import @b debug.py
from your PYTHONPATH. The relevant contents of debug.py
596 for this Task include:
604 if name ==
"lsst.ip.diffim.psfMatch":
608 di.maskTransparency = 80
610 di.displayCandidates =
True
612 di.displayKernelBasis =
False
614 di.displayKernelMosaic =
True
616 di.plotKernelSpatialModel =
False
618 di.plotKernelCoefficients =
True
620 di.showBadCandidates =
True
625 Note that
if you want additional logging info, you may add to your scripts:
629 import lsst.utils.logging
as logUtils
630 logUtils.trace_set_at(
"lsst.ip.diffim", 4)
632 ConfigClass = PsfMatchConfig
633 _DefaultName = "psfMatch"
636 """Create the psf-matching Task
641 Arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
643 Keyword arguments to be passed to ``lsst.pipe.base.task.Task.__init__``
647 The initialization sets the Psf-matching kernel configuration using the value of
648 self.config.kernel.active. If the kernel is requested
with regularization to moderate
649 the bias/variance tradeoff, currently only used when a delta function kernel basis
650 is provided, it creates a regularization matrix stored
as member variable
653 pipeBase.Task.__init__(self, *args, **kwargs)
656 if 'useRegularization' in self.
kConfig:
662 self.
hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePropertySet(self.
kConfig))
664 def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
665 """Provide logging diagnostics on quality of spatial kernel fit
670 Cellset that contains the KernelCandidates used in the fitting
672 KernelSolution of best-fit
674 Best-fit spatial Kernel model
675 spatialBg : `lsst.afw.math.Function2D`
676 Best-fit spatial background model
679 kImage = afwImage.ImageD(spatialKernel.getDimensions())
680 kSum = spatialKernel.computeImage(kImage,
False)
681 self.log.info(
"Final spatial kernel sum %.3f", kSum)
684 conditionNum = spatialSolution.getConditionNumber(
685 getattr(diffimLib.KernelSolution, self.
kConfig.conditionNumberType))
686 self.log.info(
"Spatial model condition number %.3e", conditionNum)
688 if conditionNum < 0.0:
689 self.log.warning(
"Condition number is negative (%.3e)", conditionNum)
690 if conditionNum > self.
kConfig.maxSpatialConditionNumber:
691 self.log.warning(
"Spatial solution exceeds max condition number (%.3e > %.3e)",
692 conditionNum, self.
kConfig.maxSpatialConditionNumber)
694 self.metadata[
"spatialConditionNum"] = conditionNum
695 self.metadata[
"spatialKernelSum"] = kSum
698 nBasisKernels = spatialKernel.getNBasisKernels()
699 nKernelTerms = spatialKernel.getNSpatialParameters()
700 if nKernelTerms == 0:
704 nBgTerms = spatialBg.getNParameters()
706 if spatialBg.getParameters()[0] == 0.0:
712 for cell
in kernelCellSet.getCellList():
713 for cand
in cell.begin(
False):
715 if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
717 if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
720 self.log.info(
"Doing stats of kernel candidates used in the spatial fit.")
724 self.log.warning(
"Many more candidates rejected than accepted; %d total, %d rejected, %d used",
727 self.log.info(
"%d candidates total, %d rejected, %d used", nTot, nBad, nGood)
730 if nGood < nKernelTerms:
731 self.log.warning(
"Spatial kernel model underconstrained; %d candidates, %d terms, %d bases",
732 nGood, nKernelTerms, nBasisKernels)
733 self.log.warning(
"Consider lowering the spatial order")
734 elif nGood <= 2*nKernelTerms:
735 self.log.warning(
"Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases",
736 nGood, nKernelTerms, nBasisKernels)
737 self.log.warning(
"Consider lowering the spatial order")
739 self.log.info(
"Spatial kernel model well constrained; %d candidates, %d terms, %d bases",
740 nGood, nKernelTerms, nBasisKernels)
743 self.log.warning(
"Spatial background model underconstrained; %d candidates, %d terms",
745 self.log.warning(
"Consider lowering the spatial order")
746 elif nGood <= 2*nBgTerms:
747 self.log.warning(
"Spatial background model poorly constrained; %d candidates, %d terms",
749 self.log.warning(
"Consider lowering the spatial order")
751 self.log.info(
"Spatial background model appears well constrained; %d candidates, %d terms",
755 """Provide visualization of the inputs and ouputs to the Psf-matching code
760 The SpatialCellSet used in determining the matching kernel
and background
762 Spatially varying Psf-matching kernel
763 spatialBackground : `lsst.afw.math.Function2D`
764 Spatially varying background-matching function
769 displayKernelMosaic =
lsstDebug.Info(__name__).displayKernelMosaic
770 plotKernelSpatialModel =
lsstDebug.Info(__name__).plotKernelSpatialModel
771 plotKernelCoefficients =
lsstDebug.Info(__name__).plotKernelCoefficients
774 if not maskTransparency:
776 afwDisplay.setDefaultMaskTransparency(maskTransparency)
778 if displayCandidates:
779 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
780 frame=lsstDebug.frame,
781 showBadCandidates=showBadCandidates)
783 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
784 frame=lsstDebug.frame,
785 showBadCandidates=showBadCandidates,
788 diutils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
789 frame=lsstDebug.frame,
790 showBadCandidates=showBadCandidates,
794 if displayKernelBasis:
795 diutils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
798 if displayKernelMosaic:
799 diutils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
802 if plotKernelSpatialModel:
803 diutils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
805 if plotKernelCoefficients:
806 diutils.plotKernelCoefficients(spatialKernel, kernelCellSet)
809 """Create Principal Component basis
811 If a principal component analysis is requested, typically when using a delta function basis,
812 perform the PCA here
and return a new basis list containing the new principal components.
817 a SpatialCellSet containing KernelCandidates,
from which components are derived
819 the number of stars per cell to visit when doing the PCA
821 input property set controlling the single kernel visitor
826 number of KernelCandidates rejected during PCA loop
827 spatialBasisList : `list` of `lsst.afw.math.kernel.FixedKernel`
828 basis list containing the principal shapes
as Kernels
833 If the Eigenvalues sum to zero.
835 nComponents = self.kConfig.numPrincipalComponents
836 imagePca = diffimLib.KernelPcaD()
837 importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
838 kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
839 if self.
kConfig.subtractMeanForPca:
840 importStarVisitor.subtractMean()
843 eigenValues = imagePca.getEigenValues()
844 pcaBasisList = importStarVisitor.getEigenKernels()
846 eSum = np.sum(eigenValues)
848 raise RuntimeError(
"Eigenvalues sum to zero")
849 trace_logger = getTraceLogger(self.log.getChild(
"_solve"), 5)
850 for j
in range(len(eigenValues)):
851 trace_logger.debug(
"Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
853 nToUse =
min(nComponents, len(eigenValues))
855 for j
in range(nToUse):
857 kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
858 pcaBasisList[j].computeImage(kimage,
False)
859 if not (
True in np.isnan(kimage.array)):
860 trimBasisList.append(pcaBasisList[j])
863 spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
866 singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, ps)
867 singlekvPca.setSkipBuilt(
False)
868 kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
869 singlekvPca.setSkipBuilt(
True)
870 nRejectedPca = singlekvPca.getNRejected()
872 return nRejectedPca, spatialBasisList
876 """Fill a SpatialCellSet with KernelCandidates for the Psf-matching process;
877 override in derived classes
"""
881 def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
882 """Solve for the PSF matching kernel
887 a SpatialCellSet to use in determining the matching kernel
888 (typically
as provided by _buildCellSet)
889 basisList : `list` of `lsst.afw.math.kernel.FixedKernel`
890 list of Kernels to be used
in the decomposition of the spatially varying kernel
891 (typically
as provided by makeKernelBasisList)
892 returnOnExcept : `bool`, optional
893 if True then
return (
None,
None)
if an error occurs,
else raise the exception
898 Spatially varying Psf-matching kernel
899 backgroundModel : `lsst.afw.math.Function2D`
900 Spatially varying background-matching function
905 If unable to determine PSF matching kernel
and ``returnOnExcept==
False``.
911 maxSpatialIterations = self.
kConfig.maxSpatialIterations
912 nStarPerCell = self.
kConfig.nStarPerCell
913 usePcaForSpatialKernel = self.
kConfig.usePcaForSpatialKernel
916 ps = pexConfig.makePropertySet(self.
kConfig)
918 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps, self.
hMat)
920 singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, ps)
923 ksv = diffimLib.KernelSumVisitorF(ps)
926 trace_loggers = [getTraceLogger(self.log.getChild(
"_solve"), i)
for i
in range(5)]
931 while (thisIteration < maxSpatialIterations):
935 while (nRejectedSkf != 0):
936 trace_loggers[1].debug(
"Building single kernels...")
937 kernelCellSet.visitCandidates(singlekv, nStarPerCell)
938 nRejectedSkf = singlekv.getNRejected()
939 trace_loggers[1].debug(
940 "Iteration %d, rejected %d candidates due to initial kernel fit",
941 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()
953 trace_loggers[1].debug(
954 "Iteration %d, rejected %d candidates due to kernel sum",
955 thisIteration, nRejectedKsum
959 if nRejectedKsum > 0:
968 if (usePcaForSpatialKernel):
969 trace_loggers[0].debug(
"Building Pca basis")
971 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, ps)
972 trace_loggers[1].debug(
973 "Iteration %d, rejected %d candidates due to Pca kernel fit",
974 thisIteration, nRejectedPca
986 if (nRejectedPca > 0):
990 spatialBasisList = basisList
993 regionBBox = kernelCellSet.getBBox()
994 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
995 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
996 spatialkv.solveLinearEquation()
997 trace_loggers[2].debug(
"Spatial kernel built with %d candidates", spatialkv.getNCandidates())
998 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1001 assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, ps)
1002 kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1003 nRejectedSpatial = assesskv.getNRejected()
1004 nGoodSpatial = assesskv.getNGood()
1005 trace_loggers[1].debug(
1006 "Iteration %d, rejected %d candidates due to spatial kernel fit",
1007 thisIteration, nRejectedSpatial
1009 trace_loggers[1].debug(
"%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 trace_loggers[1].debug(
"Final spatial fit")
1025 if (usePcaForSpatialKernel):
1026 nRejectedPca, spatialBasisList = self.
_createPcaBasis(kernelCellSet, nStarPerCell, ps)
1027 regionBBox = kernelCellSet.getBBox()
1028 spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, ps)
1029 kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1030 spatialkv.solveLinearEquation()
1031 trace_loggers[2].debug(
"Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1032 spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1034 spatialSolution = spatialkv.getKernelSolution()
1036 except Exception
as e:
1037 self.log.error(
"ERROR: Unable to calculate psf matching kernel")
1039 trace_loggers[1].debug(
"%s", e)
1043 trace_loggers[0].debug(
"Total time to compute the spatial kernel : %.2f s", (t1 - t0))
1046 self.
_displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1048 self.
_diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1050 return spatialSolution, spatialKernel, spatialBackground
1053PsfMatch = PsfMatchTask
A kernel that is a linear combination of fixed basis kernels.
A collection of SpatialCells covering an entire image.
Class for storing generic metadata.
Class stored in SpatialCells for spatial Kernel fitting.
_solve(self, kernelCellSet, basisList, returnOnExcept=False)
_displayDebug(self, kernelCellSet, spatialKernel, spatialBackground)
__init__(self, *args, **kwargs)
_createPcaBasis(self, kernelCellSet, nStarPerCell, ps)
_buildCellSet(self, *args)
_diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg)