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