LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
Public Member Functions | Static Public Attributes | List of all members
lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask Class Reference
Inheritance diagram for lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask:

Public Member Functions

def runDataRef (self, exposure, sensorRef, templateIdList=None)
 
def runQuantum (self, exposure, butlerQC, skyMapRef, coaddExposureRefs)
 
def getOverlapPatchList (self, exposure, skyMap)
 
def run (self, tractInfo, patchList, skyCorners, availableCoaddRefs, sensorRef=None, visitInfo=None)
 
def getCoaddDatasetName (self)
 

Static Public Attributes

 ConfigClass = GetCoaddAsTemplateConfig
 

Detailed Description

Subtask to retrieve coadd for use as an image difference template.

This is the default getTemplate Task to be run as a subtask by
``pipe.tasks.ImageDifferenceTask``. The main methods are ``run()`` and
``runGen3()``.

Notes
-----
From the given skymap, the closest tract is selected;  multiple tracts  are
not supported. The assembled template inherits the WCS of the selected
skymap tract and the resolution of the template exposures. Overlapping box
regions of the input template patches are pixel by pixel copied into the
assembled template image. There is no warping or pixel resampling.

Pixels with no overlap of any available input patches are set to ``nan`` value
and ``NO_DATA`` flagged.

Definition at line 82 of file getTemplate.py.

Member Function Documentation

◆ getCoaddDatasetName()

def lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.getCoaddDatasetName (   self)
Return coadd name for given task config

Returns
-------
CoaddDatasetName : `string`

TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)

Definition at line 393 of file getTemplate.py.

393  def getCoaddDatasetName(self):
394  """Return coadd name for given task config
395 
396  Returns
397  -------
398  CoaddDatasetName : `string`
399 
400  TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)
401  """
402  warpType = self.config.warpType
403  suffix = "" if warpType == "direct" else warpType[0].upper() + warpType[1:]
404  return self.config.coaddName + "Coadd" + suffix
405 
406 

◆ getOverlapPatchList()

def lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.getOverlapPatchList (   self,
  exposure,
  skyMap 
)
Select the relevant tract and its patches that overlap with the science exposure.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    The science exposure to define the sky region of the template coadd.

skyMap : `lsst.skymap.BaseSkyMap`
    SkyMap object that corresponds to the template coadd.

Returns
-------
result : `tuple` of
 - ``tractInfo`` : `lsst.skymap.TractInfo`
     The selected tract.
 - ``patchList`` : `list` of `lsst.skymap.PatchInfo`
     List of all overlap patches of the selected tract.
 - ``skyCorners`` : `list` of `lsst.geom.SpherePoint`
     Corners of the exposure in the sky in the order given by `lsst.geom.Box2D.getCorners`.

Definition at line 225 of file getTemplate.py.

225  def getOverlapPatchList(self, exposure, skyMap):
226  """Select the relevant tract and its patches that overlap with the science exposure.
227 
228  Parameters
229  ----------
230  exposure : `lsst.afw.image.Exposure`
231  The science exposure to define the sky region of the template coadd.
232 
233  skyMap : `lsst.skymap.BaseSkyMap`
234  SkyMap object that corresponds to the template coadd.
235 
236  Returns
237  -------
238  result : `tuple` of
239  - ``tractInfo`` : `lsst.skymap.TractInfo`
240  The selected tract.
241  - ``patchList`` : `list` of `lsst.skymap.PatchInfo`
242  List of all overlap patches of the selected tract.
243  - ``skyCorners`` : `list` of `lsst.geom.SpherePoint`
244  Corners of the exposure in the sky in the order given by `lsst.geom.Box2D.getCorners`.
245  """
246  expWcs = exposure.getWcs()
247  expBoxD = geom.Box2D(exposure.getBBox())
248  expBoxD.grow(self.config.templateBorderSize)
249  ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
250  tractInfo = skyMap.findTract(ctrSkyPos)
251  self.log.info("Using skyMap tract %s", tractInfo.getId())
252  skyCorners = [expWcs.pixelToSky(pixPos) for pixPos in expBoxD.getCorners()]
253  patchList = tractInfo.findPatchList(skyCorners)
254 
255  if not patchList:
256  raise RuntimeError("No suitable tract found")
257 
258  self.log.info("Assembling %d coadd patches", len(patchList))
259  self.log.info("exposure dimensions=%s", exposure.getDimensions())
260 
261  return (tractInfo, patchList, skyCorners)
262 
A floating-point coordinate rectangle geometry.
Definition: Box.h:413

