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