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 inputs[
"physical_filter"] = butlerQC.quantum.dataId[
"physical_filter"]
412 outputs = self.run(**inputs)
413 butlerQC.put(outputs, outputRefs)
415 def getOverlappingExposures(self, inputs):
416 """Return lists of coadds and their corresponding dataIds that overlap
419 The spatial index in the registry has generous padding
and often
420 supplies patches near, but
not directly overlapping the detector.
421 Filters inputs so that we don
't have to read in all input coadds.
425 inputs : `dict` of task Inputs, containing:
426 - coaddExposureRefs : `list`
427 [`lsst.daf.butler.DeferredDatasetHandle` of
429 Data references to exposures that might overlap the detector.
431 Template Bounding box of the detector geometry onto which to
432 resample the coaddExposures.
433 - skyMap : `lsst.skymap.SkyMap`
434 Input definition of geometry/bbox and projection/wcs
for
437 Template WCS onto which to resample the coaddExposures.
441 result : `lsst.pipe.base.Struct`
442 A struct
with attributes:
445 List of Coadd exposures that overlap the detector (`list`
448 List of data IDs of the coadd exposures that overlap the
449 detector (`list` [`lsst.daf.butler.DataCoordinate`]).
454 Raised
if no patches overlap the input detector bbox.
460 coaddExposureList = []
462 for coaddRef
in inputs[
'coaddExposures']:
463 dataId = coaddRef.dataId
464 patchWcs = inputs[
'skyMap'][dataId[
'tract']].getWcs()
465 patchBBox = inputs[
'skyMap'][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
466 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
467 inputsWcs = inputs[
'wcs']
468 if inputsWcs
is not None:
469 patchPolygon = afwGeom.Polygon(inputsWcs.skyToPixel(patchCorners))
470 if patchPolygon.intersection(detectorPolygon):
471 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
472 self.log.info(
"Using template input tract=%s, patch=%s" %
473 (dataId[
'tract'], dataId[
'patch']))
474 coaddExposureList.append(coaddRef.get())
475 dataIds.append(dataId)
477 self.log.info(
"Exposure has no WCS, so cannot create associated template.")
479 if not overlappingArea:
480 raise pipeBase.NoWorkFound(
'No patches overlap detector')
482 return pipeBase.Struct(coaddExposures=coaddExposureList,
485 def run(self, coaddExposures, bbox, wcs, dataIds, physical_filter=None, **kwargs):
486 """Warp coadds from multiple tracts to form a template for image diff.
488 Where the tracts overlap, the resulting template image is averaged.
489 The PSF on the template
is created by combining the CoaddPsf on each
490 template image into a meta-CoaddPsf.
495 Coadds to be mosaicked.
497 Template Bounding box of the detector geometry onto which to
498 resample the ``coaddExposures``.
500 Template WCS onto which to resample the ``coaddExposures``.
501 dataIds : `list` [`lsst.daf.butler.DataCoordinate`]
502 Record of the tract
and patch of each coaddExposure.
503 physical_filter : `str`, optional
504 The physical filter of the science image.
506 Any additional keyword parameters.
510 result : `lsst.pipe.base.Struct`
511 A struct
with attributes:
514 A template coadd exposure assembled out of patches
515 (`lsst.afw.image.ExposureF`).
520 If no coadds are found
with sufficient un-masked pixels.
522 If the PSF of the template can
't be calculated.
525 tractsSchema = afwTable.ExposureTable.makeMinimalSchema()
526 tractKey = tractsSchema.addField(
'tract', type=np.int32, doc=
'Which tract')
527 patchKey = tractsSchema.addField(
'patch', type=np.int32, doc=
'Which patch')
528 weightKey = tractsSchema.addField(
'weight', type=float, doc=
'Weight for each tract, should be 1')
529 tractsCatalog = afwTable.ExposureCatalog(tractsSchema)
532 bbox.grow(self.config.templateBorderSize)
539 for coaddExposure, dataId
in zip(coaddExposures, dataIds):
542 warped = self.warper.warpExposure(finalWcs, coaddExposure, maxBBox=finalBBox)
545 if not np.any(np.isfinite(warped.image.array)):
546 self.log.info(
"No overlap for warped %s. Skipping" % dataId)
549 exp = afwImage.ExposureF(finalBBox, finalWcs)
550 exp.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
551 exp.maskedImage.assign(warped.maskedImage, warped.getBBox())
553 maskedImageList.append(exp.maskedImage)
555 record = tractsCatalog.addNew()
556 record.setPsf(coaddExposure.getPsf())
557 record.setWcs(coaddExposure.getWcs())
558 record.setPhotoCalib(coaddExposure.getPhotoCalib())
559 record.setBBox(coaddExposure.getBBox())
560 record.setValidPolygon(afwGeom.Polygon(
geom.Box2D(coaddExposure.getBBox()).getCorners()))
561 record.set(tractKey, dataId[
'tract'])
562 record.set(patchKey, dataId[
'patch'])
563 record.set(weightKey, 1.)
566 if nPatchesFound == 0:
567 raise pipeBase.NoWorkFound(
"No patches found to overlap detector")
572 statsCtrl.setNanSafe(
True)
573 statsCtrl.setWeighted(
True)
574 statsCtrl.setCalcErrorMosaicMode(
True)
576 templateExposure = afwImage.ExposureF(finalBBox, finalWcs)
577 templateExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
578 xy0 = templateExposure.getXY0()
581 weightList, clipped=0, maskMap=[])
582 templateExposure.maskedImage.setXY0(xy0)
586 boolmask = templateExposure.mask.array & templateExposure.mask.getPlaneBitMask(
'NO_DATA') == 0
588 centerCoord = afwGeom.SpanSet.fromMask(maskx, 1).computeCentroid()
590 ctrl = self.config.coaddPsf.makeControl()
591 coaddPsf = CoaddPsf(tractsCatalog, finalWcs, centerCoord, ctrl.warpingKernelName, ctrl.cacheSize)
593 raise RuntimeError(
"CoaddPsf could not be constructed")
595 templateExposure.setPsf(coaddPsf)
597 if physical_filter
is None:
598 filterLabel = coaddExposure.getFilter()
601 templateExposure.setFilter(filterLabel)
602 templateExposure.setPhotoCalib(coaddExposure.getPhotoCalib())
603 return pipeBase.Struct(template=templateExposure)
607 dimensions=(
"instrument",
"visit",
"detector",
"skymap"),
608 defaultTemplates={
"coaddName":
"dcr",
609 "warpTypeSuffix":
"",
611 visitInfo = pipeBase.connectionTypes.Input(
612 doc=
"VisitInfo of calexp used to determine observing conditions.",
613 name=
"{fakesType}calexp.visitInfo",
614 storageClass=
"VisitInfo",
615 dimensions=(
"instrument",
"visit",
"detector"),
617 dcrCoadds = pipeBase.connectionTypes.Input(
618 doc=
"Input DCR template to match and subtract from the exposure",
619 name=
"{fakesType}dcrCoadd{warpTypeSuffix}",
620 storageClass=
"ExposureF",
621 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
626 def __init__(self, *, config=None):
627 super().__init__(config=config)
628 self.inputs.remove(
"coaddExposures")
631class GetDcrTemplateConfig(GetTemplateConfig,
632 pipelineConnections=GetDcrTemplateConnections):
633 numSubfilters = pexConfig.Field(
634 doc=
"Number of subfilters in the DcrCoadd.",
638 effectiveWavelength = pexConfig.Field(
639 doc=
"Effective wavelength of the filter.",
643 bandwidth = pexConfig.Field(
644 doc=
"Bandwidth of the physical filter.",
650 if self.effectiveWavelength
is None or self.bandwidth
is None:
651 raise ValueError(
"The effective wavelength and bandwidth of the physical filter "
652 "must be set in the getTemplate config for DCR coadds. "
653 "Required until transmission curves are used in DM-13668.")
656class GetDcrTemplateTask(GetTemplateTask):
657 ConfigClass = GetDcrTemplateConfig
658 _DefaultName =
"getDcrTemplate"
660 def getOverlappingExposures(self, inputs):
661 """Return lists of coadds and their corresponding dataIds that overlap
664 The spatial index in the registry has generous padding
and often
665 supplies patches near, but
not directly overlapping the detector.
666 Filters inputs so that we don
't have to read in all input coadds.
670 inputs : `dict` of task Inputs, containing:
671 - coaddExposureRefs : `list`
672 [`lsst.daf.butler.DeferredDatasetHandle` of
674 Data references to exposures that might overlap the detector.
676 Template Bounding box of the detector geometry onto which to
677 resample the coaddExposures.
678 - skyMap : `lsst.skymap.SkyMap`
679 Input definition of geometry/bbox and projection/wcs
for
682 Template WCS onto which to resample the coaddExposures.
684 Metadata
for the science image.
688 result : `lsst.pipe.base.Struct`
689 A struct
with attibutes:
692 Coadd exposures that overlap the detector (`list`
695 Data IDs of the coadd exposures that overlap the detector
696 (`list` [`lsst.daf.butler.DataCoordinate`]).
701 Raised
if no patches overlatp the input detector bbox.
707 coaddExposureRefList = []
710 for coaddRef
in inputs[
"dcrCoadds"]:
711 dataId = coaddRef.dataId
712 patchWcs = inputs[
"skyMap"][dataId[
'tract']].getWcs()
713 patchBBox = inputs[
"skyMap"][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
714 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
715 patchPolygon = afwGeom.Polygon(inputs[
"wcs"].skyToPixel(patchCorners))
716 if patchPolygon.intersection(detectorPolygon):
717 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
718 self.log.info(
"Using template input tract=%s, patch=%s, subfilter=%s" %
719 (dataId[
'tract'], dataId[
'patch'], dataId[
"subfilter"]))
720 coaddExposureRefList.append(coaddRef)
721 if dataId[
'tract']
in patchList:
722 patchList[dataId[
'tract']].append(dataId[
'patch'])
724 patchList[dataId[
'tract']] = [dataId[
'patch'], ]
725 dataIds.append(dataId)
727 if not overlappingArea:
728 raise pipeBase.NoWorkFound(
'No patches overlap detector')
730 self.checkPatchList(patchList)
732 coaddExposures = self.getDcrModel(patchList, inputs[
'dcrCoadds'], inputs[
'visitInfo'])
733 return pipeBase.Struct(coaddExposures=coaddExposures,
737 """Check that all of the DcrModel subfilters are present for each
743 Dict of the patches containing valid data for each tract.
748 If the number of exposures found
for a patch does
not match the
749 number of subfilters.
751 for tract
in patchList:
752 for patch
in set(patchList[tract]):
753 if patchList[tract].count(patch) != self.config.numSubfilters:
754 raise RuntimeError(
"Invalid number of DcrModel subfilters found: %d vs %d expected",
755 patchList[tract].count(patch), self.config.numSubfilters)
758 """Build DCR-matched coadds from a list of exposure references.
763 Dict of the patches containing valid data for each tract.
764 coaddRefs : `list` [`lsst.daf.butler.DeferredDatasetHandle`]
766 DcrModels that overlap the detector.
768 Metadata
for the science image.
773 Coadd exposures that overlap the detector.
775 coaddExposureList = []
776 for tract
in patchList:
777 for patch
in set(patchList[tract]):
778 coaddRefList = [coaddRef
for coaddRef
in coaddRefs
779 if _selectDataRef(coaddRef, tract, patch)]
781 dcrModel = DcrModel.fromQuantum(coaddRefList,
782 self.config.effectiveWavelength,
783 self.config.bandwidth,
784 self.config.numSubfilters)
785 coaddExposureList.append(dcrModel.buildMatchedExposure(visitInfo=visitInfo))
786 return coaddExposureList
789def _selectDataRef(coaddRef, tract, patch):
790 condition = (coaddRef.dataId[
'tract'] == tract) & (coaddRef.dataId[
'patch'] == patch)
798class GetMultiTractCoaddTemplateTask(GetTemplateTask):
799 ConfigClass = GetMultiTractCoaddTemplateConfig
800 _DefaultName =
"getMultiTractCoaddTemplate"
804 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 run(self, coaddExposures, bbox, wcs, dataIds, physical_filter=None, **kwargs)
def getDcrModel(self, patchList, coaddRefs, visitInfo)