Coverage for python/lsst/meas/base/wrappers.py: 62%

157 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-15 08:47 +0000

1# This file is part of meas_base. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

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 GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22import lsst.pex.config 

23from .pluginsBase import BasePlugin 

24from .pluginRegistry import generateAlgorithmName, register 

25from .apCorrRegistry import addApCorrName 

26from .sfm import SingleFramePlugin, SingleFramePluginConfig 

27from .forcedMeasurement import ForcedPlugin, ForcedPluginConfig 

28 

29__all__ = ("wrapSingleFrameAlgorithm", "wrapForcedAlgorithm", "wrapSimpleAlgorithm", 

30 "wrapAlgorithm", "wrapAlgorithmControl", "wrapTransform", "GenericPlugin") 

31 

32 

33class WrappedSingleFramePlugin(SingleFramePlugin): 

34 

35 def __init__(self, config, name, schema, metadata, logName=None): 

36 SingleFramePlugin.__init__(self, config, name, schema, metadata, logName=logName) 

37 if hasattr(self, "hasLogName") and self.hasLogName and logName is not None: 

38 self.cpp = self.factory(config, name, schema, metadata, logName=logName) 

39 else: 

40 self.cpp = self.factory(config, name, schema, metadata) 

41 

42 def measure(self, measRecord, exposure): 

43 self.cpp.measure(measRecord, exposure) 

44 

45 def measureN(self, measCat, exposure): 

46 self.cpp.measureN(measCat, exposure) 

47 

48 def fail(self, measRecord, error=None): 

49 self.cpp.fail(measRecord, error.cpp if error is not None else None) 

50 

51 

52class WrappedForcedPlugin(ForcedPlugin): 

53 

54 def __init__(self, config, name, schemaMapper, metadata, logName=None): 

55 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata, logName=logName) 

56 if hasattr(self, "hasLogName") and self.hasLogName and logName is not None: 

57 self.cpp = self.factory(config, name, schemaMapper, metadata, logName=logName) 

58 else: 

59 self.cpp = self.factory(config, name, schemaMapper, metadata) 

60 

61 def measure(self, measRecord, exposure, refRecord, refWcs): 

62 self.cpp.measureForced(measRecord, exposure, refRecord, refWcs) 

63 

64 def measureN(self, measCat, exposure, refCat, refWcs): 

65 self.cpp.measureNForced(measCat, exposure, refCat, refWcs) 

66 

67 def fail(self, measRecord, error=None): 

68 self.cpp.fail(measRecord, error.cpp if error is not None else None) 

69 

70 

71def wrapAlgorithmControl(Base, Control, module=None, hasMeasureN=False): 

72 """Wrap a C++ algorithm's control class into a Python config class. 

73 

74 Parameters 

75 ---------- 

76 Base : `SingleFramePluginConfig` or `ForcedPluginConfig` 

77 Base class for the returned config. 

78 Control : pybind11-wrapped version of a C++ class. 

79 Control class to be wrapped. 

80 module : module, `str`, `int`, or `None`; optional 

81 Either a module object, a string specifying the name of the module, or 

82 an integer specifying how far back in the stack to look for the module 

83 to use: ``0`` is `lsst.pex.config.wrap`, ``1`` is 

84 `lsst.meas.base.wrappers`, ``2`` is the immediate caller, etc. This 

85 will be used to set ``__module__`` for the new config class, and the 

86 class will also be added to the module. The default is none in which 

87 case module will be looked up from Control. 

88 hasMeasureN : `bool`, optional 

89 Whether the plugin supports fitting multiple objects at once (if so, a 

90 config option to enable/disable this will be added). 

91 

92 Returns 

93 ------- 

94 ConfigClass : `lsst.pex.config.Config` 

95 A new subclass of lsst.pex.config.Config. 

96 

97 Notes 

98 ----- 

99 This function is generally only called by `wrapAlgorithm`; it is unlikely 

100 users will have to call it directly. 

101 """ 

102 if hasMeasureN: 102 ↛ 108line 102 didn't jump to line 108, because the condition on line 102 was never true

103 # We need to add a Config field to enable multi-object measurement, to 

104 # replace the simple bool class attribute that's on the base class. 

