LSSTApplications  10.0-2-g4f67435,11.0.rc2+1,11.0.rc2+12,11.0.rc2+3,11.0.rc2+4,11.0.rc2+5,11.0.rc2+6,11.0.rc2+7,11.0.rc2+8
LSSTDataManagementBasePackage
coaddInputRecorder.py
Go to the documentation of this file.
1 #!/usr/bin/env python
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 import numpy
24 
25 import lsst.pex.config as pexConfig
26 import lsst.afw.table as afwTable
27 import lsst.afw.image as afwImage
28 import lsst.pipe.base as pipeBase
29 from lsst.meas.algorithms import CoaddPsf, makeCoaddApCorrMap
30 
31 __all__ = ["CoaddInputRecorderTask"]
32 
33 class CoaddInputRecorderConfig(pexConfig.Config):
34  """Config for CoaddInputRecorderTask
35 
36  The inputRecorder section of the various coadd tasks' configs should generally agree,
37  or the schemas created by earlier tasks (like MakeCoaddTempExpTask) will not contain
38  the fields filled by later tasks (like AssembleCoaddTask).
39  """
40  saveEmptyCcds = pexConfig.Field(
41  dtype=bool, default=False, optional=False,
42  doc=("Add records for CCDs we iterated over but did not add a coaddTempExp"
43  " due to a lack of unmasked pixels in the coadd footprint.")
44  )
45  saveErrorCcds = pexConfig.Field(
46  dtype=bool, default=False, optional=False,
47  doc=("Add records for CCDs we iterated over but did not add a coaddTempExp"
48  " due to an exception (often due to the calexp not being found on disk).")
49  )
50  saveVisitGoodPix = pexConfig.Field(
51  dtype=bool, default=True, optional=False,
52  doc=("Save the total number of good pixels in each coaddTempExp (redundant with a sum of"
53  " good pixels in associated CCDs)")
54  )
55  saveCcdWeights = pexConfig.Field(
56  dtype=bool, default=True, optional=False,
57  doc=("Save weights in the CCDs table as well as the visits table?"
58  " (This is necessary for easy construction of CoaddPsf, but otherwise duplicate information.)")
59  )
60 
62  """A helper class for CoaddInputRecorderTask, managing the CoaddInputs object for that a single
63  CoaddTempExp. This will contain single 'visit' record for the CoaddTempExp and a number of 'ccd'
64  records.
65 
66  Should generally be created by calling CoaddInputRecorderTask.makeCoaddTempExp().
67  """
68 
69  def __init__(self, task, visitId):
70  self.task = task
71  self.coaddInputs = self.task.makeCoaddInputs()
72  self.visitRecord = self.coaddInputs.visits.addNew()
73  self.visitRecord.setId(visitId)
74 
75  def addCalExp(self, calExp, ccdId, nGoodPix):
76  """Add a 'ccd' record for a calexp just added to the CoaddTempExp
77 
78  @param[in] calExp Calibrated exposure just added to the CoaddTempExp, or None in case of
79  failures that should nonetheless be tracked. Should be the original
80  calexp, in that it should contain the original Psf and Wcs, not the
81  warped and/or matched ones.
82  @param[in] ccdId A unique numeric ID for the Exposure.
83  @param[in] nGoodPix Number of good pixels this image will contribute to the CoaddTempExp.
84  If saveEmptyCcds is not set and this value is zero, no record will be
85  added.
86  """
87  if nGoodPix == 0 and not self.task.config.saveEmptyCcds:
88  return
89  record = self.coaddInputs.ccds.addNew()
90  record.setId(ccdId)
91  record.setL(self.task.ccdVisitKey, self.visitRecord.getId())
92  try:
93  record.setI(self.task.ccdCcdKey, calExp.getDetector().getId())
94  except:
95  self.task.log.warn("Error getting detector serial number in visit %d; using -1"
96  % self.visitRecord.getId())
97  record.setI(self.task.ccdCcdKey, -1)
98  record.setI(self.task.ccdGoodPixKey, nGoodPix)
99  if calExp is not None:
100  record.setPsf(calExp.getPsf())
101  record.setWcs(calExp.getWcs())
102  record.setBBox(calExp.getBBox())
103  record.setApCorrMap(calExp.getInfo().getApCorrMap())
104  record.setValidPolygon(calExp.getInfo().getValidPolygon())
105  if self.task.config.saveCcdWeights:
106  record.setD(self.task.ccdWeightKey, 1.0) # No weighting or overlap when warping
107 
108  def finish(self, coaddTempExp, nGoodPix=None):
109  """Finish creating the CoaddInputs for a CoaddTempExp.
110 
111  @param[in,out] coaddTempExp Exposure object from which to obtain the PSF, WCS, and bounding
112  box for the entry in the 'visits' table. On return, the completed
113  CoaddInputs object will be attached to it.
114  @param[in] nGoodPix Total number of good pixels in the CoaddTempExp; ignored unless
115  saveVisitGoodPix is true.
116  """
117  self.visitRecord.setPsf(coaddTempExp.getPsf())
118  self.visitRecord.setWcs(coaddTempExp.getWcs())
119  self.visitRecord.setValidPolygon(coaddTempExp.getInfo().getValidPolygon())
120  self.visitRecord.setBBox(coaddTempExp.getBBox())
121  if self.task.config.saveVisitGoodPix:
122  self.visitRecord.setI(self.task.visitGoodPixKey, nGoodPix)
123  coaddTempExp.getInfo().setCoaddInputs(self.coaddInputs)
124  wcs = coaddTempExp.getWcs()
125  if False:
126  # This causes a test failure, pending fix in issue HSC-802
127  coaddTempExp.setPsf(CoaddPsf(self.coaddInputs.ccds, wcs))
128  apCorrMap = makeCoaddApCorrMap(self.coaddInputs.ccds, coaddTempExp.getBBox(afwImage.PARENT), wcs)
129  coaddTempExp.getInfo().setApCorrMap(apCorrMap)
130 
131 class CoaddInputRecorderTask(pipeBase.Task):
132  """Subtask that handles filling a CoaddInputs object for a coadd exposure, tracking the CCDs and
133  visits that went into a coadd.
134 
135  The interface here is a little messy, but I think this is at least partly a product of a bit of
136  messiness in the coadd code it's plugged into. I hope #2590 might result in a better design.
137  """
138 
139  ConfigClass = CoaddInputRecorderConfig
140 
141  def __init__(self, *args, **kwargs):
142  pipeBase.Task.__init__(self, *args, **kwargs)
143  self.visitSchema = afwTable.ExposureTable.makeMinimalSchema()
144  if self.config.saveVisitGoodPix:
145  self.visitGoodPixKey = self.visitSchema.addField("goodpix", type=int,
146  doc="Number of good pixels in the coaddTempExp")
147  self.visitWeightKey = self.visitSchema.addField("weight", type=float,
148  doc="Weight for this visit in the coadd")
149  self.ccdSchema = afwTable.ExposureTable.makeMinimalSchema()
150  self.ccdCcdKey = self.ccdSchema.addField("ccd", type=int, doc="cameraGeom CCD serial number")
151  self.ccdVisitKey = self.ccdSchema.addField("visit", type=numpy.int64,
152  doc="Foreign key for the visits (coaddTempExp) catalog")
153  self.ccdGoodPixKey = self.ccdSchema.addField("goodpix", type=int,
154  doc="Number of good pixels in this CCD")
155  if self.config.saveCcdWeights:
156  self.ccdWeightKey = self.ccdSchema.addField("weight", type=float,
157  doc="Weight for this visit in the coadd")
158 
159  def makeCoaddTempExpRecorder(self, visitId):
160  """Return a CoaddTempExpInputRecorder instance to help with saving a CoaddTempExp's inputs.
161 
162  The visitId may be any number that is unique for each CoaddTempExp that goes into a coadd,
163  but ideally should be something more meaningful that can be used to reconstruct a data ID.
164  """
165  return CoaddTempExpInputRecorder(self, visitId)
166 
167  def makeCoaddInputs(self):
168  """Create a CoaddInputs object with schemas defined by the task configuration"""
169  return afwImage.CoaddInputs(self.visitSchema, self.ccdSchema)
170 
171  def addVisitToCoadd(self, coaddInputs, coaddTempExp, weight):
172  """Called by AssembleCoaddTask when adding (a subset of) a coaddTempExp to a coadd. The
173  base class impementation extracts the CoaddInputs from the coaddTempExp and appends
174  them to the given coaddInputs, filling in the weight column(s).
175 
176  Note that the passed coaddTempExp may be a subimage, but that this method will only be
177  called for the first subimage
178 
179  Returns the record for the visit to allow subclasses to fill in additional fields.
180  Warns and returns None if the inputRecorder catalogs for the coaddTempExp are not usable.
181  """
182  tempExpInputs = coaddTempExp.getInfo().getCoaddInputs()
183  if len(tempExpInputs.visits) != 1:
184  self.log.warn("CoaddInputs for coaddTempExp should have exactly one record in visits table "
185  "(found %d). CoaddInputs for this visit will not be saved."
186  % len(tempExpInputs.visits))
187  return None
188  inputVisitRecord = tempExpInputs.visits[0];
189  outputVisitRecord = coaddInputs.visits.addNew()
190  outputVisitRecord.assign(inputVisitRecord)
191  outputVisitRecord.setD(self.visitWeightKey, weight)
192  for inputCcdRecord in tempExpInputs.ccds:
193  if inputCcdRecord.getL(self.ccdVisitKey) != inputVisitRecord.getId():
194  self.log.warn("CoaddInputs for coaddTempExp with id %d contains CCDs with visit=%d. "
195  "CoaddInputs may be unreliable."
196  % (inputVisitRecord.getId(), inputCcdRecord.getL(self.ccdVisitKey)))
197  outputCcdRecord = coaddInputs.ccds.addNew()
198  outputCcdRecord.assign(inputCcdRecord)
199  if self.config.saveCcdWeights:
200  outputCcdRecord.setD(self.ccdWeightKey, weight)
201  return inputVisitRecord
A simple Persistable struct containing ExposureCatalogs that record the inputs to a coadd...
Definition: CoaddInputs.h:46
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
Definition: CoaddPsf.h:45