22"""Base measurement task, which subclassed by the single frame and forced
30from .pluginRegistry
import PluginMap
31from ._measBaseLib
import FatalAlgorithmError, MeasurementError
32from .pluginsBase
import BasePluginConfig, BasePlugin
33from .noiseReplacer
import NoiseReplacerConfig
35__all__ = (
"BaseMeasurementPluginConfig",
"BaseMeasurementPlugin",
36 "BaseMeasurementConfig",
"BaseMeasurementTask")
40FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
44 """Base config class for all measurement plugins.
48 Most derived classes will want to override `setDefaults` in order to
49 customize the default `executionOrder`.
51 A derived
class whose corresponding Plugin class implements
a do `measureN`
52 method should additionally add a bool `doMeasureN` field to replace the
53 bool
class attribute
defined here.
57 doc=
"whether to run this plugin in single-object mode")
63 """Base class for all measurement plugins.
67 This is class is
a placeholder
for future behavior which will be shared
68 only between measurement plugins
and is implemented
for symmetry
with the
69 measurement base plugin configuration
class
76 """Assign named plugins to measurement slots.
78 Slot configuration which assigns a particular named plugin to each of a set
79 of slots. Each slot allows a type of measurement to be fetched from the
85 The default algorithm
for each slot must be registered, even
if the default
90 centroid = Field(dtype=str, default="base_SdssCentroid", optional=
True,
91 doc=
"the name of the centroiding algorithm used to set source x,y")
92 shape =
Field(dtype=str, default=
"base_SdssShape", optional=
True,
93 doc=
"the name of the algorithm used to set source moments parameters")
94 psfShape =
Field(dtype=str, default=
"base_SdssShape_psf", optional=
True,
95 doc=
"the name of the algorithm used to set PSF moments parameters")
96 apFlux =
Field(dtype=str, default=
"base_CircularApertureFlux_12_0", optional=
True,
97 doc=
"the name of the algorithm used to set the source aperture instFlux slot")
98 modelFlux =
Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
99 doc=
"the name of the algorithm used to set the source model instFlux slot")
100 psfFlux =
Field(dtype=str, default=
"base_PsfFlux", optional=
True,
101 doc=
"the name of the algorithm used to set the source psf instFlux slot")
102 gaussianFlux =
Field(dtype=str, default=
"base_GaussianFlux", optional=
True,
103 doc=
"the name of the algorithm used to set the source Gaussian instFlux slot")
104 calibFlux =
Field(dtype=str, default=
"base_CircularApertureFlux_12_0", optional=
True,
105 doc=
"the name of the instFlux measurement algorithm used for calibration")
108 """Set up a slots in a schema following configuration directives.
113 The schema in which slots will be set up.
117 This
is defined
in this configuration
class to support use in unit
118 tests without needing to construct an `lsst.pipe.base.Task` object.
120 aliases = schema.getAliasMap()
122 aliases.set(
"slot_Centroid", self.
centroid)
123 if self.
shape is not None:
124 aliases.set(
"slot_Shape", self.
shape)
126 aliases.set(
"slot_PsfShape", self.
psfShape)
127 if self.
apFlux is not None:
128 aliases.set(
"slot_ApFlux", self.
apFlux)
130 aliases.set(
"slot_ModelFlux", self.
modelFlux)
132 aliases.set(
"slot_PsfFlux", self.
psfFlux)
136 aliases.set(
"slot_CalibFlux", self.
calibFlux)
140 """Base configuration for all measurement driver tasks.
144 ignoreSlotPluginChecks : `bool`, optional
145 Do not check that all slots have an associated plugin to run when
146 validating this config. This
is primarily
for tests that were written
147 before we made Tasks always call `config.validate()` on init.
148 DEPRECATED DM-35949: this
is a temporary workaround
while we better
149 define how config/schema validation works
for measurement tasks.
153 Subclasses should define the
'plugins' and 'undeblended' registries, e.g.
157 plugins = PluginBaseClass.registry.makeField(
160 doc=
"Plugins to be run and their configuration"
162 undeblended = PluginBaseClass.registry.makeField(
165 doc=
"Plugins to run on undeblended image"
168 where ``PluginBaseClass``
is the appropriate base
class of the plugin
169 (e.g., `SingleFramePlugin`
or `ForcedPlugin`).
171 def __new__(cls, *args, ignoreSlotPluginChecks=False, **kwargs):
172 instance = super().
__new__(cls, *args, **kwargs)
173 if ignoreSlotPluginChecks:
174 msg = (
"ignoreSlotPluginChecks is deprecated and should only be used in tests."
175 " No removal date has been set; see DM-35949.")
176 warnings.warn(msg, category=FutureWarning, stacklevel=2)
177 object.__setattr__(instance,
"_ignoreSlotPluginChecks", ignoreSlotPluginChecks)
181 dtype=SourceSlotConfig,
182 doc=
"Mapping from algorithms to special aliases in Source."
186 dtype=bool, default=
True, optional=
False,
187 doc=
'When measuring, replace other detected footprints with noise?')
190 dtype=NoiseReplacerConfig,
191 doc=
"configuration that sets how to replace neighboring sources with noise"
194 dtype=str, default=
"undeblended_",
195 doc=
"Prefix to give undeblended plugins"
200 if self._ignoreSlotPluginChecks:
202 if self.
slots.centroid
is not None and self.
slots.centroid
not in self.plugins.names:
203 raise ValueError(
"source centroid slot algorithm is not being run.")
204 if self.
slots.shape
is not None and self.
slots.shape
not in self.plugins.names:
205 raise ValueError(
"source shape slot algorithm '%s' is not being run." % self.
slots.shape)
206 for slot
in (self.
slots.psfFlux, self.
slots.apFlux, self.
slots.modelFlux,
207 self.
slots.gaussianFlux, self.
slots.calibFlux):
209 for name
in self.plugins.names:
210 if len(name) <= len(slot)
and name == slot[:len(name)]:
213 raise ValueError(
"source instFlux slot algorithm '%s' is not being run." % slot)
217 """Ultimate base class for all measurement tasks.
222 Will be modified
in-place to contain metadata about the plugins being
226 Additional arguments passed to `lsst.pipe.base.Task.__init__`.
230 This base
class for `SingleFrameMeasurementTask`
and
231 `ForcedMeasurementTask` mostly exists to share code between the two,
and
232 generally should
not be used directly.
235 ConfigClass = BaseMeasurementConfig
236 _DefaultName = "measurement"
239 """Plugins to be invoked (`PluginMap`).
241 Initially empty, this will be populated as plugins are initialized. It
242 should be considered read-only.
246 """Metadata about active plugins (`lsst.daf.base.PropertyList`).
248 Contains additional information about active plugins to be saved with
249 the output catalog. Will be filled by subclasses.
253 super(BaseMeasurementTask, self).
__init__(**kwds)
256 if algMetadata
is None:
261 """Initialize plugins (and slots) according to configuration.
266 Keyword arguments forwarded directly to plugin constructors.
270 Derived class constructors should call this method
to fill the
271 `plugins` attribute
and add corresponding output fields
and slot
272 aliases to the output schema.
274 In addition to the attributes added by `BaseMeasurementTask.__init__`,
275 a ``schema``` attribute holding the output schema must be present
276 before this method
is called.
278 Keyword arguments are forwarded directly to plugin constructors,
279 allowing derived classes to use plugins
with different signatures.
285 if self.config.slots.centroid
is not None:
286 self.
plugins[self.config.slots.centroid] =
None
289 for executionOrder, name, config, PluginClass
in sorted(self.config.plugins.apply()):
292 if getattr(PluginClass,
"hasLogName",
False):
294 logName=self.log.getChild(name).name, **kwds)
301 if self.config.slots.centroid
is not None and self.
plugins[self.config.slots.centroid]
is None:
302 del self.
plugins[self.config.slots.centroid]
304 for executionOrder, name, config, PluginClass
in sorted(self.config.undeblended.apply()):
305 undeblendedName = self.config.undeblendedPrefix + name
306 if getattr(PluginClass,
"hasLogName",
False):
309 logName=self.log.getChild(undeblendedName).name,
316 """Call ``measure`` on all plugins and consistently handle exceptions.
321 The record corresponding to the object being measured. Will be
322 updated in-place
with the results of measurement.
324 Positional arguments forwarded to ``plugin.measure``
326 Keyword arguments. Two are handled locally:
329 Beginning execution order (inclusive). Measurements
with
330 ``executionOrder`` < ``beginOrder`` are
not executed. `
None`
334 Ending execution order (exclusive). Measurements
with
335 ``executionOrder`` >= ``endOrder`` are
not executed. `
None`
338 Others are forwarded to ``plugin.measure()``.
342 This method can be used
with plugins that have different signatures;
343 the only requirement
is that ``measRecord`` be the first argument.
344 Subsequent positional arguments
and keyword arguments are forwarded
345 directly to the plugin.
347 This method should be considered
"protected": it
is intended
for use by
348 derived classes,
not users.
350 beginOrder = kwds.pop("beginOrder",
None)
351 endOrder = kwds.pop(
"endOrder",
None)
352 for plugin
in self.
plugins.iter():
353 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
355 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
360 """Call ``measure`` on the specified plugin.
362 Exceptions are handled in a consistent way.
366 plugin : subclass of `BasePlugin`
367 Plugin that will be executed.
369 The record corresponding to the object being measured. Will be
370 updated
in-place
with the results of measurement.
372 Positional arguments forwarded to ``plugin.measure()``.
374 Keyword arguments forwarded to ``plugin.measure()``.
378 This method can be used
with plugins that have different signatures;
379 the only requirement
is that ``plugin``
and ``measRecord`` be the first
380 two arguments. Subsequent positional arguments
and keyword arguments
381 are forwarded directly to the plugin.
383 This method should be considered
"protected": it
is intended
for use by
384 derived classes,
not users.
387 plugin.measure(measRecord, *args, **kwds)
388 except FATAL_EXCEPTIONS:
390 except MeasurementError
as error:
391 self.log.getChild(plugin.name).debug(
392 "MeasurementError in %s.measure on record %s: %s",
393 plugin.name, measRecord.getId(), error)
394 plugin.fail(measRecord, error)
395 except Exception
as error:
396 self.log.getChild(plugin.name).debug(
397 "Exception in %s.measure on record %s: %s",
398 plugin.name, measRecord.getId(), error)
399 plugin.fail(measRecord)
402 """Call ``measureN`` on all plugins and consistently handle exceptions.
407 Catalog containing only the records for the source family to be
408 measured,
and where outputs should be written.
410 Positional arguments forwarded to ``plugin.measure()``
412 Keyword arguments. Two are handled locally:
415 Beginning execution order (inclusive): Measurements
with
416 ``executionOrder`` < ``beginOrder`` are
not executed. `
None`
419 Ending execution order (exclusive): measurements
with
420 ``executionOrder`` >= ``endOrder`` are
not executed. `
None`
for
423 Others are are forwarded to ``plugin.measure()``.
427 This method can be used
with plugins that have different signatures;
428 the only requirement
is that ``measRecord`` be the first argument.
429 Subsequent positional arguments
and keyword arguments are forwarded
430 directly to the plugin.
432 This method should be considered
"protected": it
is intended
for use by
433 derived classes,
not users.
435 beginOrder = kwds.pop("beginOrder",
None)
436 endOrder = kwds.pop(
"endOrder",
None)
437 for plugin
in self.
plugins.iterN():
438 if beginOrder
is not None and plugin.getExecutionOrder() < beginOrder:
440 if endOrder
is not None and plugin.getExecutionOrder() >= endOrder:
445 """Call ``measureN`` on the specified plugin.
447 Exceptions are handled in a consistent way.
451 plugin : subclass of `BasePlugin`
452 Plugin that will be executed.
454 Catalog containing only the records
for the source family to be
455 measured,
and where outputs should be written.
457 Positional arguments forwarded to ``plugin.measureN()``.
459 Keyword arguments forwarded to ``plugin.measureN()``.
463 This method can be used
with plugins that have different signatures;
464 the only requirement
is that the ``plugin``
and ``measCat`` be the
465 first two arguments. Subsequent positional arguments
and keyword
466 arguments are forwarded directly to the plugin.
468 This method should be considered
"protected": it
is intended
for use by
469 derived classes,
not users.
472 plugin.measureN(measCat, *args, **kwds)
473 except FATAL_EXCEPTIONS:
476 except MeasurementError
as error:
477 for measRecord
in measCat:
478 self.log.getChild(plugin.name).debug(
479 "MeasurementError in %s.measureN on records %s-%s: %s",
480 plugin.name, measCat[0].getId(), measCat[-1].getId(), error)
481 plugin.fail(measRecord, error)
482 except Exception
as error:
483 for measRecord
in measCat:
484 plugin.fail(measRecord)
485 self.log.getChild(plugin.name).debug(
486 "Exception in %s.measureN on records %s-%s: %s",
487 plugin.name, measCat[0].getId(), measCat[-1].getId(), error)
Defines the fields and offsets for a table.
Record class that contains measurements made on a single exposure.
Table class that contains measurements made on a single exposure.
Class for storing ordered metadata with comments.
__new__(cls, *args, ignoreSlotPluginChecks=False, **kwargs)
callMeasureN(self, measCat, *args, **kwds)
__init__(self, algMetadata=None, **kwds)
callMeasure(self, measRecord, *args, **kwds)
doMeasurementN(self, plugin, measCat, *args, **kwds)
doMeasurement(self, plugin, measRecord, *args, **kwds)
initializePlugins(self, **kwds)
setupSchema(self, schema)