105 # To do that, we create the Config class dynamically here, then call 

106 # makeControlClass to finish it off by adding fields from the control 

107 # object. 

108 cls = type( 

109 Control.__name__.replace("Control", "Config"), 

110 (Base,), 

111 {"doMeasureN": lsst.pex.config.Field(dtype=bool, default=True, 

112 doc="whether to run this plugin in multi-object mode")} 

113 ) 

114 ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, cls=cls) 

115 else: 

116 # If we don't have to add that Config field, we can delegate all of 

117 # the work to pex_config's makeControlClass 

118 ConfigClass = lsst.pex.config.makeConfigClass(Control, module=module, base=Base) 

119 return ConfigClass 

120 

121 

122def wrapAlgorithm(Base, AlgClass, factory, executionOrder, name=None, Control=None, 

123 ConfigClass=None, TransformClass=None, doRegister=True, shouldApCorr=False, 

124 apCorrList=(), hasLogName=False, **kwds): 

125 """Wrap a C++ algorithm class to create a measurement plugin. 

126 

127 Parameters 

128 ---------- 

129 Base : `SingleFramePlugin` or `ForcedPlugin` 

130 Base class for the returned Plugin. 

131 AlgClass : API compatible with `SingleFrameAlgorithm` or `ForcedAlgorithm` 

132 C++ algorithm class to convert. May either derive directly from 

133 `SingleFrameAlgorithm` or `ForcedAlgorithm`, or be an unrelated class 

134 which has the same ``measure`` and ``measureN`` signatures. 

135 factory : callable 

136 A callable that is used to construct an instance of ``AlgClass``. It 

137 must take four arguments, either ``(config, name, schema, metadata)`` 

138 or ``(config, name, schemaMapper, metadata)``, depending on whether 

139 the algorithm is single-frame or forced. 

140 executionOrder : `float` 

141 The order this plugin should be run, relative to others 

142 (see `BasePlugin.getExecutionOrder`). 

143 name : `str`, optional 

144 String to use when registering the algorithm. Ignored if 

145 ``doRegistry=False``, set to ``generateAlgorithmName(AlgClass)`` if 

146 `None`. 

147 Control : Pybind11-wrapped version of a C++ class, optional 

148 Pybind11-wrapped C++ Control class for the algorithm; 

149 ``AlgClass.Control`` is used if `None`. Ignored if ``ConfigClass`` 

150 is not `None`. 

151 ConfigClass : subclass of `BaseMeasurementPluginConfig` 

152 Python config class that wraps the C++ algorithm's pybind11-wrapped 

153 Control class. If `None`, `wrapAlgorithmControl` is called to 

154 generate a Config class using the ``Control`` argument. 

155 TransformClass : subclass of `MeasurementTransform`, optional 

156 Transformation which may be used to post-process the results of 

157 measurement. If `None`, the default defined by `BasePlugin` is 

158 used. 

159 doRegister : `bool`, optional 

160 If `True` (the default), register the plugin with ``Base``'s 

161 registry, allowing it to be used by measurement tasks. 

162 shouldApCorr : `bool`, optional 

163 Does this algorithm measure an instFlux that can be aperture 

164 corrected? This is shorthand for ``apCorrList=[name]`` and is ignored 

165 if ``apCorrList`` is specified. 

166 apCorrList : iterable of `str`, optional 

167 Field name prefixes for instFlux fields to be aperture corrected. If 

168 an algorithm measures a single instFlux that should be aperture 

169 corrected, then it is simpler to set ``shouldApCorr=True``. However, 

170 if an algorithm produces multiple such fields, then specify 

171 ``apCorrList`` instead. For example, ``modelfit_CModel`` produces 

172 three such fields: ``apCorrList= ("modelfit_CModel_exp", 

173 "modelfit_CModel_exp", "modelfit_CModel_def")`` If ``apCorrList`` is 

174 not empty then ``shouldApCorr`` is ignored. If non-empty and 

175 ``doRegister`` is `True` then the names are added to the set 

176 retrieved by ``getApCorrNameSet``. 

177 hasLogName : `bool`, optional 

178 `True` if the C++ algorithm supports ``logName`` as a constructor 

179 argument. 

180 **kwds 

181 Additional keyword arguments passed to generateAlgorithmControl, which 

182 may include: 

183 

184 - ``hasMeasureN``: Whether the plugin supports fitting multiple 

185 objects at once ;if so, a config option to enable/disable this will 

186 be added (`bool`). 

187 - ``executionOrder``: If not `None`, an override for the default 

188 execution order for this plugin (the default is ``2.0``, which is 

189 usually appropriate for fluxes; `bool`). 

190 

191 Returns 

192 ------- 

193 PluginClass : subclass of ``Base`` 

194 The new plugin class. 

195 """ 

