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 isGood = numpy.logical_and.reduce([
235 numpy.fromiter((
not record.get(keys.flag)
for record
in subset1), bool),
236 numpy.isfinite(fluxes),
239 subset2 = [record
for record, good
in zip(subset1, isGood)
if good]
243 if len(subset2) - 1 < self.config.minDegreesOfFreedom:
244 if name
in self.config.allowFailure:
245 self.log.warn(
"Unable to measure aperture correction for '%s': " 246 "only %d sources, but require at least %d." %
247 (name, len(subset2), self.config.minDegreesOfFreedom+1))
249 raise RuntimeError(
"Unable to measure aperture correction for required algorithm '%s': " 250 "only %d sources, but require at least %d." %
251 (name, len(subset2), self.config.minDegreesOfFreedom+1))
254 ctrl = self.config.fitConfig.makeControl()
255 while len(subset2) - ctrl.computeSize() < self.config.minDegreesOfFreedom:
262 x = numpy.zeros(len(subset2), dtype=float)
263 y = numpy.zeros(len(subset2), dtype=float)
264 apCorrData = numpy.zeros(len(subset2), dtype=float)
265 indices = numpy.arange(len(subset2), dtype=int)
266 for n, record
in enumerate(subset2):
269 apCorrData[n] = record.get(self.
refFluxKeys.flux)/record.get(keys.flux)
271 for _i
in range(self.config.numIter):
274 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
277 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, iteration %d" % (name, _i))
281 apCorrDiffs = apCorrField.evaluate(x, y)
282 apCorrDiffs -= apCorrData
283 apCorrErr = numpy.mean(apCorrDiffs**2)**0.5
286 apCorrDiffLim = self.config.numSigmaClip * apCorrErr
287 with numpy.errstate(invalid=
"ignore"):
288 keep = numpy.fabs(apCorrDiffs) <= apCorrDiffLim
291 apCorrData = apCorrData[keep]
292 indices = indices[keep]
295 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
297 self.log.info(
"Aperture correction for %s: RMS %f from %d" %
298 (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2)**0.5, len(indices)))
301 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, final" % (name,))
307 apCorrMap[fluxName] = apCorrField
308 apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float)
313 subset2[i].set(keys.used,
True)
321 """Plot aperture correction fit residuals 323 There are two subplots: residuals against x and y. 325 Intended for debugging. 327 @param bbox Bounding box (for bounds) 328 @param xx x coordinates 329 @param yy y coordinates 330 @param zzMeasure Measured value of the aperture correction 331 @param field Fit aperture correction field 332 @param title Title for plot 334 import matplotlib.pyplot
as plt
336 zzFit = field.evaluate(xx, yy)
337 residuals = zzMeasure - zzFit
339 fig, axes = plt.subplots(2, 1)
341 axes[0].scatter(xx, residuals, s=2, marker=
'o', lw=0, alpha=0.3)
342 axes[1].scatter(yy, residuals, s=2, marker=
'o', lw=0, alpha=0.3)
344 ax.set_ylabel(
"Residual")
345 ax.set_ylim(0.9*residuals.min(), 1.1*residuals.max())
346 axes[0].set_xlabel(
"x")
347 axes[0].set_xlim(bbox.getMinX(), bbox.getMaxX())
348 axes[1].set_xlabel(
"y")
349 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.