24 __all__ = (
"MeasureApCorrConfig",
"MeasureApCorrTask")
30 from lsst.afw.math import ChebyshevBoundedField, ChebyshevBoundedFieldConfig
34 from .sourceSelector
import sourceSelectorRegistry
38 """A collection of keys for a given flux measurement algorithm 40 __slots__ = (
"flux",
"err",
"flag",
"used")
43 """Construct a FluxKeys 45 @parma[in] name name of flux measurement algorithm, e.g. "base_PsfFlux" 46 @param[in,out] schema catalog schema containing the flux field 47 read: {name}_instFlux, {name}_instFluxErr, {name}_flag 48 added: apcorr_{name}_used 50 self.
flux = schema.find(name +
"_instFlux").key
51 self.
err = schema.find(name +
"_instFluxErr").key
52 self.
flag = schema.find(name +
"_flag").key
53 self.
used = schema.addField(
"apcorr_" + name +
"_used", type=
"Flag",
54 doc=
"set if source was used in measuring aperture correction")
66 """!Configuration for MeasureApCorrTask 68 refFluxName = lsst.pex.config.Field(
69 doc=
"Field name prefix for the flux other measurements should be aperture corrected to match",
71 default=
"slot_CalibFlux",
73 sourceSelector = sourceSelectorRegistry.makeField(
74 doc=
"Selector that sets the stars that aperture corrections will be measured from.",
77 minDegreesOfFreedom = lsst.pex.config.RangeField(
78 doc=
"Minimum number of degrees of freedom (# of valid data points - # of parameters);" 79 " if this is exceeded, the order of the fit is decreased (in both dimensions), and" 80 " if we can't decrease it enough, we'll raise ValueError.",
85 fitConfig = lsst.pex.config.ConfigField(
86 doc=
"Configuration used in fitting the aperture correction fields",
87 dtype=ChebyshevBoundedFieldConfig,
89 numIter = lsst.pex.config.Field(
90 doc=
"Number of iterations for sigma clipping",
94 numSigmaClip = lsst.pex.config.Field(
95 doc=
"Number of standard devisations to clip at",
99 allowFailure = lsst.pex.config.ListField(
100 doc=
"Allow these measurement algorithms to fail without an exception",
106 lsst.pex.config.Config.validate(self)
108 raise lsst.pex.config.FieldValidationError(
109 "Star selectors that require matches are not permitted" 114 r"""!Task to measure aperture correction 116 @section measAlg_MeasureApCorrTask_Contents Contents 118 - @ref measAlg_MeasureApCorrTask_Purpose 119 - @ref measAlg_MeasureApCorrTask_Config 120 - @ref measAlg_MeasureApCorrTask_Debug 122 @section measAlg_MeasureApCorrTask_Purpose Description 124 @copybrief MeasureApCorrTask 126 This task measures aperture correction for the flux fields returned by 127 lsst.meas.base.getApCorrNameSet() 129 The main method is @ref MeasureApCorrTask.run "run". 131 @section measAlg_MeasureApCorrTask_Config Configuration parameters 133 See @ref MeasureApCorrConfig 135 @section measAlg_MeasureApCorrTask_Debug Debug variables 137 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a flag 138 `--debug` to import `debug.py` from your `$PYTHONPATH`; see @ref baseDebug for more about `debug.py`. 140 MeasureApCorrTask has a debug dictionary containing a single boolean key: 143 <dd>If True: will show plots as aperture corrections are fitted 146 For example, put something like: 150 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 151 if name == "lsst.meas.algorithms.measureApCorr": 160 lsstDebug.Info = DebugInfo 162 into your `debug.py` file and run your command-line task with the `--debug` flag (or `import debug`). 164 ConfigClass = MeasureApCorrConfig
165 _DefaultName =
"measureApCorr" 168 """!Construct a MeasureApCorrTask 170 For every name in lsst.meas.base.getApCorrNameSet(): 171 - If the corresponding flux fields exist in the schema: 172 - Add a new field apcorr_{name}_used 173 - Add an entry to the self.toCorrect dict 174 - Otherwise silently skip the name 176 Task.__init__(self, **kwds)
179 for name
in getApCorrNameSet():
185 self.makeSubtask(
"sourceSelector")
187 def run(self, exposure, catalog):
188 """!Measure aperture correction 190 @param[in] exposure Exposure aperture corrections are being measured 191 on. The bounding box is retrieved from it, and 192 it is passed to the sourceSelector. 193 The output aperture correction map is *not* 194 added to the exposure; this is left to the 197 @param[in] catalog SourceCatalog containing measurements to be used 198 to compute aperturecorrections. 200 @return an lsst.pipe.base.Struct containing: 201 - apCorrMap: an aperture correction map (lsst.afw.image.ApCorrMap) that contains two entries 203 - flux field (e.g. base_PsfFlux_instFlux): 2d model 204 - flux sigma field (e.g. base_PsfFlux_instFluxErr): 2d model of error 206 bbox = exposure.getBBox()
211 self.log.info(
"Measuring aperture corrections for %d flux fields" % (len(self.
toCorrect),))
214 subset1 = [record
for record
in self.sourceSelector.
run(catalog, exposure=exposure).sourceCat
216 numpy.isfinite(record.get(self.
refFluxKeys.flux)))]
221 for name, keys
in self.
toCorrect.items():
222 fluxName = name +
"_instFlux" 223 fluxErrName = name +
"_instFluxErr" 227 fluxes = numpy.fromiter((record.get(keys.flux)
for record
in subset1), float)
228 with numpy.errstate(invalid=
"ignore"):
229 isGood = numpy.logical_and.reduce([
230 numpy.fromiter((
not record.get(keys.flag)
for record
in subset1), bool),
231 numpy.isfinite(fluxes),
234 subset2 = [record
for record, good
in zip(subset1, isGood)
if good]
238 if len(subset2) - 1 < self.config.minDegreesOfFreedom:
239 if name
in self.config.allowFailure:
240 self.log.warn(
"Unable to measure aperture correction for '%s': " 241 "only %d sources, but require at least %d." %
242 (name, len(subset2), self.config.minDegreesOfFreedom+1))
244 raise RuntimeError(
"Unable to measure aperture correction for required algorithm '%s': " 245 "only %d sources, but require at least %d." %
246 (name, len(subset2), self.config.minDegreesOfFreedom+1))
249 ctrl = self.config.fitConfig.makeControl()
250 while len(subset2) - ctrl.computeSize() < self.config.minDegreesOfFreedom:
257 x = numpy.zeros(len(subset2), dtype=float)
258 y = numpy.zeros(len(subset2), dtype=float)
259 apCorrData = numpy.zeros(len(subset2), dtype=float)
260 indices = numpy.arange(len(subset2), dtype=int)
261 for n, record
in enumerate(subset2):
264 apCorrData[n] = record.get(self.
refFluxKeys.flux)/record.get(keys.flux)
266 for _i
in range(self.config.numIter):
269 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
272 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, iteration %d" % (name, _i), doPause)
276 apCorrDiffs = apCorrField.evaluate(x, y)
277 apCorrDiffs -= apCorrData
278 apCorrErr = numpy.mean(apCorrDiffs**2)**0.5
281 apCorrDiffLim = self.config.numSigmaClip * apCorrErr
282 with numpy.errstate(invalid=
"ignore"):
283 keep = numpy.fabs(apCorrDiffs) <= apCorrDiffLim
286 apCorrData = apCorrData[keep]
287 indices = indices[keep]
290 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
292 self.log.info(
"Aperture correction for %s: RMS %f from %d" %
293 (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2)**0.5, len(indices)))
296 plotApCorr(bbox, x, y, apCorrData, apCorrField,
"%s, final" % (name,), doPause)
302 apCorrMap[fluxName] = apCorrField
303 apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float)
308 subset2[i].set(keys.used,
True)
315 def plotApCorr(bbox, xx, yy, zzMeasure, field, title, doPause):
316 """Plot aperture correction fit residuals 318 There are two subplots: residuals against x and y. 320 Intended for debugging. 322 @param bbox Bounding box (for bounds) 323 @param xx x coordinates 324 @param yy y coordinates 325 @param zzMeasure Measured value of the aperture correction 326 @param field Fit aperture correction field 327 @param title Title for plot 328 @param doPause Pause to inspect the residuals plot? If False, 329 there will be a 4 second delay to allow for 330 inspection of the plot before closing it and 333 import matplotlib.pyplot
as plt
335 zzFit = field.evaluate(xx, yy)
336 residuals = zzMeasure - zzFit
338 fig, axes = plt.subplots(2, 1)
340 axes[0].scatter(xx, residuals, s=3, marker=
'o', lw=0, alpha=0.7)
341 axes[1].scatter(yy, residuals, s=3, marker=
'o', lw=0, alpha=0.7)
343 ax.set_ylabel(
"ApCorr Fit Residual")
344 ax.set_ylim(0.9*residuals.min(), 1.1*residuals.max())
345 axes[0].set_xlabel(
"x")
346 axes[0].set_xlim(bbox.getMinX(), bbox.getMaxX())
347 axes[1].set_xlabel(
"y")
348 axes[1].set_xlim(bbox.getMinY(), bbox.getMaxY())
356 print(
"%s: plt.pause() failed. Please close plots when done." % __name__)
359 print(
"%s: Please close plots when done." % __name__)
Task to measure aperture correction.
def run(self, exposure, catalog)
Measure aperture correction.
Configuration for MeasureApCorrTask.
def __init__(self, name, schema)
def __init__(self, schema, kwds)
Construct a MeasureApCorrTask.
def plotApCorr(bbox, xx, yy, zzMeasure, field, title, doPause)