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