LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
getTemplate.py
Go to the documentation of this file.
1 from __future__ import division, absolute_import
2 #
3 # LSST Data Management System
4 # Copyright 2016 LSST Corporation.
5 #
6 # This product includes software developed by the
7 # LSST Project (http://www.lsst.org/).
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the LSST License Statement and
20 # the GNU General Public License along with this program. If not,
21 # see <http://www.lsstcorp.org/LegalNotices/>.
22 #
23 
24 import numpy
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 
46 
47 class GetCoaddAsTemplateTask(pipeBase.Task):
48  """Subtask to retrieve coadd for use as an image difference template.
49 
50  This is the default getTemplate Task to be run as a subtask by
51  pipe.tasks.ImageDifferenceTask. The main method is run().
52  It assumes that coadds reside in the repository given by sensorRef.
53  """
54  ConfigClass = GetCoaddAsTemplateConfig
55  _DefaultName = "GetCoaddAsTemplateTask"
56 
57  def run(self, exposure, sensorRef, templateIdList=None):
58  """!Retrieve and mosaic a template coadd exposure that overlaps the exposure
59 
60  \param[in] exposure -- an exposure for which to generate an overlapping template
61  \param[in] sensorRef -- a Butler data reference that can be used to obtain coadd data
62  \param[in] templateIdList -- list of data ids (unused)
63 
64  \return a pipeBase.Struct
65  - exposure: a template coadd exposure assembled out of patches
66  - sources: None for this subtask
67  """
68  skyMap = sensorRef.get(datasetType=self.config.coaddName + "Coadd_skyMap")
69  expWcs = exposure.getWcs()
70  expBoxD = afwGeom.Box2D(exposure.getBBox())
71  expBoxD.grow(self.config.templateBorderSize)
72  ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
73  tractInfo = skyMap.findTract(ctrSkyPos)
74  self.log.info("Using skyMap tract %s" % (tractInfo.getId(),))
75  skyCorners = [expWcs.pixelToSky(pixPos) for pixPos in expBoxD.getCorners()]
76  patchList = tractInfo.findPatchList(skyCorners)
77 
78  if not patchList:
79  raise RuntimeError("No suitable tract found")
80  self.log.info("Assembling %s coadd patches" % (len(patchList),))
81 
82  # compute coadd bbox
83  coaddWcs = tractInfo.getWcs()
84  coaddBBox = afwGeom.Box2D()
85  for skyPos in skyCorners:
86  coaddBBox.include(coaddWcs.skyToPixel(skyPos))
87  coaddBBox = afwGeom.Box2I(coaddBBox)
88  self.log.info("exposure dimensions=%s; coadd dimensions=%s" %
89  (exposure.getDimensions(), coaddBBox.getDimensions()))
90 
91  # assemble coadd exposure from subregions of patches
92  coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
93  coaddExposure.getMaskedImage().set(numpy.nan, afwImage.MaskU.getPlaneBitMask("NO_DATA"), numpy.nan)
94  nPatchesFound = 0
95  coaddFilter = None
96  coaddPsf = None
97  for patchInfo in patchList:
98  patchSubBBox = patchInfo.getOuterBBox()
99  patchSubBBox.clip(coaddBBox)
100  patchArgDict = dict(
101  datasetType=self.config.coaddName + "Coadd_sub",
102  bbox=patchSubBBox,
103  tract=tractInfo.getId(),
104  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
105  )
106  if patchSubBBox.isEmpty():
107  self.log.info("skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
108  continue
109  if not sensorRef.datasetExists(**patchArgDict):
110  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
111  % patchArgDict)
112  continue
113 
114  nPatchesFound += 1
115  self.log.info("Reading patch %s" % patchArgDict)
116  coaddPatch = sensorRef.get(**patchArgDict)
117  coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
118  if coaddFilter is None:
119  coaddFilter = coaddPatch.getFilter()
120 
121  # Retrieve the PSF for this coadd tract, if not already retrieved
122  if coaddPsf is None and coaddPatch.hasPsf():
123  coaddPsf = coaddPatch.getPsf()
124 
125  if nPatchesFound == 0:
126  raise RuntimeError("No patches found!")
127 
128  if coaddPsf is None:
129  raise RuntimeError("No coadd Psf found!")
130 
131  coaddExposure.setPsf(coaddPsf)
132  coaddExposure.setFilter(coaddFilter)
133  return pipeBase.Struct(exposure=coaddExposure,
134  sources=None)
135 
136 
137 class GetCalexpAsTemplateConfig(pexConfig.Config):
138  doAddCalexpBackground = pexConfig.Field(
139  dtype=bool,
140  default=True,
141  doc="Add background to calexp before processing it."
142  )
143 
144 
145 class GetCalexpAsTemplateTask(pipeBase.Task):
146  """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
147  for use as an image difference template.
148 
149  To be run as a subtask by pipe.tasks.ImageDifferenceTask.
150  Intended for use with simulations and surveys that repeatedly visit the same pointing.
151  This code was originally part of Winter2013ImageDifferenceTask.
152  """
153 
154  ConfigClass = GetCalexpAsTemplateConfig
155  _DefaultName = "GetCalexpAsTemplateTask"
156 
157  def run(self, exposure, sensorRef, templateIdList):
158  """!Return a calexp exposure with based on input sensorRef.
159 
160  Construct a dataId based on the sensorRef.dataId combined
161  with the specifications from the first dataId in templateIdList
162 
163  \param[in] exposure -- exposure (unused)
164  \param[in] sensorRef -- a Butler data reference
165  \param[in] templateIdList -- list of data ids, which should contain a single item.
166  If there are multiple items, only the first is used.
167 
168  \return a pipeBase.Struct
169  - exposure: a template calexp
170  - sources: source catalog measured on the template
171  """
172 
173  if len(templateIdList) == 0:
174  raise RuntimeError("No template supplied! Please supply a template visit id.")
175  if len(templateIdList) > 1:
176  self.log.warn("Multiple template visits supplied. Getting template from first visit: %s" %
177  (templateIdList[0]['visit']))
178 
179  templateId = sensorRef.dataId.copy()
180  templateId.update(templateIdList[0])
181 
182  self.log.info("Fetching calexp (%s) as template." % (templateId))
183 
184  butler = sensorRef.getButler()
185  template = butler.get(datasetType="calexp", dataId=templateId)
186  if self.config.doAddCalexpBackground:
187  templateBg = butler.get(datasetType="calexpBackground", dataId=templateId)
188  mi = template.getMaskedImage()
189  mi += templateBg.getImage()
190 
191  if not template.hasPsf():
192  raise pipeBase.TaskError("Template has no psf")
193 
194  templateSources = butler.get(datasetType="src", dataId=templateId)
195  return pipeBase.Struct(exposure=template,
196  sources=templateSources)
An integer coordinate rectangle.
Definition: Box.h:53
def run
Return a calexp exposure with based on input sensorRef.
Definition: getTemplate.py:157
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
def run
Retrieve and mosaic a template coadd exposure that overlaps the exposure.
Definition: getTemplate.py:57