LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
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 from lsst.ip.diffim.dcrModel import DcrModel
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', 'goodSeeing', or 'dcr'",
43  dtype=str,
44  default="deep",
45  )
46  numSubfilters = pexConfig.Field(
47  doc="Number of subfilters in the DcrCoadd, used only if ``coaddName``='dcr'",
48  dtype=int,
49  default=3,
50  )
51  warpType = pexConfig.Field(
52  doc="Warp type of the coadd template: one of 'direct' or 'psfMatched'",
53  dtype=str,
54  default="direct",
55  )
56 
57 
58 class GetCoaddAsTemplateTask(pipeBase.Task):
59  """Subtask to retrieve coadd for use as an image difference template.
60 
61  This is the default getTemplate Task to be run as a subtask by
62  ``pipe.tasks.ImageDifferenceTask``. The main method is ``run()``.
63  It assumes that coadds reside in the repository given by sensorRef.
64  """
65 
66  ConfigClass = GetCoaddAsTemplateConfig
67  _DefaultName = "GetCoaddAsTemplateTask"
68 
69  def run(self, exposure, sensorRef, templateIdList=None):
70  """Retrieve and mosaic a template coadd exposure that overlaps the exposure
71 
72  Parameters
73  ----------
74  exposure: `lsst.afw.image.Exposure`
75  an exposure for which to generate an overlapping template
76  sensorRef : TYPE
77  a Butler data reference that can be used to obtain coadd data
78  templateIdList : TYPE, optional
79  list of data ids (unused)
80 
81  Returns
82  -------
83  result : `struct`
84  return a pipeBase.Struct:
85 
86  - ``exposure`` : a template coadd exposure assembled out of patches
87  - ``sources`` : None for this subtask
88  """
89  skyMap = sensorRef.get(datasetType=self.config.coaddName + "Coadd_skyMap")
90  expWcs = exposure.getWcs()
91  expBoxD = afwGeom.Box2D(exposure.getBBox())
92  expBoxD.grow(self.config.templateBorderSize)
93  ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
94  tractInfo = skyMap.findTract(ctrSkyPos)
95  self.log.info("Using skyMap tract %s" % (tractInfo.getId(),))
96  skyCorners = [expWcs.pixelToSky(pixPos) for pixPos in expBoxD.getCorners()]
97  patchList = tractInfo.findPatchList(skyCorners)
98 
99  if not patchList:
100  raise RuntimeError("No suitable tract found")
101  self.log.info("Assembling %s coadd patches" % (len(patchList),))
102 
103  # compute coadd bbox
104  coaddWcs = tractInfo.getWcs()
105  coaddBBox = afwGeom.Box2D()
106  for skyPos in skyCorners:
107  coaddBBox.include(coaddWcs.skyToPixel(skyPos))
108  coaddBBox = afwGeom.Box2I(coaddBBox)
109  self.log.info("exposure dimensions=%s; coadd dimensions=%s" %
110  (exposure.getDimensions(), coaddBBox.getDimensions()))
111 
112  # assemble coadd exposure from subregions of patches
113  coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
114  coaddExposure.maskedImage.set(np.nan, afwImage.Mask.getPlaneBitMask("NO_DATA"), np.nan)
115  nPatchesFound = 0
116  coaddFilter = None
117  coaddPsf = None
118  for patchInfo in patchList:
119  patchSubBBox = patchInfo.getOuterBBox()
120  patchSubBBox.clip(coaddBBox)
121  patchArgDict = dict(
122  datasetType=self.getCoaddDatasetName() + "_sub",
123  bbox=patchSubBBox,
124  tract=tractInfo.getId(),
125  patch="%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
126  numSubfilters=self.config.numSubfilters,
127  )
128  if patchSubBBox.isEmpty():
129  self.log.info("skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
130  continue
131 
132  if self.config.coaddName == 'dcr':
133  if not sensorRef.datasetExists(subfilter=0, **patchArgDict):
134  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s,"
135  " numSubfilters=%(numSubfilters)s, subfilter=0 does not exist"
136  % patchArgDict)
137  continue
138  self.log.info("Constructing DCR-matched template for patch %s" % patchArgDict)
139  dcrModel = DcrModel.fromDataRef(sensorRef, **patchArgDict)
140  coaddPatch = dcrModel.buildMatchedExposure(bbox=patchSubBBox,
141  wcs=coaddWcs,
142  visitInfo=exposure.getInfo().getVisitInfo())
143  else:
144  if not sensorRef.datasetExists(**patchArgDict):
145  self.log.warn("%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
146  % patchArgDict)
147  continue
148  self.log.info("Reading patch %s" % patchArgDict)
149  coaddPatch = sensorRef.get(**patchArgDict)
150  nPatchesFound += 1
151  coaddExposure.maskedImage.assign(coaddPatch.maskedImage, coaddPatch.getBBox())
152  if coaddFilter is None:
153  coaddFilter = coaddPatch.getFilter()
154 
155  # Retrieve the PSF for this coadd tract, if not already retrieved
156  if coaddPsf is None and coaddPatch.hasPsf():
157  coaddPsf = coaddPatch.getPsf()
158 
159  if nPatchesFound == 0:
160  raise RuntimeError("No patches found!")
161 
162  if coaddPsf is None:
163  raise RuntimeError("No coadd Psf found!")
164 
165  coaddExposure.setPsf(coaddPsf)
166  coaddExposure.setFilter(coaddFilter)
167  return pipeBase.Struct(exposure=coaddExposure,
168  sources=None)
169 
171  """Return coadd name for given task config
172 
173  Returns
174  -------
175  CoaddDatasetName : `string`
176 
177  TODO: This nearly duplicates a method in CoaddBaseTask (DM-11985)
178  """
179  warpType = self.config.warpType
180  suffix = "" if warpType == "direct" else warpType[0].upper() + warpType[1:]
181  return self.config.coaddName + "Coadd" + suffix
182 
183 
184 class GetCalexpAsTemplateConfig(pexConfig.Config):
185  doAddCalexpBackground = pexConfig.Field(
186  dtype=bool,
187  default=True,
188  doc="Add background to calexp before processing it."
189  )
190 
191 
192 class GetCalexpAsTemplateTask(pipeBase.Task):
193  """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
194  for use as an image difference template.
195 
196  To be run as a subtask by pipe.tasks.ImageDifferenceTask.
197  Intended for use with simulations and surveys that repeatedly visit the same pointing.
198  This code was originally part of Winter2013ImageDifferenceTask.
199  """
200 
201  ConfigClass = GetCalexpAsTemplateConfig
202  _DefaultName = "GetCalexpAsTemplateTask"
203 
204  def run(self, exposure, sensorRef, templateIdList):
205  """Return a calexp exposure with based on input sensorRef.
206 
207  Construct a dataId based on the sensorRef.dataId combined
208  with the specifications from the first dataId in templateIdList
209 
210  Parameters
211  ----------
212  exposure : `lsst.afw.image.Exposure`
213  exposure (unused)
214  sensorRef : TYPE
215  a Butler data reference
216  templateIdList : TYPE
217  list of data ids, which should contain a single item.
218  If there are multiple items, only the first is used.
219 
220  Returns
221  -------
222  result : `struct`
223 
224  return a pipeBase.Struct:
225 
226  - ``exposure`` : a template calexp
227  - ``sources`` : source catalog measured on the template
228  """
229 
230  if len(templateIdList) == 0:
231  raise RuntimeError("No template supplied! Please supply a template visit id.")
232  if len(templateIdList) > 1:
233  self.log.warn("Multiple template visits supplied. Getting template from first visit: %s" %
234  (templateIdList[0]['visit']))
235 
236  templateId = sensorRef.dataId.copy()
237  templateId.update(templateIdList[0])
238 
239  self.log.info("Fetching calexp (%s) as template." % (templateId))
240 
241  butler = sensorRef.getButler()
242  template = butler.get(datasetType="calexp", dataId=templateId)
243  if self.config.doAddCalexpBackground:
244  templateBg = butler.get(datasetType="calexpBackground", dataId=templateId)
245  mi = template.getMaskedImage()
246  mi += templateBg.getImage()
247 
248  if not template.hasPsf():
249  raise pipeBase.TaskError("Template has no psf")
250 
251  templateSources = butler.get(datasetType="src", dataId=templateId)
252  return pipeBase.Struct(exposure=template,
253  sources=templateSources)
A floating-point coordinate rectangle geometry.
Definition: Box.h:294
def run(self, exposure, sensorRef, templateIdList)
Definition: getTemplate.py:204
def run(self, exposure, sensorRef, templateIdList=None)
Definition: getTemplate.py:69
An integer coordinate rectangle.
Definition: Box.h:54