lsst.meas.base  13.0-33-gb1a1d47+8
wrappers.py
Go to the documentation of this file.
1 import lsst.pex.config
2 from .pluginRegistry import generateAlgorithmName
3 from .apCorrRegistry import addApCorrName
4 from .sfm import SingleFramePlugin
5 from .forcedMeasurement import ForcedPlugin
6 
7 __all__ = ("wrapSingleFrameAlgorithm", "wrapForcedAlgorithm", "wrapSimpleAlgorithm", "wrapTransform")
8 
9 
11 
12  def __init__(self, config, name, schema, metadata, logName=None):
13  SingleFramePlugin.__init__(self, config, name, schema, metadata, logName=logName)
14  if hasattr(self, "hasLogName") and self.hasLogName and logName is not None:
15  self.cpp = self.factory(config, name, schema, metadata, logName=logName)
16  else:
17  self.cpp = self.factory(config, name, schema, metadata)
18 
19  def measure(self, measRecord, exposure):
20  self.cpp.measure(measRecord, exposure)
21 
22  def measureN(self, measCat, exposure):
23  self.cpp.measureN(measCat, exposure)
24 
25  def fail(self, measRecord, error=None):
26  self.cpp.fail(measRecord, error.cpp if error is not None else None)
27 
28 
30 
31  def __init__(self, config, name, schemaMapper, metadata, logName=None):
32  ForcedPlugin.__init__(self, config, name, schemaMapper, metadata, logName=logName)
33  if hasattr(self, "hasLogName") and self.hasLogName and logName is not None:
34  self.cpp = self.factory(config, name, schemaMapper, metadata, logName=logName)
35  else:
36  self.cpp = self.factory(config, name, schemaMapper, metadata)
37 
38  def measure(self, measRecord, exposure, refRecord, refWcs):
39  self.cpp.measureForced(measRecord, exposure, refRecord, refWcs)
40 
41  def measureN(self, measCat, exposure, refCat, refWcs):
42  self.cpp.measureNForced(measCat, exposure, refCat, refWcs)
43 
44  def fail(self, measRecord, error=None):
45  self.cpp.fail(measRecord, error.cpp if error is not None else None)
46 
47 
48 def wrapAlgorithmControl(Base, Control, module=2, hasMeasureN=False):
49  """!
50  Wrap a C++ algorithm's control class into a Python Config class.
51 
52  @param[in] Base Base class for the returned ConfigClass; one of SingleFramePluginConfig or
53  ForcedPluginConfig
54  @param[in] Control Control class to be wrapped (a Swigged C++ class)
55  @param[in] module Either a module object, a string specifying the name of the module, or an
56  integer specifying how far back in the stack to look for the module to use:
57  0 is pex.config.wrap, 1 is lsst.meas.base.wrappers, 2 is the immediate
58  caller, etc. This will be used to set __module__ for the new config class,
59  and the class will also be added to the module. The default is to use the
60  callers' module.
61  @param[in] hasMeasureN Whether the plugin supports fitting multiple objects at once (if so, a
62  config option to enable/disable this will be added).
63 
64  @return a new subclass of lsst.pex.config.Config
65 
66  This function is generally only called by wrapAlgorithm; it is unlikely users will have to call it
67  directly.
68  """
69  if hasMeasureN:
70  # We need to add a Config field to enable multi-object measurement, to replace
71  # the simple bool class attribute that's on the base class. To do that, we
72  # create the Config class dynamically here, then call makeControlClass to finish
73  # it off by adding fields from the control object.
74  cls = type(
75  Control.__name__.replace("Control", "Config"),
76  (Base,),
77  {"doMeasureN": lsst.pex.config.Field(dtype=bool, default=True,
78  doc="whether to run this plugin in multi-object mode")}
79  )
80  ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, cls=cls)
81  else:
82  # If we don't have to add that Config field, we can delegate all of the work to
83  # pex_config's makeControlClass
84  ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, base=Base)
85  return ConfigClass
86 
87 
88 def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None,
89  ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False,
90  apCorrList=(), hasLogName=False, **kwds):
91  """!
92  Wrap a C++ Algorithm class into a Python Plugin class.
93 
94  @param[in] Base Base class for the returned Plugin; one of SingleFramePlugin or
95  ForcedPlugin
96  @param[in] AlgClass Swigged C++ Algorithm class to convert; must be a subclass of
97  SingleFrameAlgorithm or ForcedAlgorithm (matching the Base argument), or
98  an unrelated class with the same measure() and measureN() signatures as
99  those base classes.
100  @param[in] factory A callable that is used to construct an instance of AlgClass. It must take
101  four arguments, either (config, name, schema, metadata) or
102  (config, name, schemaMapper, metadata), depending on whether the algorithm is
103  single-frame or forced.
104  @param[in] executionOrder The order this plugin should be run, relative to others
105  (see BasePlugin.getExecutionOrder()).
106  @param[in] name String to use when registering the algorithm. Ignored if doRegistry=False,
107  set to generateAlgorithmName(AlgClass) if None.
108  @param[in] Control Swigged C++ Control class for the algorithm; AlgClass.Control is used if None.
109  Ignored if ConfigClass is not None.
110  @param[in] ConfigClass Python Config class that wraps the C++ Algorithm's swigged Control class. If
111  None, wrapAlgorithmControl is called to generate a Config class using the
112  Control argument.
113  @param[in] TransformClass Transformation which may be used to post-process the results of measurement.
114  If None, the default (defined by BasePlugin) is used.
115  @param[in] doRegister If True (the default), register the plugin with Base's registry, allowing it
116  to be used by measurement Tasks.
117  @param[in] shouldApCorr Does this algorithm measure a flux that can be aperture corrected? This is
118  shorthand for apCorrList=[name] and is ignored if apCorrList is specified.
119  @param[in] apCorrList List of field name prefixes for flux fields that should be aperture corrected.
120  If an algorithm produces a single flux that should be
121  aperture corrected then it is simpler to set shouldApCorr=True. But if an
122  algorithm produces multiple such fields then it must specify apCorrList,
123  instead. For example modelfit_CModel produces 3 such fields: apCorrList=
124  ("modelfit_CModel_exp", "modelfit_CModel_exp", "modelfit_CModel_def")
125  If apCorrList is non-empty then shouldApCorr is ignored.
126  If non-empty and doRegister is True then the names are added to the set
127  retrieved by getApCorrNameSet
128  @param[in] hasLogName Plugin supports a logName as a constructor argument
129 
130 
131  @param[in] **kwds Additional keyword arguments passed to generateAlgorithmControl, including:
132  - hasMeasureN: Whether the plugin supports fitting multiple objects at once
133  (if so, a config option to enable/disable this will be added).
134  - executionOrder: If not None, an override for the default executionOrder for
135  this plugin (the default is 2.0, which is usually appropriate for fluxes).
136 
137  @return the new Plugin class, a subclass of Base
138 
139  This function is generally only called by the public wrapSingleFrameAlgorithm, wrapForcedAlgorithm, and
140  wrapSimpleAlgorithm functions; it is unlikely users will have to call it directly.
141  """
142  if ConfigClass is None:
143  if Control is None:
144  Control = AlgClass.Control
145  ConfigClass = wrapAlgorithmControl(Base.ConfigClass, Control, **kwds)
146 
147  def getExecutionOrder():
148  return executionOrder
149  typeDict = dict(AlgClass=AlgClass, ConfigClass=ConfigClass, factory=staticmethod(factory),
150  getExecutionOrder=staticmethod(getExecutionOrder))
151  if TransformClass:
152  typeDict['getTransformClass'] = staticmethod(lambda: TransformClass)
153  PluginClass = type(AlgClass.__name__ + Base.__name__, (Base,), typeDict)
154  if doRegister:
155  if name is None:
156  name = generateAlgorithmName(AlgClass)
157  Base.registry.register(name, PluginClass)
158  if shouldApCorr:
159  addApCorrName(name)
160  PluginClass.hasLogName = hasLogName
161  return PluginClass
162 
163 
164 def wrapSingleFrameAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False,
165  hasLogName=False, **kwds):
166  """!
167  Wrap a C++ SingleFrameAlgorithm class into a Python SingleFramePlugin class.
168 
169  @param[in] AlgClass Swigged C++ Algorithm class to convert; must be a subclass of
170  SingleFrameAlgorithm, or an unrelated class with the same measure(),
171  measureN(), and fail() signatures.
172  @param[in] executionOrder The order this plugin should be run, relative to others
173  (see BasePlugin.getExecutionOrder()).
174  @param[in] name String to use when registering the algorithm. Ignored if doRegistry=False,
175  set to generateAlgorithmName(AlgClass) if None.
176  @param[in] needsMetadata Sets whether the AlgClass's constructor should be passed a PropertySet
177  metadata argument.
178  @param[in] hasMeasureN Whether the algorithm supports simultaneous measurement of multiple sources.
179  If True, a bool doMeasureN field will be added to the generated Config class,
180  and its value will be passed as the last argument when calling the AlgClass
181  constructor.
182  @param[in] hasLogName Plugin supports a logName as a constructor argument
183  @param[in] **kwds Additional keyword arguments passed to the lower-level wrapAlgorithm and
184  wrapAlgorithmControl classes. These include:
185  - Control: Swigged C++ Control class for the algorithm; AlgClass.Control
186  is used if None. Ignored if ConfigClass is not None.
187  - ConfigClass: Python Config class that wraps the C++ Algorithm's swigged
188  Control class. If None, wrapAlgorithmControl is called to generate a
189  Config class using the Control argument.
190  - doRegister: If True (the default), register the plugin with
191  SingleFramePlugin.registry, allowing it to be used by
192  SingleFrameMeasurementTask.
193  - shouldApCorr: does this algorithm measure a flux that can be aperture
194  corrected? This is shorthand for apCorrList=[name] and is ignored if
195  apCorrList is specified.
196  - apCorrList: list of field name prefixes for flux fields that should be
197  aperture corrected. If an algorithm produces a single flux that should be
198  aperture corrected then it is simpler to set shouldApCorr=True. But if an
199  algorithm produces multiple such fields then it must specify apCorrList,
200  instead. For example modelfit_CModel produces 3 such fields: apCorrList=
201  ("modelfit_CModel_exp", "modelfit_CModel_exp", "modelfit_CModel_def")
202  If apCorrList is non-empty then shouldApCorr is ignored.
203  If non-empty and doRegister is True then the names are added to the set
204  retrieved by getApCorrNameSet
205  - executionOrder: If not None, an override for the default executionOrder for
206  this plugin (the default is 2.0, which is usually appropriate for fluxes).
207 
208  @return the new SingleFramePlugin subclass
209 
210  The needsMetadata and hasMeasureN arguments combine to determine the expected constructor signature;
211  we always expect the first three arguments to be:
212  @verbatim
213  Control const & ctrl, std::string const & name, Schema & schema
214  @endverbatim
215  If needsMetadata, we also append:
216  @verbatim
217  PropertySet & metadata
218  @endverbatim
219  If hasMeasureN, we also append:
220  @verbatim
221  bool doMeasureN
222  @endverbatim
223  If hasLogName, we also append:
224  @verbatim
225  std::string logName
226  @endverbatim
227  If more than one is True, the metadata PropertySet precedes the doMeasureN bool
228  and the logName comes last of the three
229  """
230  if hasMeasureN:
231  if needsMetadata:
232  def factory(config, name, schema, metadata, **kwargs):
233  return AlgClass(config.makeControl(), name, schema, metadata, config.doMeasureN, **kwargs)
234  else:
235  def factory(config, name, schema, metadata, **kwargs):
236  return AlgClass(config.makeControl(), name, schema, config.doMeasureN, **kwargs)
237  else:
238  if needsMetadata:
239  def factory(config, name, schema, metadata, **kwargs):
240  return AlgClass(config.makeControl(), name, schema, metadata, **kwargs)
241  else:
242  def factory(config, name, schema, metadata, **kwargs):
243  return AlgClass(config.makeControl(), name, schema, **kwargs)
244 
245  return wrapAlgorithm(WrappedSingleFramePlugin, AlgClass, executionOrder=executionOrder, name=name,
246  factory=factory, hasMeasureN=hasMeasureN, hasLogName=hasLogName, **kwds)
247 
248 
249 def wrapForcedAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False,
250  hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, **kwds):
251  """!
252  Wrap a C++ ForcedAlgorithm class into a Python ForcedPlugin class.
253 
254  @param[in] AlgClass Swigged C++ Algorithm class to convert; must be a subclass of
255  ForcedAlgorithm, or an unrelated class with the same measure(), measureN(),
256  and fail() signatures.
257  @param[in] executionOrder The order this plugin should be run, relative to others
258  (see BasePlugin.getExecutionOrder()).
259  @param[in] name String to use when registering the algorithm. Ignored if doRegistry=False,
260  set to generateAlgorithmName(AlgClass) if None.
261  @param[in] needsMetadata Sets whether the AlgClass's constructor should be passed a PropertySet
262  metadata argument.
263  @param[in] hasMeasureN Whether the algorithm supports simultaneous measurement of multiple sources.
264  If True, a bool doMeasureN field will be added to the generated Config class,
265  and its value will be passed as the last argument when calling the AlgClass
266  constructor.
267  @param[in] hasLogName Plugin supports a logName as a constructor argument
268  @param[in] needsSchemaOnly Whether the algorithm constructor expects a Schema argument (representing the
269  output Schema) rather than the full SchemaMapper (which provides access to
270  both the reference Schema and the output Schema).
271  @param[in] **kwds Additional keyword arguments passed to the lower-level wrapAlgorithm and
272  wrapAlgorithmControl classes. These include:
273  - Control: Swigged C++ Control class for the algorithm; AlgClass.Control
274  is used if None. Ignored if ConfigClass is not None.
275  - ConfigClass: Python Config class that wraps the C++ Algorithm's swigged
276  Control class. If None, wrapAlgorithmControl is called to generate a
277  Config class using the Control argument.
278  - doRegister: If True (the default), register the plugin with
279  ForcedPlugin.registry, allowing it to be used by ForcedMeasurementTask.
280  - shouldApCorr: does this algorithm measure a flux that can be aperture
281  corrected? This is shorthand for apCorrList=[name] and is ignored if
282  apCorrList is specified.
283  - apCorrList: list of field name prefixes for flux fields that should be
284  aperture corrected. If an algorithm produces a single flux that should be
285  aperture corrected then it is simpler to set shouldApCorr=True. But if an
286  algorithm produces multiple such fields then it must specify apCorrList,
287  instead. For example modelfit_CModel produces 3 such fields: apCorrList=
288  ("modelfit_CModel_exp", "modelfit_CModel_exp", "modelfit_CModel_def")
289  If apCorrList is non-empty then shouldApCorr is ignored.
290  If non-empty and doRegister is True then the names are added to the set
291  retrieved by getApCorrNameSet
292  - executionOrder: If not None, an override for the default executionOrder for
293  this plugin (the default is 2.0, which is usually appropriate for fluxes).
294 
295  @return the new ForcedPlugin subclass
296 
297  The needsMetadata, hasMeasureN, and needsSchemaOnly arguments combine to determine the expected
298  constructor signature; we always expect the first two arguments to be:
299  @verbatim
300  Control const & ctrl, std::string const & name
301  @endverbatim
302  If needsSchemaOnly is True, then the third argument will be
303  @verbatim
304  Schema & schema
305  @endverbatim
306  otherwise, it will be:
307  @verbatim
308  SchemaMapper & schemaMapper
309  @endverbatim
310  If needsMetadata, we also append:
311  @verbatim
312  PropertySet & metadata
313  @endverbatim
314  If hasMeasureN, we also append:
315  @verbatim
316  bool doMeasureN
317  @endverbatim
318  If hasLogName, we also append:
319  @verbatim
320  std::string logName
321  @endverbatim
322  If more than one is True, the metadata PropertySet precedes the doMeasureN bool
323  and the logName comes last of the three
324  """
325  if needsSchemaOnly:
326  def extractSchemaArg(m):
327  return m.editOutputSchema()
328  else:
329  def extractSchemaArg(m):
330  return m
331  if hasMeasureN:
332  if needsMetadata:
333  def factory(config, name, schemaMapper, metadata, **kwargs):
334  return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
335  metadata, config.doMeasureN, **kwargs)
336  else:
337  def factory(config, name, schemaMapper, metadata, **kwargs):
338  return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
339  config.doMeasureN, **kwargs)
340  else:
341  if needsMetadata:
342  def factory(config, name, schemaMapper, metadata, **kwargs):
343  return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper),
344  metadata, **kwargs)
345  else:
346  def factory(config, name, schemaMapper, metadata, **kwargs):
347  return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), **kwargs)
348 
349  return wrapAlgorithm(WrappedForcedPlugin, AlgClass, executionOrder=executionOrder, name=name,
350  factory=factory, hasLogName=hasLogName, **kwds)
351 
352 
353 def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False,
354  hasLogName=False, **kwds):
355  """!
356  Wrap a C++ SimpleAlgorithm class into both a Python SingleFramePlugin and ForcedPlugin classes
357 
358  @param[in] AlgClass Swigged C++ Algorithm class to convert; must be a subclass of
359  simpleAlgorithm, or an unrelated class with the same measure(), measureN(),
360  and fail() signatures.
361  @param[in] executionOrder The order this plugin should be run, relative to others
362  (see BasePlugin.getExecutionOrder()).
363  @param[in] name String to use when registering the algorithm. Ignored if doRegistry=False,
364  set to generateAlgorithmName(AlgClass) if None.
365  @param[in] needsMetadata Sets whether the AlgClass's constructor should be passed a PropertySet
366  metadata argument.
367  @param[in] hasMeasureN Whether the algorithm supports simultaneous measurement of multiple sources.
368  If True, a bool doMeasureN field will be added to the generated Config class,
369  and its value will be passed as the last argument when calling the AlgClass
370  constructor.
371  @param[in] hasLogName Plugin supports a logName as a constructor argument
372  @param[in] **kwds Additional keyword arguments passed to the lower-level wrapAlgorithm and
373  wrapAlgorithmControl classes. These include:
374  - Control: Swigged C++ Control class for the algorithm; AlgClass.Control
375  is used if None. Ignored if ConfigClass is not None.
376  - ConfigClass: Python Config class that wraps the C++ Algorithm's swigged
377  Control class. If None, wrapAlgorithmControl is called to generate a
378  Config class using the Control argument.
379  - doRegister: If True (the default), register the plugins with Base's
380  registry, allowing it to be used by measurement Tasks.
381  - shouldApCorr: does this algorithm measure a flux that can be aperture
382  corrected? This is shorthand for apCorrList=[name] and is ignored if
383  apCorrList is specified.
384  - apCorrList: list of field name prefixes for flux fields that should be
385  aperture corrected. If an algorithm produces a single flux that should be
386  aperture corrected then it is simpler to set shouldApCorr=True. But if an
387  algorithm produces multiple such fields then it must specify apCorrList,
388  instead. For example modelfit_CModel produces 3 such fields: apCorrList=
389  ("modelfit_CModel_exp", "modelfit_CModel_exp", "modelfit_CModel_def")
390  If apCorrList is non-empty then shouldApCorr is ignored.
391  If non-empty and doRegister is True then the names are added to the set
392  retrieved by getApCorrNameSet
393  - executionOrder: If not None, an override for the default executionOrder for
394  this plugin (the default is 2.0, which is usually appropriate for fluxes).
395 
396  @return a two-element tuple, containing the new SingleFramePlugin and ForcedPlugin subclasses
397 
398  The needsMetadata and hasMeasureN arguments combine to determine the expected constructor signature;
399  we always expect the first three arguments to be:
400  @verbatim
401  Control const & ctrl, std::string const & name, Schema & schema
402  @endverbatim
403  If needsMetadata, we also append:
404  @verbatim
405  PropertySet & metadata
406  @endverbatim
407  If hasMeasureN, we also append:
408  @verbatim
409  bool doMeasureN
410  @endverbatim
411  If hasLogName, we also append:
412  @verbatim
413  std::string logName
414  @endverbatim
415  If more than one is True, the metadata PropertySet precedes the doMeasureN bool
416  and the logName comes last of the three
417  """
418  return (wrapSingleFrameAlgorithm(AlgClass, executionOrder=executionOrder, name=name,
419  needsMetadata=needsMetadata, hasLogName=hasLogName, **kwds),
420  wrapForcedAlgorithm(AlgClass, executionOrder=executionOrder, name=name,
421  needsMetadata=needsMetadata, hasLogName=hasLogName,
422  needsSchemaOnly=True, **kwds))
423 
424 
425 def wrapTransform(transformClass, hasLogName=False):
426  """Modify a C++ Transform class so that it can be configured with either a Config or a Control.
427 
428  Parameters
429  ----------
430  transformClass: class
431  A Transform class. Its constructor must take a Control, a string, and
432  a SchemaMapper, in that order.
433  """
434  oldInit = transformClass.__init__
435 
436  def _init(self, ctrl, name, mapper, logName=None):
437  if hasattr(ctrl, "makeControl"):
438  ctrl = ctrl.makeControl()
439  # logName signature needs to be on this Class __init__, but is not needed by the C++ plugin
440  oldInit(self, ctrl, name, mapper)
441 
442  transformClass.__init__ = _init
def fail(self, measRecord, error=None)
Definition: wrappers.py:44
def wrapForcedAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, kwds)
Wrap a C++ ForcedAlgorithm class into a Python ForcedPlugin class.
Definition: wrappers.py:250
def addApCorrName(name)
Add to the set of field name prefixes for fluxes that should be aperture corrected.
def measure(self, measRecord, exposure)
Definition: wrappers.py:19
Base class for single-frame plugin algorithms.
Definition: sfm.py:50
def __init__(self, config, name, schema, metadata, logName=None)
Definition: wrappers.py:12
def fail(self, measRecord, error=None)
Definition: wrappers.py:25
def measureN(self, measCat, exposure)
Definition: wrappers.py:22
def __init__(self, config, name, schemaMapper, metadata, logName=None)
Definition: wrappers.py:31
def measure(self, measRecord, exposure, refRecord, refWcs)
Definition: wrappers.py:38
def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None, ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False, apCorrList=(), hasLogName=False, kwds)
Wrap a C++ Algorithm class into a Python Plugin class.
Definition: wrappers.py:90
def wrapTransform(transformClass, hasLogName=False)
Definition: wrappers.py:425
def measureN(self, measCat, exposure, refCat, refWcs)
Definition: wrappers.py:41
def wrapSingleFrameAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, kwds)
Wrap a C++ SingleFrameAlgorithm class into a Python SingleFramePlugin class.
Definition: wrappers.py:165
def wrapAlgorithmControl(Base, Control, module=2, hasMeasureN=False)
Wrap a C++ algorithm's control class into a Python Config class.
Definition: wrappers.py:48
def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, hasLogName=False, kwds)
Wrap a C++ SimpleAlgorithm class into both a Python SingleFramePlugin and ForcedPlugin classes...
Definition: wrappers.py:354