◆ run()

def lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.run (   self,
  tractInfo,
  patchList,
  skyCorners,
  availableCoaddRefs,
  sensorRef = None,
  visitInfo = None 
)
Gen2 and gen3 shared code: determination of exposure dimensions and
copying of pixels from overlapping patch regions.

Parameters
----------
skyMap : `lsst.skymap.BaseSkyMap`
    SkyMap object that corresponds to the template coadd.
tractInfo : `lsst.skymap.TractInfo`
    The selected tract.
patchList : iterable of `lsst.skymap.patchInfo.PatchInfo`
    Patches to consider for making the template exposure.
skyCorners : list of `lsst.geom.SpherePoint`
    Sky corner coordinates to be covered by the template exposure.
availableCoaddRefs : `dict` [`int`]
    Dictionary of spatially relevant retrieved coadd patches,
    indexed by their sequential patch number. In Gen3 mode, values are
    `lsst.daf.butler.DeferredDatasetHandle` and ``.get()`` is called,
    in Gen2 mode, ``sensorRef.get(**coaddef)`` is called to retrieve the coadd.
sensorRef : `lsst.daf.persistence.ButlerDataRef`, Gen2 only
    Butler data reference to get coadd data.
    Must be `None` for Gen3.
visitInfo : `lsst.afw.image.VisitInfo`, Gen2 only
    VisitInfo to make dcr model.

Returns
-------
templateExposure: `lsst.afw.image.ExposureF`
    The created template exposure.

Definition at line 263 of file getTemplate.py.

