27 import lsst.pex.config
as pexConfig
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 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)
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` 225 sensorRef : `list` of `lsst.daf.persistence.ButlerDataRef` 226 Data reference of the calexp(s) to subtract from. 227 templateIdList : `list` of `lsst.daf.persistence.ButlerDataRef` 228 Data reference of the template calexp to be subtraced. 229 Can be incomplete, fields are initialized from `sensorRef`. 230 If there are multiple items, only the first one is used. 236 return a pipeBase.Struct: 238 - ``exposure`` : a template calexp 239 - ``sources`` : source catalog measured on the template 242 if len(templateIdList) == 0:
243 raise RuntimeError(
"No template data reference supplied.")
244 if len(templateIdList) > 1:
245 self.log.
warn(
"Multiple template data references supplied. Using the first one only.")
247 templateId = sensorRef.dataId.copy()
248 templateId.update(templateIdList[0])
250 self.log.
info(
"Fetching calexp (%s) as template." % (templateId))
252 butler = sensorRef.getButler()
253 template = butler.get(datasetType=
"calexp", dataId=templateId)
254 if self.config.doAddCalexpBackground:
255 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
256 mi = template.getMaskedImage()
257 mi += templateBg.getImage()
259 if not template.hasPsf():
260 raise pipeBase.TaskError(
"Template has no psf")
262 templateSources = butler.get(datasetType=
"src", dataId=templateId)
263 return pipeBase.Struct(exposure=template,
264 sources=templateSources)
def getCoaddDatasetName(self)
A floating-point coordinate rectangle geometry.
def run(self, exposure, sensorRef, templateIdList)
def run(self, exposure, sensorRef, templateIdList=None)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
An integer coordinate rectangle.