27 import lsst.pex.config
as pexConfig
28 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. 66 ConfigClass = GetCoaddAsTemplateConfig
67 _DefaultName =
"GetCoaddAsTemplateTask" 69 def run(self, exposure, sensorRef, templateIdList=None):
70 """Retrieve and mosaic a template coadd exposure that overlaps the exposure 74 exposure: `lsst.afw.image.Exposure` 75 an exposure for which to generate an overlapping template 77 a Butler data reference that can be used to obtain coadd data 78 templateIdList : TYPE, optional 79 list of data ids (unused) 84 return a pipeBase.Struct: 86 - ``exposure`` : a template coadd exposure assembled out of patches 87 - ``sources`` : None for this subtask 89 skyMap = sensorRef.get(datasetType=self.config.coaddName +
"Coadd_skyMap")
90 expWcs = exposure.getWcs()
92 expBoxD.grow(self.config.templateBorderSize)
93 ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
94 tractInfo = skyMap.findTract(ctrSkyPos)
95 self.log.info(
"Using skyMap tract %s" % (tractInfo.getId(),))
96 skyCorners = [expWcs.pixelToSky(pixPos)
for pixPos
in expBoxD.getCorners()]
97 patchList = tractInfo.findPatchList(skyCorners)
100 raise RuntimeError(
"No suitable tract found")
101 self.log.info(
"Assembling %s coadd patches" % (len(patchList),))
104 coaddWcs = tractInfo.getWcs()
106 for skyPos
in skyCorners:
107 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
109 self.log.info(
"exposure dimensions=%s; coadd dimensions=%s" %
110 (exposure.getDimensions(), coaddBBox.getDimensions()))
113 coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
114 coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask(
"NO_DATA"), np.nan)
118 coaddPhotoCalib =
None 119 for patchInfo
in patchList:
120 patchSubBBox = patchInfo.getOuterBBox()
121 patchSubBBox.clip(coaddBBox)
125 tract=tractInfo.getId(),
126 patch=
"%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
127 numSubfilters=self.config.numSubfilters,
129 if patchSubBBox.isEmpty():
130 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
133 if self.config.coaddName ==
'dcr':
134 if not sensorRef.datasetExists(subfilter=0, **patchArgDict):
135 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s," 136 " numSubfilters=%(numSubfilters)s, subfilter=0 does not exist" 139 patchInnerBBox = patchInfo.getInnerBBox()
140 patchInnerBBox.clip(coaddBBox)
141 if np.min(patchInnerBBox.getDimensions()) <= 2*self.config.templateBorderSize:
142 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; too few pixels." % patchArgDict)
144 self.log.info(
"Constructing DCR-matched template for patch %s" % patchArgDict)
146 dcrModel = DcrModel.fromDataRef(sensorRef, **patchArgDict)
154 dcrBBox.grow(-self.config.templateBorderSize)
155 dcrBBox.include(patchInnerBBox)
156 coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
158 visitInfo=exposure.getInfo().getVisitInfo())
160 if not sensorRef.datasetExists(**patchArgDict):
161 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist" 164 self.log.info(
"Reading patch %s" % patchArgDict)
165 coaddPatch = sensorRef.get(**patchArgDict)
167 coaddExposure.maskedImage.assign(coaddPatch.maskedImage, coaddPatch.getBBox())
168 if coaddFilter
is None:
169 coaddFilter = coaddPatch.getFilter()
172 if coaddPsf
is None and coaddPatch.hasPsf():
173 coaddPsf = coaddPatch.getPsf()
176 if coaddPhotoCalib
is None:
177 coaddPhotoCalib = coaddPatch.getPhotoCalib()
179 if nPatchesFound == 0:
180 raise RuntimeError(
"No patches found!")
183 raise RuntimeError(
"No coadd Psf found!")
185 if coaddPhotoCalib
is None:
186 raise RuntimeError(
"No coadd PhotoCalib found!")
188 coaddExposure.setPhotoCalib(coaddPhotoCalib)
189 coaddExposure.setPsf(coaddPsf)
190 coaddExposure.setFilter(coaddFilter)
191 return pipeBase.Struct(exposure=coaddExposure,
195 """Return coadd name for given task config 199 CoaddDatasetName : `string` 201 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985) 203 warpType = self.config.warpType
204 suffix =
"" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
205 return self.config.coaddName +
"Coadd" + suffix
209 doAddCalexpBackground = pexConfig.Field(
212 doc=
"Add background to calexp before processing it." 217 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef 218 for use as an image difference template. 220 To be run as a subtask by pipe.tasks.ImageDifferenceTask. 221 Intended for use with simulations and surveys that repeatedly visit the same pointing. 222 This code was originally part of Winter2013ImageDifferenceTask. 225 ConfigClass = GetCalexpAsTemplateConfig
226 _DefaultName =
"GetCalexpAsTemplateTask" 228 def run(self, exposure, sensorRef, templateIdList):
229 """Return a calexp exposure with based on input sensorRef. 231 Construct a dataId based on the sensorRef.dataId combined 232 with the specifications from the first dataId in templateIdList 236 exposure : `lsst.afw.image.Exposure` 238 sensorRef : `list` of `lsst.daf.persistence.ButlerDataRef` 239 Data reference of the calexp(s) to subtract from. 240 templateIdList : `list` of `lsst.daf.persistence.ButlerDataRef` 241 Data reference of the template calexp to be subtraced. 242 Can be incomplete, fields are initialized from `sensorRef`. 243 If there are multiple items, only the first one is used. 249 return a pipeBase.Struct: 251 - ``exposure`` : a template calexp 252 - ``sources`` : source catalog measured on the template 255 if len(templateIdList) == 0:
256 raise RuntimeError(
"No template data reference supplied.")
257 if len(templateIdList) > 1:
258 self.log.warn(
"Multiple template data references supplied. Using the first one only.")
260 templateId = sensorRef.dataId.copy()
261 templateId.update(templateIdList[0])
263 self.log.info(
"Fetching calexp (%s) as template." % (templateId))
265 butler = sensorRef.getButler()
266 template = butler.get(datasetType=
"calexp", dataId=templateId)
267 if self.config.doAddCalexpBackground:
268 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
269 mi = template.getMaskedImage()
270 mi += templateBg.getImage()
272 if not template.hasPsf():
273 raise pipeBase.TaskError(
"Template has no psf")
275 templateSources = butler.get(datasetType=
"src", dataId=templateId)
276 return pipeBase.Struct(exposure=template,
277 sources=templateSources)
def getCoaddDatasetName(self)
def run(self, exposure, sensorRef, templateIdList)
def run(self, exposure, sensorRef, templateIdList=None)