264  sensorRef=None, visitInfo=None):
265  """Gen2 and gen3 shared code: determination of exposure dimensions and
266  copying of pixels from overlapping patch regions.
267 
268  Parameters
269  ----------
270  skyMap : `lsst.skymap.BaseSkyMap`
271  SkyMap object that corresponds to the template coadd.
272  tractInfo : `lsst.skymap.TractInfo`
273  The selected tract.
274  patchList : iterable of `lsst.skymap.patchInfo.PatchInfo`
275  Patches to consider for making the template exposure.
276  skyCorners : list of `lsst.geom.SpherePoint`
277  Sky corner coordinates to be covered by the template exposure.
278  availableCoaddRefs : `dict` [`int`]
279  Dictionary of spatially relevant retrieved coadd patches,
280  indexed by their sequential patch number. In Gen3 mode, values are
281  `lsst.daf.butler.DeferredDatasetHandle` and ``.get()`` is called,
282  in Gen2 mode, ``sensorRef.get(**coaddef)`` is called to retrieve the coadd.
283  sensorRef : `lsst.daf.persistence.ButlerDataRef`, Gen2 only
284  Butler data reference to get coadd data.
285  Must be `None` for Gen3.
286  visitInfo : `lsst.afw.image.VisitInfo`, Gen2 only
287  VisitInfo to make dcr model.
288 
289  Returns
290  -------
291  templateExposure: `lsst.afw.image.ExposureF`
292  The created template exposure.
293  """
294  coaddWcs = tractInfo.getWcs()
295 
296  # compute coadd bbox
297  coaddBBox = geom.Box2D()
298  for skyPos in skyCorners:
299  coaddBBox.include(coaddWcs.skyToPixel(skyPos))
300  coaddBBox = geom.Box2I(coaddBBox)
301  self.log.info("coadd dimensions=%s", coaddBBox.getDimensions())
302 
303  coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
304  coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask("NO_DATA"), np.nan)
305  nPatchesFound = 0
306  coaddFilterLabel = None
307  coaddPsf = None
308  coaddPhotoCalib = None
309  for patchInfo in patchList:
310  patchNumber = tractInfo.getSequentialPatchIndex(patchInfo)
311  patchSubBBox = patchInfo.getOuterBBox()
312  patchSubBBox.clip(coaddBBox)
313  if patchNumber not in availableCoaddRefs:
314  self.log.warning("skip patch=%d; patch does not exist for this coadd", patchNumber)
315  continue
316  if patchSubBBox.isEmpty():
317  if isinstance(availableCoaddRefs[patchNumber], DeferredDatasetHandle):
318  tract = availableCoaddRefs[patchNumber].dataId['tract']
319  else:
320  tract = availableCoaddRefs[patchNumber]['tract']
321  self.log.info("skip tract=%d patch=%d; no overlapping pixels", tract, patchNumber)
322  continue
323 
324  if self.config.coaddName == 'dcr':
325  patchInnerBBox = patchInfo.getInnerBBox()
326  patchInnerBBox.clip(coaddBBox)
327  if np.min(patchInnerBBox.getDimensions()) <= 2*self.config.templateBorderSize:
328  self.log.info("skip tract=%(tract)s, patch=%(patch)s; too few pixels.",
329  availableCoaddRefs[patchNumber])
330  continue
331  self.log.info("Constructing DCR-matched template for patch %s",
332  availableCoaddRefs[patchNumber])
333 
334  if sensorRef:
335  dcrModel = DcrModel.fromDataRef(sensorRef,
336  self.config.effectiveWavelength,
337  self.config.bandwidth,
338  **availableCoaddRefs[patchNumber])
339  else:
340  dcrModel = DcrModel.fromQuantum(availableCoaddRefs[patchNumber],
341  self.config.effectiveWavelength,
342  self.config.bandwidth)
343  # The edge pixels of the DcrCoadd may contain artifacts due to missing data.
344  # Each patch has significant overlap, and the contaminated edge pixels in
345  # a new patch will overwrite good pixels in the overlap region from
346  # previous patches.
347  # Shrink the BBox to remove the contaminated pixels,
348  # but make sure it is only the overlap region that is reduced.
349  dcrBBox = geom.Box2I(patchSubBBox)
350  dcrBBox.grow(-self.config.templateBorderSize)
351  dcrBBox.include(patchInnerBBox)
352  coaddPatch = dcrModel.buildMatchedExposure(bbox=dcrBBox,
353  wcs=coaddWcs,
354  visitInfo=visitInfo)
355  else:
356  if sensorRef is None:
357  # Gen3
358  coaddPatch = availableCoaddRefs[patchNumber].get()
359  else:
360  # Gen2
361  coaddPatch = sensorRef.get(**availableCoaddRefs[patchNumber])
362  nPatchesFound += 1
363 
364  # Gen2 get() seems to clip based on bbox kwarg but we removed bbox
365  # calculation from caller code. Gen3 also does not do this.
366  overlapBox = coaddPatch.getBBox()
367  overlapBox.clip(coaddBBox)
368  coaddExposure.maskedImage.assign(coaddPatch.maskedImage[overlapBox], overlapBox)
369 
370  if coaddFilterLabel is None:
371  coaddFilterLabel = coaddPatch.getFilterLabel()
372 
373  # Retrieve the PSF for this coadd tract, if not already retrieved
374  if coaddPsf is None and coaddPatch.hasPsf():
375  coaddPsf = coaddPatch.getPsf()
376 
377  # Retrieve the calibration for this coadd tract, if not already retrieved
378  if coaddPhotoCalib is None:
379  coaddPhotoCalib = coaddPatch.getPhotoCalib()
380 
381  if coaddPhotoCalib is None:
382  raise RuntimeError("No coadd PhotoCalib found!")
383  if nPatchesFound == 0:
384  raise RuntimeError("No patches found!")
385  if coaddPsf is None:
386  raise RuntimeError("No coadd Psf found!")
387 
388  coaddExposure.setPhotoCalib(coaddPhotoCalib)
389  coaddExposure.setPsf(coaddPsf)
390  coaddExposure.setFilterLabel(coaddFilterLabel)
391  return coaddExposure
392 
An integer coordinate rectangle.
Definition: Box.h:55

◆ runDataRef()

def lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.runDataRef (   self,
  exposure,
  sensorRef,
  templateIdList = None 
)
Gen2 task entry point. Retrieve and mosaic a template coadd exposure
that overlaps the science exposure.

Parameters
----------
exposure: `lsst.afw.image.Exposure`
    an exposure for which to generate an overlapping template
sensorRef : TYPE
    a Butler data reference that can be used to obtain coadd data
templateIdList : TYPE, optional
    list of data ids, unused here, in the case of coadd template

Returns
-------
result : `lsst.pipe.base.Struct`
    - ``exposure`` : `lsst.afw.image.ExposureF`
        a template coadd exposure assembled out of patches
    - ``sources`` :  None for this subtask

Definition at line 104 of file getTemplate.py.

104  def runDataRef(self, exposure, sensorRef, templateIdList=None):
105  """Gen2 task entry point. Retrieve and mosaic a template coadd exposure
106  that overlaps the science exposure.
107 
108  Parameters
109  ----------
110  exposure: `lsst.afw.image.Exposure`
111  an exposure for which to generate an overlapping template
112  sensorRef : TYPE
113  a Butler data reference that can be used to obtain coadd data
114  templateIdList : TYPE, optional
115  list of data ids, unused here, in the case of coadd template
116 
117  Returns
118  -------
119  result : `lsst.pipe.base.Struct`
120  - ``exposure`` : `lsst.afw.image.ExposureF`
121  a template coadd exposure assembled out of patches
122  - ``sources`` : None for this subtask
123  """
124  skyMap = sensorRef.get(datasetType=self.config.coaddName + "Coadd_skyMap")
125  tractInfo, patchList, skyCorners = self.getOverlapPatchList(exposure, skyMap)
126 
127  availableCoaddRefs = dict()
128  for patchInfo in patchList:
129  patchNumber = tractInfo.getSequentialPatchIndex(patchInfo)
130  patchArgDict = dict(
131  datasetType=self.getCoaddDatasetName() + "_sub",
132  bbox=patchInfo.getOuterBBox(),
133  tract=tractInfo.getId(),
134  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
135  subfilter=0,
136  numSubfilters=self.config.numSubfilters,
137  )
138 
139  if sensorRef.datasetExists(**patchArgDict):
140  self.log.info("Reading patch %s", patchArgDict)
141  availableCoaddRefs[patchNumber] = patchArgDict
142 
143  templateExposure = self.run(
144  tractInfo, patchList, skyCorners, availableCoaddRefs,
145  sensorRef=sensorRef, visitInfo=exposure.getInfo().getVisitInfo()
146  )
147  return pipeBase.Struct(exposure=templateExposure, sources=None)
148 

◆ runQuantum()

def lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.runQuantum (   self,
  exposure,
  butlerQC,
  skyMapRef,
  coaddExposureRefs 
)
Gen3 task entry point. Retrieve and mosaic a template coadd exposure
that overlaps the science exposure.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    The science exposure to define the sky region of the template coadd.
butlerQC : `lsst.pipe.base.ButlerQuantumContext`
    Butler like object that supports getting data by DatasetRef.
skyMapRef : `lsst.daf.butler.DatasetRef`
    Reference to SkyMap object that corresponds to the template coadd.
coaddExposureRefs : iterable of `lsst.daf.butler.DeferredDatasetRef`
    Iterable of references to the available template coadd patches.

Returns
-------
result : `lsst.pipe.base.Struct`
    - ``exposure`` : `lsst.afw.image.ExposureF`
        a template coadd exposure assembled out of patches
    - ``sources`` :  `None` for this subtask

Definition at line 149 of file getTemplate.py.

