lsst.ip.diffim  13.0-26-g703d095+10
 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.Mask\
95  .getPlaneBitMask("NO_DATA"), np.nan)
96  nPatchesFound = 0
97  coaddFilter = None
98  coaddPsf = None
99  for patchInfo in patchList:
100  patchSubBBox = patchInfo.getOuterBBox()
101  patchSubBBox.clip(coaddBBox)
102  patchArgDict = dict(
103  datasetType=self.config.coaddName + "Coadd_sub",
104  bbox=patchSubBBox,
105  tract=tractInfo.getId(),
106  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
107  )
108  if patchSubBBox.isEmpty():
109  self.log.info("skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
110  continue
111  if not sensorRef.datasetExists(**patchArgDict):
112  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
113  % patchArgDict)
114  continue
115 
116  nPatchesFound += 1
117  self.log.info("Reading patch %s" % patchArgDict)
118  coaddPatch = sensorRef.get(**patchArgDict)
119  coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
120  if coaddFilter is None:
121  coaddFilter = coaddPatch.getFilter()
122 
123  # Retrieve the PSF for this coadd tract, if not already retrieved
124  if coaddPsf is None and coaddPatch.hasPsf():
125  coaddPsf = coaddPatch.getPsf()
126 
127  if nPatchesFound == 0:
128  raise RuntimeError("No patches found!")
129 
130  if coaddPsf is None:
131  raise RuntimeError("No coadd Psf found!")
132 
133  coaddExposure.setPsf(coaddPsf)
134  coaddExposure.setFilter(coaddFilter)
135  return pipeBase.Struct(exposure=coaddExposure,
136  sources=None)
137 
138 
139 class GetCalexpAsTemplateConfig(pexConfig.Config):
140  doAddCalexpBackground = pexConfig.Field(
141  dtype=bool,
142  default=True,
143  doc="Add background to calexp before processing it."
144  )
145 
146 
147 class GetCalexpAsTemplateTask(pipeBase.Task):
148  """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
149  for use as an image difference template.
150 
151  To be run as a subtask by pipe.tasks.ImageDifferenceTask.
152  Intended for use with simulations and surveys that repeatedly visit the same pointing.
153  This code was originally part of Winter2013ImageDifferenceTask.
154  """
155 
156  ConfigClass = GetCalexpAsTemplateConfig
157  _DefaultName = "GetCalexpAsTemplateTask"
158 
159  def run(self, exposure, sensorRef, templateIdList):
160  """!Return a calexp exposure with based on input sensorRef.
161 
162  Construct a dataId based on the sensorRef.dataId combined
163  with the specifications from the first dataId in templateIdList
164 
165  \param[in] exposure -- exposure (unused)
166  \param[in] sensorRef -- a Butler data reference
167  \param[in] templateIdList -- list of data ids, which should contain a single item.
168  If there are multiple items, only the first is used.
169 
170  \return a pipeBase.Struct
171  - exposure: a template calexp
172  - sources: source catalog measured on the template
173  """
174 
175  if len(templateIdList) == 0:
176  raise RuntimeError("No template supplied! Please supply a template visit id.")
177  if len(templateIdList) > 1:
178  self.log.warn("Multiple template visits supplied. Getting template from first visit: %s" %
179  (templateIdList[0]['visit']))
180 
181  templateId = sensorRef.dataId.copy()
182  templateId.update(templateIdList[0])
183 
184  self.log.info("Fetching calexp (%s) as template." % (templateId))
185 
186  butler = sensorRef.getButler()
187  template = butler.get(datasetType="calexp", dataId=templateId)
188  if self.config.doAddCalexpBackground:
189  templateBg = butler.get(datasetType="calexpBackground", dataId=templateId)
190  mi = template.getMaskedImage()
191  mi += templateBg.getImage()
192 
193  if not template.hasPsf():
194  raise pipeBase.TaskError("Template has no psf")
195 
196  templateSources = butler.get(datasetType="src", dataId=templateId)
197  return pipeBase.Struct(exposure=template,
198  sources=templateSources)
def run
Return a calexp exposure with based on input sensorRef.
Definition: getTemplate.py:159
def run
Retrieve and mosaic a template coadd exposure that overlaps the exposure.
Definition: getTemplate.py:58