26 import lsst.pipe.base
as pipeBase
31 __all__ = [
"GetCoaddAsTemplateTask",
"GetCoaddAsTemplateConfig",
32 "GetCalexpAsTemplateTask",
"GetCalexpAsTemplateConfig"]
36 templateBorderSize = pexConfig.Field(
39 doc=
"Number of pixels to grow the requested template image to account for warping" 41 coaddName = pexConfig.Field(
42 doc=
"coadd name: typically one of 'deep', 'goodSeeing', or 'dcr'",
46 numSubfilters = pexConfig.Field(
47 doc=
"Number of subfilters in the DcrCoadd, used only if ``coaddName``='dcr'",
51 warpType = pexConfig.Field(
52 doc=
"Warp type of the coadd template: one of 'direct' or 'psfMatched'",
59 """Subtask to retrieve coadd for use as an image difference template. 61 This is the default getTemplate Task to be run as a subtask by 62 pipe.tasks.ImageDifferenceTask. The main method is run(). 63 It assumes that coadds reside in the repository given by sensorRef. 65 ConfigClass = GetCoaddAsTemplateConfig
66 _DefaultName =
"GetCoaddAsTemplateTask" 68 def run(self, exposure, sensorRef, templateIdList=None):
69 """!Retrieve and mosaic a template coadd exposure that overlaps the exposure 71 @param[in] exposure -- an exposure for which to generate an overlapping template 72 @param[in] sensorRef -- a Butler data reference that can be used to obtain coadd data 73 @param[in] templateIdList -- list of data ids (unused) 75 @return a pipeBase.Struct 76 - exposure: a template coadd exposure assembled out of patches 77 - sources: None for this subtask 79 skyMap = sensorRef.get(datasetType=self.config.coaddName +
"Coadd_skyMap")
80 expWcs = exposure.getWcs()
81 expBoxD = afwGeom.Box2D(exposure.getBBox())
82 expBoxD.grow(self.config.templateBorderSize)
83 ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
84 tractInfo = skyMap.findTract(ctrSkyPos)
85 self.log.info(
"Using skyMap tract %s" % (tractInfo.getId(),))
86 skyCorners = [expWcs.pixelToSky(pixPos)
for pixPos
in expBoxD.getCorners()]
87 patchList = tractInfo.findPatchList(skyCorners)
90 raise RuntimeError(
"No suitable tract found")
91 self.log.info(
"Assembling %s coadd patches" % (len(patchList),))
94 coaddWcs = tractInfo.getWcs()
95 coaddBBox = afwGeom.Box2D()
96 for skyPos
in skyCorners:
97 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
98 coaddBBox = afwGeom.Box2I(coaddBBox)
99 self.log.info(
"exposure dimensions=%s; coadd dimensions=%s" %
100 (exposure.getDimensions(), coaddBBox.getDimensions()))
103 coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
104 coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
108 for patchInfo
in patchList:
109 patchSubBBox = patchInfo.getOuterBBox()
110 patchSubBBox.clip(coaddBBox)
114 tract=tractInfo.getId(),
115 patch=
"%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
116 numSubfilters=self.config.numSubfilters,
118 if patchSubBBox.isEmpty():
119 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
122 if self.config.coaddName ==
'dcr':
123 if not sensorRef.datasetExists(subfilter=0, **patchArgDict):
124 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s," 125 " numSubfilters=%(numSubfilters)s, subfilter=0 does not exist" 128 self.log.info(
"Constructing DCR-matched template for patch %s" % patchArgDict)
129 dcrModel = DcrModel.fromDataRef(sensorRef, **patchArgDict)
130 coaddPatch = dcrModel.buildMatchedExposure(bbox=patchSubBBox,
132 visitInfo=exposure.getInfo().getVisitInfo())
134 if not sensorRef.datasetExists(**patchArgDict):
135 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist" 138 self.log.info(
"Reading patch %s" % patchArgDict)
139 coaddPatch = sensorRef.get(**patchArgDict)
141 coaddExposure.maskedImage.assign(coaddPatch.maskedImage, coaddPatch.getBBox())
142 if coaddFilter
is None:
143 coaddFilter = coaddPatch.getFilter()
146 if coaddPsf
is None and coaddPatch.hasPsf():
147 coaddPsf = coaddPatch.getPsf()
149 if nPatchesFound == 0:
150 raise RuntimeError(
"No patches found!")
153 raise RuntimeError(
"No coadd Psf found!")
155 coaddExposure.setPsf(coaddPsf)
156 coaddExposure.setFilter(coaddFilter)
157 return pipeBase.Struct(exposure=coaddExposure,
161 """Return coadd name for given task config 165 CoaddDatasetName : `string` 167 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985) 169 warpType = self.config.warpType
170 suffix =
"" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
171 return self.config.coaddName +
"Coadd" + suffix
175 doAddCalexpBackground = pexConfig.Field(
178 doc=
"Add background to calexp before processing it." 183 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef 184 for use as an image difference template. 186 To be run as a subtask by pipe.tasks.ImageDifferenceTask. 187 Intended for use with simulations and surveys that repeatedly visit the same pointing. 188 This code was originally part of Winter2013ImageDifferenceTask. 191 ConfigClass = GetCalexpAsTemplateConfig
192 _DefaultName =
"GetCalexpAsTemplateTask" 194 def run(self, exposure, sensorRef, templateIdList):
195 """!Return a calexp exposure with based on input sensorRef. 197 Construct a dataId based on the sensorRef.dataId combined 198 with the specifications from the first dataId in templateIdList 200 @param[in] exposure -- exposure (unused) 201 @param[in] sensorRef -- a Butler data reference 202 @param[in] templateIdList -- list of data ids, which should contain a single item. 203 If there are multiple items, only the first is used. 205 @return a pipeBase.Struct 206 - exposure: a template calexp 207 - sources: source catalog measured on the template 210 if len(templateIdList) == 0:
211 raise RuntimeError(
"No template supplied! Please supply a template visit id.")
212 if len(templateIdList) > 1:
213 self.log.warn(
"Multiple template visits supplied. Getting template from first visit: %s" %
214 (templateIdList[0][
'visit']))
216 templateId = sensorRef.dataId.copy()
217 templateId.update(templateIdList[0])
219 self.log.info(
"Fetching calexp (%s) as template." % (templateId))
221 butler = sensorRef.getButler()
222 template = butler.get(datasetType=
"calexp", dataId=templateId)
223 if self.config.doAddCalexpBackground:
224 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
225 mi = template.getMaskedImage()
226 mi += templateBg.getImage()
228 if not template.hasPsf():
229 raise pipeBase.TaskError(
"Template has no psf")
231 templateSources = butler.get(datasetType=
"src", dataId=templateId)
232 return pipeBase.Struct(exposure=template,
233 sources=templateSources)
def getCoaddDatasetName(self)
def run(self, exposure, sensorRef, templateIdList)
Return a calexp exposure with based on input sensorRef.
def run(self, exposure, sensorRef, templateIdList=None)
Retrieve and mosaic a template coadd exposure that overlaps the exposure.