30from lsst.skymap
import BaseSkyMap
31from lsst.daf.butler
import DeferredDatasetHandle
33from lsst.meas.algorithms
import CoaddPsf, CoaddPsfConfig
35__all__ = [
"GetCoaddAsTemplateTask",
"GetCoaddAsTemplateConfig",
36 "GetTemplateTask",
"GetTemplateConfig",
37 "GetDcrTemplateTask",
"GetDcrTemplateConfig",
38 "GetMultiTractCoaddTemplateTask",
"GetMultiTractCoaddTemplateConfig"]
42 templateBorderSize = pexConfig.Field(
45 doc=
"Number of pixels to grow the requested template image to account for warping"
47 coaddName = pexConfig.Field(
48 doc=
"coadd name: typically one of 'deep', 'goodSeeing', or 'dcr'",
52 warpType = pexConfig.Field(
53 doc=
"Warp type of the coadd template: one of 'direct' or 'psfMatched'",
60 """Subtask to retrieve coadd for use as an image difference template.
62 This is the default getTemplate Task to be run
as a subtask by
63 ``pipe.tasks.ImageDifferenceTask``.
67 ConfigClass = GetCoaddAsTemplateConfig
68 _DefaultName = "GetCoaddAsTemplateTask"
70 def runQuantum(self, exposure, butlerQC, skyMapRef, coaddExposureRefs):
71 """Gen3 task entry point. Retrieve and mosaic a template coadd exposure
72 that overlaps the science exposure.
77 The science exposure to define the sky region of the template
79 butlerQC : `lsst.pipe.base.ButlerQuantumContext`
80 Butler like object that supports getting data by DatasetRef.
81 skyMapRef : `lsst.daf.butler.DatasetRef`
82 Reference to SkyMap object that corresponds to the template coadd.
83 coaddExposureRefs : iterable of `lsst.daf.butler.DeferredDatasetRef`
84 Iterable of references to the available template coadd patches.
88 result : `lsst.pipe.base.Struct`
89 A struct with attibutes:
92 Template coadd exposure assembled out of patches
93 (`lsst.afw.image.ExposureF`).
95 Always `
None`
for this subtask.
98 self.log.warning("GetCoaddAsTemplateTask is deprecated. Use GetTemplateTask instead.")
99 skyMap = butlerQC.get(skyMapRef)
100 coaddExposureRefs = butlerQC.get(coaddExposureRefs)
101 tracts = [ref.dataId[
'tract']
for ref
in coaddExposureRefs]
102 if tracts.count(tracts[0]) == len(tracts):
103 tractInfo = skyMap[tracts[0]]
105 raise RuntimeError(
"Templates constructed from multiple Tracts not supported by this task. "
106 "Use GetTemplateTask instead.")
108 detectorWcs = exposure.getWcs()
109 if detectorWcs
is None:
110 templateExposure =
None
112 self.log.info(
"Exposure has no WCS, so cannot create associated template.")
114 detectorBBox = exposure.getBBox()
115 detectorCorners = detectorWcs.pixelToSky(
geom.Box2D(detectorBBox).getCorners())
116 validPolygon = exposure.getInfo().getValidPolygon()
117 detectorPolygon = validPolygon
if validPolygon
else geom.Box2D(detectorBBox)
119 availableCoaddRefs = dict()
121 for coaddRef
in coaddExposureRefs:
122 dataId = coaddRef.dataId
123 patchWcs = skyMap[dataId[
'tract']].getWcs()
124 patchBBox = skyMap[dataId[
'tract']][dataId[
'patch']].getOuterBBox()
125 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
126 patchPolygon = afwGeom.Polygon(detectorWcs.skyToPixel(patchCorners))
127 if patchPolygon.intersection(detectorPolygon):
128 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
129 if self.config.coaddName ==
'dcr':
130 self.log.info(
"Using template input tract=%s, patch=%s, subfilter=%s",
131 dataId[
'tract'], dataId[
'patch'], dataId[
'subfilter'])
132 if dataId[
'patch']
in availableCoaddRefs:
133 availableCoaddRefs[dataId[
'patch']].append(coaddRef)
135 availableCoaddRefs[dataId[
'patch']] = [coaddRef, ]
137 self.log.info(
"Using template input tract=%s, patch=%s",
138 dataId[
'tract'], dataId[
'patch'])
139 availableCoaddRefs[dataId[
'patch']] = coaddRef
141 if overlappingArea == 0:
142 templateExposure =
None
144 self.log.warning(
"No overlapping template patches found")
146 patchList = [tractInfo[patch]
for patch
in availableCoaddRefs.keys()]
147 templateExposure = self.
run(tractInfo, patchList, detectorCorners, availableCoaddRefs,
148 visitInfo=exposure.getInfo().getVisitInfo())
152 pixNoData = np.count_nonzero(templateExposure.mask.array
153 & templateExposure.mask.getPlaneBitMask(
'NO_DATA'))
154 pixGood = templateExposure.getBBox().getArea() - pixNoData
155 self.log.info(
"template has %d good pixels (%.1f%%)", pixGood,
156 100*pixGood/templateExposure.getBBox().getArea())
157 return pipeBase.Struct(exposure=templateExposure, sources=
None, area=pixGood)
160 """Select the relevant tract and its patches that overlap with the
166 The science exposure to define the sky region of the template
169 skyMap : `lsst.skymap.BaseSkyMap`
170 SkyMap object that corresponds to the template coadd.
175 - ``tractInfo`` : `lsst.skymap.TractInfo`
177 - ``patchList`` : `list` [`lsst.skymap.PatchInfo`]
178 List of all overlap patches of the selected tract.
180 Corners of the exposure in the sky
in the order given by
181 `lsst.geom.Box2D.getCorners`.
183 expWcs = exposure.getWcs()
185 expBoxD.grow(self.config.templateBorderSize)
186 ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
187 tractInfo = skyMap.findTract(ctrSkyPos)
188 self.log.info("Using skyMap tract %s", tractInfo.getId())
189 skyCorners = [expWcs.pixelToSky(pixPos)
for pixPos
in expBoxD.getCorners()]
190 patchList = tractInfo.findPatchList(skyCorners)
193 raise RuntimeError(
"No suitable tract found")
195 self.log.info(
"Assembling %d coadd patches", len(patchList))
196 self.log.info(
"exposure dimensions=%s", exposure.getDimensions())
198 return (tractInfo, patchList, skyCorners)
200 def run(self, tractInfo, patchList, skyCorners, availableCoaddRefs,
201 sensorRef=None, visitInfo=None):
202 """Determination of exposure dimensions and copying of pixels from
203 overlapping patch regions.
207 skyMap : `lsst.skymap.BaseSkyMap`
208 SkyMap object that corresponds to the template coadd.
209 tractInfo : `lsst.skymap.TractInfo`
211 patchList : iterable of `lsst.skymap.patchInfo.PatchInfo`
212 Patches to consider for making the template exposure.
214 Sky corner coordinates to be covered by the template exposure.
215 availableCoaddRefs : `dict` [`int`]
216 Dictionary of spatially relevant retrieved coadd patches,
217 indexed by their sequential patch number. Values are
218 `lsst.daf.butler.DeferredDatasetHandle`
and ``.get()``
is called.
220 Must always be `
None`. Gen2 parameters are no longer used.
222 VisitInfo to make dcr model.
226 templateExposure : `lsst.afw.image.ExposureF`
227 The created template exposure.
229 if sensorRef
is not None:
230 raise ValueError(
"sensorRef parameter is a Gen2 parameter that is no longer usable."
231 " Please move to Gen3 middleware.")
232 coaddWcs = tractInfo.getWcs()
236 for skyPos
in skyCorners:
237 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
239 self.log.info(
"coadd dimensions=%s", coaddBBox.getDimensions())
241 coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
242 coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
244 coaddFilterLabel =
None
246 coaddPhotoCalib =
None
247 for patchInfo
in patchList:
248 patchNumber = tractInfo.getSequentialPatchIndex(patchInfo)
249 patchSubBBox = patchInfo.getOuterBBox()
250 patchSubBBox.clip(coaddBBox)
251 if patchNumber
not in availableCoaddRefs:
252 self.log.warning(
"skip patch=%d; patch does not exist for this coadd", patchNumber)
254 if patchSubBBox.isEmpty():
255 if isinstance(availableCoaddRefs[patchNumber], DeferredDatasetHandle):
256 tract = availableCoaddRefs[patchNumber].dataId[
'tract']
258 tract = availableCoaddRefs[patchNumber][
'tract']
259 self.log.info(
"skip tract=%d patch=%d; no overlapping pixels", tract, patchNumber)
262 if self.config.coaddName ==
'dcr':
263 patchInnerBBox = patchInfo.getInnerBBox()
264 patchInnerBBox.clip(coaddBBox)
265 if np.min(patchInnerBBox.getDimensions()) <= 2*self.config.templateBorderSize:
266 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; too few pixels.",
267 availableCoaddRefs[patchNumber])
269 self.log.info(
"Constructing DCR-matched template for patch %s",
270 availableCoaddRefs[patchNumber])
272 dcrModel = DcrModel.fromQuantum(availableCoaddRefs[patchNumber],
273 self.config.effectiveWavelength,
274 self.config.bandwidth)
282 dcrBBox.grow(-self.config.templateBorderSize)
283 dcrBBox.include(patchInnerBBox)
284 coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
287 coaddPatch = availableCoaddRefs[patchNumber].get()
293 overlapBox = coaddPatch.getBBox()
294 overlapBox.clip(coaddBBox)
295 coaddExposure.maskedImage.assign(coaddPatch.maskedImage[overlapBox], overlapBox)
297 if coaddFilterLabel
is None:
298 coaddFilterLabel = coaddPatch.getFilter()
301 if coaddPsf
is None and coaddPatch.hasPsf():
302 coaddPsf = coaddPatch.getPsf()
306 if coaddPhotoCalib
is None:
307 coaddPhotoCalib = coaddPatch.getPhotoCalib()
309 if coaddPhotoCalib
is None:
310 raise RuntimeError(
"No coadd PhotoCalib found!")
311 if nPatchesFound == 0:
312 raise RuntimeError(
"No patches found!")
314 raise RuntimeError(
"No coadd Psf found!")
316 coaddExposure.setPhotoCalib(coaddPhotoCalib)
317 coaddExposure.setPsf(coaddPsf)
318 coaddExposure.setFilter(coaddFilterLabel)
322 """Return coadd name for given task config
326 CoaddDatasetName : `string`
328 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)
330 warpType = self.config.warpType
331 suffix = "" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
332 return self.config.coaddName +
"Coadd" + suffix
336 dimensions=(
"instrument",
"visit",
"detector",
"skymap"),
337 defaultTemplates={
"coaddName":
"goodSeeing",
338 "warpTypeSuffix":
"",
340 bbox = pipeBase.connectionTypes.Input(
341 doc=
"BBoxes of calexp used determine geometry of output template",
342 name=
"{fakesType}calexp.bbox",
343 storageClass=
"Box2I",
344 dimensions=(
"instrument",
"visit",
"detector"),
346 wcs = pipeBase.connectionTypes.Input(
347 doc=
"WCS of the calexp that we want to fetch the template for",
348 name=
"{fakesType}calexp.wcs",
350 dimensions=(
"instrument",
"visit",
"detector"),
352 skyMap = pipeBase.connectionTypes.Input(
353 doc=
"Input definition of geometry/bbox and projection/wcs for template exposures",
354 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
355 dimensions=(
"skymap", ),
356 storageClass=
"SkyMap",
360 coaddExposures = pipeBase.connectionTypes.Input(
361 doc=
"Input template to match and subtract from the exposure",
362 dimensions=(
"tract",
"patch",
"skymap",
"band"),
363 storageClass=
"ExposureF",
364 name=
"{fakesType}{coaddName}Coadd{warpTypeSuffix}",
368 template = pipeBase.connectionTypes.Output(
369 doc=
"Warped template used to create `subtractedExposure`.",
370 dimensions=(
"instrument",
"visit",
"detector"),
371 storageClass=
"ExposureF",
372 name=
"{fakesType}{coaddName}Diff_templateExp{warpTypeSuffix}",
376class GetTemplateConfig(pipeBase.PipelineTaskConfig,
377 pipelineConnections=GetTemplateConnections):
378 templateBorderSize = pexConfig.Field(
381 doc=
"Number of pixels to grow the requested template image to account for warping"
383 warp = pexConfig.ConfigField(
384 dtype=afwMath.Warper.ConfigClass,
385 doc=
"warper configuration",
387 coaddPsf = pexConfig.ConfigField(
388 doc=
"Configuration for CoaddPsf",
389 dtype=CoaddPsfConfig,
392 def setDefaults(self):
393 self.warp.warpingKernelName =
'lanczos5'
394 self.coaddPsf.warpingKernelName =
'lanczos5'
397class GetTemplateTask(pipeBase.PipelineTask):
398 ConfigClass = GetTemplateConfig
399 _DefaultName =
"getTemplate"
401 def __init__(self, *args, **kwargs):
402 super().__init__(*args, **kwargs)
403 self.warper = afwMath.Warper.fromConfig(self.config.warp)
405 def runQuantum(self, butlerQC, inputRefs, outputRefs):
407 inputs = butlerQC.get(inputRefs)
408 results = self.getOverlappingExposures(inputs)
409 inputs[
"coaddExposures"] = results.coaddExposures
410 inputs[
"dataIds"] = results.dataIds
411 outputs = self.run(**inputs)
412 butlerQC.put(outputs, outputRefs)
414 def getOverlappingExposures(self, inputs):
415 """Return lists of coadds and their corresponding dataIds that overlap
418 The spatial index in the registry has generous padding
and often
419 supplies patches near, but
not directly overlapping the detector.
420 Filters inputs so that we don
't have to read in all input coadds.
424 inputs : `dict` of task Inputs, containing:
425 - coaddExposureRefs : `list`
426 [`lsst.daf.butler.DeferredDatasetHandle` of
428 Data references to exposures that might overlap the detector.
430 Template Bounding box of the detector geometry onto which to
431 resample the coaddExposures.
432 - skyMap : `lsst.skymap.SkyMap`
433 Input definition of geometry/bbox and projection/wcs
for
436 Template WCS onto which to resample the coaddExposures.
440 result : `lsst.pipe.base.Struct`
441 A struct
with attributes:
444 List of Coadd exposures that overlap the detector (`list`
447 List of data IDs of the coadd exposures that overlap the
448 detector (`list` [`lsst.daf.butler.DataCoordinate`]).
453 Raised
if no patches overlap the input detector bbox.
459 coaddExposureList = []
461 for coaddRef
in inputs[
'coaddExposures']:
462 dataId = coaddRef.dataId
463 patchWcs = inputs[
'skyMap'][dataId[
'tract']].getWcs()
464 patchBBox = inputs[
'skyMap'][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
465 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
466 inputsWcs = inputs[
'wcs']
467 if inputsWcs
is not None:
468 patchPolygon = afwGeom.Polygon(inputsWcs.skyToPixel(patchCorners))
469 if patchPolygon.intersection(detectorPolygon):
470 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
471 self.log.info(
"Using template input tract=%s, patch=%s" %
472 (dataId[
'tract'], dataId[
'patch']))
473 coaddExposureList.append(coaddRef.get())
474 dataIds.append(dataId)
476 self.log.info(
"Exposure has no WCS, so cannot create associated template.")
478 if not overlappingArea:
479 raise pipeBase.NoWorkFound(
'No patches overlap detector')
481 return pipeBase.Struct(coaddExposures=coaddExposureList,
484 def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs):
485 """Warp coadds from multiple tracts to form a template for image diff.
487 Where the tracts overlap, the resulting template image is averaged.
488 The PSF on the template
is created by combining the CoaddPsf on each
489 template image into a meta-CoaddPsf.
494 Coadds to be mosaicked.
496 Template Bounding box of the detector geometry onto which to
497 resample the ``coaddExposures``.
499 Template WCS onto which to resample the ``coaddExposures``.
500 dataIds : `list` [`lsst.daf.butler.DataCoordinate`]
501 Record of the tract
and patch of each coaddExposure.
503 Any additional keyword parameters.
507 result : `lsst.pipe.base.Struct`
508 A struct
with attributes:
511 A template coadd exposure assembled out of patches
512 (`lsst.afw.image.ExposureF`).
515 tractsSchema = afwTable.ExposureTable.makeMinimalSchema()
516 tractKey = tractsSchema.addField(
'tract', type=np.int32, doc=
'Which tract')
517 patchKey = tractsSchema.addField(
'patch', type=np.int32, doc=
'Which patch')
518 weightKey = tractsSchema.addField(
'weight', type=float, doc=
'Weight for each tract, should be 1')
519 tractsCatalog = afwTable.ExposureCatalog(tractsSchema)
522 bbox.grow(self.config.templateBorderSize)
529 for coaddExposure, dataId
in zip(coaddExposures, dataIds):
532 warped = self.warper.warpExposure(finalWcs, coaddExposure, maxBBox=finalBBox)
535 if not np.any(np.isfinite(warped.image.array)):
536 self.log.info(
"No overlap for warped %s. Skipping" % dataId)
539 exp = afwImage.ExposureF(finalBBox, finalWcs)
540 exp.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
541 exp.maskedImage.assign(warped.maskedImage, warped.getBBox())
543 maskedImageList.append(exp.maskedImage)
545 record = tractsCatalog.addNew()
546 record.setPsf(coaddExposure.getPsf())
547 record.setWcs(coaddExposure.getWcs())
548 record.setPhotoCalib(coaddExposure.getPhotoCalib())
549 record.setBBox(coaddExposure.getBBox())
550 record.setValidPolygon(afwGeom.Polygon(
geom.Box2D(coaddExposure.getBBox()).getCorners()))
551 record.set(tractKey, dataId[
'tract'])
552 record.set(patchKey, dataId[
'patch'])
553 record.set(weightKey, 1.)
556 if nPatchesFound == 0:
557 raise pipeBase.NoWorkFound(
"No patches found to overlap detector")
562 statsCtrl.setNanSafe(
True)
563 statsCtrl.setWeighted(
True)
564 statsCtrl.setCalcErrorMosaicMode(
True)
566 templateExposure = afwImage.ExposureF(finalBBox, finalWcs)
567 templateExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
568 xy0 = templateExposure.getXY0()
571 weightList, clipped=0, maskMap=[])
572 templateExposure.maskedImage.setXY0(xy0)
576 boolmask = templateExposure.mask.array & templateExposure.mask.getPlaneBitMask(
'NO_DATA') == 0
578 centerCoord = afwGeom.SpanSet.fromMask(maskx, 1).computeCentroid()
580 ctrl = self.config.coaddPsf.makeControl()
581 coaddPsf = CoaddPsf(tractsCatalog, finalWcs, centerCoord, ctrl.warpingKernelName, ctrl.cacheSize)
583 raise RuntimeError(
"CoaddPsf could not be constructed")
585 templateExposure.setPsf(coaddPsf)
586 templateExposure.setFilter(coaddExposure.getFilter())
587 templateExposure.setPhotoCalib(coaddExposure.getPhotoCalib())
588 return pipeBase.Struct(template=templateExposure)
592 dimensions=(
"instrument",
"visit",
"detector",
"skymap"),
593 defaultTemplates={
"coaddName":
"dcr",
594 "warpTypeSuffix":
"",
596 visitInfo = pipeBase.connectionTypes.Input(
597 doc=
"VisitInfo of calexp used to determine observing conditions.",
598 name=
"{fakesType}calexp.visitInfo",
599 storageClass=
"VisitInfo",
600 dimensions=(
"instrument",
"visit",
"detector"),
602 dcrCoadds = pipeBase.connectionTypes.Input(
603 doc=
"Input DCR template to match and subtract from the exposure",
604 name=
"{fakesType}dcrCoadd{warpTypeSuffix}",
605 storageClass=
"ExposureF",
606 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
611 def __init__(self, *, config=None):
612 super().__init__(config=config)
613 self.inputs.remove(
"coaddExposures")
616class GetDcrTemplateConfig(GetTemplateConfig,
617 pipelineConnections=GetDcrTemplateConnections):
618 numSubfilters = pexConfig.Field(
619 doc=
"Number of subfilters in the DcrCoadd.",
623 effectiveWavelength = pexConfig.Field(
624 doc=
"Effective wavelength of the filter.",
628 bandwidth = pexConfig.Field(
629 doc=
"Bandwidth of the physical filter.",
635 if self.effectiveWavelength
is None or self.bandwidth
is None:
636 raise ValueError(
"The effective wavelength and bandwidth of the physical filter "
637 "must be set in the getTemplate config for DCR coadds. "
638 "Required until transmission curves are used in DM-13668.")
641class GetDcrTemplateTask(GetTemplateTask):
642 ConfigClass = GetDcrTemplateConfig
643 _DefaultName =
"getDcrTemplate"
645 def getOverlappingExposures(self, inputs):
646 """Return lists of coadds and their corresponding dataIds that overlap
649 The spatial index in the registry has generous padding
and often
650 supplies patches near, but
not directly overlapping the detector.
651 Filters inputs so that we don
't have to read in all input coadds.
655 inputs : `dict` of task Inputs, containing:
656 - coaddExposureRefs : `list`
657 [`lsst.daf.butler.DeferredDatasetHandle` of
659 Data references to exposures that might overlap the detector.
661 Template Bounding box of the detector geometry onto which to
662 resample the coaddExposures.
663 - skyMap : `lsst.skymap.SkyMap`
664 Input definition of geometry/bbox and projection/wcs
for
667 Template WCS onto which to resample the coaddExposures.
669 Metadata
for the science image.
673 result : `lsst.pipe.base.Struct`
674 A struct
with attibutes:
677 Coadd exposures that overlap the detector (`list`
680 Data IDs of the coadd exposures that overlap the detector
681 (`list` [`lsst.daf.butler.DataCoordinate`]).
686 Raised
if no patches overlatp the input detector bbox.
692 coaddExposureRefList = []
695 for coaddRef
in inputs[
"dcrCoadds"]:
696 dataId = coaddRef.dataId
697 patchWcs = inputs[
"skyMap"][dataId[
'tract']].getWcs()
698 patchBBox = inputs[
"skyMap"][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
699 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
700 patchPolygon = afwGeom.Polygon(inputs[
"wcs"].skyToPixel(patchCorners))
701 if patchPolygon.intersection(detectorPolygon):
702 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
703 self.log.info(
"Using template input tract=%s, patch=%s, subfilter=%s" %
704 (dataId[
'tract'], dataId[
'patch'], dataId[
"subfilter"]))
705 coaddExposureRefList.append(coaddRef)
706 if dataId[
'tract']
in patchList:
707 patchList[dataId[
'tract']].append(dataId[
'patch'])
709 patchList[dataId[
'tract']] = [dataId[
'patch'], ]
710 dataIds.append(dataId)
712 if not overlappingArea:
713 raise pipeBase.NoWorkFound(
'No patches overlap detector')
715 self.checkPatchList(patchList)
717 coaddExposures = self.getDcrModel(patchList, inputs[
'dcrCoadds'], inputs[
'visitInfo'])
718 return pipeBase.Struct(coaddExposures=coaddExposures,
722 """Check that all of the DcrModel subfilters are present for each
728 Dict of the patches containing valid data for each tract.
733 If the number of exposures found
for a patch does
not match the
734 number of subfilters.
736 for tract
in patchList:
737 for patch
in set(patchList[tract]):
738 if patchList[tract].count(patch) != self.config.numSubfilters:
739 raise RuntimeError(
"Invalid number of DcrModel subfilters found: %d vs %d expected",
740 patchList[tract].count(patch), self.config.numSubfilters)
743 """Build DCR-matched coadds from a list of exposure references.
748 Dict of the patches containing valid data for each tract.
749 coaddRefs : `list` [`lsst.daf.butler.DeferredDatasetHandle`]
751 DcrModels that overlap the detector.
753 Metadata
for the science image.
758 Coadd exposures that overlap the detector.
760 coaddExposureList = []
761 for tract
in patchList:
762 for patch
in set(patchList[tract]):
763 coaddRefList = [coaddRef
for coaddRef
in coaddRefs
764 if _selectDataRef(coaddRef, tract, patch)]
766 dcrModel = DcrModel.fromQuantum(coaddRefList,
767 self.config.effectiveWavelength,
768 self.config.bandwidth,
769 self.config.numSubfilters)
770 coaddExposureList.append(dcrModel.buildMatchedExposure(visitInfo=visitInfo))
771 return coaddExposureList
774def _selectDataRef(coaddRef, tract, patch):
775 condition = (coaddRef.dataId[
'tract'] == tract) & (coaddRef.dataId[
'patch'] == patch)
783class GetMultiTractCoaddTemplateTask(GetTemplateTask):
784 ConfigClass = GetMultiTractCoaddTemplateConfig
785 _DefaultName =
"getMultiTractCoaddTemplate"
789 self.log.warning(
"GetMultiTractCoaddTemplateTask is deprecated. Use GetTemplateTask instead.")
def getCoaddDatasetName(self)
def getOverlapPatchList(self, exposure, skyMap)
def run(self, tractInfo, patchList, skyCorners, availableCoaddRefs, sensorRef=None, visitInfo=None)
def runQuantum(self, exposure, butlerQC, skyMapRef, coaddExposureRefs)
def __init__(self, *args, **kwargs)
std::shared_ptr< lsst::afw::image::Image< PixelT > > statisticsStack(std::vector< std::shared_ptr< lsst::afw::image::Image< PixelT > > > &images, Property flags, StatisticsControl const &sctrl=StatisticsControl(), std::vector< lsst::afw::image::VariancePixel > const &wvector=std::vector< lsst::afw::image::VariancePixel >(0))
Property stringToStatisticsProperty(std::string const property)
def checkPatchList(self, patchList)
def getDcrModel(self, patchList, coaddRefs, visitInfo)
def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs)