LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
psfMatch.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2016 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import time
23 import numpy as num
24 import lsst.afw.image as afwImage
25 import lsst.pex.logging as pexLog
26 import lsst.pex.config as pexConfig
27 import lsst.afw.math as afwMath
28 import lsst.afw.display.ds9 as ds9
29 import lsst.pipe.base as pipeBase
30 from lsst.meas.algorithms import SubtractBackgroundConfig
31 from . import utils as diUtils
32 from . import diffimLib
33 
34 class DetectionConfig(pexConfig.Config):
35  """!Configuration for detecting sources on images for building a PSF-matching kernel
36 
37  Configuration for turning detected lsst.afw.detection.FootPrints into an acceptable
38  (unmasked, high signal-to-noise, not too large or not too small) list of
39  lsst.ip.diffim.KernelSources that are used to build the Psf-matching kernel"""
40 
41  detThreshold = pexConfig.Field(
42  dtype = float,
43  doc = "Value of footprint detection threshold",
44  default = 10.0,
45  check = lambda x : x >= 3.0
46  )
47  detThresholdType = pexConfig.ChoiceField(
48  dtype = str,
49  doc = "Type of detection threshold",
50  default = "pixel_stdev",
51  allowed = {
52  "value" : "Use counts as the detection threshold type",
53  "stdev" : "Use standard deviation of image plane",
54  "variance" : "Use variance of image plane",
55  "pixel_stdev" : "Use stdev derived from variance plane"
56  }
57  )
58  detOnTemplate = pexConfig.Field(
59  dtype = bool,
60  doc = """If true run detection on the template (image to convolve);
61  if false run detection on the science image""",
62  default = True
63  )
64  badMaskPlanes = pexConfig.ListField(
65  dtype = str,
66  doc = """Mask planes that lead to an invalid detection.
67  Options: NO_DATA EDGE SAT BAD CR INTRP""",
68  default = ("NO_DATA", "EDGE", "SAT")
69  )
70  fpNpixMin = pexConfig.Field(
71  dtype = int,
72  doc = "Minimum number of pixels in an acceptable Footprint",
73  default = 5,
74  check = lambda x : x >= 5
75  )
76  fpNpixMax = pexConfig.Field(
77  dtype = int,
78  doc = """Maximum number of pixels in an acceptable Footprint;
79  too big and the subsequent convolutions become unwieldy""",
80  default = 500,
81  check = lambda x : x <= 500
82  )
83  fpGrowKernelScaling = pexConfig.Field(
84  dtype = float,
85  doc = """If config.scaleByFwhm, grow the footprint based on
86  the final kernelSize. Each footprint will be
87  2*fpGrowKernelScaling*kernelSize x
88  2*fpGrowKernelScaling*kernelSize. With the value
89  of 1.0, the remaining pixels in each KernelCandiate
90  after convolution by the basis functions will be
91  eqaul to the kernel size iteslf.""",
92  default = 1.0,
93  check = lambda x : x >= 1.0
94  )
95  fpGrowPix = pexConfig.Field(
96  dtype = int,
97  doc = """Growing radius (in pixels) for each raw detection
98  footprint. The smaller the faster; however the
99  kernel sum does not converge if the stamp is too
100  small; and the kernel is not constrained at all if
101  the stamp is the size of the kernel. The grown stamp
102  is 2 * fpGrowPix pixels larger in each dimension.
103  This is overridden by fpGrowKernelScaling if scaleByFwhm""",
104  default = 30,
105  check = lambda x : x >= 10
106  )
107  scaleByFwhm = pexConfig.Field(
108  dtype = bool,
109  doc = "Scale fpGrowPix by input Fwhm?",
110  default = True,
111  )
112 
113 
114 class PsfMatchConfig(pexConfig.Config):
115  """!Base configuration for Psf-matching
116 
117  The base configuration of the Psf-matching kernel, and of the warping, detection,
118  and background modeling subTasks."""
119 
120  warpingConfig = pexConfig.ConfigField("Config for warping exposures to a common alignment",
121  afwMath.warper.WarperConfig)
122  detectionConfig = pexConfig.ConfigField("Controlling the detection of sources for kernel building",
123  DetectionConfig)
124  afwBackgroundConfig = pexConfig.ConfigField("Controlling the Afw background fitting",
125  SubtractBackgroundConfig)
126 
127  useAfwBackground = pexConfig.Field(
128  dtype = bool,
129  doc = "Use afw background subtraction instead of ip_diffim",
130  default = False,
131  )
132  fitForBackground = pexConfig.Field(
133  dtype = bool,
134  doc = "Include terms (including kernel cross terms) for background in ip_diffim",
135  default = False,
136  )
137  kernelBasisSet = pexConfig.ChoiceField(
138  dtype = str,
139  doc = "Type of basis set for PSF matching kernel.",
140  default = "alard-lupton",
141  allowed = {
142  "alard-lupton" : """Alard-Lupton sum-of-gaussians basis set,
143  * The first term has no spatial variation
144  * The kernel sum is conserved
145  * You may want to turn off 'usePcaForSpatialKernel'""",
146  "delta-function" : """Delta-function kernel basis set,
147  * You may enable the option useRegularization
148  * You should seriously consider usePcaForSpatialKernel, which will also
149  enable kernel sum conservation for the delta function kernels"""
150  }
151  )
152  kernelSize = pexConfig.Field(
153  dtype = int,
154  doc = """Number of rows/columns in the convolution kernel; should be odd-valued.
155  Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
156  default = 21,
157  )
158  scaleByFwhm = pexConfig.Field(
159  dtype = bool,
160  doc = "Scale kernelSize, alardGaussians by input Fwhm",
161  default = True,
162  )
163  kernelSizeFwhmScaling = pexConfig.Field(
164  dtype = float,
165  doc = """How much to scale the kernel size based on the largest AL Sigma""",
166  default = 6.0,
167  check = lambda x : x >= 1.0
168  )
169  kernelSizeMin = pexConfig.Field(
170  dtype = int,
171  doc = """Minimum Kernel Size""",
172  default = 21,
173  )
174  kernelSizeMax = pexConfig.Field(
175  dtype = int,
176  doc = """Maximum Kernel Size""",
177  default = 35,
178  )
179  spatialModelType = pexConfig.ChoiceField(
180  dtype = str,
181  doc = "Type of spatial functions for kernel and background",
182  default = "chebyshev1",
183  allowed = {
184  "chebyshev1" : "Chebyshev polynomial of the first kind",
185  "polynomial" : "Standard x,y polynomial",
186  }
187  )
188  spatialKernelOrder = pexConfig.Field(
189  dtype = int,
190  doc = "Spatial order of convolution kernel variation",
191  default = 2,
192  check = lambda x : x >= 0
193  )
194  spatialBgOrder = pexConfig.Field(
195  dtype = int,
196  doc = "Spatial order of differential background variation",
197  default = 1,
198  check = lambda x : x >= 0
199  )
200  sizeCellX = pexConfig.Field(
201  dtype = int,
202  doc = "Size (rows) in pixels of each SpatialCell for spatial modeling",
203  default = 128,
204  check = lambda x : x >= 32
205  )
206  sizeCellY = pexConfig.Field(
207  dtype = int,
208  doc = "Size (columns) in pixels of each SpatialCell for spatial modeling",
209  default = 128,
210  check = lambda x : x >= 32
211  )
212  nStarPerCell = pexConfig.Field(
213  dtype = int,
214  doc = "Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
215  default = 3,
216  check = lambda x : x >= 1
217  )
218  maxSpatialIterations = pexConfig.Field(
219  dtype = int,
220  doc = "Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
221  default = 3,
222  check = lambda x : x >= 1 and x <= 5
223  )
224  usePcaForSpatialKernel = pexConfig.Field(
225  dtype = bool,
226  doc = """Use Pca to reduce the dimensionality of the kernel basis sets.
227  This is particularly useful for delta-function kernels.
228  Functionally, after all Cells have their raw kernels determined, we run
229  a Pca on these Kernels, re-fit the Cells using the eigenKernels and then
230  fit those for spatial variation using the same technique as for Alard-Lupton kernels.
231  If this option is used, the first term will have no spatial variation and the
232  kernel sum will be conserved.""",
233  default = False,
234  )
235  subtractMeanForPca = pexConfig.Field(
236  dtype = bool,
237  doc = "Subtract off the mean feature before doing the Pca",
238  default = True,
239  )
240  numPrincipalComponents = pexConfig.Field(
241  dtype = int,
242  doc = """Number of principal components to use for Pca basis, including the
243  mean kernel if requested.""",
244  default = 5,
245  check = lambda x : x >= 3
246  )
247  singleKernelClipping = pexConfig.Field(
248  dtype = bool,
249  doc = "Do sigma clipping on each raw kernel candidate",
250  default = True,
251  )
252  kernelSumClipping = pexConfig.Field(
253  dtype = bool,
254  doc = "Do sigma clipping on the ensemble of kernel sums",
255  default = True,
256  )
257  spatialKernelClipping = pexConfig.Field(
258  dtype = bool,
259  doc = "Do sigma clipping after building the spatial model",
260  default = True,
261  )
262  checkConditionNumber = pexConfig.Field(
263  dtype = bool,
264  doc = """Test for maximum condition number when inverting a kernel matrix.
265  Anything above maxConditionNumber is not used and the candidate is set as BAD.
266  Also used to truncate inverse matrix in estimateBiasedRisk. However,
267  if you are doing any deconvolution you will want to turn this off, or use
268  a large maxConditionNumber""",
269  default = False,
270  )
271  badMaskPlanes = pexConfig.ListField(
272  dtype = str,
273  doc = """Mask planes to ignore when calculating diffim statistics
274  Options: NO_DATA EDGE SAT BAD CR INTRP""",
275  default = ("NO_DATA", "EDGE", "SAT")
276  )
277  candidateResidualMeanMax = pexConfig.Field(
278  dtype = float,
279  doc = """Rejects KernelCandidates yielding bad difference image quality.
280  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
281  Represents average over pixels of (image/sqrt(variance)).""",
282  default = 0.25,
283  check = lambda x : x >= 0.0
284  )
285  candidateResidualStdMax = pexConfig.Field(
286  dtype = float,
287  doc = """Rejects KernelCandidates yielding bad difference image quality.
288  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
289  Represents stddev over pixels of (image/sqrt(variance)).""",
290  default = 1.50,
291  check = lambda x : x >= 0.0
292  )
293  useCoreStats = pexConfig.Field(
294  dtype = bool,
295  doc = """Use the core of the footprint for the quality statistics, instead of the entire footprint.
296  WARNING: if there is deconvolution we probably will need to turn this off""",
297  default = False,
298  )
299  candidateCoreRadius = pexConfig.Field(
300  dtype = int,
301  doc = """Radius for calculation of stats in 'core' of KernelCandidate diffim.
302  Total number of pixels used will be (2*radius)**2.
303  This is used both for 'core' diffim quality as well as ranking of
304  KernelCandidates by their total flux in this core""",
305  default = 3,
306  check = lambda x : x >= 1
307  )
308  maxKsumSigma = pexConfig.Field(
309  dtype = float,
310  doc = """Maximum allowed sigma for outliers from kernel sum distribution.
311  Used to reject variable objects from the kernel model""",
312  default = 3.0,
313  check = lambda x : x >= 0.0
314  )
315  maxConditionNumber = pexConfig.Field(
316  dtype = float,
317  doc = "Maximum condition number for a well conditioned matrix",
318  default = 5.0e7,
319  check = lambda x : x >= 0.0
320  )
321  conditionNumberType = pexConfig.ChoiceField(
322  dtype = str,
323  doc = "Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
324  default = "EIGENVALUE",
325  allowed = {
326  "SVD" : "Use singular values",
327  "EIGENVALUE" : "Use eigen values (faster)",
328  }
329  )
330  maxSpatialConditionNumber = pexConfig.Field(
331  dtype = float,
332  doc = "Maximum condition number for a well conditioned spatial matrix",
333  default = 1.0e10,
334  check = lambda x : x >= 0.0
335  )
336  iterateSingleKernel = pexConfig.Field(
337  dtype = bool,
338  doc = """Remake KernelCandidate using better variance estimate after first pass?
339  Primarily useful when convolving a single-depth image, otherwise not necessary.""",
340  default = False,
341  )
342  constantVarianceWeighting = pexConfig.Field(
343  dtype = bool,
344  doc = """Use constant variance weighting in single kernel fitting?
345  In some cases this is better for bright star residuals.""",
346  default = True,
347  )
348  calculateKernelUncertainty = pexConfig.Field(
349  dtype = bool,
350  doc = """Calculate kernel and background uncertainties for each kernel candidate?
351  This comes from the inverse of the covariance matrix.
352  Warning: regularization can cause problems for this step.""",
353  default = False,
354  )
355  useBicForKernelBasis = pexConfig.Field(
356  dtype = bool,
357  doc = """Use Bayesian Information Criterion to select the number of bases going into the kernel""",
358  default = False,
359  )
360 
361 
363  """!The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis"""
364 
365  def setDefaults(self):
366  PsfMatchConfig.setDefaults(self)
367  self.kernelBasisSet = "alard-lupton"
368  self.maxConditionNumber = 5.0e7
369 
370  alardNGauss = pexConfig.Field(
371  dtype = int,
372  doc = "Number of Gaussians in alard-lupton basis",
373  default = 3,
374  check = lambda x : x >= 1
375  )
376  alardDegGauss = pexConfig.ListField(
377  dtype = int,
378  doc = "Polynomial order of spatial modification of Gaussians. Must in number equal alardNGauss",
379  default = (4, 2, 2),
380  )
381  alardSigGauss = pexConfig.ListField(
382  dtype = float,
383  doc = """Sigma in pixels of Gaussians (FWHM = 2.35 sigma). Must in number equal alardNGauss""",
384  default = (0.7, 1.5, 3.0),
385  )
386  alardGaussBeta = pexConfig.Field(
387  dtype = float,
388  doc = """Default scale factor between Gaussian sigmas """,
389  default = 2.0,
390  check = lambda x: x >= 0.0,
391  )
392  alardMinSig = pexConfig.Field(
393  dtype = float,
394  doc = """Minimum Sigma (pixels) for Gaussians""",
395  default = 0.7,
396  check = lambda x : x >= 0.25
397  )
398  alardDegGaussDeconv = pexConfig.Field(
399  dtype = int,
400  doc = """Degree of spatial modification of ALL gaussians in AL basis during deconvolution""",
401  default = 3,
402  check = lambda x : x >= 1
403  )
404  alardMinSigDeconv = pexConfig.Field(
405  dtype = float,
406  doc = """Minimum Sigma (pixels) for Gaussians during deconvolution;
407  make smaller than alardMinSig as this is only indirectly used""",
408  default = 0.4,
409  check = lambda x : x >= 0.25
410  )
411  alardNGaussDeconv = pexConfig.Field(
412  dtype = int,
413  doc = "Number of Gaussians in AL basis during deconvolution",
414  default = 3,
415  check = lambda x : x >= 1
416  )
417 
418 
419 
421  """!The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis"""
422 
423  def setDefaults(self):
424  PsfMatchConfig.setDefaults(self)
425  self.kernelBasisSet = "delta-function"
426  self.maxConditionNumber = 5.0e6
428  self.subtractMeanForPca = True
429  self.useBicForKernelBasis = False
430 
431  useRegularization = pexConfig.Field(
432  dtype = bool,
433  doc = "Use regularization to smooth the delta function kernels",
434  default = True,
435  )
436  regularizationType = pexConfig.ChoiceField(
437  dtype = str,
438  doc = "Type of regularization.",
439  default = "centralDifference",
440  allowed = {
441  "centralDifference": "Penalize second derivative using 2-D stencil of central finite difference",
442  "forwardDifference": "Penalize first, second, third derivatives using forward finite differeces"
443  }
444  )
445  centralRegularizationStencil = pexConfig.ChoiceField(
446  dtype = int,
447  doc = "Type of stencil to approximate central derivative (for centralDifference only)",
448  default = 9,
449  allowed = {
450  5 : "5-point stencil including only adjacent-in-x,y elements",
451  9 : "9-point stencil including diagonal elements"
452  }
453  )
454  forwardRegularizationOrders = pexConfig.ListField(
455  dtype = int,
456  doc = "Array showing which order derivatives to penalize (for forwardDifference only)",
457  default = (1, 2),
458  itemCheck = lambda x: (x > 0) and (x < 4)
459  )
460  regularizationBorderPenalty = pexConfig.Field(
461  dtype = float,
462  doc = "Value of the penalty for kernel border pixels",
463  default = 3.0,
464  check = lambda x : x >= 0.0
465  )
466  lambdaType = pexConfig.ChoiceField(
467  dtype = str,
468  doc = "How to choose the value of the regularization strength",
469  default = "absolute",
470  allowed = {
471  "absolute" : "Use lambdaValue as the value of regularization strength",
472  "relative" : "Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
473  "minimizeBiasedRisk" : "Minimize biased risk estimate",
474  "minimizeUnbiasedRisk" : "Minimize unbiased risk estimate",
475  }
476  )
477  lambdaValue = pexConfig.Field(
478  dtype = float,
479  doc = "Value used for absolute determinations of regularization strength",
480  default = 0.2,
481  )
482  lambdaScaling = pexConfig.Field(
483  dtype = float,
484  doc = "Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
485  default = 1e-4,
486  )
487  lambdaStepType = pexConfig.ChoiceField(
488  dtype = str,
489  doc = """If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
490  use log or linear steps""",
491  default = "log",
492  allowed = {
493  "log" : "Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
494  "linear" : "Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
495  }
496  )
497  lambdaMin = pexConfig.Field(
498  dtype = float,
499  doc = """If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
500  start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
501  default = -1.0,
502  )
503  lambdaMax = pexConfig.Field(
504  dtype = float,
505  doc = """If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
506  stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
507  default = 2.0,
508  )
509  lambdaStep = pexConfig.Field(
510  dtype = float,
511  doc = """If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
512  step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
513  default = 0.1,
514  )
515 
516 
517 ## \addtogroup LSST_task_documentation
518 ## \{
519 ## \page PsfMatchTask
520 ## \ref PsfMatchTask_ "PsfMatchTask"
521 ## \copybrief PsfMatchTask
522 ## \}
523 
524 class PsfMatchTask(pipeBase.Task):
525  """!
526 
527 \anchor PsfMatchTask_
528 
529 \brief Base class for Psf Matching; should not be called directly
530 
531 \section ip_diffim_psfmatch_Contents Contents
532 
533  - \ref ip_diffim_psfmatch_Purpose
534  - \ref ip_diffim_psfmatch_Initialize
535  - \ref ip_diffim_psfmatch_IO
536  - \ref ip_diffim_psfmatch_Config
537  - \ref ip_diffim_psfmatch_Metadata
538  - \ref ip_diffim_psfmatch_Debug
539  - \ref ip_diffim_psfmatch_Example
540 
541 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
542 
543 \section ip_diffim_psfmatch_Purpose Description
544 
545 PsfMatchTask is a base class that implements the core functionality for matching the
546 Psfs of two images using a spatially varying Psf-matching lsst.afw.math.LinearCombinationKernel.
547 The Task requires the user to provide an instance of an lsst.afw.math.SpatialCellSet,
548 filled with lsst.ip.diffim.KernelCandidate instances, and an lsst.afw.math.KernelList
549 of basis shapes that will be used for the decomposition. If requested, the Task
550 also performs background matching and returns the differential background model as an
551 lsst.afw.math.Kernel.SpatialFunction.
552 
553 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
554 
555 \section ip_diffim_psfmatch_Initialize Task initialization
556 
557 \copydoc \_\_init\_\_
558 
559 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
560 
561 \section ip_diffim_psfmatch_IO Invoking the Task
562 
563 As a base class, this Task is not directly invoked. However, run() methods that are
564 implemented on derived classes will make use of the core _solve() functionality,
565 which defines a sequence of lsst.afw.math.CandidateVisitor classes that iterate
566 through the KernelCandidates, first building up a per-candidate solution and then
567 building up a spatial model from the ensemble of candidates. Sigma clipping is
568 performed using the mean and standard deviation of all kernel sums (to reject
569 variable objects), on the per-candidate substamp diffim residuals
570 (to indicate a bad choice of kernel basis shapes for that particular object),
571 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad
572 choice of spatial kernel order, or poor constraints on the spatial model). The
573 _diagnostic() method logs information on the quality of the spatial fit, and also
574 modifies the Task metadata.
575 
576 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
577 
578 \section ip_diffim_psfmatch_Config Configuration parameters
579 
580 See \ref PsfMatchConfig, \ref PsfMatchConfigAL, \ref PsfMatchConfigDF, and \ref DetectionConfig.
581 
582 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
583 
584 \section ip_diffim_psfmatch_Metadata Quantities set in Metadata
585 
586 
587 <DL>
588 <DT> spatialConditionNum <DD> Condition number of the spatial kernel fit;
589  via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
590 <DT> spatialKernelSum <DD> Kernel sum (10^{-0.4 * &Delta; zeropoint}) of the spatial Psf-matching kernel;
591  via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
592 
593 <DT> ALBasisNGauss <DD> If using sum-of-Gaussian basis, the number of gaussians used;
594  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
595  generateAlardLuptonBasisList\endlink </DD> </DT>
596 <DT> ALBasisDegGauss <DD> If using sum-of-Gaussian basis, the degree of spatial variation of the Gaussians;
597  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
598  generateAlardLuptonBasisList\endlink </DD> </DT>
599 <DT> ALBasisSigGauss <DD> If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians;
600  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
601  generateAlardLuptonBasisList\endlink </DD> </DT>
602 <DT> ALKernelSize <DD> If using sum-of-Gaussian basis, the kernel size;
603  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
604  generateAlardLuptonBasisList\endlink </DD> </DT>
605 
606 <DT> NFalsePositivesTotal <DD> Total number of diaSources;
607  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
608 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the reference catalog;
609  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
610 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the source catalog;
611  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
612 <DT> NFalsePositivesUnassociated <DD> Number of diaSources that are orphans;
613  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
614 <DT> metric_MEAN <DD> Mean value of substamp diffim quality metrics across all KernelCandidates,
615  for both the per-candidate (LOCAL) and SPATIAL residuals;
616  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
617 <DT> metric_MEDIAN <DD> Median value of substamp diffim quality metrics across all KernelCandidates,
618  for both the per-candidate (LOCAL) and SPATIAL residuals;
619  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
620 <DT> metric_STDEV <DD> Standard deviation of substamp diffim quality metrics across all KernelCandidates,
621  for both the per-candidate (LOCAL) and SPATIAL residuals;
622  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
623 </DL>
624 
625 
626 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
627 
628 \section ip_diffim_psfmatch_Debug Debug variables
629 
630 
631 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
632 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py
633 for this Task include:
634 
635 \code{.py}
636  import sys
637  import lsstDebug
638  def DebugInfo(name):
639  di = lsstDebug.getInfo(name)
640  if name == "lsst.ip.diffim.psfMatch":
641  di.display = True # enable debug output
642  di.maskTransparency = 80 # ds9 mask transparency
643  di.displayCandidates = True # show all the candidates and residuals
644  di.displayKernelBasis = False # show kernel basis functions
645  di.displayKernelMosaic = True # show kernel realized across the image
646  di.plotKernelSpatialModel = False # show coefficients of spatial model
647  di.showBadCandidates = True # show the bad candidates (red) along with good (green)
648  return di
649  lsstDebug.Info = DebugInfo
650  lsstDebug.frame = 1
651 
652 \endcode
653 
654 Note that if you want addional logging info, you may add to your scripts:
655 \code{.py}
656 import lsst.pex.logging as pexLog
657 pexLog.Trace_setVerbosity('lsst.ip.diffim', 5)
658 \endcode
659 
660 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
661 
662 \section ip_diffim_psfmatch_Example Example code
663 
664 As a base class, there is no example code for PsfMatchTask.
665 However, see \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask ImagePsfMatchTask\endlink,
666 \link lsst.ip.diffim.snapPsfMatch.SnapPsfMatchTask SnapPsfMatchTask\endlink, and
667 \link lsst.ip.diffim.modelPsfMatch.ModelPsfMatchTask ModelPsfMatchTask\endlink.
668 
669 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
670 
671  """
672  ConfigClass = PsfMatchConfig
673  _DefaultName = "psfMatch"
674 
675  def __init__(self, *args, **kwargs):
676  """!Create the psf-matching Task
677 
678  \param *args arguments to be passed to lsst.pipe.base.task.Task.__init__
679  \param **kwargs keyword arguments to be passed to lsst.pipe.base.task.Task.__init__
680 
681  The initialization sets the Psf-matching kernel configuration using the value of
682  self.config.kernel.active. If the kernel is requested with regularization to moderate
683  the bias/variance tradeoff, currently only used when a delta function kernel basis
684  is provided, it creates a regularization matrix stored as member variable
685  self.hMat.
686  """
687  pipeBase.Task.__init__(self, *args, **kwargs)
688  self.kConfig = self.config.kernel.active
689 
690  #
691  if 'useRegularization' in self.kConfig.keys():
692  self.useRegularization = self.kConfig.useRegularization
693  else:
694  self.useRegularization = False
695 
696  if self.useRegularization:
697  self.hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePolicy(self.kConfig))
698 
699  def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
700  """!Provide logging diagnostics on quality of spatial kernel fit
701 
702  @param kernelCellSet: Cellset that contains the KernelCandidates used in the fitting
703  @param spatialSolution: KernelSolution of best-fit
704  @param spatialKernel: Best-fit spatial Kernel model
705  @param spatialBg: Best-fit spatial background model
706 
707  """
708  # What is the final kernel sum
709  kImage = afwImage.ImageD(spatialKernel.getDimensions())
710  kSum = spatialKernel.computeImage(kImage, False)
711  self.log.info("Final spatial kernel sum %.3f" % (kSum))
712 
713  # Look at how well conditioned the matrix is
714  conditionNum = spatialSolution.getConditionNumber(
715  getattr(diffimLib.KernelSolution, self.kConfig.conditionNumberType))
716  self.log.info("Spatial model condition number %.3e" % (conditionNum))
717 
718  if conditionNum < 0.0:
719  self.log.warn("Condition number is negative (%.3e)" % (conditionNum))
720  if conditionNum > self.kConfig.maxSpatialConditionNumber:
721  self.log.warn("Spatial solution exceeds max condition number (%.3e > %.3e)" % (
722  conditionNum, self.kConfig.maxSpatialConditionNumber))
723 
724  self.metadata.set("spatialConditionNum", conditionNum)
725  self.metadata.set("spatialKernelSum", kSum)
726 
727  # Look at how well the solution is constrained
728  nBasisKernels = spatialKernel.getNBasisKernels()
729  nKernelTerms = spatialKernel.getNSpatialParameters()
730  if nKernelTerms == 0: # order 0
731  nKernelTerms = 1
732 
733  # Not fit for
734  nBgTerms = spatialBg.getNParameters()
735  if nBgTerms == 1:
736  if spatialBg.getParameters()[0] == 0.0:
737  nBgTerms = 0
738 
739  nGood = 0
740  nBad = 0
741  nTot = 0
742  for cell in kernelCellSet.getCellList():
743  for cand in cell.begin(False): # False = include bad candidates
744  cand = diffimLib.cast_KernelCandidateF(cand)
745  nTot += 1
746  if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
747  nGood += 1
748  if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
749  nBad += 1
750 
751  self.log.info("Doing stats of kernel candidates used in the spatial fit.")
752 
753  # Counting statistics
754  if nBad > 2*nGood:
755  self.log.warn("Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
756  nTot, nBad, nGood) )
757  else:
758  self.log.info("%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
759 
760  # Some judgements on the quality of the spatial models
761  if nGood < nKernelTerms:
762  self.log.warn("Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
763  nGood, nKernelTerms, nBasisKernels))
764  self.log.warn("Consider lowering the spatial order")
765  elif nGood <= 2*nKernelTerms:
766  self.log.warn("Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
767  nGood, nKernelTerms, nBasisKernels))
768  self.log.warn("Consider lowering the spatial order")
769  else:
770  self.log.info("Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
771  nGood, nKernelTerms, nBasisKernels))
772 
773  if nGood < nBgTerms:
774  self.log.warn("Spatial background model underconstrained; %d candidates, %d terms" % (
775  nGood, nBgTerms))
776  self.log.warn("Consider lowering the spatial order")
777  elif nGood <= 2*nBgTerms:
778  self.log.warn("Spatial background model poorly constrained; %d candidates, %d terms" % (
779  nGood, nBgTerms))
780  self.log.warn("Consider lowering the spatial order")
781  else:
782  self.log.info("Spatial background model appears well constrained; %d candidates, %d terms" % (
783  nGood, nBgTerms))
784 
785  def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
786  """!Provide visualization of the inputs and ouputs to the Psf-matching code
787 
788  @param kernelCellSet: the SpatialCellSet used in determining the matching kernel and background
789  @param spatialKernel: spatially varying Psf-matching kernel
790  @param spatialBackground: spatially varying background-matching function
791 
792  """
793  import lsstDebug
794  displayCandidates = lsstDebug.Info(__name__).displayCandidates
795  displayKernelBasis = lsstDebug.Info(__name__).displayKernelBasis
796  displayKernelMosaic = lsstDebug.Info(__name__).displayKernelMosaic
797  plotKernelSpatialModel = lsstDebug.Info(__name__).plotKernelSpatialModel
798  showBadCandidates = lsstDebug.Info(__name__).showBadCandidates
799  maskTransparency = lsstDebug.Info(__name__).maskTransparency
800  if not maskTransparency:
801  maskTransparency = 0
802  ds9.setMaskTransparency(maskTransparency)
803 
804  if displayCandidates:
805  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
806  frame=lsstDebug.frame,
807  showBadCandidates=showBadCandidates)
808  lsstDebug.frame += 1
809  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
810  frame=lsstDebug.frame,
811  showBadCandidates=showBadCandidates,
812  kernels=True)
813  lsstDebug.frame += 1
814  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
815  frame=lsstDebug.frame,
816  showBadCandidates=showBadCandidates,
817  resids=True)
818  lsstDebug.frame += 1
819 
820  if displayKernelBasis:
821  diUtils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
822  lsstDebug.frame += 1
823 
824  if displayKernelMosaic:
825  diUtils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
826  lsstDebug.frame += 1
827 
828  if plotKernelSpatialModel:
829  diUtils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
830 
831 
832  def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy):
833  """!Create Principal Component basis
834 
835  If a principal component analysis is requested, typically when using a delta function basis,
836  perform the PCA here and return a new basis list containing the new principal components.
837 
838  @param kernelCellSet: a SpatialCellSet containing KernelCandidates, from which components are derived
839  @param nStarPerCell: the number of stars per cell to visit when doing the PCA
840  @param policy: input policy controlling the single kernel visitor
841 
842  @return
843  - nRejectedPca: number of KernelCandidates rejected during PCA loop
844  - spatialBasisList: basis list containing the principal shapes as Kernels
845 
846  """
847  nComponents = self.kConfig.numPrincipalComponents
848  imagePca = diffimLib.KernelPcaD()
849  importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
850  kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
851  if self.kConfig.subtractMeanForPca:
852  importStarVisitor.subtractMean()
853  imagePca.analyze()
854 
855  eigenValues = imagePca.getEigenValues()
856  pcaBasisList = importStarVisitor.getEigenKernels()
857 
858  eSum = num.sum(eigenValues)
859  if eSum == 0.0:
860  raise RuntimeError("Eigenvalues sum to zero")
861  for j in range(len(eigenValues)):
862  pexLog.Trace(self.log.getName()+"._solve", 6,
863  "Eigenvalue %d : %f (%f)" % (j, eigenValues[j], eigenValues[j]/eSum))
864 
865  nToUse = min(nComponents, len(eigenValues))
866  trimBasisList = afwMath.KernelList()
867  for j in range(nToUse):
868  # Check for NaNs?
869  kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
870  pcaBasisList[j].computeImage(kimage, False)
871  if not (True in num.isnan(kimage.getArray())):
872  trimBasisList.push_back(pcaBasisList[j])
873 
874  # Put all the power in the first kernel, which will not vary spatially
875  spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
876 
877  # New Kernel visitor for this new basis list (no regularization explicitly)
878  singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, policy)
879  singlekvPca.setSkipBuilt(False)
880  kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
881  singlekvPca.setSkipBuilt(True)
882  nRejectedPca = singlekvPca.getNRejected()
883 
884  return nRejectedPca, spatialBasisList
885 
886 
887  def _buildCellSet(self, *args):
888  """!Fill a SpatialCellSet with KernelCandidates for the Psf-matching process;
889  override in derived classes"""
890  return
891 
892  @pipeBase.timeMethod
893  def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
894  """!Solve for the PSF matching kernel
895 
896  @param kernelCellSet: a SpatialCellSet to use in determining the matching kernel
897  (typically as provided by _buildCellSet)
898  @param basisList: list of Kernels to be used in the decomposition of the spatially varying kernel
899  (typically as provided by makeKernelBasisList)
900  @param returnOnExcept: if True then return (None, None) if an error occurs, else raise the exception
901 
902  @return
903  - psfMatchingKernel: PSF matching kernel
904  - backgroundModel: differential background model
905 
906  Raise Exception if unable to determine PSF matching kernel and returnOnExcept False
907  """
908 
909  import lsstDebug
910  display = lsstDebug.Info(__name__).display
911 
912  maxSpatialIterations = self.kConfig.maxSpatialIterations
913  nStarPerCell = self.kConfig.nStarPerCell
914  usePcaForSpatialKernel = self.kConfig.usePcaForSpatialKernel
915 
916  # Visitor for the single kernel fit
917  policy = pexConfig.makePolicy(self.kConfig)
918  if self.useRegularization:
919  singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy, self.hMat)
920  else:
921  singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
922 
923  # Visitor for the kernel sum rejection
924  ksv = diffimLib.KernelSumVisitorF(policy)
925 
926  # Main loop
927  t0 = time.time()
928  try:
929  totalIterations = 0
930  thisIteration = 0
931  while (thisIteration < maxSpatialIterations):
932 
933  # Make sure there are no uninitialized candidates as active occupants of Cell
934  nRejectedSkf = -1
935  while (nRejectedSkf != 0):
936  pexLog.Trace(self.log.getName()+"._solve", 2,
937  "Building single kernels...")
938  kernelCellSet.visitCandidates(singlekv, nStarPerCell)
939  nRejectedSkf = singlekv.getNRejected()
940  pexLog.Trace(self.log.getName()+"._solve", 2,
941  "Iteration %d, rejected %d candidates due to initial kernel fit" %
942  (thisIteration, nRejectedSkf))
943 
944  # Reject outliers in kernel sum
945  ksv.resetKernelSum()
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)
951 
952  nRejectedKsum = ksv.getNRejected()
953  pexLog.Trace(self.log.getName()+"._solve", 2,
954  "Iteration %d, rejected %d candidates due to kernel sum" %
955  (thisIteration, nRejectedKsum))
956 
957 
958  # Do we jump back to the top without incrementing thisIteration?
959  if nRejectedKsum > 0:
960  totalIterations += 1
961  continue
962 
963  # At this stage we can either apply the spatial fit to
964  # the kernels, or we run a PCA, use these as a *new*
965  # basis set with lower dimensionality, and then apply
966  # the spatial fit to these kernels
967 
968  if (usePcaForSpatialKernel):
969  pexLog.Trace(self.log.getName()+"._solve", 1, "Building Pca basis")
970 
971  nRejectedPca, spatialBasisList = self._createPcaBasis(kernelCellSet, nStarPerCell, policy)
972  pexLog.Trace(self.log.getName()+"._solve", 2,
973  "Iteration %d, rejected %d candidates due to Pca kernel fit" % (
974  thisIteration, nRejectedPca))
975 
976  # We don't want to continue on (yet) with the
977  # spatial modeling, because we have bad objects
978  # contributing to the Pca basis. We basically
979  # need to restart from the beginning of this loop,
980  # since the cell-mates of those objects that were
981  # rejected need their original Kernels built by
982  # singleKernelFitter.
983 
984  # Don't count against thisIteration
985  if (nRejectedPca > 0):
986  totalIterations += 1
987  continue
988  else:
989  spatialBasisList = basisList
990 
991  # We have gotten on to the spatial modeling part
992  regionBBox = kernelCellSet.getBBox()
993  spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
994  kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
995  spatialkv.solveLinearEquation()
996  pexLog.Trace(self.log.getName()+"._solve", 3,
997  "Spatial kernel built with %d candidates" % (spatialkv.getNCandidates()))
998  spatialKernel, spatialBackground = spatialkv.getSolutionPair()
999 
1000  # Check the quality of the spatial fit (look at residuals)
1001  assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, policy)
1002  kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1003  nRejectedSpatial = assesskv.getNRejected()
1004  nGoodSpatial = assesskv.getNGood()
1005  pexLog.Trace(self.log.getName()+"._solve", 2,
1006  "Iteration %d, rejected %d candidates due to spatial kernel fit" % (
1007  thisIteration, nRejectedSpatial))
1008  pexLog.Trace(self.log.getName()+"._solve", 2,
1009  "%d candidates used in fit" % (nGoodSpatial))
1010 
1011  # If only nGoodSpatial == 0, might be other candidates in the cells
1012  if nGoodSpatial == 0 and nRejectedSpatial == 0:
1013  raise RuntimeError("No kernel candidates for spatial fit")
1014 
1015  if nRejectedSpatial == 0:
1016  # Nothing rejected, finished with spatial fit
1017  break
1018 
1019  # Otherwise, iterate on...
1020  thisIteration += 1
1021 
1022  # Final fit if above did not converge
1023  if (nRejectedSpatial > 0) and (thisIteration == maxSpatialIterations):
1024  pexLog.Trace(self.log.getName()+"._solve", 2, "Final spatial fit")
1025  if (usePcaForSpatialKernel):
1026  nRejectedPca, spatialBasisList = self._createPcaBasis(kernelCellSet, nStarPerCell, policy)
1027  regionBBox = kernelCellSet.getBBox()
1028  spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
1029  kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1030  spatialkv.solveLinearEquation()
1031  pexLog.Trace(self.log.getName()+"._solve", 3,
1032  "Spatial kernel built with %d candidates" % (spatialkv.getNCandidates()))
1033  spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1034 
1035  spatialSolution = spatialkv.getKernelSolution()
1036 
1037  except Exception as e:
1038  pexLog.Trace(self.log.getName()+"._solve", 1, "ERROR: Unable to calculate psf matching kernel")
1039  pexLog.Trace(self.log.getName()+"._solve", 2, str(e))
1040  raise e
1041 
1042  t1 = time.time()
1043  pexLog.Trace(self.log.getName()+"._solve", 1,
1044  "Total time to compute the spatial kernel : %.2f s" % (t1 - t0))
1045 
1046  if display:
1047  self._displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1048 
1049  self._diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1050 
1051  return spatialSolution, spatialKernel, spatialBackground
1052 
1053 PsfMatch=PsfMatchTask
Configuration for detecting sources on images for building a PSF-matching kernel. ...
Definition: psfMatch.py:34
The parameters specific to the &quot;Alard-Lupton&quot; (sum-of-Gaussian) Psf-matching basis.
Definition: psfMatch.py:362
Base configuration for Psf-matching.
Definition: psfMatch.py:114
The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis.
Definition: psfMatch.py:420
Base class for Psf Matching; should not be called directly.
Definition: psfMatch.py:524
def _displayDebug
Provide visualization of the inputs and ouputs to the Psf-matching code.
Definition: psfMatch.py:785
def _diagnostic
Provide logging diagnostics on quality of spatial kernel fit.
Definition: psfMatch.py:699
limited backward compatibility to the DC2 run-time trace facilities
Definition: Trace.h:93
def __init__
Create the psf-matching Task.
Definition: psfMatch.py:675
def _buildCellSet
Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; override in derived classes...
Definition: psfMatch.py:887
def _createPcaBasis
Create Principal Component basis.
Definition: psfMatch.py:832
def _solve
Solve for the PSF matching kernel.
Definition: psfMatch.py:893
std::vector< boost::shared_ptr< Kernel > > KernelList
Definition: Kernel.h:539