34from lsst.skymap
import BaseSkyMap
36from lsst.meas.algorithms
import CoaddPsf, CoaddPsfConfig, SubtractBackgroundTask
37from lsst.utils.timer
import timeMethod
43 "GetDcrTemplateConfig",
48 pipeBase.PipelineTaskConnections,
49 dimensions=(
"instrument",
"visit",
"detector"),
50 defaultTemplates={
"coaddName":
"goodSeeing",
"warpTypeSuffix":
"",
"fakesType":
""},
52 bbox = pipeBase.connectionTypes.Input(
53 doc=
"Bounding box of exposure to determine the geometry of the output template.",
54 name=
"{fakesType}calexp.bbox",
56 dimensions=(
"instrument",
"visit",
"detector"),
58 wcs = pipeBase.connectionTypes.Input(
59 doc=
"WCS of the exposure that we will construct the template for.",
60 name=
"{fakesType}calexp.wcs",
62 dimensions=(
"instrument",
"visit",
"detector"),
64 skyMap = pipeBase.connectionTypes.Input(
65 doc=
"Geometry of the tracts and patches that the coadds are defined on.",
66 name=BaseSkyMap.SKYMAP_DATASET_TYPE_NAME,
67 dimensions=(
"skymap",),
68 storageClass=
"SkyMap",
70 coaddExposures = pipeBase.connectionTypes.Input(
71 doc=
"Coadds that may overlap the desired region, as possible inputs to the template."
72 " Will be restricted to those that directly overlap the projected bounding box.",
73 dimensions=(
"tract",
"patch",
"skymap",
"band"),
74 storageClass=
"ExposureF",
75 name=
"{fakesType}{coaddName}Coadd{warpTypeSuffix}",
78 deferGraphConstraint=
True,
81 template = pipeBase.connectionTypes.Output(
82 doc=
"Warped template, pixel matched to the bounding box and WCS.",
83 dimensions=(
"instrument",
"visit",
"detector"),
84 storageClass=
"ExposureF",
85 name=
"{fakesType}{coaddName}Diff_templateExp{warpTypeSuffix}",
89class GetTemplateConfig(
90 pipeBase.PipelineTaskConfig, pipelineConnections=GetTemplateConnections
92 templateBorderSize = pexConfig.Field(
95 doc=
"Number of pixels to grow the requested template image to account for warping",
97 warp = pexConfig.ConfigField(
98 dtype=afwMath.Warper.ConfigClass,
99 doc=
"warper configuration",
101 coaddPsf = pexConfig.ConfigField(
102 doc=
"Configuration for CoaddPsf",
103 dtype=CoaddPsfConfig,
105 varianceBackground = pexConfig.ConfigurableField(
106 target=SubtractBackgroundTask,
107 doc=
"Task to estimate the background variance.",
109 highVarianceThreshold = pexConfig.RangeField(
113 doc=
"Set the HIGH_VARIANCE mask plane for regions with variance"
114 " greater than the median by this factor.",
116 highVarianceMaskFraction = pexConfig.Field(
119 doc=
"Minimum fraction of unmasked pixels needed to set the"
120 " HIGH_VARIANCE mask plane.",
123 def setDefaults(self):
126 self.warp.cacheSize = 100000
127 self.coaddPsf.cacheSize = self.warp.cacheSize
130 self.warp.interpLength = 100
131 self.warp.warpingKernelName =
"lanczos3"
132 self.coaddPsf.warpingKernelName = self.warp.warpingKernelName
135 self.varianceBackground.algorithm =
"LINEAR"
136 self.varianceBackground.binSize = 32
137 self.varianceBackground.useApprox =
False
138 self.varianceBackground.statisticsProperty =
"MEDIAN"
139 self.varianceBackground.doFilterSuperPixels =
True
140 self.varianceBackground.ignoredPixelMask = [
"BAD",
148class GetTemplateTask(pipeBase.PipelineTask):
149 ConfigClass = GetTemplateConfig
150 _DefaultName =
"getTemplate"
152 def __init__(self, *args, **kwargs):
153 super().__init__(*args, **kwargs)
154 self.warper = afwMath.Warper.fromConfig(self.config.warp)
155 self.schema = afwTable.ExposureTable.makeMinimalSchema()
156 self.schema.addField(
157 "tract", type=np.int32, doc=
"Which tract this exposure came from."
159 self.schema.addField(
162 doc=
"Which patch in the tract this exposure came from.",
164 self.schema.addField(
167 doc=
"Weight for each exposure, used to make the CoaddPsf; should always be 1.",
169 self.makeSubtask(
"varianceBackground")
171 def runQuantum(self, butlerQC, inputRefs, outputRefs):
172 inputs = butlerQC.get(inputRefs)
173 bbox = inputs.pop(
"bbox")
174 wcs = inputs.pop(
"wcs")
175 coaddExposures = inputs.pop(
"coaddExposures")
176 skymap = inputs.pop(
"skyMap")
179 assert not inputs,
"runQuantum got more inputs than expected"
181 results = self.getExposures(coaddExposures, bbox, skymap, wcs)
182 physical_filter = butlerQC.quantum.dataId[
"physical_filter"]
184 coaddExposureHandles=results.coaddExposures,
187 dataIds=results.dataIds,
188 physical_filter=physical_filter,
190 butlerQC.put(outputs, outputRefs)
192 def getExposures(self, coaddExposureHandles, bbox, skymap, wcs):
193 """Return a data structure containing the coadds that overlap the
194 specified bbox projected onto the sky, and a corresponding data
195 structure of their dataIds.
196 These are the appropriate inputs to this task's `run` method.
198 The spatial index in the butler registry has generous padding and often
199 supplies patches near, but not directly overlapping the desired region.
200 This method filters the inputs so that `run` does not have to read in
201 all possibly-matching coadd exposures.
205 coaddExposureHandles : `iterable` \
206 [`lsst.daf.butler.DeferredDatasetHandle` of \
207 `lsst.afw.image.Exposure`]
208 Dataset handles to exposures that might overlap the desired
210 bbox : `lsst.geom.Box2I`
211 Template bounding box of the pixel geometry onto which the
212 coaddExposures will be resampled.
213 skymap : `lsst.skymap.SkyMap`
214 Geometry of the tracts and patches the coadds are defined on.
215 wcs : `lsst.afw.geom.SkyWcs`
216 Template WCS onto which the coadds will be resampled.
220 result : `lsst.pipe.base.Struct`
221 A struct with attributes:
224 Dict of coadd exposures that overlap the projected bbox,
226 (`dict` [`int`, `list` [`lsst.daf.butler.DeferredDatasetHandle` of
227 `lsst.afw.image.Exposure`] ]).
229 Dict of data IDs of the coadd exposures that overlap the
230 projected bbox, indexed on tract id
231 (`dict` [`int`, `list [`lsst.daf.butler.DataCoordinate`] ]).
236 Raised if no patches overlap the input detector bbox, or the input
240 raise pipeBase.NoWorkFound(
241 "WCS is None; cannot find overlapping exposures."
246 detectorCorners = wcs.pixelToSky(detectorPolygon.getCorners())
248 coaddExposures = collections.defaultdict(list)
249 dataIds = collections.defaultdict(list)
251 for coaddRef
in coaddExposureHandles:
252 dataId = coaddRef.dataId
253 patchWcs = skymap[dataId[
"tract"]].getWcs()
254 patchBBox = skymap[dataId[
"tract"]][dataId[
"patch"]].getOuterBBox()
260 detectorInPatchCoordinates = afwGeom.Polygon(patchWcs.skyToPixel(detectorCorners))
261 if patchPolygon.intersection(detectorInPatchCoordinates):
262 overlappingArea += patchPolygon.intersectionSingle(
263 detectorInPatchCoordinates
266 "Using template input tract=%s, patch=%s",
270 coaddExposures[dataId[
"tract"]].append(coaddRef)
271 dataIds[dataId[
"tract"]].append(dataId)
273 if not overlappingArea:
274 raise pipeBase.NoWorkFound(
"No patches overlap detector")
276 return pipeBase.Struct(coaddExposures=coaddExposures, dataIds=dataIds)
279 def run(self, *, coaddExposureHandles, bbox, wcs, dataIds, physical_filter):
280 """Warp coadds from multiple tracts and patches to form a template to
281 subtract from a science image.
283 Tract and patch overlap regions are combined by a variance-weighted
284 average, and the variance planes are combined with the same weights,
285 not added in quadrature; the overlap regions are not statistically
286 independent, because they're derived from the same original data.
287 The PSF on the template is created by combining the CoaddPsf on each
288 template image into a meta-CoaddPsf.
292 coaddExposureHandles : `dict` [`int`, `list` of \
293 [`lsst.daf.butler.DeferredDatasetHandle` of \
294 `lsst.afw.image.Exposure`]]
295 Coadds to be mosaicked, indexed on tract id.
296 bbox : `lsst.geom.Box2I`
297 Template Bounding box of the detector geometry onto which to
298 resample the ``coaddExposureHandles``. Modified in-place to include the
300 wcs : `lsst.afw.geom.SkyWcs`
301 Template WCS onto which to resample the ``coaddExposureHandles``.
302 dataIds : `dict` [`int`, `list` [`lsst.daf.butler.DataCoordinate`]]
303 Record of the tract and patch of each coaddExposure, indexed on
305 physical_filter : `str`
306 Physical filter of the science image.
310 result : `lsst.pipe.base.Struct`
311 A struct with attributes:
314 A template coadd exposure assembled out of patches
315 (`lsst.afw.image.ExposureF`).
320 If no coadds are found with sufficient un-masked pixels.
322 band, photoCalib = self._checkInputs(dataIds, coaddExposureHandles)
324 bbox.grow(self.config.templateBorderSize)
328 for tract
in coaddExposureHandles:
329 maskedImages, catalog, totalBox = self._makeExposureCatalog(
330 coaddExposureHandles[tract], dataIds[tract]
332 warpedBox = computeWarpedBBox(catalog[0].wcs, bbox, wcs)
335 unwarped, count, included = self._merge(
336 maskedImages, warpedBox, catalog[0].wcs
342 "No valid pixels from coadd patches in tract %s; not including in output.",
346 warpedBox.clip(totalBox)
347 potentialInput = self.warper.warpExposure(
348 wcs, unwarped.subset(warpedBox), destBBox=bbox
354 potentialInput.mask.array
355 & potentialInput.mask.getPlaneBitMask(
"NO_DATA")
358 "No overlap from coadd patches in tract %s; not including in output.",
364 tempCatalog = afwTable.ExposureCatalog(self.schema)
365 tempCatalog.reserve(len(included))
367 tempCatalog.append(catalog[i])
368 catalogs.append(tempCatalog)
369 warped[tract] = potentialInput.maskedImage
372 raise pipeBase.NoWorkFound(
"No patches found to overlap science exposure.")
374 template, count, _ = self._merge(warped, bbox, wcs)
376 raise pipeBase.NoWorkFound(
"No valid pixels in warped template.")
379 catalog = afwTable.ExposureCatalog(self.schema)
380 catalog.reserve(sum([len(c)
for c
in catalogs]))
385 self.checkHighVariance(template)
386 template.setPsf(self._makePsf(template, catalog, wcs))
388 template.setPhotoCalib(photoCalib)
389 return pipeBase.Struct(template=template)
392 """Set a mask plane for regions with unusually high variance.
396 template : `lsst.afw.image.Exposure`
397 The warped template exposure, which will be modified in place.
399 highVarianceMaskPlaneBit = template.mask.addMaskPlane(
"HIGH_VARIANCE")
400 ignoredPixelBits = template.mask.getPlaneBitMask(self.varianceBackground.config.ignoredPixelMask)
401 goodMask = (template.mask.array & ignoredPixelBits) == 0
402 goodFraction = np.count_nonzero(goodMask)/template.mask.array.size
403 if goodFraction < self.config.highVarianceMaskFraction:
404 self.log.info(
"Not setting HIGH_VARIANCE mask plane, only %2.1f%% of"
405 " pixels were unmasked for background estimation, but"
406 " %2.1f%% are required", 100*goodFraction, 100*self.config.highVarianceMaskFraction)
408 varianceExposure = template.clone()
409 varianceExposure.image.array = varianceExposure.variance.array
410 varianceBackground = self.varianceBackground.
run(varianceExposure).background.getImage().array
411 threshold = self.config.highVarianceThreshold*np.nanmedian(varianceBackground)
412 highVariancePix = varianceBackground > threshold
413 template.mask.array[highVariancePix] |= 2**highVarianceMaskPlaneBit
416 def _checkInputs(dataIds, coaddExposures):
417 """Check that the all the dataIds are from the same band and that
418 the exposures all have the same photometric calibration.
422 dataIds : `dict` [`int`, `list` [`lsst.daf.butler.DataCoordinate`]]
423 Record of the tract and patch of each coaddExposure.
424 coaddExposures : `dict` [`int`, `list` of \
425 [`lsst.daf.butler.DeferredDatasetHandle` of \
426 `lsst.afw.image.Exposure` or
427 `lsst.afw.image.Exposure`]]
428 Coadds to be mosaicked.
433 Filter band of all the input exposures.
434 photoCalib : `lsst.afw.image.PhotoCalib`
435 Photometric calibration of all of the input exposures.
440 Raised if the bands or calibrations of the input exposures are not
443 bands = set(dataId[
"band"]
for tract
in dataIds
for dataId
in dataIds[tract])
445 raise RuntimeError(f
"GetTemplateTask called with multiple bands: {bands}")
448 exposure.get(component=
"photoCalib")
449 for exposures
in coaddExposures.values()
450 for exposure
in exposures
452 if not all([photoCalibs[0] == x
for x
in photoCalibs]):
453 msg = f
"GetTemplateTask called with exposures with different photoCalibs: {photoCalibs}"
454 raise RuntimeError(msg)
455 photoCalib = photoCalibs[0]
456 return band, photoCalib
459 """Make an exposure catalog for one tract.
463 exposureRefs : `list` of [`lsst.daf.butler.DeferredDatasetHandle` of \
464 `lsst.afw.image.Exposure`]
465 Exposures to include in the catalog.
466 dataIds : `list` [`lsst.daf.butler.DataCoordinate`]
467 Data ids of each of the included exposures; must have "tract" and
472 images : `dict` [`lsst.afw.image.MaskedImage`]
473 MaskedImages of each of the input exposures, for warping.
474 catalog : `lsst.afw.table.ExposureCatalog`
475 Catalog of metadata for each exposure
476 totalBox : `lsst.geom.Box2I`
477 The union of the bounding boxes of all the input exposures.
479 catalog = afwTable.ExposureCatalog(self.schema)
480 catalog.reserve(len(exposureRefs))
481 exposures = (exposureRef.get()
for exposureRef
in exposureRefs)
485 for coadd, dataId
in zip(exposures, dataIds):
486 images[dataId] = coadd.maskedImage
487 bbox = coadd.getBBox()
488 totalBox = totalBox.expandedTo(bbox)
489 record = catalog.addNew()
490 record.setPsf(coadd.psf)
491 record.setWcs(coadd.wcs)
492 record.setPhotoCalib(coadd.photoCalib)
494 record.setValidPolygon(afwGeom.Polygon(
geom.Box2D(bbox).getCorners()))
495 record.set(
"tract", dataId[
"tract"])
496 record.set(
"patch", dataId[
"patch"])
499 record.set(
"weight", 1)
501 return images, catalog, totalBox
503 def _merge(self, maskedImages, bbox, wcs):
504 """Merge the images that came from one tract into one larger image,
505 ignoring NaN pixels and non-finite variance pixels from individual
510 maskedImages : `dict` [`lsst.afw.image.MaskedImage` or
511 `lsst.afw.image.Exposure`]
512 Images to be merged into one larger bounding box.
513 bbox : `lsst.geom.Box2I`
514 Bounding box defining the image to merge into.
515 wcs : `lsst.afw.geom.SkyWcs`
516 WCS of all of the input images to set on the output image.
520 merged : `lsst.afw.image.MaskedImage`
521 Merged image with all of the inputs at their respective bbox
524 Count of the number of good pixels (those with positive weights)
526 included : `list` [`int`]
527 List of indexes of patches that were included in the merged
528 result, to be used to trim the exposure catalog.
530 merged = afwImage.ExposureF(bbox, wcs)
531 weights = afwImage.ImageF(bbox)
533 for i, (dataId, maskedImage)
in enumerate(maskedImages.items()):
535 clippedBox =
geom.Box2I(maskedImage.getBBox())
536 clippedBox.clip(bbox)
537 if clippedBox.area == 0:
538 self.log.debug(
"%s does not overlap template region.", dataId)
540 maskedImage = maskedImage.subset(clippedBox)
542 good = (maskedImage.variance.array > 0) & (
543 np.isfinite(maskedImage.variance.array)
545 weight = maskedImage.variance.array[good] ** (-0.5)
546 bad = np.isnan(maskedImage.image.array) | ~good
549 maskedImage.image.array[bad] = 0.0
550 maskedImage.variance.array[bad] = 0.0
552 maskedImage.mask.array[bad] = 0
556 maskedImage.image.array[good] *= weight
557 maskedImage.variance.array[good] *= weight
558 weights[clippedBox].array[good] += weight
561 merged.maskedImage[clippedBox] += maskedImage
564 good = weights.array > 0
569 weights = weights.array[good]
570 merged.image.array[good] /= weights
571 merged.variance.array[good] /= weights
573 merged.mask.array[~good] |= merged.mask.getPlaneBitMask(
"NO_DATA")
575 return merged, good.sum(), included
578 """Return a PSF containing the PSF at each of the input regions.
580 Note that although this includes all the exposures from the catalog,
581 the PSF knows which part of the template the inputs came from, so when
582 evaluated at a given position it will not include inputs that never
583 went in to those pixels.
587 template : `lsst.afw.image.Exposure`
588 Generated template the PSF is for.
589 catalog : `lsst.afw.table.ExposureCatalog`
590 Catalog of exposures that went into the template that contains all
592 wcs : `lsst.afw.geom.SkyWcs`
593 WCS of the template, to warp the PSFs to.
597 coaddPsf : `lsst.meas.algorithms.CoaddPsf`
598 The meta-psf constructed from all of the input catalogs.
602 boolmask = template.mask.array & template.mask.getPlaneBitMask(
"NO_DATA") == 0
604 centerCoord = afwGeom.SpanSet.fromMask(maskx, 1).computeCentroid()
606 ctrl = self.config.coaddPsf.makeControl()
608 catalog, wcs, centerCoord, ctrl.warpingKernelName, ctrl.cacheSize
614 GetTemplateConnections,
615 dimensions=(
"instrument",
"visit",
"detector"),
616 defaultTemplates={
"coaddName":
"dcr",
"warpTypeSuffix":
"",
"fakesType":
""},
618 visitInfo = pipeBase.connectionTypes.Input(
619 doc=
"VisitInfo of calexp used to determine observing conditions.",
620 name=
"{fakesType}calexp.visitInfo",
621 storageClass=
"VisitInfo",
622 dimensions=(
"instrument",
"visit",
"detector"),
624 dcrCoadds = pipeBase.connectionTypes.Input(
625 doc=
"Input DCR template to match and subtract from the exposure",
626 name=
"{fakesType}dcrCoadd{warpTypeSuffix}",
627 storageClass=
"ExposureF",
628 dimensions=(
"tract",
"patch",
"skymap",
"band",
"subfilter"),
633 def __init__(self, *, config=None):
634 super().__init__(config=config)
635 self.inputs.remove(
"coaddExposures")
638class GetDcrTemplateConfig(
639 GetTemplateConfig, pipelineConnections=GetDcrTemplateConnections
641 numSubfilters = pexConfig.Field(
642 doc=
"Number of subfilters in the DcrCoadd.",
646 effectiveWavelength = pexConfig.Field(
647 doc=
"Effective wavelength of the filter in nm.",
651 bandwidth = pexConfig.Field(
652 doc=
"Bandwidth of the physical filter.",
658 if self.effectiveWavelength
is None or self.bandwidth
is None:
660 "The effective wavelength and bandwidth of the physical filter "
661 "must be set in the getTemplate config for DCR coadds. "
662 "Required until transmission curves are used in DM-13668."
666class GetDcrTemplateTask(GetTemplateTask):
667 ConfigClass = GetDcrTemplateConfig
668 _DefaultName =
"getDcrTemplate"
670 def runQuantum(self, butlerQC, inputRefs, outputRefs):
671 inputs = butlerQC.get(inputRefs)
672 bbox = inputs.pop(
"bbox")
673 wcs = inputs.pop(
"wcs")
674 dcrCoaddExposureHandles = inputs.pop(
"dcrCoadds")
675 skymap = inputs.pop(
"skyMap")
676 visitInfo = inputs.pop(
"visitInfo")
679 assert not inputs,
"runQuantum got more inputs than expected"
681 results = self.getExposures(
682 dcrCoaddExposureHandles, bbox, skymap, wcs, visitInfo
684 physical_filter = butlerQC.quantum.dataId[
"physical_filter"]
686 coaddExposureHandles=results.coaddExposures,
689 dataIds=results.dataIds,
690 physical_filter=physical_filter,
692 butlerQC.put(outputs, outputRefs)
694 def getExposures(self, dcrCoaddExposureHandles, bbox, skymap, wcs, visitInfo):
695 """Return lists of coadds and their corresponding dataIds that overlap
698 The spatial index in the registry has generous padding and often
699 supplies patches near, but not directly overlapping the detector.
700 Filters inputs so that we don't have to read in all input coadds.
704 dcrCoaddExposureHandles : `list` \
705 [`lsst.daf.butler.DeferredDatasetHandle` of \
706 `lsst.afw.image.Exposure`]
707 Data references to exposures that might overlap the detector.
708 bbox : `lsst.geom.Box2I`
709 Template Bounding box of the detector geometry onto which to
710 resample the coaddExposures.
711 skymap : `lsst.skymap.SkyMap`
712 Input definition of geometry/bbox and projection/wcs for
714 wcs : `lsst.afw.geom.SkyWcs`
715 Template WCS onto which to resample the coaddExposures.
716 visitInfo : `lsst.afw.image.VisitInfo`
717 Metadata for the science image.
721 result : `lsst.pipe.base.Struct`
722 A struct with attibutes:
725 Dict of coadd exposures that overlap the projected bbox,
727 (`dict` [`int`, `list` [`lsst.afw.image.Exposure`] ]).
729 Dict of data IDs of the coadd exposures that overlap the
730 projected bbox, indexed on tract id
731 (`dict` [`int`, `list [`lsst.daf.butler.DataCoordinate`] ]).
736 Raised if no patches overlatp the input detector bbox.
741 raise pipeBase.NoWorkFound(
"Exposure has no WCS; cannot create a template.")
745 dataIds = collections.defaultdict(list)
747 for coaddRef
in dcrCoaddExposureHandles:
748 dataId = coaddRef.dataId
749 subfilter = dataId[
"subfilter"]
750 patchWcs = skymap[dataId[
"tract"]].getWcs()
751 patchBBox = skymap[dataId[
"tract"]][dataId[
"patch"]].getOuterBBox()
752 patchCorners = patchWcs.pixelToSky(
geom.Box2D(patchBBox).getCorners())
753 patchPolygon = afwGeom.Polygon(wcs.skyToPixel(patchCorners))
754 if patchPolygon.intersection(detectorPolygon):
755 overlappingArea += patchPolygon.intersectionSingle(
759 "Using template input tract=%s, patch=%s, subfilter=%s"
760 % (dataId[
"tract"], dataId[
"patch"], dataId[
"subfilter"])
762 if dataId[
"tract"]
in patchList:
763 patchList[dataId[
"tract"]].append(dataId[
"patch"])
765 patchList[dataId[
"tract"]] = [
769 dataIds[dataId[
"tract"]].append(dataId)
771 if not overlappingArea:
772 raise pipeBase.NoWorkFound(
"No patches overlap detector")
774 self.checkPatchList(patchList)
776 coaddExposures = self.getDcrModel(patchList, dcrCoaddExposureHandles, visitInfo)
777 return pipeBase.Struct(coaddExposures=coaddExposures, dataIds=dataIds)
780 """Check that all of the DcrModel subfilters are present for each
786 Dict of the patches containing valid data for each tract.
791 If the number of exposures found for a patch does not match the
792 number of subfilters.
794 for tract
in patchList:
795 for patch
in set(patchList[tract]):
796 if patchList[tract].count(patch) != self.config.numSubfilters:
798 "Invalid number of DcrModel subfilters found: %d vs %d expected",
799 patchList[tract].count(patch),
800 self.config.numSubfilters,
804 """Build DCR-matched coadds from a list of exposure references.
809 Dict of the patches containing valid data for each tract.
810 coaddRefs : `list` [`lsst.daf.butler.DeferredDatasetHandle`]
811 Data references to `~lsst.afw.image.Exposure` representing
812 DcrModels that overlap the detector.
813 visitInfo : `lsst.afw.image.VisitInfo`
814 Metadata for the science image.
818 coaddExposures : `list` [`lsst.afw.image.Exposure`]
819 Coadd exposures that overlap the detector.
821 coaddExposures = collections.defaultdict(list)
822 for tract
in patchList:
823 for patch
in set(patchList[tract]):
826 for coaddRef
in coaddRefs
830 dcrModel = DcrModel.fromQuantum(
832 self.config.effectiveWavelength,
833 self.config.bandwidth,
834 self.config.numSubfilters,
836 coaddExposures[tract].append(dcrModel.buildMatchedExposureHandle(visitInfo=visitInfo))
837 return coaddExposures
841 condition = (coaddRef.dataId[
"tract"] == tract) & (
842 coaddRef.dataId[
"patch"] == patch
_selectDataRef(coaddRef, tract, patch)
checkPatchList(self, patchList)
_merge(self, maskedImages, bbox, wcs)
_makeExposureCatalog(self, exposureRefs, dataIds)
run(self, *, coaddExposureHandles, bbox, wcs, dataIds, physical_filter)
getDcrModel(self, patchList, coaddRefs, visitInfo)
checkHighVariance(self, template)
_makePsf(self, template, catalog, wcs)