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. 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()
91 expBoxD = afwGeom.Box2D(exposure.getBBox())
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()
105 coaddBBox = afwGeom.Box2D()
106 for skyPos
in skyCorners:
107 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
108 coaddBBox = afwGeom.Box2I(coaddBBox)
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 for patchInfo
in patchList:
119 patchSubBBox = patchInfo.getOuterBBox()
120 patchSubBBox.clip(coaddBBox)
124 tract=tractInfo.getId(),
125 patch=
"%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
126 numSubfilters=self.config.numSubfilters,
128 if patchSubBBox.isEmpty():
129 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
132 if self.config.coaddName ==
'dcr':
133 if not sensorRef.datasetExists(subfilter=0, **patchArgDict):
134 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s," 135 " numSubfilters=%(numSubfilters)s, subfilter=0 does not exist" 138 self.log.info(
"Constructing DCR-matched template for patch %s" % patchArgDict)
139 dcrModel = DcrModel.fromDataRef(sensorRef, **patchArgDict)
146 patchInnerBBox = patchInfo.getInnerBBox()
147 patchInnerBBox.clip(coaddBBox)
148 dcrBBox = afwGeom.Box2I(patchSubBBox)
149 dcrBBox.grow(-self.config.templateBorderSize)
150 dcrBBox.include(patchInnerBBox)
151 coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
153 visitInfo=exposure.getInfo().getVisitInfo())
155 if not sensorRef.datasetExists(**patchArgDict):
156 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist" 159 self.log.info(
"Reading patch %s" % patchArgDict)
160 coaddPatch = sensorRef.get(**patchArgDict)
162 coaddExposure.maskedImage.assign(coaddPatch.maskedImage, coaddPatch.getBBox())
163 if coaddFilter
is None:
164 coaddFilter = coaddPatch.getFilter()
167 if coaddPsf
is None and coaddPatch.hasPsf():
168 coaddPsf = coaddPatch.getPsf()
170 if nPatchesFound == 0:
171 raise RuntimeError(
"No patches found!")
174 raise RuntimeError(
"No coadd Psf found!")
176 coaddExposure.setPsf(coaddPsf)
177 coaddExposure.setFilter(coaddFilter)
178 return pipeBase.Struct(exposure=coaddExposure,
182 """Return coadd name for given task config 186 CoaddDatasetName : `string` 188 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985) 190 warpType = self.config.warpType
191 suffix =
"" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
192 return self.config.coaddName +
"Coadd" + suffix
196 doAddCalexpBackground = pexConfig.Field(
199 doc=
"Add background to calexp before processing it." 204 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef 205 for use as an image difference template. 207 To be run as a subtask by pipe.tasks.ImageDifferenceTask. 208 Intended for use with simulations and surveys that repeatedly visit the same pointing. 209 This code was originally part of Winter2013ImageDifferenceTask. 212 ConfigClass = GetCalexpAsTemplateConfig
213 _DefaultName =
"GetCalexpAsTemplateTask" 215 def run(self, exposure, sensorRef, templateIdList):
216 """Return a calexp exposure with based on input sensorRef. 218 Construct a dataId based on the sensorRef.dataId combined 219 with the specifications from the first dataId in templateIdList 223 exposure : `lsst.afw.image.Exposure` 226 a Butler data reference 227 templateIdList : TYPE 228 list of data ids, which should contain a single item. 229 If there are multiple items, only the first is used. 235 return a pipeBase.Struct: 237 - ``exposure`` : a template calexp 238 - ``sources`` : source catalog measured on the template 241 if len(templateIdList) == 0:
242 raise RuntimeError(
"No template data reference supplied.")
243 if len(templateIdList) > 1:
244 self.log.warn(
"Multiple template data references supplied. Using the first one only.")
246 templateId = sensorRef.dataId.copy()
247 templateId.update(templateIdList[0])
249 self.log.info(
"Fetching calexp (%s) as template." % (templateId))
251 butler = sensorRef.getButler()
252 template = butler.get(datasetType=
"calexp", dataId=templateId)
253 if self.config.doAddCalexpBackground:
254 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
255 mi = template.getMaskedImage()
256 mi += templateBg.getImage()
258 if not template.hasPsf():
259 raise pipeBase.TaskError(
"Template has no psf")
261 templateSources = butler.get(datasetType=
"src", dataId=templateId)
262 return pipeBase.Struct(exposure=template,
263 sources=templateSources)
def getCoaddDatasetName(self)
def run(self, exposure, sensorRef, templateIdList)
def run(self, exposure, sensorRef, templateIdList=None)