196 if ConfigClass is None: 196 ↛ 201line 196 didn't jump to line 201, because the condition on line 196 was never false

197 if Control is None: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true

198 Control = AlgClass.Control 

199 ConfigClass = wrapAlgorithmControl(Base.ConfigClass, Control, **kwds) 

200 

201 def getExecutionOrder(): 

202 return executionOrder 

203 typeDict = dict(AlgClass=AlgClass, ConfigClass=ConfigClass, factory=staticmethod(factory), 

204 getExecutionOrder=staticmethod(getExecutionOrder)) 

205 if TransformClass: 

206 typeDict['getTransformClass'] = staticmethod(lambda: TransformClass) 206 ↛ exitline 206 didn't run the lambda on line 206

207 PluginClass = type(AlgClass.__name__ + Base.__name__, (Base,), typeDict) 

208 if doRegister: 208 ↛ 214line 208 didn't jump to line 214, because the condition on line 208 was never false

209 if name is None: 

210 name = generateAlgorithmName(AlgClass) 

211 Base.registry.register(name, PluginClass) 

212 if shouldApCorr: 

213 addApCorrName(name) 

214 PluginClass.hasLogName = hasLogName 

215 return PluginClass 

216 

217 

218def wrapSingleFrameAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, 

219 hasLogName=False, **kwds): 

220 """Expose a C++ ``SingleFrameAlgorithm`` class as a measurement plugin. 

221 

222 Parameters 

223 ---------- 

224 AlgClass : API compatible with `SingleFrameAlgorithm` 

225 C++ algorithm class to convert. May either derive directly from 

226 `SingleFrameAlgorithm` or be an unrelated class which has the same 

227 ``measure``, ``measureN`` and ``fail`` signatures. 

228 executionOrder : `float` 

229 The order this plugin should be run, relative to others 

230 (see `BasePlugin.getExecutionOrder`). 

231 name : `str`, optional 

232 Name to use when registering the algorithm. Ignored if 

233 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if 

234 `None`. 

235 needsMetadata : `bool`, optional 

236 Sets whether the ``AlgClass``'s constructor should be passed a 

237 `~lsst.daf.base.PropertySet` metadata argument. 

238 hasMeasureN : `bool`, optional 

239 Does the algorithm support simultaneous measurement of multiple 

240 sources? If `True`, a `bool` ``doMeasureN`` field will be added to 

241 the generated config class, and its value will be passed as the last 

242 argument when calling the ``AlgClass`` constructor. 

243 hasLogName : `bool`, optional 

244 `True` if the C++ algorithm supports ``logName`` as a constructor 

245 argument. 

246 **kwds 

247 Additional keyword arguments are passed to the lower-level 

248 `wrapAlgorithm` and `wrapAlgorithmControl` classes. 

249 

250 Returns 

251 ------- 

252 singleFramePlugin : subclass of `SingleFramePlugin` 

253 The new measurement plugin class. 

254 

255 Notes 

256 ----- 

257 The first three arguments to the C++ constructor are expected to be 

258 ``Control const & ctrl, std::string const & name, Schema & schema``. 

259 

260 If ``needsMetadata`` is `True`, we also append ``PropertySet & metadata``. 

261 

262 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``. 

263 

264 If ``hasLogName`` is `True`, we also append ``std::string logName``. 

265 

266 If more than one of the above is `True`, the metadata ``PropertySet`` 

267 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the 

268 three. 

269 """ 

270 if hasMeasureN: 270 ↛ 271line 270 didn't jump to line 271, because the condition on line 270 was never true

271 if needsMetadata: 

