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 patchInnerBBox = patchInfo.getInnerBBox()
139 patchInnerBBox.clip(coaddBBox)
140 if np.min(patchInnerBBox.getDimensions()) <= 2*self.config.templateBorderSize:
141 self.log.
info(
"skip tract=%(tract)s, patch=%(patch)s; too few pixels." % patchArgDict)
143 self.log.
info(
"Constructing DCR-matched template for patch %s" % patchArgDict)
145 dcrModel = DcrModel.fromDataRef(sensorRef, **patchArgDict)
153 dcrBBox.grow(-self.config.templateBorderSize)
154 dcrBBox.include(patchInnerBBox)
155 coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
157 visitInfo=exposure.getInfo().getVisitInfo())
159 if not sensorRef.datasetExists(**patchArgDict):
160 self.log.
warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist" 163 self.log.
info(
"Reading patch %s" % patchArgDict)
164 coaddPatch = sensorRef.get(**patchArgDict)
166 coaddExposure.maskedImage.assign(coaddPatch.maskedImage, coaddPatch.getBBox())
167 if coaddFilter
is None:
168 coaddFilter = coaddPatch.getFilter()
171 if coaddPsf
is None and coaddPatch.hasPsf():
172 coaddPsf = coaddPatch.getPsf()
174 if nPatchesFound == 0:
175 raise RuntimeError(
"No patches found!")
178 raise RuntimeError(
"No coadd Psf found!")
180 coaddExposure.setPsf(coaddPsf)
181 coaddExposure.setFilter(coaddFilter)
182 return pipeBase.Struct(exposure=coaddExposure,
186 """Return coadd name for given task config 190 CoaddDatasetName : `string` 192 TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985) 194 warpType = self.config.warpType
195 suffix =
"" if warpType ==
"direct" else warpType[0].upper() + warpType[1:]
196 return self.config.coaddName +
"Coadd" + suffix
200 doAddCalexpBackground = pexConfig.Field(
203 doc=
"Add background to calexp before processing it." 208 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef 209 for use as an image difference template. 211 To be run as a subtask by pipe.tasks.ImageDifferenceTask. 212 Intended for use with simulations and surveys that repeatedly visit the same pointing. 213 This code was originally part of Winter2013ImageDifferenceTask. 216 ConfigClass = GetCalexpAsTemplateConfig
217 _DefaultName =
"GetCalexpAsTemplateTask" 219 def run(self, exposure, sensorRef, templateIdList):
220 """Return a calexp exposure with based on input sensorRef. 222 Construct a dataId based on the sensorRef.dataId combined 223 with the specifications from the first dataId in templateIdList 227 exposure : `lsst.afw.image.Exposure` 229 sensorRef : `list` of `lsst.daf.persistence.ButlerDataRef` 230 Data reference of the calexp(s) to subtract from. 231 templateIdList : `list` of `lsst.daf.persistence.ButlerDataRef` 232 Data reference of the template calexp to be subtraced. 233 Can be incomplete, fields are initialized from `sensorRef`. 234 If there are multiple items, only the first one is used. 240 return a pipeBase.Struct: 242 - ``exposure`` : a template calexp 243 - ``sources`` : source catalog measured on the template 246 if len(templateIdList) == 0:
247 raise RuntimeError(
"No template data reference supplied.")
248 if len(templateIdList) > 1:
249 self.log.
warn(
"Multiple template data references supplied. Using the first one only.")
251 templateId = sensorRef.dataId.copy()
252 templateId.update(templateIdList[0])
254 self.log.
info(
"Fetching calexp (%s) as template." % (templateId))
256 butler = sensorRef.getButler()
257 template = butler.get(datasetType=
"calexp", dataId=templateId)
258 if self.config.doAddCalexpBackground:
259 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
260 mi = template.getMaskedImage()
261 mi += templateBg.getImage()
263 if not template.hasPsf():
264 raise pipeBase.TaskError(
"Template has no psf")
266 templateSources = butler.get(datasetType=
"src", dataId=templateId)
267 return pipeBase.Struct(exposure=template,
268 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.