lsst.ip.diffim  16.0-4-gcfd1396+4
getTemplate.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2016 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 import numpy as np
24 
25 import lsst.pex.config as pexConfig
26 import lsst.pipe.base as pipeBase
27 import lsst.afw.geom as afwGeom
28 import lsst.afw.image as afwImage
29 
30 __all__ = ["GetCoaddAsTemplateTask", "GetCoaddAsTemplateConfig",
31  "GetCalexpAsTemplateTask", "GetCalexpAsTemplateConfig"]
32 
33 
34 class GetCoaddAsTemplateConfig(pexConfig.Config):
35  templateBorderSize = pexConfig.Field(
36  dtype=int,
37  default=10,
38  doc="Number of pixels to grow the requested template image to account for warping"
39  )
40  coaddName = pexConfig.Field(
41  doc="coadd name: typically one of deep or goodSeeing",
42  dtype=str,
43  default="deep",
44  )
45  warpType = pexConfig.Field(
46  doc="Warp type of the coadd template: one of 'direct' or 'psfMatched'",
47  dtype=str,
48  default="direct",
49  )
50 
51 
52 class GetCoaddAsTemplateTask(pipeBase.Task):
53  """Subtask to retrieve coadd for use as an image difference template.
54 
55  This is the default getTemplate Task to be run as a subtask by
56  pipe.tasks.ImageDifferenceTask. The main method is run().
57  It assumes that coadds reside in the repository given by sensorRef.
58  """
59  ConfigClass = GetCoaddAsTemplateConfig
60  _DefaultName = "GetCoaddAsTemplateTask"
61 
62  def run(self, exposure, sensorRef, templateIdList=None):
63  """!Retrieve and mosaic a template coadd exposure that overlaps the exposure
64 
65  @param[in] exposure -- an exposure for which to generate an overlapping template
66  @param[in] sensorRef -- a Butler data reference that can be used to obtain coadd data
67  @param[in] templateIdList -- list of data ids (unused)
68 
69  @return a pipeBase.Struct
70  - exposure: a template coadd exposure assembled out of patches
71  - sources: None for this subtask
72  """
73  skyMap = sensorRef.get(datasetType=self.config.coaddName + "Coadd_skyMap")
74  expWcs = exposure.getWcs()
75  expBoxD = afwGeom.Box2D(exposure.getBBox())
76  expBoxD.grow(self.config.templateBorderSize)
77  ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
78  tractInfo = skyMap.findTract(ctrSkyPos)
79  self.log.info("Using skyMap tract %s" % (tractInfo.getId(),))
80  skyCorners = [expWcs.pixelToSky(pixPos) for pixPos in expBoxD.getCorners()]
81  patchList = tractInfo.findPatchList(skyCorners)
82 
83  if not patchList:
84  raise RuntimeError("No suitable tract found")
85  self.log.info("Assembling %s coadd patches" % (len(patchList),))
86 
87  # compute coadd bbox
88  coaddWcs = tractInfo.getWcs()
89  coaddBBox = afwGeom.Box2D()
90  for skyPos in skyCorners:
91  coaddBBox.include(coaddWcs.skyToPixel(skyPos))
92  coaddBBox = afwGeom.Box2I(coaddBBox)
93  self.log.info("exposure dimensions=%s; coadd dimensions=%s" %
94  (exposure.getDimensions(), coaddBBox.getDimensions()))
95 
96  # assemble coadd exposure from subregions of patches
97  coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
98  coaddExposure.getMaskedImage().set(np.nan, afwImage.Mask.getPlaneBitMask("NO_DATA"), np.nan)
99  nPatchesFound = 0
100  coaddFilter = None
101  coaddPsf = None
102  for patchInfo in patchList:
103  patchSubBBox = patchInfo.getOuterBBox()
104  patchSubBBox.clip(coaddBBox)
105  patchArgDict = dict(
106  datasetType=self.getCoaddDatasetName() + "_sub",
107  bbox=patchSubBBox,
108  tract=tractInfo.getId(),
109  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
110  )
111  if patchSubBBox.isEmpty():
112  self.log.info("skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
113  continue
114  if not sensorRef.datasetExists(**patchArgDict):
115  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
116  % patchArgDict)
117  continue
118 
119  nPatchesFound += 1
120  self.log.info("Reading patch %s" % patchArgDict)
121  coaddPatch = sensorRef.get(**patchArgDict)
122  coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
123  if coaddFilter is None:
124  coaddFilter = coaddPatch.getFilter()
125 
126  # Retrieve the PSF for this coadd tract, if not already retrieved
127  if coaddPsf is None and coaddPatch.hasPsf():
128  coaddPsf = coaddPatch.getPsf()
129 
130  if nPatchesFound == 0:
131  raise RuntimeError("No patches found!")
132 
133  if coaddPsf is None:
134  raise RuntimeError("No coadd Psf found!")
135 
136  coaddExposure.setPsf(coaddPsf)
137  coaddExposure.setFilter(coaddFilter)
138  return pipeBase.Struct(exposure=coaddExposure,
139  sources=None)
140 
142  """Return coadd name for given task config
143 
144  Returns
145  -------
146  CoaddDatasetName : `string`
147 
148  TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)
149  """
150  warpType = self.config.warpType
151  suffix = "" if warpType == "direct" else warpType[0].upper() + warpType[1:]
152  return self.config.coaddName + "Coadd" + suffix
153 
154 
155 class GetCalexpAsTemplateConfig(pexConfig.Config):
156  doAddCalexpBackground = pexConfig.Field(
157  dtype=bool,
158  default=True,
159  doc="Add background to calexp before processing it."
160  )
161 
162 
163 class GetCalexpAsTemplateTask(pipeBase.Task):
164  """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
165  for use as an image difference template.
166 
167  To be run as a subtask by pipe.tasks.ImageDifferenceTask.
168  Intended for use with simulations and surveys that repeatedly visit the same pointing.
169  This code was originally part of Winter2013ImageDifferenceTask.
170  """
171 
172  ConfigClass = GetCalexpAsTemplateConfig
173  _DefaultName = "GetCalexpAsTemplateTask"
174 
175  def run(self, exposure, sensorRef, templateIdList):
176  """!Return a calexp exposure with based on input sensorRef.
177 
178  Construct a dataId based on the sensorRef.dataId combined
179  with the specifications from the first dataId in templateIdList
180 
181  @param[in] exposure -- exposure (unused)
182  @param[in] sensorRef -- a Butler data reference
183  @param[in] templateIdList -- list of data ids, which should contain a single item.
184  If there are multiple items, only the first is used.
185 
186  @return a pipeBase.Struct
187  - exposure: a template calexp
188  - sources: source catalog measured on the template
189  """
190 
191  if len(templateIdList) == 0:
192  raise RuntimeError("No template supplied! Please supply a template visit id.")
193  if len(templateIdList) > 1:
194  self.log.warn("Multiple template visits supplied. Getting template from first visit: %s" %
195  (templateIdList[0]['visit']))
196 
197  templateId = sensorRef.dataId.copy()
198  templateId.update(templateIdList[0])
199 
200  self.log.info("Fetching calexp (%s) as template." % (templateId))
201 
202  butler = sensorRef.getButler()
203  template = butler.get(datasetType="calexp", dataId=templateId)
204  if self.config.doAddCalexpBackground:
205  templateBg = butler.get(datasetType="calexpBackground", dataId=templateId)
206  mi = template.getMaskedImage()
207  mi += templateBg.getImage()
208 
209  if not template.hasPsf():
210  raise pipeBase.TaskError("Template has no psf")
211 
212  templateSources = butler.get(datasetType="src", dataId=templateId)
213  return pipeBase.Struct(exposure=template,
214  sources=templateSources)
def run(self, exposure, sensorRef, templateIdList)
Return a calexp exposure with based on input sensorRef.
Definition: getTemplate.py:175
def run(self, exposure, sensorRef, templateIdList=None)
Retrieve and mosaic a template coadd exposure that overlaps the exposure.
Definition: getTemplate.py:62