272 def factory(config, name, schema, metadata, **kwargs): 

273 return AlgClass(config.makeControl(), name, schema, metadata, config.doMeasureN, **kwargs) 

274 else: 

275 def factory(config, name, schema, metadata, **kwargs): 

276 return AlgClass(config.makeControl(), name, schema, config.doMeasureN, **kwargs) 

277 else: 

278 if needsMetadata: 

279 def factory(config, name, schema, metadata, **kwargs): 

280 return AlgClass(config.makeControl(), name, schema, metadata, **kwargs) 

281 else: 

282 def factory(config, name, schema, metadata, **kwargs): 

283 return AlgClass(config.makeControl(), name, schema, **kwargs) 

284 

285 return wrapAlgorithm(WrappedSingleFramePlugin, AlgClass, executionOrder=executionOrder, name=name, 

286 factory=factory, hasMeasureN=hasMeasureN, hasLogName=hasLogName, **kwds) 

287 

288 

289def wrapForcedAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, 

290 hasMeasureN=False, needsSchemaOnly=False, hasLogName=False, **kwds): 

291 """Expose a C++ ``ForcedAlgorithm`` class as a measurement plugin. 

292 

293 Parameters 

294 ---------- 

295 AlgClass : API compatible with `ForcedAlgorithm` 

296 C++ algorithm class to convert. May either derive directly from 

297 `ForcedAlgorithm` or be an unrelated class which has the same 

298 ``measure``, ``measureN`` and ``fail`` signatures. 

299 executionOrder : `float` 

300 The order this plugin should be run, relative to others 

301 (see `BasePlugin.getExecutionOrder`). 

302 name : `str`, optional 

303 Name to use when registering the algorithm. Ignored if 

304 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if 

305 `None`. 

306 needsMetadata : `bool`, optional 

307 Sets whether the ``AlgClass``'s constructor should be passed a 

308 `~lsst.daf.base.PropertySet` metadata argument. 

309 hasMeasureN : `bool`, optional 

310 Does the algorithm support simultaneous measurement of multiple 

311 sources? If `True`, a `bool` ``doMeasureN`` field will be added to 

312 the generated config class, and its value will be passed as the last 

313 argument when calling the ``AlgClass`` constructor. 

314 hasLogName : `bool`, optional 

315 `True` if the C++ algorithm supports ``logName`` as a constructor 

316 argument. 

317 needsSchemaOnly : `bool`, optional 

318 Whether the algorithm constructor expects a Schema argument 

319 (representing the output `~lsst.afw.table.Schema`) rather than the 

320 full `~lsst.afw.table.SchemaMapper` (which provides access to both the 

321 reference schema and the output schema). 

322 **kwds 

323 Additional keyword arguments are passed to the lower-level 

324 `wrapAlgorithm` and `wrapAlgorithmControl` classes. 

325 

326 Returns 

327 ------- 

328 forcedPlugin : subclass of `ForcedPlugin` 

329 The new measurement plugin class. 

330 

331 Notes 

332 ----- 

333 The first two arguments to the C++ constructor are expected to be 

334 ``Control const & ctrl, std::string const & name`` 

335 

336 If ``needsSchemaOnly`` is `True`, then the third argument will be 

337 ``Schema & schema``; otherwise, it will be ``SchemaMapper & 

338 schemaMapper``. 

339 

340 If ``needsMetadata`` is `True`, we also append ``PropertySet & 

341 metadata``. 

342 

343 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``. 

344 

345 If ``hasLogName`` is `True`, we also append ``std::string logName``. 

346 

347 If more than one of the above is `True`, the metadata ``PropertySet`` 

348 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the 

349 three. 

350 """ 

351 if needsSchemaOnly: 351 ↛ 355line 351 didn't jump to line 355, because the condition on line 351 was never false

352 def extractSchemaArg(m): 

353 return m.editOutputSchema() 

354 else: 

355 def extractSchemaArg(m): 

356 return m 

357 if hasMeasureN: 357 ↛ 358line 357 didn't jump to line 358, because the condition on line 357 was never true

358 if needsMetadata: 

359 def factory(config, name, schemaMapper, metadata, **kwargs): 

360 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), 

361 metadata, config.doMeasureN, **kwargs) 

