23 """Base measurement task, which subclassed by the single frame and forced measurement tasks.
30 from .applyApCorr
import ApplyApCorrTask
31 from .pluginRegistry
import PluginMap
32 from .baseLib
import FatalAlgorithmError, MeasurementError
33 from .noiseReplacer
import NoiseReplacerConfig
34 from .transforms
import PassThroughTransform
36 __all__ = (
"BasePluginConfig",
"BasePlugin",
"BaseMeasurementConfig",
"BaseMeasurementTask")
39 FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
43 Base class measurement Plugin config classes.
45 Most derived classes will want to override setDefaults() in order to customize
46 the default exceutionOrder.
48 A derived class whose corresponding Plugin class implements measureN() should
49 additionally add a bool doMeasureN field to replace the bool class attribute
53 doMeasure = lsst.pex.config.Field(dtype=bool, default=
True,
54 doc=
"whether to run this plugin in single-object mode")
60 Base class for measurement plugins.
62 This is the base class for SingleFramePlugin and ForcedPlugin; derived classes should inherit
74 """Sets the relative order of plugins (smaller numbers run first).
76 In general, the following class constants should be used (other values
77 are also allowed, but should be avoided unless they are needed):
78 CENTROID_ORDER centroids and other algorithms that require only a Footprint and its Peaks as input
79 SHAPE_ORDER shape measurements and other algorithms that require getCentroid() to return
80 a good centroid (in addition to a Footprint and its Peaks).
81 FLUX_ORDER flux algorithms that require both getShape() and getCentroid(),
82 in addition to a Footprint and its Peaks
83 APCORR_ORDER aperture corrections
84 CLASSIFY_ORDER algorithms that operate on aperture-corrected fluxes
86 Must be reimplemented as a class method by concrete derived classes.
88 This approach was chosen instead of a full graph-based analysis of dependencies
89 because algorithm dependencies are usually both quite simple and entirely substitutable:
90 an algorithm that requires a centroid can typically make use of any centroid algorithms
91 outputs. That makes it relatively easy to figure out the correct value to use for any
94 raise NotImplementedError(
"All plugins must implement getExecutionOrder()")
98 Initialize the measurement object.
100 @param[in] config An instance of this class's ConfigClass.
101 @param[in] name The string the plugin was registered with.
103 object.__init__(self)
107 def fail(self, measRecord, error=None):
109 Record a failure of the measure or measureN() method.
111 When measure() raises an exception, the measurement framework
112 will call fail() to allow the plugin to set its failure flag
113 field(s). When measureN() raises an exception, fail() will be
114 called repeatedly with all the records that were being
117 If the exception is a MeasurementError, it will be passed as
118 the error argument; in all other cases the error argument will
119 be None, and the failure will be logged by the measurement
120 framework as a warning.
122 traceback.print_exc()
123 message = (
"The algorithm '%s' thinks it cannot fail, but it did; "
124 "please report this as a bug (the full traceback is above)."
125 % self.__class__.__name__)
126 raise NotImplementedError(message)
131 Get the measurement transformation appropriate to this plugin.
133 This returns a subclass of MeasurementTransform, which may be
134 instantiated with details of the algorithm configuration and then
135 called with information about calibration and WCS to convert from raw
136 measurement quantities to calibrated units. Calibrated data is then
137 provided in a separate output table.
139 By default, we copy everything from the input to the output without
142 return PassThroughTransform
147 Slot configuration which assigns a particular named plugin to each of a set of
148 slots. Each slot allows a type of measurement to be fetched from the SourceTable
149 without knowing which algorithm was used to produced the data.
151 NOTE: the default algorithm for each slot must be registered, even if the default is not used.
154 centroid = lsst.pex.config.Field(dtype=str, default=
"base_SdssCentroid", optional=
True,
155 doc=
"the name of the centroiding algorithm used to set source x,y")
156 shape = lsst.pex.config.Field(dtype=str, default=
"base_SdssShape", optional=
True,
157 doc=
"the name of the algorithm used to set source moments parameters")
158 apFlux = lsst.pex.config.Field(dtype=str, default=
"base_CircularApertureFlux_3_0", optional=
True,
159 doc=
"the name of the algorithm used to set the source aperture flux slot")
160 modelFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
161 doc=
"the name of the algorithm used to set the source model flux slot")
162 psfFlux = lsst.pex.config.Field(dtype=str, default=
"base_PsfFlux", optional=
True,
163 doc=
"the name of the algorithm used to set the source psf flux slot")
164 instFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
165 doc=
"the name of the algorithm used to set the source inst flux slot")
166 calibFlux = lsst.pex.config.Field(dtype=str, default=
"base_CircularApertureFlux_12_0", optional=
True,
167 doc=
"the name of the flux measurement algorithm used for calibration")
170 """Convenience method to setup a Schema's slots according to the config definition.
172 This is defined in the Config class to support use in unit tests without needing
173 to construct a Task object.
175 aliases = schema.getAliasMap()
177 if self.
shape is not None: aliases.set(
"slot_Shape", self.
shape)
178 if self.
apFlux is not None: aliases.set(
"slot_ApFlux", self.
apFlux)
180 if self.
psfFlux is not None: aliases.set(
"slot_PsfFlux", self.
psfFlux)
186 Base config class for all measurement driver tasks.
189 slots = lsst.pex.config.ConfigField(
190 dtype = SourceSlotConfig,
191 doc=
"Mapping from algorithms to special aliases in Source."
194 doReplaceWithNoise = lsst.pex.config.Field(dtype=bool, default=
True, optional=
False,
195 doc=
'When measuring, replace other detected footprints with noise?')
197 noiseReplacer = lsst.pex.config.ConfigField(
198 dtype=NoiseReplacerConfig,
199 doc=
"configuration that sets how to replace neighboring sources with noise"
202 doApplyApCorr = lsst.pex.config.ChoiceField(
204 doc =
"Apply aperture corrections? Silently ignored if endOrder <= lsst.meas.base.APCORR_ORDER"
206 default =
"noButWarn",
208 "yes":
"apply aperture corrections; fail if data not available",
209 "yesOrWarn":
"apply aperture corrections if data available, else warn",
210 "noButWarn":
"do not apply aperture corrections, but warn if data available"
211 " (since aperture corrections could have been applied)",
212 "no":
"do not apply aperture corrections",
216 applyApCorr = lsst.pex.config.ConfigurableField(
217 target = ApplyApCorrTask,
218 doc =
"subtask to apply aperture corrections",
222 lsst.pex.config.Config.validate(self)
223 if self.slots.centroid
is not None and self.slots.centroid
not in self.plugins.names:
224 raise ValueError(
"source centroid slot algorithm is not being run.")
225 if self.slots.shape
is not None and self.slots.shape
not in self.plugins.names:
226 raise ValueError(
"source shape slot algorithm '%s' is not being run." % self.slots.shape)
227 for slot
in (self.slots.psfFlux, self.slots.apFlux, self.slots.modelFlux,
228 self.slots.instFlux, self.slots.calibFlux):
230 for name
in self.plugins.names:
231 if len(name) <= len(slot)
and name == slot[:len(name)]:
234 raise ValueError(
"source flux slot algorithm '%s' is not being run." % slot)
244 Ultimate base class for all measurement tasks.
246 This base class for SingleFrameMeasurementTask and ForcedMeasurementTask mostly exists to share
247 code between the two, and generally should not be used directly.
249 @note Tasks that use this task should usually set the default value of config parameter doApplyApCorr
250 to "yes" or "no", depending if aperture corrections are wanted. The default value of "noButWarn"
251 is intended to alert users who forget, and is appropriate for unit tests and temporary scripts
252 that do not need aperture corrections.
255 ConfigClass = BaseMeasurementConfig
256 _DefaultName =
"measurement"
260 Constructor; only called by derived classes.
262 @param[in] algMetadata An lsst.daf.base.PropertyList that will be filled with metadata
263 about the plugins being run. If None, an empty PropertyList will
265 @param[in] **kwds Additional arguments passed to lsst.pipe.base.Task.__init__.
267 This attaches two public attributes to the class for use by derived classes and parent tasks:
268 - plugins: an empty PluginMap, which will eventually contain all active plugins that will by
269 invoked by the run() method (to be filled by subclasses). This should be considered read-only.
270 - algMetadata: a lsst.daf.base.PropertyList that will contain additional information about the
271 active plugins to be saved with the output catalog (to be filled by subclasses).
273 super(BaseMeasurementTask, self).
__init__(**kwds)
275 if algMetadata
is None:
280 """Initialize the plugins (and slots) according to the configuration.
282 Derived class constructors should call this method to fill the self.plugins
283 attribute and add correspond output fields and slot aliases to the output schema.
285 In addition to the attributes added by BaseMeasurementTask.__init__, a self.schema
286 attribute holding the output schema must also be present before this method is called, .
288 Keyword arguments are forwarded directly to plugin constructors, allowing derived
289 classes to use plugins with different signatures.
294 if self.config.slots.centroid !=
None:
295 self.
plugins[self.config.slots.centroid] =
None
297 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
302 if self.config.slots.centroid
is not None and self.
plugins[self.config.slots.centroid]
is None:
303 del self.
plugins[self.config.slots.centroid]
307 Call the measure() method on all plugins, handling exceptions in a consistent way.
309 @param[in,out] measRecord lsst.afw.table.SourceRecord that corresponds to the object being
310 measured, and where outputs should be written.
311 @param[in] *args Positional arguments forwarded to Plugin.measure()
312 @param[in] **kwds Keyword arguments. Two are handled locally:
313 - beginOrder: beginning execution order (inclusive): measurements with
314 executionOrder < beginOrder are not executed. None for no limit.
315 - endOrder: ending execution order (exclusive): measurements with
316 executionOrder >= endOrder are not executed. None for no limit.
317 the rest are forwarded to Plugin.measure()
319 This method can be used with plugins that have different signatures; the only requirement is that
320 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
321 forwarded directly to the plugin.
323 This method should be considered "protected"; it is intended for use by derived classes, not users.
325 beginOrder = kwds.pop(
"beginOrder",
None)
326 endOrder = kwds.pop(
"endOrder",
None)
327 for plugin
in self.plugins.iter():
328 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
330 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
333 plugin.measure(measRecord, *args, **kwds)
334 except FATAL_EXCEPTIONS:
336 except MeasurementError
as error:
337 plugin.fail(measRecord, error)
338 except Exception
as error:
339 self.log.warn(
"Error in %s.measure on record %s: %s"
340 % (plugin.name, measRecord.getId(), error))
341 plugin.fail(measRecord)
345 Call the measureN() method on all plugins, handling exceptions in a consistent way.
347 @param[in,out] measCat lsst.afw.table.SourceCatalog containing records for just
348 the source family to be measured, and where outputs should
350 @param[in] beginOrder beginning execution order (inclusive): measurements with
351 executionOrder < beginOrder are not executed. None for no limit.
352 @param[in] endOrder ending execution order (exclusive): measurements with
353 executionOrder >= endOrder are not executed. None for no limit.
354 @param[in] *args Positional arguments forwarded to Plugin.measure()
355 @param[in] **kwds Keyword arguments. Two are handled locally:
356 - beginOrder: beginning execution order (inclusive): measurements with
357 executionOrder < beginOrder are not executed. None for no limit.
358 - endOrder: ending execution order (exclusive): measurements with
359 executionOrder >= endOrder are not executed. None for no limit.
360 the rest are forwarded to Plugin.measure()
362 This method can be used with plugins that have different signatures; the only requirement is that
363 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
364 forwarded directly to the plugin.
366 This method should be considered "protected"; it is intended for use by derived classes, not users.
368 beginOrder = kwds.pop(
"beginOrder",
None)
369 endOrder = kwds.pop(
"endOrder",
None)
370 for plugin
in self.plugins.iterN():
371 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
373 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
376 plugin.measureN(measCat, *args, **kwds)
377 except FATAL_EXCEPTIONS:
379 except MeasurementError
as error:
380 for measRecord
in measCat:
381 plugin.fail(measRecord, error)
382 except Exception
as error:
383 for measRecord
in measCat:
384 plugin.fail(measRecord)
385 self.log.warn(
"Error in %s.measureN on records %s-%s: %s"
386 % (plugin.name, measCat[0].getId(), measCat[-1].getId(), error))
389 """!Apply aperture corrections to a catalog, if wanted
391 This method is intended to be called at the end of every subclass's run method or other
392 measurement sequence. This is a thin wrapper around self.applyApCorr.run.
394 @param[in,out] sources catalog of sources to which to apply aperture corrections
395 @param[in] apCorrMap aperture correction map (lsst.afw.image.ApCorrMap) or None;
396 typically found in an lsst.afw.image.ExposureInfo
397 if provided then it must contain two entries for each flux field:
398 - flux field (e.g. base_PsfFlux_flux): 2d model
399 - flux sigma field (e.g. base_PsfFlux_fluxSigma): 2d model of error
400 @param[in] endOrder ending execution order, or None; if provided then aperture corrections
401 are only wanted if endOrder > lsst.meas.base.BasePlugin.APCORR_ORDER
402 @return the results from applyApCorr if run, else None
404 @throw lsst.pipe.base.TaskError if aperture corrections are wanted and the exposure does not contain
405 an aperture correction map.
407 if endOrder
is not None and endOrder <= BasePlugin.APCORR_ORDER:
411 if self.config.doApplyApCorr.startswith(
"yes"):
412 if apCorrMap
is not None:
413 self.applyApCorr.run(catalog=sources, apCorrMap=apCorrMap)
415 errMsg =
"Cannot apply aperture corrections; apCorrMap is None"
416 if self.config.doApplyApCorr ==
"yesOrWarn":
417 self.log.warn(errMsg)
419 raise lsst.pipe.base.TaskError(errMsg)
420 elif self.config.doApplyApCorr ==
"noButWarn":
421 if apCorrMap
is not None:
422 self.log.warn(
"Aperture corrections are disabled but the data to apply them is available;"
423 " change doApplyApCorr to suppress this warning")
def __init__
Constructor; only called by derived classes.
Class for storing ordered metadata with comments.
def getTransformClass
Get the measurement transformation appropriate to this plugin.
Base class measurement Plugin config classes.
def __init__
Initialize the measurement object.
def callMeasure
Call the measure() method on all plugins, handling exceptions in a consistent way.
def fail
Record a failure of the measure or measureN() method.
def _applyApCorrIfWanted
Apply aperture corrections to a catalog, if wanted.
Slot configuration which assigns a particular named plugin to each of a set of slots.
Base class for measurement plugins.
Ultimate base class for all measurement tasks.
Base config class for all measurement driver tasks.
def callMeasureN
Call the measureN() method on all plugins, handling exceptions in a consistent way.