32from lsst.skymap
import BaseSkyMap
33from lsst.daf.butler
import DeferredDatasetHandle
35from lsst.meas.algorithms
import CoaddPsf, CoaddPsfConfig
37__all__ = [
"GetCoaddAsTemplateTask",
"GetCoaddAsTemplateConfig",
38 "GetTemplateTask",
"GetTemplateConfig",
39 "GetDcrTemplateTask",
"GetDcrTemplateConfig",
40 "GetMultiTractCoaddTemplateTask",
"GetMultiTractCoaddTemplateConfig"]
44 templateBorderSize = pexConfig.Field(
47 doc=
"Number of pixels to grow the requested template image to account for warping"
49 coaddName = pexConfig.Field(
50 doc=
"coadd name: typically one of 'deep', 'goodSeeing', or 'dcr'",
54 warpType = pexConfig.Field(
55 doc=
"Warp type of the coadd template: one of 'direct' or 'psfMatched'",
62 """Subtask to retrieve coadd for use as an image difference template.
64 This is the default getTemplate Task to be run
as a subtask by
65 ``pipe.tasks.ImageDifferenceTask``.
69 From the given skymap, the closest tract
is selected; multiple tracts are
70 not supported. The assembled template inherits the WCS of the selected
71 skymap tract
and the resolution of the template exposures. Overlapping box
72 regions of the input template patches are pixel by pixel copied into the
73 assembled template image. There
is no warping
or pixel resampling.
75 Pixels
with no overlap of any available input patches are set to ``nan`` value
76 and ``NO_DATA`` flagged.
79 ConfigClass = GetCoaddAsTemplateConfig
80 _DefaultName = "GetCoaddAsTemplateTask"
82 def runQuantum(self, exposure, butlerQC, skyMapRef, coaddExposureRefs):
83 """Gen3 task entry point. Retrieve and mosaic a template coadd exposure
84 that overlaps the science exposure.
89 The science exposure to define the sky region of the template coadd.
90 butlerQC : `lsst.pipe.base.ButlerQuantumContext`
91 Butler like object that supports getting data by DatasetRef.
92 skyMapRef : `lsst.daf.butler.DatasetRef`
93 Reference to SkyMap object that corresponds to the template coadd.
94 coaddExposureRefs : iterable of `lsst.daf.butler.DeferredDatasetRef`
95 Iterable of references to the available template coadd patches.
99 result : `lsst.pipe.base.Struct`
100 - ``exposure`` : `lsst.afw.image.ExposureF`
101 a template coadd exposure assembled out of patches
102 - ``sources`` : `None`
for this subtask
104 self.log.warn("GetCoaddAsTemplateTask is deprecated. Use GetTemplateTask instead.")
105 skyMap = butlerQC.get(skyMapRef)
106 coaddExposureRefs = butlerQC.get(coaddExposureRefs)
107 tracts = [ref.dataId[
'tract']
for ref
in coaddExposureRefs]
108 if tracts.count(tracts[0]) == len(tracts):
109 tractInfo = skyMap[tracts[0]]
111 raise RuntimeError(
"Templates constructed from multiple Tracts not supported by this task. "
112 "Use GetTemplateTask instead.")
114 detectorBBox = exposure.getBBox()
115 detectorWcs = exposure.getWcs()
116 detectorCorners = detectorWcs.pixelToSky(
geom.Box2D(detectorBBox).getCorners())
117 validPolygon = exposure.getInfo().getValidPolygon()
118 detectorPolygon = validPolygon
if validPolygon
else geom.Box2D(detectorBBox)
120 availableCoaddRefs = dict()
122 for coaddRef
in coaddExposureRefs:
123 dataId = coaddRef.dataId
124 patchWcs = skyMap[dataId[
'tract']].getWcs()
125 patchBBox = skyMap[dataId[
'tract']][dataId[
'patch']].getOuterBBox()
126 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
127 patchPolygon = afwGeom.Polygon(detectorWcs.skyToPixel(patchCorners))
128 if patchPolygon.intersection(detectorPolygon):
129 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
130 if self.config.coaddName ==
'dcr':
131 self.log.info(
"Using template input tract=%s, patch=%s, subfilter=%s",
132 dataId[
'tract'], dataId[
'patch'], dataId[
'subfilter'])
133 if dataId[
'patch']
in availableCoaddRefs:
134 availableCoaddRefs[dataId[
'patch']].append(coaddRef)
136 availableCoaddRefs[dataId[
'patch']] = [coaddRef, ]
138 self.log.info(
"Using template input tract=%s, patch=%s",
139 dataId[
'tract'], dataId[
'patch'])
140 availableCoaddRefs[dataId[
'patch']] = coaddRef
142 if overlappingArea == 0:
143 templateExposure =
None
145 self.log.warning(
"No overlapping template patches found")
147 patchList = [tractInfo[patch]
for patch
in availableCoaddRefs.keys()]
148 templateExposure = self.
run(tractInfo, patchList, detectorCorners, availableCoaddRefs,
149 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 science exposure.
165 The science exposure to define the sky region of the template coadd.
167 skyMap : `lsst.skymap.BaseSkyMap`
168 SkyMap object that corresponds to the template coadd.
173 - ``tractInfo`` : `lsst.skymap.TractInfo`
175 - ``patchList`` : `list` of `lsst.skymap.PatchInfo`
176 List of all overlap patches of the selected tract.
178 Corners of the exposure in the sky
in the order given by `lsst.geom.Box2D.getCorners`.
180 expWcs = exposure.getWcs()
182 expBoxD.grow(self.config.templateBorderSize)
183 ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
184 tractInfo = skyMap.findTract(ctrSkyPos)
185 self.log.info("Using skyMap tract %s", tractInfo.getId())
186 skyCorners = [expWcs.pixelToSky(pixPos)
for pixPos
in expBoxD.getCorners()]
187 patchList = tractInfo.findPatchList(skyCorners)
190 raise RuntimeError(
"No suitable tract found")
192 self.log.info(
"Assembling %d coadd patches", len(patchList))
193 self.log.info(
"exposure dimensions=%s", exposure.getDimensions())
195 return (tractInfo, patchList, skyCorners)
197 def run(self, tractInfo, patchList, skyCorners, availableCoaddRefs,
198 sensorRef=None, visitInfo=None):
199 """Determination of exposure dimensions and copying of pixels from
200 overlapping patch regions.
204 skyMap : `lsst.skymap.BaseSkyMap`
205 SkyMap object that corresponds to the template coadd.
206 tractInfo : `lsst.skymap.TractInfo`
208 patchList : iterable of `lsst.skymap.patchInfo.PatchInfo`
209 Patches to consider for making the template exposure.
211 Sky corner coordinates to be covered by the template exposure.
212 availableCoaddRefs : `dict` [`int`]
213 Dictionary of spatially relevant retrieved coadd patches,
214 indexed by their sequential patch number. Values are
215 `lsst.daf.butler.DeferredDatasetHandle`
and ``.get()``
is called.
217 Must always be `
None`. Gen2 parameters are no longer used.
219 VisitInfo to make dcr model.
223 templateExposure: `lsst.afw.image.ExposureF`
224 The created template exposure.
226 if sensorRef
is not None:
227 raise ValueError(
"sensorRef parameter is a Gen2 parameter that is no longer usable."
228 " Please move to Gen3 middleware.")
229 coaddWcs = tractInfo.getWcs()
233 for skyPos
in skyCorners:
234 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
236 self.log.info(
"coadd dimensions=%s", coaddBBox.getDimensions())
238 coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
239 coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
241 coaddFilterLabel =
None
243 coaddPhotoCalib =
None
244 for patchInfo
in patchList:
245 patchNumber = tractInfo.getSequentialPatchIndex(patchInfo)
246 patchSubBBox = patchInfo.getOuterBBox()
247 patchSubBBox.clip(coaddBBox)
248 if patchNumber
not in availableCoaddRefs:
249 self.log.warning(
"skip patch=%d; patch does not exist for this coadd", patchNumber)
251 if patchSubBBox.isEmpty():
252 if isinstance(availableCoaddRefs[patchNumber], DeferredDatasetHandle):
253 tract = availableCoaddRefs[patchNumber].dataId[
'tract']
255 tract = availableCoaddRefs[patchNumber][
'tract']
256 self.log.info(
"skip tract=%d patch=%d; no overlapping pixels", tract, patchNumber)
259 if self.config.coaddName ==
'dcr':
260 patchInnerBBox = patchInfo.getInnerBBox()
261 patchInnerBBox.clip(coaddBBox)
262 if np.min(patchInnerBBox.getDimensions()) <= 2*self.config.templateBorderSize:
263 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; too few pixels.",
264 availableCoaddRefs[patchNumber])
266 self.log.info(
"Constructing DCR-matched template for patch %s",
267 availableCoaddRefs[patchNumber])
269 dcrModel = DcrModel.fromQuantum(availableCoaddRefs[patchNumber],
270 self.config.effectiveWavelength,
271 self.config.bandwidth)
279 dcrBBox.grow(-self.config.templateBorderSize)
280 dcrBBox.include(patchInnerBBox)
281 coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
284 coaddPatch = availableCoaddRefs[patchNumber].get()
290 overlapBox = coaddPatch.getBBox()
291 overlapBox.clip(coaddBBox)
292 coaddExposure.maskedImage.assign(coaddPatch.maskedImage[overlapBox], overlapBox)
294 if coaddFilterLabel
is None:
295 coaddFilterLabel = coaddPatch.getFilter()
298 if coaddPsf
is None and coaddPatch.hasPsf():
299 coaddPsf = coaddPatch.getPsf()
302 if coaddPhotoCalib
is None:
303 coaddPhotoCalib = coaddPatch.getPhotoCalib()
305 if coaddPhotoCalib
is None:
306 raise RuntimeError(
"No coadd PhotoCalib found!")
307 if nPatchesFound == 0:
308 raise RuntimeError(
"No patches found!")
310 raise RuntimeError(
"No coadd Psf found!")
312 coaddExposure.setPhotoCalib(coaddPhotoCalib)
313 coaddExposure.setPsf(coaddPsf)
314 coaddExposure.setFilter(coaddFilterLabel)
318 """Return coadd name for given task config
322 CoaddDatasetName : `string`
324 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)
326 warpType = self.config.warpType
327 suffix = "" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
328 return self.config.coaddName +
"Coadd" + suffix
332 dimensions=(
"instrument",
"visit",
"detector",
"skymap"),
333 defaultTemplates={
"coaddName":
"goodSeeing",
334 "warpTypeSuffix":
"",
336 bbox = pipeBase.connectionTypes.Input(
337 doc=
"BBoxes of calexp used determine geometry of output template",
338 name=
"{fakesType}calexp.bbox",
339 storageClass=
"Box2I",
340 dimensions=(
"instrument",
"visit",
"detector"),
342 wcs = pipeBase.connectionTypes.Input(
343 doc=
"WCS of the calexp that we want to fetch the template for",
344 name=
"{fakesType}calexp.wcs",
346 dimensions=(
"instrument",
"visit",
"detector"),
348 skyMap = pipeBase.connectionTypes.Input(
349 doc=
"Input definition of geometry/bbox and projection/wcs for template exposures",
350 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
351 dimensions=(
"skymap", ),
352 storageClass=
"SkyMap",
356 coaddExposures = pipeBase.connectionTypes.Input(
357 doc=
"Input template to match and subtract from the exposure",
358 dimensions=(
"tract",
"patch",
"skymap",
"band"),
359 storageClass=
"ExposureF",
360 name=
"{fakesType}{coaddName}Coadd{warpTypeSuffix}",
364 template = pipeBase.connectionTypes.Output(
365 doc=
"Warped template used to create `subtractedExposure`.",
366 dimensions=(
"instrument",
"visit",
"detector"),
367 storageClass=
"ExposureF",
368 name=
"{fakesType}{coaddName}Diff_templateExp{warpTypeSuffix}",
372class GetTemplateConfig(pipeBase.PipelineTaskConfig,
373 pipelineConnections=GetTemplateConnections):
374 templateBorderSize = pexConfig.Field(
377 doc=
"Number of pixels to grow the requested template image to account for warping"
379 warp = pexConfig.ConfigField(
380 dtype=afwMath.Warper.ConfigClass,
381 doc=
"warper configuration",
383 coaddPsf = pexConfig.ConfigField(
384 doc=
"Configuration for CoaddPsf",
385 dtype=CoaddPsfConfig,
388 def setDefaults(self):
389 self.warp.warpingKernelName =
'lanczos5'
390 self.coaddPsf.warpingKernelName =
'lanczos5'
393class GetTemplateTask(pipeBase.PipelineTask):
394 ConfigClass = GetTemplateConfig
395 _DefaultName =
"getTemplate"
397 def __init__(self, *args, **kwargs):
398 super().__init__(*args, **kwargs)
399 self.warper = afwMath.Warper.fromConfig(self.config.warp)
401 def runQuantum(self, butlerQC, inputRefs, outputRefs):
403 inputs = butlerQC.get(inputRefs)
404 results = self.getOverlappingExposures(inputs)
405 inputs[
"coaddExposures"] = results.coaddExposures
406 inputs[
"dataIds"] = results.dataIds
407 outputs = self.run(**inputs)
408 butlerQC.put(outputs, outputRefs)
410 def getOverlappingExposures(self, inputs):
411 """Return lists of coadds and their corresponding dataIds that overlap the detector.
413 The spatial index in the registry has generous padding
and often supplies
414 patches near, but
not directly overlapping the detector.
415 Filters inputs so that we don
't have to read in all input coadds.
419 inputs : `dict` of task Inputs, containing:
420 - coaddExposureRefs : list of elements of type
421 `lsst.daf.butler.DeferredDatasetHandle` of
423 Data references to exposures that might overlap the detector.
425 Template Bounding box of the detector geometry onto which to
426 resample the coaddExposures
427 - skyMap : `lsst.skymap.SkyMap`
428 Input definition of geometry/bbox and projection/wcs
for template exposures
430 Template WCS onto which to resample the coaddExposures
434 result : `lsst.pipe.base.Struct` containing these fields:
436 Coadd exposures that overlap the detector.
437 - dataIds : `list` of `lsst.daf.butler.DataCoordinate`
438 Data IDs of the coadd exposures that overlap the detector.
443 Raised
if no patches overlap the input detector bbox
449 coaddExposureList = []
451 for coaddRef
in inputs[
'coaddExposures']:
452 dataId = coaddRef.dataId
453 patchWcs = inputs[
'skyMap'][dataId[
'tract']].getWcs()
454 patchBBox = inputs[
'skyMap'][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
455 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
456 patchPolygon = afwGeom.Polygon(inputs[
'wcs'].skyToPixel(patchCorners))
457 if patchPolygon.intersection(detectorPolygon):
458 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
459 self.log.info(
"Using template input tract=%s, patch=%s" %
460 (dataId[
'tract'], dataId[
'patch']))
461 coaddExposureList.append(coaddRef.get())
462 dataIds.append(dataId)
464 if not overlappingArea:
465 raise pipeBase.NoWorkFound(
'No patches overlap detector')
467 return pipeBase.Struct(coaddExposures=coaddExposureList,
470 def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs):
471 """Warp coadds from multiple tracts to form a template for image diff.
473 Where the tracts overlap, the resulting template image is averaged.
474 The PSF on the template
is created by combining the CoaddPsf on each
475 template image into a meta-CoaddPsf.
480 Coadds to be mosaicked
482 Template Bounding box of the detector geometry onto which to
483 resample the coaddExposures
485 Template WCS onto which to resample the coaddExposures
486 dataIds : `list` of `lsst.daf.butler.DataCoordinate`
487 Record of the tract
and patch of each coaddExposure.
489 Any additional keyword parameters.
493 result : `lsst.pipe.base.Struct` containing
494 - ``template`` : a template coadd exposure assembled out of patches
497 tractsSchema = afwTable.ExposureTable.makeMinimalSchema()
498 tractKey = tractsSchema.addField(
'tract', type=np.int32, doc=
'Which tract')
499 patchKey = tractsSchema.addField(
'patch', type=np.int32, doc=
'Which patch')
500 weightKey = tractsSchema.addField(
'weight', type=float, doc=
'Weight for each tract, should be 1')
501 tractsCatalog = afwTable.ExposureCatalog(tractsSchema)
504 bbox.grow(self.config.templateBorderSize)
511 for coaddExposure, dataId
in zip(coaddExposures, dataIds):
514 warped = self.warper.warpExposure(finalWcs, coaddExposure, maxBBox=finalBBox)
517 if not np.any(np.isfinite(warped.image.array)):
518 self.log.info(
"No overlap for warped %s. Skipping" % dataId)
521 exp = afwImage.ExposureF(finalBBox, finalWcs)
522 exp.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
523 exp.maskedImage.assign(warped.maskedImage, warped.getBBox())
525 maskedImageList.append(exp.maskedImage)
527 record = tractsCatalog.addNew()
528 record.setPsf(coaddExposure.getPsf())
529 record.setWcs(coaddExposure.getWcs())
530 record.setPhotoCalib(coaddExposure.getPhotoCalib())
531 record.setBBox(coaddExposure.getBBox())
532 record.setValidPolygon(afwGeom.Polygon(
geom.Box2D(coaddExposure.getBBox()).getCorners()))
533 record.set(tractKey, dataId[
'tract'])
534 record.set(patchKey, dataId[
'patch'])
535 record.set(weightKey, 1.)
538 if nPatchesFound == 0:
539 raise pipeBase.NoWorkFound(
"No patches found to overlap detector")
544 statsCtrl.setNanSafe(
True)
545 statsCtrl.setWeighted(
True)
546 statsCtrl.setCalcErrorFromInputVariance(
True)
548 templateExposure = afwImage.ExposureF(finalBBox, finalWcs)
549 templateExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
550 xy0 = templateExposure.getXY0()
553 weightList, clipped=0, maskMap=[])
554 templateExposure.maskedImage.setXY0(xy0)
558 boolmask = templateExposure.mask.array & templateExposure.mask.getPlaneBitMask(
'NO_DATA') == 0
560 centerCoord = afwGeom.SpanSet.fromMask(maskx, 1).computeCentroid()
562 ctrl = self.config.coaddPsf.makeControl()
563 coaddPsf = CoaddPsf(tractsCatalog, finalWcs, centerCoord, ctrl.warpingKernelName, ctrl.cacheSize)
565 raise RuntimeError(
"CoaddPsf could not be constructed")
567 templateExposure.setPsf(coaddPsf)
568 templateExposure.setFilter(coaddExposure.getFilter())
569 templateExposure.setPhotoCalib(coaddExposure.getPhotoCalib())
570 return pipeBase.Struct(template=templateExposure)
574 dimensions=(
"instrument",
"visit",
"detector",
"skymap"),
575 defaultTemplates={
"coaddName":
"dcr",
576 "warpTypeSuffix":
"",
578 visitInfo = pipeBase.connectionTypes.Input(
579 doc=
"VisitInfo of calexp used to determine observing conditions.",
580 name=
"{fakesType}calexp.visitInfo",
581 storageClass=
"VisitInfo",
582 dimensions=(
"instrument",
"visit",
"detector"),
584 dcrCoadds = pipeBase.connectionTypes.Input(
585 doc=
"Input DCR template to match and subtract from the exposure",
586 name=
"{fakesType}dcrCoadd{warpTypeSuffix}",
587 storageClass=
"ExposureF",
588 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
593 def __init__(self, *, config=None):
594 super().__init__(config=config)
595 self.inputs.remove(
"coaddExposures")
598class GetDcrTemplateConfig(GetTemplateConfig,
599 pipelineConnections=GetDcrTemplateConnections):
600 numSubfilters = pexConfig.Field(
601 doc=
"Number of subfilters in the DcrCoadd.",
605 effectiveWavelength = pexConfig.Field(
606 doc=
"Effective wavelength of the filter.",
610 bandwidth = pexConfig.Field(
611 doc=
"Bandwidth of the physical filter.",
617 if self.effectiveWavelength
is None or self.bandwidth
is None:
618 raise ValueError(
"The effective wavelength and bandwidth of the physical filter "
619 "must be set in the getTemplate config for DCR coadds. "
620 "Required until transmission curves are used in DM-13668.")
623class GetDcrTemplateTask(GetTemplateTask):
624 ConfigClass = GetDcrTemplateConfig
625 _DefaultName =
"getDcrTemplate"
627 def getOverlappingExposures(self, inputs):
628 """Return lists of coadds and their corresponding dataIds that overlap the detector.
630 The spatial index in the registry has generous padding
and often supplies
631 patches near, but
not directly overlapping the detector.
632 Filters inputs so that we don
't have to read in all input coadds.
636 inputs : `dict` of task Inputs, containing:
637 - coaddExposureRefs : `list` of elements of type
638 `lsst.daf.butler.DeferredDatasetHandle` of
640 Data references to exposures that might overlap the detector.
642 Template Bounding box of the detector geometry onto which to
643 resample the coaddExposures
644 - skyMap : `lsst.skymap.SkyMap`
645 Input definition of geometry/bbox and projection/wcs
for template exposures
647 Template WCS onto which to resample the coaddExposures
649 Metadata
for the science image.
653 result : `lsst.pipe.base.Struct` containing these fields:
655 Coadd exposures that overlap the detector.
656 - dataIds : `list` of `lsst.daf.butler.DataCoordinate`
657 Data IDs of the coadd exposures that overlap the detector.
662 Raised
if no patches overlatp the input detector bbox
668 coaddExposureRefList = []
671 for coaddRef
in inputs[
"dcrCoadds"]:
672 dataId = coaddRef.dataId
673 patchWcs = inputs[
"skyMap"][dataId[
'tract']].getWcs()
674 patchBBox = inputs[
"skyMap"][dataId[
'tract']][dataId[
'patch']].getOuterBBox()
675 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
676 patchPolygon = afwGeom.Polygon(inputs[
"wcs"].skyToPixel(patchCorners))
677 if patchPolygon.intersection(detectorPolygon):
678 overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
679 self.log.info(
"Using template input tract=%s, patch=%s, subfilter=%s" %
680 (dataId[
'tract'], dataId[
'patch'], dataId[
"subfilter"]))
681 coaddExposureRefList.append(coaddRef)
682 if dataId[
'tract']
in patchList:
683 patchList[dataId[
'tract']].append(dataId[
'patch'])
685 patchList[dataId[
'tract']] = [dataId[
'patch'], ]
686 dataIds.append(dataId)
688 if not overlappingArea:
689 raise pipeBase.NoWorkFound(
'No patches overlap detector')
691 self.checkPatchList(patchList)
693 coaddExposures = self.getDcrModel(patchList, inputs[
'dcrCoadds'], inputs[
'visitInfo'])
694 return pipeBase.Struct(coaddExposures=coaddExposures,
698 """Check that all of the DcrModel subfilters are present for each patch.
703 Dict of the patches containing valid data for each tract
708 If the number of exposures found
for a patch does
not match the number of subfilters.
710 for tract
in patchList:
711 for patch
in set(patchList[tract]):
712 if patchList[tract].count(patch) != self.config.numSubfilters:
713 raise RuntimeError(
"Invalid number of DcrModel subfilters found: %d vs %d expected",
714 patchList[tract].count(patch), self.config.numSubfilters)
717 """Build DCR-matched coadds from a list of exposure references.
722 Dict of the patches containing valid data for each tract
723 coaddRefs : `list` of elements of type
724 `lsst.daf.butler.DeferredDatasetHandle` of
726 Data references to DcrModels that overlap the detector.
728 Metadata
for the science image.
733 Coadd exposures that overlap the detector.
735 coaddExposureList = []
736 for tract
in patchList:
737 for patch
in set(patchList[tract]):
738 coaddRefList = [coaddRef
for coaddRef
in coaddRefs
739 if _selectDataRef(coaddRef, tract, patch)]
741 dcrModel = DcrModel.fromQuantum(coaddRefList,
742 self.config.effectiveWavelength,
743 self.config.bandwidth,
744 self.config.numSubfilters)
745 coaddExposureList.append(dcrModel.buildMatchedExposure(visitInfo=visitInfo))
746 return coaddExposureList
749def _selectDataRef(coaddRef, tract, patch):
750 condition = (coaddRef.dataId[
'tract'] == tract) & (coaddRef.dataId[
'patch'] == patch)
758class GetMultiTractCoaddTemplateTask(GetTemplateTask):
759 ConfigClass = GetMultiTractCoaddTemplateConfig
760 _DefaultName =
"getMultiTractCoaddTemplate"
764 self.log.warn(
"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)