362 else: 

363 def factory(config, name, schemaMapper, metadata, **kwargs): 

364 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), 

365 config.doMeasureN, **kwargs) 

366 else: 

367 if needsMetadata: 

368 def factory(config, name, schemaMapper, metadata, **kwargs): 

369 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), 

370 metadata, **kwargs) 

371 else: 

372 def factory(config, name, schemaMapper, metadata, **kwargs): 

373 return AlgClass(config.makeControl(), name, extractSchemaArg(schemaMapper), **kwargs) 

374 

375 return wrapAlgorithm(WrappedForcedPlugin, AlgClass, executionOrder=executionOrder, name=name, 

376 factory=factory, hasLogName=hasLogName, **kwds) 

377 

378 

379def wrapSimpleAlgorithm(AlgClass, executionOrder, name=None, needsMetadata=False, hasMeasureN=False, 

380 hasLogName=False, **kwds): 

381 r"""Expose a C++ ``SimpleAlgorithm`` class as a measurement plugin. 

382 

383 ``SimpleAlgorithm``\ s are made available as both `SingleFramePlugin`\ s 

384 and `ForcedPlugin`\ s. 

385 

386 Parameters 

387 ---------- 

388 AlgClass : Subclass of C++ ``SimpleAlgorithm``, or API compatible 

389 Algorithm class to convert. The C++ class should be wrapped with 

390 Pybind11, and must provide ``measure()``, ``measureN()`` and ``fail()` 

391 signatures equivalent to ``SimpleAlgorithm``. 

392 executionOrder : `float` 

393 The order this plugin should be run, relative to others 

394 (see `~BasePlugin.getExecutionOrder`). 

395 name : `str`, optional 

396 Name to use when registering the algorithm. Ignored if 

397 ``doRegistry=False``; set to ``generateAlgorithmName(AlgClass)`` if 

398 `None`. 

399 needsMetadata : `bool`, optional 

400 Sets whether the ``AlgClass``'s constructor should be passed a 

401 `~lsst.daf.base.PropertySet` metadata argument. 

402 hasMeasureN : `bool`, optional 

403 Does the algorithm support simultaneous measurement of multiple 

404 sources? If `True`, a `bool` ``doMeasureN`` field will be added to 

405 the generated config class, and its value will be passed as the last 

406 argument when calling the ``AlgClass`` constructor. 

407 hasLogName : `bool`, optional 

408 `True` if the C++ algorithm supports ``logName`` as a constructor 

409 argument. 

410 **kwds 

411 Additional keyword arguments are passed to the lower-level 

412 `wrapAlgorithm` and `wrapAlgorithmControl` classes. 

413 

414 Returns 

415 ------- 

416 singleFramePlugin : subclass of `SingleFramePlugin` 

417 The new single frame measurement plugin class. 

418 forcedPlugin : subclass of `ForcedPlugin` 

419 The new forced measurement plugin class. 

420 

421 Notes 

422 ----- 

423 The first three arguments to the C++ constructor are expected to be 

424 ``Control const & ctrl, std::string const & name, Schema & schema``. 

425 

426 If ``needsMetadata`` is `True`, we also append ``PropertySet & 

427 metadata``. 

428 

429 If ``hasMeasureN`` is `True`, we also append ``bool doMeasureN``. 

430 

431 If ``hasLogName`` is `True`, we also append ``std::string logName``. 

432 

433 If more than one of the above is `True`, the metadata ``PropertySet`` 

434 precedes the ``doMeasureN`` ``bool`` and the ``logName`` comes last of the 

435 three. 

436 """ 

437 return (wrapSingleFrameAlgorithm(AlgClass, executionOrder=executionOrder, name=name, 

438 needsMetadata=needsMetadata, hasLogName=hasLogName, **kwds), 

439 wrapForcedAlgorithm(AlgClass, executionOrder=executionOrder, name=name, 

440 needsMetadata=needsMetadata, hasLogName=hasLogName, 

441 needsSchemaOnly=True, **kwds)) 

442 

443 

444def wrapTransform(transformClass, hasLogName=False): 

