LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
baseMeasurement.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008-2016 AURA/LSST.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 """Base measurement task, which subclassed by the single frame and forced measurement tasks.
24 """
25 import lsst.pipe.base
26 import lsst.pex.config
27 
28 from .pluginRegistry import PluginMap
29 from .baseLib import FatalAlgorithmError, MeasurementError
30 from .pluginsBase import BasePluginConfig, BasePlugin
31 from .noiseReplacer import NoiseReplacerConfig
32 
33 __all__ = ("BaseMeasurementPluginConfig", "BaseMeasurementPlugin",
34  "BaseMeasurementConfig", "BaseMeasurementTask")
35 
36 # Exceptions that the measurement tasks should always propagate up to their callers
37 FATAL_EXCEPTIONS = (MemoryError, FatalAlgorithmError)
38 
39 
40 class BaseMeasurementPluginConfig(BasePluginConfig):
41  """!
42  Base config class for all measurement plugins
43 
44  Most derived classes will want to override setDefaults() in order to customize
45  the default exceutionOrder.
46 
47  A derived class whose corresponding Plugin class implements measureN() should
48  additionally add a bool doMeasureN field to replace the bool class attribute
49  defined here.
50  """
51 
52  doMeasure = lsst.pex.config.Field(dtype=bool, default=True,
53  doc="whether to run this plugin in single-object mode")
54 
55  doMeasureN = False # replace this class attribute with a Field if measureN-capable
56 
57 
58 class BaseMeasurementPlugin(BasePlugin):
59  '''
60  Base class for all measurement plugins
61 
62  This is class is a placeholder for future behavior which will be shared only between
63  measurement plugins and is implemented for symmetry with the measurement base plugin
64  configuration class
65  '''
66  pass
67 
68 
69 class SourceSlotConfig(lsst.pex.config.Config):
70  """!
71  Slot configuration which assigns a particular named plugin to each of a set of
72  slots. Each slot allows a type of measurement to be fetched from the SourceTable
73  without knowing which algorithm was used to produced the data.
74 
75  NOTE: the default algorithm for each slot must be registered, even if the default is not used.
76  """
77 
78  centroid = lsst.pex.config.Field(dtype=str, default="base_SdssCentroid", optional=True,
79  doc="the name of the centroiding algorithm used to set source x,y")
80  shape = lsst.pex.config.Field(dtype=str, default="base_SdssShape", optional=True,
81  doc="the name of the algorithm used to set source moments parameters")
82  apFlux = lsst.pex.config.Field(dtype=str, default="base_CircularApertureFlux_12_0", optional=True,
83  doc="the name of the algorithm used to set the source aperture flux slot")
84  modelFlux = lsst.pex.config.Field(dtype=str, default="base_GaussianFlux", optional=True,
85  doc="the name of the algorithm used to set the source model flux slot")
86  psfFlux = lsst.pex.config.Field(dtype=str, default="base_PsfFlux", optional=True,
87  doc="the name of the algorithm used to set the source psf flux slot")
88  instFlux = lsst.pex.config.Field(dtype=str, default="base_GaussianFlux", optional=True,
89  doc="the name of the algorithm used to set the source inst flux slot")
90  calibFlux = lsst.pex.config.Field(dtype=str, default="base_CircularApertureFlux_12_0", optional=True,
91  doc="the name of the flux measurement algorithm used for calibration")
92 
93  def setupSchema(self, schema):
94  """Convenience method to setup a Schema's slots according to the config definition.
95 
96  This is defined in the Config class to support use in unit tests without needing
97  to construct a Task object.
98  """
99  aliases = schema.getAliasMap()
100  if self.centroid is not None:
101  aliases.set("slot_Centroid", self.centroid)
102  if self.shape is not None:
103  aliases.set("slot_Shape", self.shape)
104  if self.apFlux is not None:
105  aliases.set("slot_ApFlux", self.apFlux)
106  if self.modelFlux is not None:
107  aliases.set("slot_ModelFlux", self.modelFlux)
108  if self.psfFlux is not None:
109  aliases.set("slot_PsfFlux", self.psfFlux)
110  if self.instFlux is not None:
111  aliases.set("slot_InstFlux", self.instFlux)
112  if self.calibFlux is not None:
113  aliases.set("slot_CalibFlux", self.calibFlux)
114 
115 
116 class BaseMeasurementConfig(lsst.pex.config.Config):
117  """!
118  Base config class for all measurement driver tasks.
119  """
120 
121  slots = lsst.pex.config.ConfigField(
122  dtype=SourceSlotConfig,
123  doc="Mapping from algorithms to special aliases in Source."
124  )
125 
126  doReplaceWithNoise = lsst.pex.config.Field(
127  dtype=bool, default=True, optional=False,
128  doc='When measuring, replace other detected footprints with noise?')
129 
130  noiseReplacer = lsst.pex.config.ConfigField(
131  dtype=NoiseReplacerConfig,
132  doc="configuration that sets how to replace neighboring sources with noise"
133  )
134 
135  def validate(self):
136  lsst.pex.config.Config.validate(self)
137  if self.slots.centroid is not None and self.slots.centroid not in self.plugins.names:
138  raise ValueError("source centroid slot algorithm is not being run.")
139  if self.slots.shape is not None and self.slots.shape not in self.plugins.names:
140  raise ValueError("source shape slot algorithm '%s' is not being run." % self.slots.shape)
141  for slot in (self.slots.psfFlux, self.slots.apFlux, self.slots.modelFlux,
142  self.slots.instFlux, self.slots.calibFlux):
143  if slot is not None:
144  for name in self.plugins.names:
145  if len(name) <= len(slot) and name == slot[:len(name)]:
146  break
147  else:
148  raise ValueError("source flux slot algorithm '%s' is not being run." % slot)
149 
150 ## @addtogroup LSST_task_documentation
151 ## @{
152 ## @page baseMeasurementTask
153 ## BaseMeasurementTask @copybrief BaseMeasurementTask
154 ## @}
155 
156 
157 class BaseMeasurementTask(lsst.pipe.base.Task):
158  """!
159  Ultimate base class for all measurement tasks.
160 
161  This base class for SingleFrameMeasurementTask and ForcedMeasurementTask mostly exists to share
162  code between the two, and generally should not be used directly.
163  """
164 
165  ConfigClass = BaseMeasurementConfig
166  _DefaultName = "measurement"
167 
168  def __init__(self, algMetadata=None, **kwds):
169  """!
170  Constructor; only called by derived classes.
171 
172  @param[in] algMetadata An lsst.daf.base.PropertyList that will be filled with metadata
173  about the plugins being run. If None, an empty PropertyList will
174  be created.
175  @param[in] **kwds Additional arguments passed to lsst.pipe.base.Task.__init__.
176 
177  This attaches two public attributes to the class for use by derived classes and parent tasks:
178  - plugins: an empty PluginMap, which will eventually contain all active plugins that will by
179  invoked by the run() method (to be filled by subclasses). This should be considered read-only.
180  - algMetadata: a lsst.daf.base.PropertyList that will contain additional information about the
181  active plugins to be saved with the output catalog (to be filled by subclasses).
182  """
183  super(BaseMeasurementTask, self).__init__(**kwds)
184  self.plugins = PluginMap()
185  if algMetadata is None:
186  algMetadata = lsst.daf.base.PropertyList()
187  self.algMetadata = algMetadata
188 
189  def initializePlugins(self, **kwds):
190  """Initialize the plugins (and slots) according to the configuration.
191 
192  Derived class constructors should call this method to fill the self.plugins
193  attribute and add correspond output fields and slot aliases to the output schema.
194 
195  In addition to the attributes added by BaseMeasurementTask.__init__, a self.schema
196  attribute holding the output schema must also be present before this method is called, .
197 
198  Keyword arguments are forwarded directly to plugin constructors, allowing derived
199  classes to use plugins with different signatures.
200  """
201  # Make a place at the beginning for the centroid plugin to run first (because it's an OrderedDict,
202  # adding an empty element in advance means it will get run first when it's reassigned to the
203  # actual Plugin).
204  if self.config.slots.centroid is not None:
205  self.plugins[self.config.slots.centroid] = None
206  # Init the plugins, sorted by execution order. At the same time add to the schema
207  for executionOrder, name, config, PluginClass in sorted(self.config.plugins.apply()):
208  self.plugins[name] = PluginClass(config, name, metadata=self.algMetadata, **kwds)
209  # In rare circumstances (usually tests), the centroid slot not be coming from an algorithm,
210  # which means we'll have added something we don't want to the plugins map, and we should
211  # remove it.
212  if self.config.slots.centroid is not None and self.plugins[self.config.slots.centroid] is None:
213  del self.plugins[self.config.slots.centroid]
214 
215  def callMeasure(self, measRecord, *args, **kwds):
216  """!
217  Call the measure() method on all plugins, handling exceptions in a consistent way.
218 
219  @param[in,out] measRecord lsst.afw.table.SourceRecord that corresponds to the object being
220  measured, and where outputs should be written.
221  @param[in] *args Positional arguments forwarded to Plugin.measure()
222  @param[in] **kwds Keyword arguments. Two are handled locally:
223  - beginOrder: beginning execution order (inclusive): measurements with
224  executionOrder < beginOrder are not executed. None for no limit.
225  - endOrder: ending execution order (exclusive): measurements with
226  executionOrder >= endOrder are not executed. None for no limit.
227  the rest are forwarded to Plugin.measure()
228 
229  This method can be used with plugins that have different signatures; the only requirement is that
230  'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
231  forwarded directly to the plugin.
232 
233  This method should be considered "protected"; it is intended for use by derived classes, not users.
234  """
235  beginOrder = kwds.pop("beginOrder", None)
236  endOrder = kwds.pop("endOrder", None)
237  for plugin in self.plugins.iter():
238  if beginOrder is not None and plugin.getExecutionOrder() < beginOrder:
239  continue
240  if endOrder is not None and plugin.getExecutionOrder() >= endOrder:
241  break
242  try:
243  plugin.measure(measRecord, *args, **kwds)
244  except FATAL_EXCEPTIONS:
245  raise
246  except MeasurementError as error:
247  plugin.fail(measRecord, error)
248  except Exception as error:
249  self.log.warn("Error in %s.measure on record %s: %s"
250  % (plugin.name, measRecord.getId(), error))
251  plugin.fail(measRecord)
252 
253  def callMeasureN(self, measCat, *args, **kwds):
254  """!
255  Call the measureN() method on all plugins, handling exceptions in a consistent way.
256 
257  @param[in,out] measCat lsst.afw.table.SourceCatalog containing records for just
258  the source family to be measured, and where outputs should
259  be written.
260  @param[in] beginOrder beginning execution order (inclusive): measurements with
261  executionOrder < beginOrder are not executed. None for no limit.
262  @param[in] endOrder ending execution order (exclusive): measurements with
263  executionOrder >= endOrder are not executed. None for no limit.
264  @param[in] *args Positional arguments forwarded to Plugin.measure()
265  @param[in] **kwds Keyword arguments. Two are handled locally:
266  - beginOrder: beginning execution order (inclusive): measurements with
267  executionOrder < beginOrder are not executed. None for no limit.
268  - endOrder: ending execution order (exclusive): measurements with
269  executionOrder >= endOrder are not executed. None for no limit.
270  the rest are forwarded to Plugin.measure()
271 
272  This method can be used with plugins that have different signatures; the only requirement is that
273  'measRecord' be the first argument. Subsequent positional arguments and keyword arguments are
274  forwarded directly to the plugin.
275 
276  This method should be considered "protected"; it is intended for use by derived classes, not users.
277  """
278  beginOrder = kwds.pop("beginOrder", None)
279  endOrder = kwds.pop("endOrder", None)
280  for plugin in self.plugins.iterN():
281  if beginOrder is not None and plugin.getExecutionOrder() < beginOrder:
282  continue
283  if endOrder is not None and plugin.getExecutionOrder() >= endOrder:
284  break
285  try:
286  plugin.measureN(measCat, *args, **kwds)
287  except FATAL_EXCEPTIONS:
288  raise
289  except MeasurementError as error:
290  for measRecord in measCat:
291  plugin.fail(measRecord, error)
292  except Exception as error:
293  for measRecord in measCat:
294  plugin.fail(measRecord)
295  self.log.warn("Error in %s.measureN on records %s-%s: %s"
296  % (plugin.name, measCat[0].getId(), measCat[-1].getId(), error))
Base config class for all measurement plugins.
def __init__
Constructor; only called by derived classes.
Class for storing ordered metadata with comments.
Definition: PropertyList.h:82
def callMeasure
Call the measure() method on all plugins, handling exceptions in a consistent way.
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.