262 def run(self, measCat, exposure, refCat, refWcs, exposureId=None, beginOrder=None, endOrder=None):
263 r"""Perform forced measurement.
267 exposure : `lsst.afw.image.exposureF`
268 Image to be measured. Must have at least a `lsst.afw.geom.SkyWcs`
270 measCat : `lsst.afw.table.SourceCatalog`
271 Source catalog for measurement results; must be initialized with
272 empty records already corresponding to those in ``refCat`` (via
273 e.g. `generateMeasCat`).
274 refCat : `lsst.afw.table.SourceCatalog`
275 A sequence of `lsst.afw.table.SourceRecord` objects that provide
276 reference information for the measurement. These will be passed
277 to each plugin in addition to the output
278 `~lsst.afw.table.SourceRecord`.
279 refWcs : `lsst.afw.geom.SkyWcs`
280 Defines the X,Y coordinate system of ``refCat``.
281 exposureId : `int`, optional
282 Optional unique exposureId used to calculate random number
283 generator seed in the NoiseReplacer.
284 beginOrder : `int`, optional
285 Beginning execution order (inclusive). Algorithms with
286 ``executionOrder`` < ``beginOrder`` are not executed. `None` for no limit.
287 endOrder : `int`, optional
288 Ending execution order (exclusive). Algorithms with
289 ``executionOrder`` >= ``endOrder`` are not executed. `None` for no limit.
293 Fills the initial empty `~lsst.afw.table.SourceCatalog` with forced
294 measurement results. Two steps must occur before `run` can be called:
296 - `generateMeasCat` must be called to create the output ``measCat``
298 - `~lsst.afw.detection.Footprint`\ s appropriate for the forced sources
299 must be attached to the ``measCat`` records. The
300 `attachTransformedFootprints` method can be used to do this, but
301 this degrades "heavy" (i.e., including pixel values)
302 `~lsst.afw.detection.Footprint`\s to regular
303 `~lsst.afw.detection.Footprint`\s, leading to non-deblended
304 measurement, so most callers should provide
305 `~lsst.afw.detection.Footprint`\s some other way. Typically, calling
306 code will have access to information that will allow them to provide
307 heavy footprints - for instance, `ForcedPhotCoaddTask` uses the
308 heavy footprints from deblending run in the same band just before
309 non-forced is run measurement in that band.
321 refCatIdDict = {ref.getId(): ref.getParent()
for ref
in refCat}
326 if topId
not in refCatIdDict:
327 raise RuntimeError(
"Reference catalog contains a child for which at least "
328 "one parent in its parent chain is not in the catalog.")
329 topId = refCatIdDict[topId]
334 footprints = {ref.getId(): (ref.getParent(), measRecord.getFootprint())
335 for (ref, measRecord)
in zip(refCat, measCat)}
337 self.log.info(
"Performing forced measurement on %d source%s", len(refCat),
338 "" if len(refCat) == 1
else "s")
341 periodicLog = PeriodicLogger(self.log)
343 if self.config.doReplaceWithNoise:
344 noiseReplacer =
NoiseReplacer(self.config.noiseReplacer, exposure,
345 footprints, log=self.log, exposureId=exposureId)
346 algMetadata = measCat.getTable().getMetadata()
347 if algMetadata
is not None:
348 algMetadata.addInt(
"NOISE_SEED_MULTIPLIER", self.config.noiseReplacer.noiseSeedMultiplier)
349 algMetadata.addString(
"NOISE_SOURCE", self.config.noiseReplacer.noiseSource)
350 algMetadata.addDouble(
"NOISE_OFFSET", self.config.noiseReplacer.noiseOffset)
351 if exposureId
is not None:
352 algMetadata.addLong(
"NOISE_EXPOSURE_ID", exposureId)
358 refParentCat, measParentCat = refCat.getChildren(0, measCat)
359 childrenIter = refCat.getChildren((refParentRecord.getId()
for refParentRecord
in refCat), measCat)
360 for parentIdx, records
in enumerate(zip(refParentCat, measParentCat, childrenIter)):
362 refParentRecord, measParentRecord, (refChildCat, measChildCat) = records
365 for refChildRecord, measChildRecord
in zip(refChildCat, measChildCat):
366 noiseReplacer.insertSource(refChildRecord.getId())
367 self.
callMeasure(measChildRecord, exposure, refChildRecord, refWcs,
368 beginOrder=beginOrder, endOrder=endOrder)
369 noiseReplacer.removeSource(refChildRecord.getId())
372 noiseReplacer.insertSource(refParentRecord.getId())
373 self.
callMeasure(measParentRecord, exposure, refParentRecord, refWcs,
374 beginOrder=beginOrder, endOrder=endOrder)
375 self.
callMeasureN(measParentCat[parentIdx:parentIdx+1], exposure,
376 refParentCat[parentIdx:parentIdx+1],
377 beginOrder=beginOrder, endOrder=endOrder)
380 beginOrder=beginOrder, endOrder=endOrder)
381 noiseReplacer.removeSource(refParentRecord.getId())
383 periodicLog.log(
"Forced measurement complete for %d parents (and their children) out of %d",
384 parentIdx + 1, len(refParentCat))
389 for recordIndex, (measRecord, refRecord)
in enumerate(zip(measCat, refCat)):
391 self.
doMeasurement(plugin, measRecord, exposure, refRecord, refWcs)
392 periodicLog.log(
"Undeblended forced measurement complete for %d sources out of %d",
393 recordIndex + 1, len(refCat))
459 """Attach Footprints to blank sources prior to measurement, by
460 creating elliptical Footprints from the PSF moments.
464 sources : `lsst.afw.table.SourceCatalog`
465 Blank catalog (with all rows and columns, but values other than
466 ``coord_ra``, ``coord_dec`` unpopulated).
467 to which footprints should be attached.
468 exposure : `lsst.afw.image.Exposure`
469 Image object from which peak values and the PSF are obtained.
470 scaling : `int`, optional
471 Scaling factor to apply to the PSF second-moments ellipse in order
472 to determine the footprint boundary.
476 This is a utility function for use by parent tasks; see
477 `attachTransformedFootprints` for more information.
479 psf = exposure.getPsf()
481 raise RuntimeError(
"Cannot construct Footprints from PSF shape without a PSF.")
482 bbox = exposure.getBBox()
483 wcs = exposure.getWcs()
484 for record
in sources:
485 localPoint = wcs.skyToPixel(record.getCoord())
487 assert bbox.contains(localIntPoint), (
488 f
"Center for record {record.getId()} is not in exposure; this should be guaranteed by "
492 ellipse.getCore().scale(scaling)
495 footprint.addPeak(localIntPoint.getX(), localIntPoint.getY(),
496 exposure.image._get(localIntPoint, lsst.afw.image.PARENT))
497 record.setFootprint(footprint)