445 """Modify a C++ transform to accept either a ``Config`` or a ``Control``. 

446 

447 That is, the configuration may either be provided as a (C++) ``Control`` 

448 object or an instance of a Python class derived from 

449 `~lsst.meas.base.BasePluginConfig`. 

450 

451 Parameters 

452 ---------- 

453 transformClass : Subclass of C++ ``BaseTransform`` 

454 A C++ transform class, wrapped with pybind11. Its constructor must 

455 take a ``Control`` object, a ``std::string``, and a 

456 `~lsst.afw.table.SchemaMapper`, in that order. 

457 hasLogName : `bool`, optional 

458 Unused. 

459 """ 

460 oldInit = transformClass.__init__ 

461 

462 def _init(self, ctrl, name, mapper, logName=None): 

463 if hasattr(ctrl, "makeControl"): 

464 ctrl = ctrl.makeControl() 

465 # logName signature needs to be on this Class __init__, but is not 

466 # needed by the C++ plugin. 

467 oldInit(self, ctrl, name, mapper) 

468 

469 transformClass.__init__ = _init 

470 

471 

472class GenericPlugin(BasePlugin): 

473 """Abstract base class for a generic plugin. 

474 

475 Parameters 

476 ---------- 

477 config : `lsst.pex.config.Config` 

478 An instance of this class' ``ConfigClass``. 

479 name : `str` 

480 Name of this measurement plguin, for registering. 

481 schema : `lsst.afw.table.Schema` 

482 The catalog schema. New fields should be added here to 

483 hold measurements produced by this plugin. 

484 metadata : `lsst.daf.base.PropertySet` 

485 Metadata that will be attached to the output catalog. 

486 logName : `str`, optional 

487 Name of log component. 

488 

489 Notes 

490 ----- 

491 A generic plugin can be used with the `singleFramePluginFromGeneric` 

492 and/or `forcedPluginFromGeneric` wrappers to create classes that can be 

493 used for single frame measurement and/or forced measurement (as 

494 appropriate). The only real difference between `SingleFramePlugin` and 

495 `ForcedPlugin` is the ``measure`` method; this class introduces a shared 

496 signature for `measure` that, in combination with the aforementioned 

497 wrappers, allows both plugin styles to share a single implementation. 

498 

499 This doesn't use `abc.ABCMeta` because I couldn't get it to work 

500 with a superclass. 

501 

502 Sub-classes should set `ConfigClass` and implement the `measure` and 

503 `measureN` methods. They may optionally provide alternative 

504 implementations for the `__init__`, `fail` and `getExecutionOrder` 

505 methods. 

506 

507 This default implementation simply adds a field for recording 

508 a fatal failure of the measurement plugin. 

509 """ 

510 ConfigClass = None 

511 

512 @classmethod 

513 def getExecutionOrder(cls): 

514 return 0 

515 

516 def __init__(self, config, name, schema, metadata, logName=None): 

517 BasePlugin.__init__(self, config, name, logName=logName) 

518 self._failKey = schema.addField(name + '_flag', type="Flag", doc="Set for any fatal failure") 

519 

520 def measure(self, measRecord, exposure, center): 

521 """Measure a single source. 

522 

523 It is the responsibility of this method to perform the desired 

524 measurement and record the result in the `measRecord`. 

525 

526 Parameters 

527 ---------- 

528 measRecord : `lsst.afw.table.SourceRecord` 

529 Catalog record for the source being measured. 

530 exposure : `lsst.afw.image.Exposure` 

531 Exposure on which the source is being measured. 

532 center : `lsst.geom.Point2D` 

533 Pixel coordinates of the object. 

534 

535 Raises 

536 ------ 

537 MeasurementError 

538 Raised if the measurement fails for a known/justifiable reason. 

539 """ 

540 raise NotImplementedError() 

541 

542 def measureN(self, measCat, exposure, refCat, refWcs): 

543 """Measure multiple sources. 

544 

545 It is the responsibility of this method to perform the desired 

546 measurement and record the result in the `measCat`. 

547 

548 Parameters 

549 ---------- 

550 measCat : `lsst.afw.table.SourceCatalog` 

551 Catalog for the sources being measured. 

552 exposure : `lsst.afw.image.Exposure` 

553 Exposure on which the source is being measured. 

554 refCat : `lsst.afw.table.SourceCatalog` 

555 Reference catalog. 

556 refWcs : `lsst.afw.image.Wcs` 

557 Astrometric solution for the reference image. 

558 

559 Raises 

560 ------ 

561 MeasurementError 

562 Raised if the measurement fails for a known/justifiable reason. 

563 """ 

