LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
makeCoaddTempExp.py
Go to the documentation of this file.
1 from __future__ import division, absolute_import
2 #
3 # LSST Data Management System
4 # Copyright 2008, 2009, 2010, 2011, 2012 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 
26 import lsst.pex.config as pexConfig
27 import lsst.afw.image as afwImage
28 import lsst.coadd.utils as coaddUtils
29 import lsst.pipe.base as pipeBase
30 from .coaddBase import CoaddBaseTask
31 from .warpAndPsfMatch import WarpAndPsfMatchTask
32 from .coaddHelpers import groupPatchExposures, getGroupDataRef
33 
34 __all__ = ["MakeCoaddTempExpTask"]
35 
36 class MakeCoaddTempExpConfig(CoaddBaseTask.ConfigClass):
37  """Config for MakeCoaddTempExpTask
38  """
39  warpAndPsfMatch = pexConfig.ConfigurableField(
40  target = WarpAndPsfMatchTask,
41  doc = "Task to warp and PSF-match calexp",
42  )
43  doWrite = pexConfig.Field(
44  doc = "persist <coaddName>Coadd_tempExp",
45  dtype = bool,
46  default = True,
47  )
48  doOverwrite = pexConfig.Field(
49  doc = "overwrite <coaddName>Coadd_tempExp; If False, continue if the file exists on disk",
50  dtype = bool,
51  default = True,
52  )
53  bgSubtracted = pexConfig.Field(
54  doc = "Work with a background subtracted calexp?",
55  dtype = bool,
56  default = False,
57  )
58 
59 
60 class MakeCoaddTempExpTask(CoaddBaseTask):
61  """Task to produce <coaddName>Coadd_tempExp images
62  """
63  ConfigClass = MakeCoaddTempExpConfig
64  _DefaultName = "makeCoaddTempExp"
65 
66  def __init__(self, *args, **kwargs):
67  CoaddBaseTask.__init__(self, *args, **kwargs)
68  self.makeSubtask("warpAndPsfMatch")
69 
70  @pipeBase.timeMethod
71  def run(self, patchRef, selectDataList=[]):
72  """Produce <coaddName>Coadd_tempExp images
73 
74  <coaddName>Coadd_tempExp are produced by PSF-matching (optional) and warping.
75 
76  @param[in] patchRef: data reference for sky map patch. Must include keys "tract", "patch",
77  plus the camera-specific filter key (e.g. "filter" or "band")
78  @return: dataRefList: a list of data references for the new <coaddName>Coadd_tempExp
79 
80  @warning: this task assumes that all exposures in a coaddTempExp have the same filter.
81 
82  @warning: this task sets the Calib of the coaddTempExp to the Calib of the first calexp
83  with any good pixels in the patch. For a mosaic camera the resulting Calib should be ignored
84  (assembleCoadd should determine zeropoint scaling without referring to it).
85  """
86  skyInfo = self.getSkyInfo(patchRef)
87 
88  calExpRefList = self.selectExposures(patchRef, skyInfo, selectDataList=selectDataList)
89  if len(calExpRefList) == 0:
90  self.log.warn("No exposures to coadd for patch %s" % patchRef.dataId)
91  return None
92  self.log.info("Selected %d calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
93  calExpRefList = [calExpRef for calExpRef in calExpRefList if calExpRef.datasetExists("calexp")]
94  self.log.info("Processing %d existing calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
95 
96  groupData = groupPatchExposures(patchRef, calExpRefList, self.getCoaddDatasetName(),
97  self.getTempExpDatasetName())
98  self.log.info("Processing %d tempExps for patch %s" % (len(groupData.groups), patchRef.dataId))
99 
100  dataRefList = []
101  for i, (tempExpTuple, calexpRefList) in enumerate(groupData.groups.iteritems()):
102  tempExpRef = getGroupDataRef(patchRef.getButler(), self.getTempExpDatasetName(),
103  tempExpTuple, groupData.keys)
104  if not self.config.doOverwrite and tempExpRef.datasetExists(datasetType=self.getTempExpDatasetName()):
105  self.log.info("tempCoaddExp %s exists; skipping" % (tempExpRef.dataId,))
106  dataRefList.append(tempExpRef)
107  continue
108  self.log.info("Processing tempExp %d/%d: id=%s" % (i, len(groupData.groups), tempExpRef.dataId))
109 
110  # TODO: mappers should define a way to go from the "grouping keys" to a numeric ID (#2776).
111  # For now, we try to get a long integer "visit" key, and if we can't, we just use the index
112  # of the visit in the list.
113  try:
114  visitId = long(tempExpRef.dataId["visit"])
115  except (KeyError, ValueError):
116  visitId = i
117  inputRecorder = self.inputRecorder.makeCoaddTempExpRecorder(visitId)
118 
119  exp = self.createTempExp(calexpRefList, skyInfo, inputRecorder)
120  if exp is not None:
121  dataRefList.append(tempExpRef)
122  if self.config.doWrite:
123  self.writeCoaddOutput(tempExpRef, exp, "tempExp")
124  else:
125  self.log.warn("tempExp %s could not be created" % (tempExpRef.dataId,))
126  return dataRefList
127 
128  def createTempExp(self, calexpRefList, skyInfo, inputRecorder):
129  """Create a tempExp from inputs
130 
131  We iterate over the multiple calexps in a single exposure to construct
132  the warp ("tempExp") of that exposure to the supplied tract/patch.
133 
134  Pixels that receive no pixels are set to NAN; this is not correct
135  (violates LSST algorithms group policy), but will be fixed up by
136  interpolating after the coaddition.
137 
138  @param calexpRefList: List of data references for calexps that (may)
139  overlap the patch of interest
140  @param skyInfo: Struct from CoaddBaseTask.getSkyInfo() with geometric
141  information about the patch
142  @param inputRecorder: CoaddTempExpInputRecorder that builds a catalog
143  of calexps that went into this tempExp.
144  @return warped exposure, or None if no pixels overlap
145  """
146  coaddTempExp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
147  edgeMask = afwImage.MaskU.getPlaneBitMask("EDGE")
148  coaddTempExp.getMaskedImage().set(numpy.nan, edgeMask, numpy.inf) # XXX these are the wrong values!
149  totGoodPix = 0
150  didSetMetadata = False
151  modelPsf = self.config.modelPsf.apply() if self.config.doPsfMatch else None
152  for calExpInd, calExpRef in enumerate(calexpRefList):
153  self.log.info("Processing calexp %d of %d for this tempExp: id=%s" %
154  (calExpInd+1, len(calexpRefList), calExpRef.dataId))
155  try:
156  ccdId = calExpRef.get("ccdExposureId", immediate=True)
157  except Exception:
158  ccdId = calExpInd
159  numGoodPix = 0
160  try:
161  calExp = self.getCalExp(calExpRef, bgSubtracted=self.config.bgSubtracted)
162  exposure = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf, wcs=skyInfo.wcs,
163  maxBBox=skyInfo.bbox).exposure
164  numGoodPix = coaddUtils.copyGoodPixels(
165  coaddTempExp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
166  totGoodPix += numGoodPix
167  self.log.logdebug("Calexp %s has %d good pixels in this patch (%.1f%%)" %
168  (calExpRef.dataId, numGoodPix, 100.0*numGoodPix/skyInfo.bbox.getArea()))
169  if numGoodPix > 0 and not didSetMetadata:
170  coaddTempExp.setCalib(exposure.getCalib())
171  coaddTempExp.setFilter(exposure.getFilter())
172  didSetMetadata = True
173  except Exception, e:
174  self.log.warn("Error processing calexp %s; skipping it: %s" % (calExpRef.dataId, e))
175  continue
176  inputRecorder.addCalExp(calExp, ccdId, numGoodPix)
177 
178  inputRecorder.finish(coaddTempExp, totGoodPix)
179 
180  self.log.info("coaddTempExp has %d good pixels (%.1f%%)" %
181  (totGoodPix, 100.0*totGoodPix/skyInfo.bbox.getArea()))
182  return coaddTempExp if totGoodPix > 0 and didSetMetadata else None
int copyGoodPixels(lsst::afw::image::MaskedImage< ImagePixelT, lsst::afw::image::MaskPixel, lsst::afw::image::VariancePixel > &destImage, lsst::afw::image::MaskedImage< ImagePixelT, lsst::afw::image::MaskPixel, lsst::afw::image::VariancePixel > const &srcImage, lsst::afw::image::MaskPixel const badPixelMask)
copy good pixels from one masked image to another