149  def runQuantum(self, exposure, butlerQC, skyMapRef, coaddExposureRefs):
150  """Gen3 task entry point. Retrieve and mosaic a template coadd exposure
151  that overlaps the science exposure.
152 
153  Parameters
154  ----------
155  exposure : `lsst.afw.image.Exposure`
156  The science exposure to define the sky region of the template coadd.
157  butlerQC : `lsst.pipe.base.ButlerQuantumContext`
158  Butler like object that supports getting data by DatasetRef.
159  skyMapRef : `lsst.daf.butler.DatasetRef`
160  Reference to SkyMap object that corresponds to the template coadd.
161  coaddExposureRefs : iterable of `lsst.daf.butler.DeferredDatasetRef`
162  Iterable of references to the available template coadd patches.
163 
164  Returns
165  -------
166  result : `lsst.pipe.base.Struct`
167  - ``exposure`` : `lsst.afw.image.ExposureF`
168  a template coadd exposure assembled out of patches
169  - ``sources`` : `None` for this subtask
170  """
171  skyMap = butlerQC.get(skyMapRef)
172  coaddExposureRefs = butlerQC.get(coaddExposureRefs)
173  tracts = [ref.dataId['tract'] for ref in coaddExposureRefs]
174  if tracts.count(tracts[0]) == len(tracts):
175  tractInfo = skyMap[tracts[0]]
176  else:
177  raise RuntimeError("Templates constructed from multiple Tracts not supported by this task. "
178  "Use GetMultiTractCoaddTemplateTask instead.")
179 
180  detectorBBox = exposure.getBBox()
181  detectorWcs = exposure.getWcs()
182  detectorCorners = detectorWcs.pixelToSky(geom.Box2D(detectorBBox).getCorners())
183  validPolygon = exposure.getInfo().getValidPolygon()
184  detectorPolygon = validPolygon if validPolygon else geom.Box2D(detectorBBox)
185 
186  availableCoaddRefs = dict()
187  overlappingArea = 0
188  for coaddRef in coaddExposureRefs:
189  dataId = coaddRef.dataId
190  patchWcs = skyMap[dataId['tract']].getWcs()
191  patchBBox = skyMap[dataId['tract']][dataId['patch']].getOuterBBox()
192  patchCorners = patchWcs.pixelToSky(geom.Box2D(patchBBox).getCorners())
193  patchPolygon = afwGeom.Polygon(detectorWcs.skyToPixel(patchCorners))
194  if patchPolygon.intersection(detectorPolygon):
195  overlappingArea += patchPolygon.intersectionSingle(detectorPolygon).calculateArea()
196  if self.config.coaddName == 'dcr':
197  self.log.info("Using template input tract=%s, patch=%s, subfilter=%s",
198  dataId['tract'], dataId['patch'], dataId['subfilter'])
199  if dataId['patch'] in availableCoaddRefs:
200  availableCoaddRefs[dataId['patch']].append(coaddRef)
201  else:
202  availableCoaddRefs[dataId['patch']] = [coaddRef, ]
203  else:
204  self.log.info("Using template input tract=%s, patch=%s",
205  dataId['tract'], dataId['patch'])
206  availableCoaddRefs[dataId['patch']] = coaddRef
207 
208  if overlappingArea == 0:
209  templateExposure = None
210  pixGood = 0
211  self.log.warning("No overlapping template patches found")
212  else:
213  patchList = [tractInfo[patch] for patch in availableCoaddRefs.keys()]
214  templateExposure = self.run(tractInfo, patchList, detectorCorners, availableCoaddRefs,
215  visitInfo=exposure.getInfo().getVisitInfo())
216  # Count the number of pixels with the NO_DATA mask bit set
217  # counting NaN pixels is insufficient because pixels without data are often intepolated over)
218  pixNoData = np.count_nonzero(templateExposure.mask.array
219  & templateExposure.mask.getPlaneBitMask('NO_DATA'))
220  pixGood = templateExposure.getBBox().getArea() - pixNoData
221  self.log.info("template has %d good pixels (%.1f%%)", pixGood,
222  100*pixGood/templateExposure.getBBox().getArea())
223  return pipeBase.Struct(exposure=templateExposure, sources=None, area=pixGood)
224 
Cartesian polygons.
Definition: Polygon.h:59
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33

Member Data Documentation

◆ ConfigClass

lsst.ip.diffim.getTemplate.GetCoaddAsTemplateTask.ConfigClass = GetCoaddAsTemplateConfig
static

Definition at line 101 of file getTemplate.py.


The documentation for this class was generated from the following file: