LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
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 lsst.meas.algorithms import CoaddPsf
31 from .coaddBase import CoaddBaseTask
32 from .warpAndPsfMatch import WarpAndPsfMatchTask
33 from .coaddHelpers import groupPatchExposures, getGroupDataRef
34 
35 __all__ = ["MakeCoaddTempExpTask"]
36 
37 class MakeCoaddTempExpConfig(CoaddBaseTask.ConfigClass):
38  """Config for MakeCoaddTempExpTask
39  """
40  warpAndPsfMatch = pexConfig.ConfigurableField(
41  target = WarpAndPsfMatchTask,
42  doc = "Task to warp and PSF-match calexp",
43  )
44  doWrite = pexConfig.Field(
45  doc = "persist <coaddName>Coadd_tempExp",
46  dtype = bool,
47  default = True,
48  )
49  doOverwrite = pexConfig.Field(
50  doc = "overwrite <coaddName>Coadd_tempExp; If False, continue if the file exists on disk",
51  dtype = bool,
52  default = True,
53  )
54  bgSubtracted = pexConfig.Field(
55  doc = "Work with a background subtracted calexp?",
56  dtype = bool,
57  default = False,
58  )
59 
60 
61 class MakeCoaddTempExpTask(CoaddBaseTask):
62  """Task to produce <coaddName>Coadd_tempExp images
63  """
64  ConfigClass = MakeCoaddTempExpConfig
65  _DefaultName = "makeCoaddTempExp"
66 
67  def __init__(self, *args, **kwargs):
68  CoaddBaseTask.__init__(self, *args, **kwargs)
69  self.makeSubtask("warpAndPsfMatch")
70 
71  @pipeBase.timeMethod
72  def run(self, patchRef, selectDataList=[]):
73  """Produce <coaddName>Coadd_tempExp images
74 
75  <coaddName>Coadd_tempExp are produced by PSF-matching (optional) and warping.
76 
77  @param[in] patchRef: data reference for sky map patch. Must include keys "tract", "patch",
78  plus the camera-specific filter key (e.g. "filter" or "band")
79  @return: dataRefList: a list of data references for the new <coaddName>Coadd_tempExp
80 
81  @warning: this task assumes that all exposures in a coaddTempExp have the same filter.
82 
83  @warning: this task sets the Calib of the coaddTempExp to the Calib of the first calexp
84  with any good pixels in the patch. For a mosaic camera the resulting Calib should be ignored
85  (assembleCoadd should determine zeropoint scaling without referring to it).
86  """
87  skyInfo = self.getSkyInfo(patchRef)
88 
89  calExpRefList = self.selectExposures(patchRef, skyInfo, selectDataList=selectDataList)
90  if len(calExpRefList) == 0:
91  self.log.warn("No exposures to coadd for patch %s" % patchRef.dataId)
92  return None
93  self.log.info("Selected %d calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
94  calExpRefList = [calExpRef for calExpRef in calExpRefList if calExpRef.datasetExists("calexp")]
95  self.log.info("Processing %d existing calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
96 
97  groupData = groupPatchExposures(patchRef, calExpRefList, self.getCoaddDatasetName(),
98  self.getTempExpDatasetName())
99  self.log.info("Processing %d tempExps for patch %s" % (len(groupData.groups), patchRef.dataId))
100 
101  dataRefList = []
102  for i, (tempExpTuple, calexpRefList) in enumerate(groupData.groups.iteritems()):
103  tempExpRef = getGroupDataRef(patchRef.getButler(), self.getTempExpDatasetName(),
104  tempExpTuple, groupData.keys)
105  if not self.config.doOverwrite and tempExpRef.datasetExists(datasetType=self.getTempExpDatasetName()):
106  self.log.info("tempCoaddExp %s exists; skipping" % (tempExpRef.dataId,))
107  dataRefList.append(tempExpRef)
108  continue
109  self.log.info("Processing tempExp %d/%d: id=%s" % (i, len(groupData.groups), tempExpRef.dataId))
110 
111  # TODO: mappers should define a way to go from the "grouping keys" to a numeric ID (#2776).
112  # For now, we try to get a long integer "visit" key, and if we can't, we just use the index
113  # of the visit in the list.
114  try:
115  visitId = long(tempExpRef.dataId["visit"])
116  except (KeyError, ValueError):
117  visitId = i
118 
119  exp = self.createTempExp(calexpRefList, skyInfo, visitId)
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, visitId=0):
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 visitId: integer identifier for visit, for the table that will
143  produce the CoaddPsf
144  @return warped exposure, or None if no pixels overlap
145  """
146  inputRecorder = self.inputRecorder.makeCoaddTempExpRecorder(visitId)
147  coaddTempExp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
148  coaddTempExp.getMaskedImage().set(numpy.nan, afwImage.MaskU.getPlaneBitMask("NO_DATA"), numpy.inf)
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  # We augment the dataRef here with the tract, which is harmless for loading things
162  # like calexps that don't need the tract, and necessary for meas_mosaic outputs,
163  # which do.
164  calExpRef = calExpRef.butlerSubset.butler.dataRef("calexp", dataId=calExpRef.dataId,
165  tract=skyInfo.tractInfo.getId())
166  calExp = self.getCalExp(calExpRef, bgSubtracted=self.config.bgSubtracted)
167  exposure = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf, wcs=skyInfo.wcs,
168  maxBBox=skyInfo.bbox).exposure
169  if didSetMetadata:
170  mimg = exposure.getMaskedImage()
171  mimg *= (coaddTempExp.getCalib().getFluxMag0()[0] / exposure.getCalib().getFluxMag0()[0])
172  del mimg
173  numGoodPix = coaddUtils.copyGoodPixels(
174  coaddTempExp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
175  totGoodPix += numGoodPix
176  self.log.logdebug("Calexp %s has %d good pixels in this patch (%.1f%%)" %
177  (calExpRef.dataId, numGoodPix, 100.0*numGoodPix/skyInfo.bbox.getArea()))
178  if numGoodPix > 0 and not didSetMetadata:
179  coaddTempExp.setCalib(exposure.getCalib())
180  coaddTempExp.setFilter(exposure.getFilter())
181  didSetMetadata = True
182  except Exception, e:
183  self.log.warn("Error processing calexp %s; skipping it: %s" % (calExpRef.dataId, e))
184  continue
185  inputRecorder.addCalExp(calExp, ccdId, numGoodPix)
186 
187  inputRecorder.finish(coaddTempExp, totGoodPix)
188  if totGoodPix > 0 and didSetMetadata:
189  coaddTempExp.setPsf(modelPsf if self.config.doPsfMatch else
190  CoaddPsf(inputRecorder.coaddInputs.ccds, skyInfo.wcs))
191 
192  self.log.info("coaddTempExp has %d good pixels (%.1f%%)" %
193  (totGoodPix, 100.0*totGoodPix/skyInfo.bbox.getArea()))
194  return coaddTempExp if totGoodPix > 0 and didSetMetadata else None
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
Definition: CoaddPsf.h:45
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