lsst.ip.diffim  13.0-17-gc2cefa3
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 from __future__ import absolute_import, division, print_function
23 
24 import numpy as np
25 
26 import lsst.pex.config as pexConfig
27 import lsst.pipe.base as pipeBase
28 import lsst.afw.geom as afwGeom
29 import lsst.afw.image as afwImage
30 
31 __all__ = ["GetCoaddAsTemplateTask", "GetCoaddAsTemplateConfig",
32  "GetCalexpAsTemplateTask", "GetCalexpAsTemplateConfig"]
33 
34 
35 class GetCoaddAsTemplateConfig(pexConfig.Config):
36  templateBorderSize = pexConfig.Field(
37  dtype=int,
38  default=10,
39  doc="Number of pixels to grow the requested template image to account for warping"
40  )
41  coaddName = pexConfig.Field(
42  doc="coadd name: typically one of deep or goodSeeing",
43  dtype=str,
44  default="deep",
45  )
46 
47 
48 class GetCoaddAsTemplateTask(pipeBase.Task):
49  """Subtask to retrieve coadd for use as an image difference template.
50 
51  This is the default getTemplate Task to be run as a subtask by
52  pipe.tasks.ImageDifferenceTask. The main method is run().
53  It assumes that coadds reside in the repository given by sensorRef.
54  """
55  ConfigClass = GetCoaddAsTemplateConfig
56  _DefaultName = "GetCoaddAsTemplateTask"
57 
58  def run(self, exposure, sensorRef, templateIdList=None):
59  """!Retrieve and mosaic a template coadd exposure that overlaps the exposure
60 
61  \param[in] exposure -- an exposure for which to generate an overlapping template
62  \param[in] sensorRef -- a Butler data reference that can be used to obtain coadd data
63  \param[in] templateIdList -- list of data ids (unused)
64 
65  \return a pipeBase.Struct
66  - exposure: a template coadd exposure assembled out of patches
67  - sources: None for this subtask
68  """
69  skyMap = sensorRef.get(datasetType=self.config.coaddName + "Coadd_skyMap")
70  expWcs = exposure.getWcs()
71  expBoxD = afwGeom.Box2D(exposure.getBBox())
72  expBoxD.grow(self.config.templateBorderSize)
73  ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
74  tractInfo = skyMap.findTract(ctrSkyPos)
75  self.log.info("Using skyMap tract %s" % (tractInfo.getId(),))
76  skyCorners = [expWcs.pixelToSky(pixPos) for pixPos in expBoxD.getCorners()]
77  patchList = tractInfo.findPatchList(skyCorners)
78 
79  if not patchList:
80  raise RuntimeError("No suitable tract found")
81  self.log.info("Assembling %s coadd patches" % (len(patchList),))
82 
83  # compute coadd bbox
84  coaddWcs = tractInfo.getWcs()
85  coaddBBox = afwGeom.Box2D()
86  for skyPos in skyCorners:
87  coaddBBox.include(coaddWcs.skyToPixel(skyPos))
88  coaddBBox = afwGeom.Box2I(coaddBBox)
89  self.log.info("exposure dimensions=%s; coadd dimensions=%s" %
90  (exposure.getDimensions(), coaddBBox.getDimensions()))
91 
92  # assemble coadd exposure from subregions of patches
93  coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
94  coaddExposure.getMaskedImage().set(np.nan, afwImage.MaskU.getPlaneBitMask("NO_DATA"), np.nan)
95  nPatchesFound = 0
96  coaddFilter = None
97  coaddPsf = None
98  for patchInfo in patchList:
99  patchSubBBox = patchInfo.getOuterBBox()
100  patchSubBBox.clip(coaddBBox)
101  patchArgDict = dict(
102  datasetType=self.config.coaddName + "Coadd_sub",
103  bbox=patchSubBBox,
104  tract=tractInfo.getId(),
105  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
106  )
107  if patchSubBBox.isEmpty():
108  self.log.info("skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
109  continue
110  if not sensorRef.datasetExists(**patchArgDict):
111  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
112  % patchArgDict)
113  continue
114 
115  nPatchesFound += 1
116  self.log.info("Reading patch %s" % patchArgDict)
117  coaddPatch = sensorRef.get(**patchArgDict)
118  coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
119  if coaddFilter is None:
120  coaddFilter = coaddPatch.getFilter()
121 
122  # Retrieve the PSF for this coadd tract, if not already retrieved
123  if coaddPsf is None and coaddPatch.hasPsf():
124  coaddPsf = coaddPatch.getPsf()
125 
126  if nPatchesFound == 0:
127  raise RuntimeError("No patches found!")
128 
129  if coaddPsf is None:
130  raise RuntimeError("No coadd Psf found!")
131 
132  coaddExposure.setPsf(coaddPsf)
133  coaddExposure.setFilter(coaddFilter)
134  return pipeBase.Struct(exposure=coaddExposure,
135  sources=None)
136 
137 
138 class GetCalexpAsTemplateConfig(pexConfig.Config):
139  doAddCalexpBackground = pexConfig.Field(
140  dtype=bool,
141  default=True,
142  doc="Add background to calexp before processing it."
143  )
144 
145 
146 class GetCalexpAsTemplateTask(pipeBase.Task):
147  """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
148  for use as an image difference template.
149 
150  To be run as a subtask by pipe.tasks.ImageDifferenceTask.
151  Intended for use with simulations and surveys that repeatedly visit the same pointing.
152  This code was originally part of Winter2013ImageDifferenceTask.
153  """
154 
155  ConfigClass = GetCalexpAsTemplateConfig
156  _DefaultName = "GetCalexpAsTemplateTask"
157 
158  def run(self, exposure, sensorRef, templateIdList):
159  """!Return a calexp exposure with based on input sensorRef.
160 
161  Construct a dataId based on the sensorRef.dataId combined
162  with the specifications from the first dataId in templateIdList
163 
164  \param[in] exposure -- exposure (unused)
165  \param[in] sensorRef -- a Butler data reference
166  \param[in] templateIdList -- list of data ids, which should contain a single item.
167  If there are multiple items, only the first is used.
168 
169  \return a pipeBase.Struct
170  - exposure: a template calexp
171  - sources: source catalog measured on the template
172  """
173 
174  if len(templateIdList) == 0:
175  raise RuntimeError("No template supplied! Please supply a template visit id.")
176  if len(templateIdList) > 1:
177  self.log.warn("Multiple template visits supplied. Getting template from first visit: %s" %
178  (templateIdList[0]['visit']))
179 
180  templateId = sensorRef.dataId.copy()
181  templateId.update(templateIdList[0])
182 
183  self.log.info("Fetching calexp (%s) as template." % (templateId))
184 
185  butler = sensorRef.getButler()
186  template = butler.get(datasetType="calexp", dataId=templateId)
187  if self.config.doAddCalexpBackground:
188  templateBg = butler.get(datasetType="calexpBackground", dataId=templateId)
189  mi = template.getMaskedImage()
190  mi += templateBg.getImage()
191 
192  if not template.hasPsf():
193  raise pipeBase.TaskError("Template has no psf")
194 
195  templateSources = butler.get(datasetType="src", dataId=templateId)
196  return pipeBase.Struct(exposure=template,
197  sources=templateSources)
def run
Return a calexp exposure with based on input sensorRef.
Definition: getTemplate.py:158
def run
Retrieve and mosaic a template coadd exposure that overlaps the exposure.
Definition: getTemplate.py:58