23 from __future__
import absolute_import, division
25 __all__ = (
"MeasureApCorrConfig",
"MeasureApCorrTask")
27 from builtins
import zip
28 from builtins
import range
29 from builtins
import object
34 from lsst.afw.math import ChebyshevBoundedField, ChebyshevBoundedFieldConfig
38 from .
import flaggedStarSelector
39 from .starSelector
import starSelectorRegistry
43 """A collection of keys for a given flux measurement algorithm 45 __slots__ = (
"flux",
"err",
"flag",
"used")
48 """Construct a FluxKeys 50 @parma[in] name name of flux measurement algorithm, e.g. "base_PsfFlux" 51 @param[in,out] schema catalog schema containing the flux field 52 read: {name}_flux, {name}_fluxSigma, {name}_flag 53 added: apcorr_{name}_used 55 self.
flux = schema.find(name +
"_flux").key
56 self.
err = schema.find(name +
"_fluxSigma").key
57 self.
flag = schema.find(name +
"_flag").key
58 self.
used = schema.addField(
"apcorr_" + name +
"_used", type=
"Flag",
59 doc=
"set if source was used in measuring aperture correction")
71 """!Configuration for MeasureApCorrTask 73 refFluxName = lsst.pex.config.Field(
74 doc=
"Field name prefix for the flux other measurements should be aperture corrected to match",
76 default=
"slot_CalibFlux",
78 starSelector = starSelectorRegistry.makeField(
79 doc=
"Selector that sets the stars that aperture corrections will be measured from",
82 minDegreesOfFreedom = lsst.pex.config.RangeField(
83 doc=
"Minimum number of degrees of freedom (# of valid data points - # of parameters);" +
84 " if this is exceeded, the order of the fit is decreased (in both dimensions), and" +
85 " if we can't decrease it enough, we'll raise ValueError.",
90 fitConfig = lsst.pex.config.ConfigField(
91 doc=
"Configuration used in fitting the aperture correction fields",
92 dtype=ChebyshevBoundedFieldConfig,
94 numIter = lsst.pex.config.Field(
95 doc=
"Number of iterations for sigma clipping",
99 numSigmaClip = lsst.pex.config.Field(
100 doc=
"Number of standard devisations to clip at",
104 allowFailure = lsst.pex.config.ListField(
105 doc=
"Allow these measurement algorithms to fail without an exception",
111 lsst.pex.config.Config.validate(self)
113 raise lsst.pex.config.FieldValidationError(
114 "Star selectors that require matches are not permitted" 119 """!Task to measure aperture correction 121 \section measAlg_MeasureApCorrTask_Contents Contents 123 - \ref measAlg_MeasureApCorrTask_Purpose 124 - \ref measAlg_MeasureApCorrTask_Config 125 - \ref measAlg_MeasureApCorrTask_Debug 127 \section measAlg_MeasureApCorrTask_Purpose Description 129 \copybrief MeasureApCorrTask 131 This task measures aperture correction for the flux fields returned by 132 lsst.meas.base.getApCorrNameSet() 134 The main method is \ref MeasureApCorrTask.run "run". 136 \section measAlg_MeasureApCorrTask_Config Configuration parameters 138 See \ref MeasureApCorrConfig 140 \section measAlg_MeasureApCorrTask_Debug Debug variables 142 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag 143 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`. 145 MeasureApCorrTask has a debug dictionary containing a single boolean key: 148 <dd>If True: will show plots as aperture corrections are fitted 151 For example, put something like: 155 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 156 if name == "lsst.meas.algorithms.measureApCorr": 165 lsstDebug.Info = DebugInfo 167 into your `debug.py` file and run your command-line task with the `--debug` flag (or `import debug`). 169 ConfigClass = MeasureApCorrConfig
170 _DefaultName =
"measureApCorr" 173 """!Construct a MeasureApCorrTask 175 For every name in lsst.meas.base.getApCorrNameSet(): 176 - If the corresponding flux fields exist in the schema: 177 - Add a new field apcorr_{name}_used 178 - Add an entry to the self.toCorrect dict 179 - Otherwise silently skip the name 181 Task.__init__(self, **kwds)
184 for name
in getApCorrNameSet():
190 self.makeSubtask(
"starSelector", schema=schema)
192 def run(self, exposure, catalog):
193 """!Measure aperture correction 195 @param[in] exposure Exposure aperture corrections are being measured 196 on. Aside from the bounding box, the exposure 197 is only used by the starSelector subtask (which 198 may need it to construct PsfCandidates, as 199 PsfCanidate construction can do some filtering). 200 The output aperture correction map is *not* 201 added to the exposure; this is left to the 204 @param[in] catalog SourceCatalog containing measurements to be used 205 to compute aperturecorrections. 207 @return an lsst.pipe.base.Struct containing: 208 - apCorrMap: an aperture correction map (lsst.afw.image.ApCorrMap) that contains two entries 210 - flux field (e.g. base_PsfFlux_flux): 2d model 211 - flux sigma field (e.g. base_PsfFlux_fluxSigma): 2d model of error 213 bbox = exposure.getBBox()
217 self.log.info(
"Measuring aperture corrections for %d flux fields" % (len(self.
toCorrect),))
220 subset1 = [record
for record
in self.starSelector.selectStars(exposure, catalog).starCat
222 numpy.isfinite(record.get(self.
refFluxKeys.flux)))]
227 for name, keys
in self.
toCorrect.items():
228 fluxName = name +
"_flux" 229 fluxSigmaName = name +
"_fluxSigma" 233 fluxes = numpy.fromiter((record.get(keys.flux)
for record
in subset1), float)
234 with numpy.errstate(invalid=
"ignore"):
235 isGood = numpy.logical_and.reduce([
236 numpy.fromiter((
not record.get(keys.flag)
for record
in subset1), bool),
237 numpy.isfinite(fluxes),
240 subset2 = [record
for record, good
in zip(subset1, isGood)
if good]
244 if len(subset2) - 1 < self.config.minDegreesOfFreedom:
245 if name
in self.config.allowFailure:
246 self.log.warn(
"Unable to measure aperture correction for '%s': " 247 "only %d sources, but require at least %d." %
248 (name, len(subset2), self.config.minDegreesOfFreedom+1))
250 raise RuntimeError(
"Unable to measure aperture correction for required algorithm '%s': " 251 "only %d sources, but require at least %d." %
252 (name, len(subset2), self.config.minDegreesOfFreedom+1))
255 ctrl = self.config.fitConfig.makeControl()
256 while len(subset2) - ctrl.computeSize() < self.config.minDegreesOfFreedom:
263 x = numpy.zeros(len(subset2), dtype=float)
264 y = numpy.zeros(len(subset2), dtype=float)
265 apCorrData = numpy.zeros(len(subset2), dtype=float)
266 indices = numpy.arange(len(subset2), dtype=int)
267 for n, record
in enumerate(subset2):
270 apCorrData[n] = record.get(self.
refFluxKeys.flux)/record.get(keys.flux)
272 for _i
in range(self.config.numIter):
275 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
278 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, iteration %d" % (name, _i))
282 apCorrDiffs = apCorrField.evaluate(x, y)
283 apCorrDiffs -= apCorrData
284 apCorrErr = numpy.mean(apCorrDiffs**2)**0.5
287 apCorrDiffLim = self.config.numSigmaClip * apCorrErr
288 with numpy.errstate(invalid=
"ignore"):
289 keep = numpy.fabs(apCorrDiffs) <= apCorrDiffLim
292 apCorrData = apCorrData[keep]
293 indices = indices[keep]
296 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
298 self.log.info(
"Aperture correction for %s: RMS %f from %d" %
299 (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2)**0.5, len(indices)))
302 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, final" % (name,))
308 apCorrMap[fluxName] = apCorrField
309 apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float)
314 subset2[i].set(keys.used,
True)
322 """Plot aperture correction fit residuals 324 There are two subplots: residuals against x and y. 326 Intended for debugging. 328 @param bbox Bounding box (for bounds) 329 @param xx x coordinates 330 @param yy y coordinates 331 @param zzMeasure Measured value of the aperture correction 332 @param field Fit aperture correction field 333 @param title Title for plot 335 import matplotlib.pyplot
as plt
337 zzFit = field.evaluate(xx, yy)
338 residuals = zzMeasure - zzFit
340 fig, axes = plt.subplots(2, 1)
342 axes[0].scatter(xx, residuals, s=2, marker=
'o', lw=0, alpha=0.3)
343 axes[1].scatter(yy, residuals, s=2, marker=
'o', lw=0, alpha=0.3)
345 ax.set_ylabel(
"Residual")
346 ax.set_ylim(0.9*residuals.min(), 1.1*residuals.max())
347 axes[0].set_xlabel(
"x")
348 axes[0].set_xlim(bbox.getMinX(), bbox.getMaxX())
349 axes[1].set_xlabel(
"y")
350 axes[1].set_xlim(bbox.getMinY(), bbox.getMaxY())
Task to measure aperture correction.
def plotApCorr(bbox, xx, yy, zzMeasure, field, title)
def run(self, exposure, catalog)
Measure aperture correction.
Configuration for MeasureApCorrTask.
def __init__(self, name, schema)
def __init__(self, schema, kwds)
Construct a MeasureApCorrTask.