23 """Base and utility classes for measurement frameworks
25 This includes base classes for plugins and tasks and utility classes such as PluginMap
26 that are shared by the single-frame measurement framework and the forced measurement framework.
35 from .baseLib
import *
36 from .noiseReplacer
import *
37 from .transforms
import PassThroughTransform
40 FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
43 """Generate a string name for an algorithm class that strips away terms that are generally redundant
44 while (hopefully) remaining easy to trace to the code.
46 The returned name will cobmine the package name, with any "lsst" and/or "meas" prefix removed,
47 with the class name, with any "Algorithm" suffix removed. For instance,
48 lsst.meas.base.SdssShapeAlgorithm becomes "base_SdssShape".
50 name = AlgClass.__name__
51 pkg = AlgClass.__module__
52 name = name.replace(
"Algorithm",
"")
53 terms = pkg.split(
".")
54 if terms[-1].endswith(
"Lib"):
56 if terms[0] ==
"lsst":
58 if terms[0] ==
"meas":
60 if name.lower().startswith(terms[-1].lower()):
62 return "%s_%s" % (
"_".join(terms), name)
66 Base class for plugin registries
68 The Plugin class allowed in the registry is defined in the ctor of the registry.
70 Single-frame and forced plugins have different registries.
75 Class used as the actual element in the registry
77 Rather than constructing a Plugin instance, its __call__ method
78 (invoked by RegistryField.apply) returns a tuple
79 of (executionOrder, name, config, PluginClass), which can then
80 be sorted before the plugins are instantiated.
83 __slots__ =
"PluginClass",
"name"
87 Create a Configurable object for the given PluginClass and name
96 return (self.PluginClass.getExecutionOrder(), self.
name, config, self.
PluginClass)
100 Register a Plugin class with the given name.
102 The same Plugin may be registered multiple times with different names; this can
103 be useful if we often want to run it multiple times with different configuration.
105 The name will be used as a prefix for all fields produced by the Plugin, and it
106 should generally contain the name of the Plugin or Algorithm class itself
107 as well as enough of the namespace to make it clear where to find the code.
109 lsst.pex.config.Registry.register(self, name, self.
Configurable(name, PluginClass))
111 def makeField(self, doc, default=None, optional=False, multi=False):
112 return lsst.pex.config.RegistryField(doc, self, default, optional, multi)
117 A Python decorator that registers a class, using the given name, in its base class's PluginRegistry.
120 @register("base_TransformedCentroid")
121 class ForcedTransformedCentroidPlugin(ForcedPlugin):
126 class ForcedTransformedCentroidPlugin(ForcedPlugin):
128 @ForcedPlugin.registry.register("base_TransformedCentroid", ForcedTransformedCentroidPlugin)
131 def decorate(PluginClass):
132 PluginClass.registry.register(name, PluginClass)
139 Map of plugins to be run for a task
141 We assume Plugins are added to the PluginMap according to their "Execution Order", so this
142 class doesn't actually do any of the sorting (though it does have to maintain that order,
143 which it does by inheriting from OrderedDict).
147 """Call each plugin in the map which has a measure() method
149 for plugin
in self.itervalues():
150 if plugin.config.doMeasure:
154 """Call each plugin in the map which has a measureN() method
156 for plugin
in self.itervalues():
157 if plugin.config.doMeasureN:
162 Base class measurement Plugin config classes.
164 Most derived classes will want to override setDefaults() in order to customize
165 the default exceutionOrder.
167 A derived class whose corresponding Plugin class implements measureN() should
168 additionally add a bool doMeasureN field to replace the bool class attribute
172 doMeasure = lsst.pex.config.Field(dtype=bool, default=
True,
173 doc=
"whether to run this plugin in single-object mode")
179 Base class for measurement plugins.
181 This is the base class for SingleFramePlugin and ForcedPlugin; derived classes should inherit
187 """Sets the relative order of plugins (smaller numbers run first).
189 In general, the following values should be used (intermediate values
190 are also allowed, but should be avoided unless they are needed):
191 0.0 ------ centroids and other algorithms that require only a Footprint and
193 1.0 ------ shape measurements and other algorithms that require
194 getCentroid() to return a good centroid in addition to a
195 Footprint and its Peaks.
196 2.0 ------ flux algorithms that require both getShape() and getCentroid()
197 in addition to the Footprint and its Peaks
198 3.0 ------ algorithms that operate on fluxes (e.g. classification,
199 aperture correction).
201 Must be reimplemented (as a static or class method) by concrete derived classes.
203 This approach was chosen instead of a full graph-based analysis of dependencies
204 because algorithm dependencies are usually both quite simple and entirely substitutable:
205 an algorithm that requires a centroid can typically make use of any centroid algorithms
206 outputs. That makes it relatively easy to figure out the correct value to use for any
207 particular algorithm.
209 raise NotImplementedError(
"All plugins must implement getExecutionOrder()")
213 Initialize the measurement object.
215 @param[in] config An instance of this class's ConfigClass.
216 @param[in] name The string the plugin was registered with.
218 object.__init__(self)
222 def fail(self, measRecord, error=None):
224 Record a failure of the measure or measureN() method.
226 When measure() raises an exception, the measurement framework
227 will call fail() to allow the plugin to set its failure flag
228 field(s). When measureN() raises an exception, fail() will be
229 called repeatedly with all the records that were being
232 If the exception is a MeasurementError, it will be passed as
233 the error argument; in all other cases the error argument will
234 be None, and the failure will be logged by the measurement
235 framework as a warning.
237 traceback.print_exc()
238 message = (
"The algorithm '%s' thinks it cannot fail, but it did; "
239 "please report this as a bug (the full traceback is above)."
240 % self.__class__.__name__)
241 raise NotImplementedError(message)
246 Get the measurement transformation appropriate to this plugin.
248 This returns a subclass of MeasurementTransform, which may be
249 instantiated with details of the algorithm configuration and then
250 called with information about calibration and WCS to convert from raw
251 measurement quantities to calibrated units. Calibrated data is then
252 provided in a separate output table.
254 By default, we copy everything from the input to the output without
257 return PassThroughTransform
262 Slot configuration which assigns a particular named plugin to each of a set of
263 slots. Each slot allows a type of measurement to be fetched from the SourceTable
264 without knowing which algorithm was used to produced the data.
266 NOTE: the default algorithm for each slot must be registered, even if the default is not used.
269 centroid = lsst.pex.config.Field(dtype=str, default=
"base_SdssCentroid", optional=
True,
270 doc=
"the name of the centroiding algorithm used to set source x,y")
271 shape = lsst.pex.config.Field(dtype=str, default=
"base_SdssShape", optional=
True,
272 doc=
"the name of the algorithm used to set source moments parameters")
273 apFlux = lsst.pex.config.Field(dtype=str, default=
"base_CircularApertureFlux_0", optional=
True,
274 doc=
"the name of the algorithm used to set the source aperture flux slot")
275 modelFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
276 doc=
"the name of the algorithm used to set the source model flux slot")
277 psfFlux = lsst.pex.config.Field(dtype=str, default=
"base_PsfFlux", optional=
True,
278 doc=
"the name of the algorithm used to set the source psf flux slot")
279 instFlux = lsst.pex.config.Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
280 doc=
"the name of the algorithm used to set the source inst flux slot")
283 """Convenience method to setup a Schema's slots according to the config definition.
285 This is defined in the Config class to support use in unit tests without needing
286 to construct a Task object.
288 aliases = schema.getAliasMap()
290 if self.
shape is not None: aliases.set(
"slot_Shape", self.
shape)
291 if self.
apFlux is not None: aliases.set(
"slot_ApFlux", self.
apFlux)
293 if self.
psfFlux is not None: aliases.set(
"slot_PsfFlux", self.
psfFlux)
299 Base config class for all measurement driver tasks.
302 slots = lsst.pex.config.ConfigField(
303 dtype = SourceSlotConfig,
304 doc=
"Mapping from algorithms to special aliases in Source."
307 doReplaceWithNoise = lsst.pex.config.Field(dtype=bool, default=
True, optional=
False,
308 doc=
'When measuring, replace other detected footprints with noise?')
310 noiseReplacer = lsst.pex.config.ConfigField(
311 dtype=NoiseReplacerConfig,
312 doc=
"configuration that sets how to replace neighboring sources with noise"
316 lsst.pex.config.Config.validate(self)
317 if self.slots.centroid
is not None and self.slots.centroid
not in self.plugins.names:
318 raise ValueError(
"source centroid slot algorithm is not being run.")
319 if self.slots.shape
is not None and self.slots.shape
not in self.plugins.names:
320 raise ValueError(
"source shape slot algorithm '%s' is not being run." % self.slots.shape)
321 for slot
in (self.slots.psfFlux, self.slots.apFlux, self.slots.modelFlux, self.slots.instFlux):
323 for name
in self.plugins.names:
324 if len(name) <= len(slot)
and name == slot[:len(name)]:
327 raise ValueError(
"source flux slot algorithm '%s' is not being run." % slot)
338 Ultimate base class for all measurement tasks.
340 This base class for SingleFrameMeasurementTask and ForcedMeasurementTask mostly exists to share
341 code between the two, and generally should not be used directly.
344 ConfigClass = BaseMeasurementConfig
345 _DefaultName =
"measurement"
349 Constructor; only called by derived classes.
351 @param[in] algMetadata An lsst.daf.base.PropertyList that will be filled with metadata
352 about the plugins being run. If None, an empty PropertyList will
354 @param[in] **kwds Additional arguments passed to lsst.pipe.base.Task.__init__.
356 This attaches two public attributes to the class for use by derived classes and parent tasks:
357 - plugins: an empty PluginMap, which will eventually contain all active plugins that will by
358 invoked by the run() method (to be filled by subclasses). This should be considered read-only.
359 - algMetadata: a lsst.daf.base.PropertyList that will contain additional information about the
360 active plugins to be saved with the output catalog (to be filled by subclasses).
362 super(BaseMeasurementTask, self).
__init__(**kwds)
364 if algMetadata
is None:
369 """Initialize the plugins (and slots) according to the configuration.
371 Derived class constructors should call this method to fill the self.plugins
372 attribute and add correspond output fields and slot aliases to the output schema.
374 In addition to the attributes added by BaseMeasurementTask.__init__, a self.schema
375 attribute holding the output schema must also be present before this method is called, .
377 Keyword arguments are forwarded directly to plugin constructors, allowing derived
378 classes to use plugins with different signatures.
383 if self.config.slots.centroid !=
None:
384 self.
plugins[self.config.slots.centroid] =
None
386 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
391 if self.config.slots.centroid
is not None and self.
plugins[self.config.slots.centroid]
is None:
392 del self.
plugins[self.config.slots.centroid]
396 Call the measure() method on all plugins, handling exceptions in a consistent way.
398 @param[in,out] measRecord lsst.afw.table.SourceRecord that corresponds to the object being
399 measured, and where outputs should be written.
400 @param[in] *args Positional arguments forwarded to Plugin.measure()
401 @param[in] **kwds Keyword arguments forwarded to Plugin.measure()
403 This method can be used with plugins that have different signatures; the only requirement is that
404 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
405 forwarded directly to the plugin.
407 This method should be considered "protected"; it is intended for use by derived classes, not users.
409 for plugin
in self.plugins.iter():
411 plugin.measure(measRecord, *args, **kwds)
412 except FATAL_EXCEPTIONS:
414 except MeasurementError
as error:
415 plugin.fail(measRecord, error)
416 except Exception
as error:
417 self.log.warn(
"Error in %s.measure on record %s: %s"
418 % (plugin.name, measRecord.getId(), error))
419 plugin.fail(measRecord)
423 Call the measureN() method on all plugins, handling exceptions in a consistent way.
425 @param[in,out] measCat lsst.afw.table.SourceCatalog containing records for just
426 the source family to be measured, and where outputs should
428 @param[in] *args Positional arguments forwarded to Plugin.measure()
429 @param[in] **kwds Keyword arguments forwarded to Plugin.measure()
431 This method can be used with plugins that have different signatures; the only requirement is that
432 'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
433 forwarded directly to the plugin.
435 This method should be considered "protected"; it is intended for use by derived classes, not users.
437 for plugin
in self.plugins.iterN():
439 plugin.measureN(measCat, *args, **kwds)
440 except FATAL_EXCEPTIONS:
442 except MeasurementError
as error:
443 for measRecord
in measCat:
444 plugin.fail(measRecord, error)
445 except Exception
as error:
446 for measRecord
in measCat:
447 plugin.fail(measRecord)
448 self.log.warn(
"Error in %s.measureN on records %s-%s: %s"
449 % (plugin.name, measCat[0].getId(), measCat[-1].getId(), error))
def __init__
Constructor; only called by derived classes.
Slot configuration which assigns a particular named plugin to each of a set of slots.
Base class measurement Plugin config classes.
def generateAlgorithmName
Class for storing ordered metadata with comments.
def __init__
Initialize the measurement object.
Class used as the actual element in the registry.
Ultimate base class for all measurement tasks.
Base class for plugin registries.
def __init__
Create a Configurable object for the given PluginClass and name.
def callMeasure
Call the measure() method on all plugins, handling exceptions in a consistent way.
def getTransformClass
Get the measurement transformation appropriate to this plugin.
def register
Register a Plugin class with the given name.
Base class for measurement plugins.
Map of plugins to be run for a task.
Base config class for all measurement driver tasks.
def register
A Python decorator that registers a class, using the given name, in its base class's PluginRegistry...
def callMeasureN
Call the measureN() method on all plugins, handling exceptions in a consistent way.
def fail
Record a failure of the measure or measureN() method.