564 raise NotImplementedError() 

565 

566 def fail(self, measRecord, error=None): 

567 """Record a measurement failure. 

568 

569 This default implementation simply records the failure in the source 

570 record. 

571 

572 Parameters 

573 ---------- 

574 measRecord : `lsst.afw.table.SourceRecord` 

575 Catalog record for the source being measured. 

576 error : `Exception` 

577 Error causing failure, or `None`. 

578 """ 

579 measRecord.set(self._failKey, True) 

580 

581 @classmethod 

582 def makeSingleFramePlugin(cls, name): 

583 """Produce a SingleFramePlugin subclass from this GenericPlugin class. 

584 

585 The class is also registered. 

586 

587 Parameters 

588 ---------- 

589 name : `str` 

590 Name of plugin to register. 

591 """ 

592 class SingleFrameFromGenericConfig(cls.ConfigClass, SingleFramePluginConfig): 

593 pass 

594 

595 @register(name) 

596 class SingleFrameFromGenericPlugin(SingleFramePlugin): 

597 ConfigClass = SingleFrameFromGenericConfig 

598 

599 def __init__(self, config, name, schema, metadata, logName=None): 

600 SingleFramePlugin.__init__(self, config, name, schema, metadata, logName=logName) 

601 self._generic = cls(config, name, schema, metadata) 

602 

603 def measure(self, measRecord, exposure): 

604 center = measRecord.getCentroid() 

605 return self._generic.measure(measRecord, exposure, center) 

606 

607 def measureN(self, measCat, exposure, refCat, refWcs): 

608 return self._generic.measureN(measCat, exposure, refCat, refWcs) 

609 

610 def fail(self, measRecord, error=None): 

611 self._generic.fail(measRecord, error if error is not None else None) 

612 

613 @staticmethod 

614 def getExecutionOrder(): 

615 return cls.getExecutionOrder() 

616 

617 def getTransformClass(self): 

618 return self._generic.getTransformClass() 

619 

620 return SingleFrameFromGenericPlugin 

621 

622 @classmethod 

623 def makeForcedPlugin(cls, name): 

624 """Produce a ForcedPlugin subclass from this GenericPlugin class. 

625 

626 The class is also registered. 

627 

628 Parameters 

629 ---------- 

630 name : `str` 

631 Name of plugin to register. 

632 """ 

633 class ForcedFromGenericConfig(cls.ConfigClass, ForcedPluginConfig): 

634 pass 

635 

636 @register(name) 

637 class ForcedFromGenericPlugin(ForcedPlugin): 

638 ConfigClass = ForcedFromGenericConfig 

639 

640 def __init__(self, config, name, schemaMapper, metadata, logName=None): 

641 ForcedPlugin.__init__(self, config, name, schemaMapper, metadata, logName=logName) 

642 schema = schemaMapper.editOutputSchema() 

643 self._generic = cls(config, name, schema, metadata) 

644 

645 def measure(self, measRecord, exposure, refRecord, refWcs): 

646 # Forced photometry tasks should almost be configured with a 

647 # centroider (populating measRecord.getCentroid()) that 

648 # transforms the reference centroid, but we respect their 

649 # decision if they decided to re-centroid (or do something more 

650 # unusual) on the image being measured. 

651 center = measRecord.getCentroid() 

652 return self._generic.measure(measRecord, exposure, center) 

653 

654 def measureN(self, measCat, exposure, refCat, refWcs): 

655 return self._generic.measureN(measCat, exposure, refCat, refWcs) 

656 

657 def fail(self, measRecord, error=None): 

658 self._generic.fail(measRecord, error if error is not None else None) 

659 

660 @staticmethod 

661 def getExecutionOrder(): 

662 return cls.getExecutionOrder() 

663 

664 def getTransformClass(self): 

665 return self._generic.getTransformClass() 

666 

667 return ForcedFromGenericPlugin