1 from __future__
import absolute_import, division
28 from lsst.afw.math import ChebyshevBoundedField, ChebyshevBoundedFieldConfig
30 from .apCorrRegistry
import getApCorrNameSet
32 __all__ = (
"MeasureApCorrConfig",
"MeasureApCorrTask")
35 """A collection of keys for a given flux measurement algorithm
37 __slots__ = (
"flux",
"err",
"flag",
"used")
40 """Construct a FluxKeys
42 @parma[in] name name of flux measurement algorithm, e.g. "base_PsfFlux"
43 @param[in,out] schema catalog schema containing the flux field
44 read: {name}_flux, {name}_fluxSigma, {name}_flag
45 added: apcorr_{name}_used
47 self.
flux = schema.find(name +
"_flux").key
48 self.
err = schema.find(name +
"_fluxSigma").key
49 self.
flag = schema.find(name +
"_flag").key
50 self.
used = schema.addField(
"apcorr_" + name +
"_used", type=
"Flag",
51 doc=
"set if source was used in measuring aperture correction")
62 """!Configuration for MeasureApCorrTask
64 refFluxName = lsst.pex.config.Field(
65 doc =
"Field name prefix for the flux other measurements should be aperture corrected to match",
67 default =
"base_CircularApertureFlux_17_0",
69 inputFilterFlag = lsst.pex.config.Field(
70 doc =
"Name of a flag field that indicates that a source should be used to constrain the" +
71 " aperture corrections",
73 default =
"calib_psfUsed",
75 minDegreesOfFreedom = lsst.pex.config.RangeField(
76 doc =
"Minimum number of degrees of freedom (# of valid data points - # of parameters);" +
77 " if this is exceeded, the order of the fit is decreased (in both dimensions), and" +
78 " if we can't decrease it enough, we'll raise ValueError.",
83 fitConfig = lsst.pex.config.ConfigField(
84 doc =
"Configuration used in fitting the aperture correction fields",
85 dtype = ChebyshevBoundedFieldConfig,
87 numIter = lsst.pex.config.Field(
88 doc =
"Number of iterations for sigma clipping",
92 numSigmaClip = lsst.pex.config.Field(
93 doc =
"Number of standard devisations to clip at",
99 """!Task to measure aperture correction
101 \section measBase_MeasureApCorrTask_Contents Contents
103 - \ref measBase_MeasureApCorrTask_Purpose
104 - \ref measBase_MeasureApCorrTask_Config
105 - \ref measBase_MeasureApCorrTask_Debug
107 \section measBase_MeasureApCorrTask_Purpose Description
109 \copybrief MeasureApCorrTask
111 This task measures aperture correction for the flux fields returned by
112 lsst.meas.base.getApCorrNameSet()
114 The main method is \ref MeasureApCorrTask.run "run".
116 \section measBase_MeasureApCorrTask_Config Configuration parameters
118 See \ref MeasureApCorrConfig
120 \section measBase_MeasureApCorrTask_Debug Debug variables
122 This task has no debug variables.
124 ConfigClass = MeasureApCorrConfig
125 _DefaultName =
"measureApCorr"
128 """!Construct a MeasureApCorrTask
130 For every name in lsst.meas.base.getApCorrNameSet():
131 - If the corresponding flux fields exist in the schema:
132 - Add a new field apcorr_{name}_used
133 - Add an entry to the self.toCorrect dict
134 - Otherwise silently skip the name
136 Task.__init__(self, **kwds)
147 def run(self, bbox, catalog):
148 """!Measure aperture correction
150 @return an lsst.pipe.base.Struct containing:
151 - apCorrMap: an aperture correction map (lsst.afw.image.ApCorrMap) that contains two entries
153 - flux field (e.g. base_PsfFlux_flux): 2d model
154 - flux sigma field (e.g. base_PsfFlux_fluxSigma): 2d model of error
156 self.log.info(
"Measuring aperture corrections for %d flux fields" % (len(self.
toCorrect),))
159 subset1 = [record
for record
in catalog
160 if record.get(self.
inputFilterFlag)
and not record.get(self.refFluxKeys.flag)]
165 for name, keys
in self.toCorrect.iteritems():
166 fluxName = name +
"_flux"
167 fluxSigmaName = name +
"_fluxSigma"
171 subset2 = [record
for record
in subset1
if not record.get(keys.flag)]
175 if len(subset2) - 1 < self.config.minDegreesOfFreedom:
176 self.log.warn(
"Only %d sources for calculation of aperture correction for '%s'; "
177 "setting to 1.0" % (len(subset2), name,))
183 ctrl = self.config.fitConfig.makeControl()
184 while len(subset2) - ctrl.computeSize() < self.config.minDegreesOfFreedom:
191 x = numpy.zeros(len(subset2), dtype=float)
192 y = numpy.zeros(len(subset2), dtype=float)
193 apCorrData = numpy.zeros(len(subset2), dtype=float)
194 indices = numpy.arange(len(subset2), dtype=int)
195 for n, record
in enumerate(subset2):
198 apCorrData[n] = record.get(self.refFluxKeys.flux)/record.get(keys.flux)
200 for _i
in range(self.config.numIter):
203 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
207 apCorrDiffs = apCorrField.evaluate(x, y)
208 apCorrDiffs -= apCorrData
209 apCorrErr = numpy.mean(apCorrDiffs**2)**0.5
212 apCorrDiffLim = self.config.numSigmaClip * apCorrErr
213 keep = numpy.fabs(apCorrDiffs) < apCorrDiffLim
216 apCorrData = apCorrData[keep]
217 indices = indices[keep]
220 apCorrField = ChebyshevBoundedField.fit(bbox, x, y, apCorrData, ctrl)
222 self.log.info(
"Aperture correction for %s: RMS %f from %d" %
223 (name, numpy.mean((apCorrField.evaluate(x, y) - apCorrData)**2)**0.5, len(indices)))
229 apCorrMap[fluxName] = apCorrField
230 apCorrErrCoefficients = numpy.array([[apCorrErr]], dtype=float)
235 subset2[i].set(keys.used,
True)
238 apCorrMap = apCorrMap,
Task to measure aperture correction.
A thin wrapper around std::map to allow aperture corrections to be attached to Exposures.
def run
Measure aperture correction.
def getApCorrNameSet
Return a copy of the set of field name prefixes for fluxes that should be aperture corrected...
def __init__
Construct a MeasureApCorrTask.
A BoundedField based on 2-d Chebyshev polynomials of the first kind.
Configuration for MeasureApCorrTask.