LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
coaddInputRecorder.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010, 2011, 2012 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 import numpy
23 
24 import lsst.pex.config as pexConfig
25 import lsst.afw.table as afwTable
26 import lsst.afw.image as afwImage
27 import lsst.pipe.base as pipeBase
28 from lsst.meas.algorithms import CoaddPsf, makeCoaddApCorrMap
29 
30 __all__ = ["CoaddInputRecorderTask"]
31 
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 
41  saveEmptyCcds = pexConfig.Field(
42  dtype=bool, default=False, optional=False,
43  doc=("Add records for CCDs we iterated over but did not add a coaddTempExp"
44  " due to a lack of unmasked pixels in the coadd footprint.")
45  )
46  saveErrorCcds = pexConfig.Field(
47  dtype=bool, default=False, optional=False,
48  doc=("Add records for CCDs we iterated over but did not add a coaddTempExp"
49  " due to an exception (often due to the calexp not being found on disk).")
50  )
51  saveVisitGoodPix = pexConfig.Field(
52  dtype=bool, default=True, optional=False,
53  doc=("Save the total number of good pixels in each coaddTempExp (redundant with a sum of"
54  " good pixels in associated CCDs)")
55  )
56  saveCcdWeights = pexConfig.Field(
57  dtype=bool, default=True, optional=False,
58  doc=("Save weights in the CCDs table as well as the visits table?"
59  " (This is necessary for easy construction of CoaddPsf, but otherwise duplicate information.)")
60  )
61 
62 
64  """A helper class for CoaddInputRecorderTask, managing the CoaddInputs object for that single
65  CoaddTempExp. This will contain a single 'visit' record for the CoaddTempExp and a number of 'ccd'
66  records.
67 
68  Should generally be created by calling CoaddInputRecorderTask.makeCoaddTempExp().
69  """
70 
71  def __init__(self, task, visitId, num=0):
72  """Constructor
73 
74  @param task The CoaddInputRecorderTask that is utilising us
75  @param visitId Identifier (integer) for the visit
76  @param num Number of CCDs for this visit that overlap this
77  patch (for reserving memory)
78  """
79  self.tasktask = task
80  self.coaddInputscoaddInputs = self.tasktask.makeCoaddInputs()
81  self.coaddInputscoaddInputs.visits.reserve(1)
82  if num > 0:
83  self.coaddInputscoaddInputs.ccds.reserve(num)
84  self.visitRecordvisitRecord = self.coaddInputscoaddInputs.visits.addNew()
85  self.visitRecordvisitRecord.setId(visitId)
86 
87  def addCalExp(self, calExp, ccdId, nGoodPix):
88  """Add a 'ccd' record for a calexp just added to the CoaddTempExp
89 
90  @param[in] calExp Calibrated exposure just added to the CoaddTempExp, or None in case of
91  failures that should nonetheless be tracked. Should be the original
92  calexp, in that it should contain the original Psf and Wcs, not the
93  warped and/or matched ones.
94  @param[in] ccdId A unique numeric ID for the Exposure.
95  @param[in] nGoodPix Number of good pixels this image will contribute to the CoaddTempExp.
96  If saveEmptyCcds is not set and this value is zero, no record will be
97  added.
98  """
99  if nGoodPix == 0 and not self.tasktask.config.saveEmptyCcds:
100  return
101  record = self.coaddInputscoaddInputs.ccds.addNew()
102  record.setId(ccdId)
103  record.setL(self.tasktask.ccdVisitKey, self.visitRecordvisitRecord.getId())
104  try:
105  record.setI(self.tasktask.ccdCcdKey, calExp.getDetector().getId())
106  except Exception as e:
107  self.tasktask.log.warning("Error getting detector serial number in visit %d; using -1; error=%s",
108  self.visitRecordvisitRecord.getId(), e)
109  record.setI(self.tasktask.ccdCcdKey, -1)
110  record.setI(self.tasktask.ccdGoodPixKey, nGoodPix)
111  if calExp is not None:
112  self._setExposureInfoInRecord_setExposureInfoInRecord(exposure=calExp, record=record)
113  if self.tasktask.config.saveCcdWeights:
114  record.setD(self.tasktask.ccdWeightKey, 1.0) # No weighting or overlap when warping
115  record.set(self.tasktask.ccdFilterKey, calExp.getFilterLabel().physicalLabel)
116 
117  def finish(self, coaddTempExp, nGoodPix=None):
118  """Finish creating the CoaddInputs for a CoaddTempExp.
119 
120  @param[in,out] coaddTempExp Exposure object from which to obtain the PSF, WCS, and bounding
121  box for the entry in the 'visits' table. On return, the completed
122  CoaddInputs object will be attached to it.
123  @param[in] nGoodPix Total number of good pixels in the CoaddTempExp; ignored unless
124  saveVisitGoodPix is true.
125  """
126  self._setExposureInfoInRecord_setExposureInfoInRecord(exposure=coaddTempExp, record=self.visitRecordvisitRecord)
127  if self.tasktask.config.saveVisitGoodPix:
128  self.visitRecordvisitRecord.setI(self.tasktask.visitGoodPixKey, nGoodPix)
129  coaddTempExp.getInfo().setCoaddInputs(self.coaddInputscoaddInputs)
130  wcs = coaddTempExp.getWcs()
131  if False:
132  # This causes a test failure, pending fix in issue HSC-802
133  coaddTempExp.setPsf(CoaddPsf(self.coaddInputscoaddInputs.ccds, wcs))
134  apCorrMap = makeCoaddApCorrMap(self.coaddInputscoaddInputs.ccds, coaddTempExp.getBBox(afwImage.PARENT), wcs)
135  coaddTempExp.getInfo().setApCorrMap(apCorrMap)
136 
137  def _setExposureInfoInRecord(self, exposure, record):
138  """Set exposure info and bbox in an ExposureTable record
139 
140  @param[in] exposure exposure whose info is to be recorded
141  @param[in,out] record record of an ExposureTable to set
142  """
143  info = exposure.getInfo()
144  record.setPsf(info.getPsf())
145  record.setWcs(info.getWcs())
146  record.setPhotoCalib(info.getPhotoCalib())
147  record.setApCorrMap(info.getApCorrMap())
148  record.setValidPolygon(info.getValidPolygon())
149  record.setVisitInfo(info.getVisitInfo())
150  record.setBBox(exposure.getBBox())
151  record.setTransmissionCurve(info.getTransmissionCurve())
152 
153 
154 class CoaddInputRecorderTask(pipeBase.Task):
155  """Subtask that handles filling a CoaddInputs object for a coadd exposure, tracking the CCDs and
156  visits that went into a coadd.
157 
158  The interface here is a little messy, but I think this is at least partly a product of a bit of
159  messiness in the coadd code it's plugged into. I hope #2590 might result in a better design.
160  """
161 
162  ConfigClass = CoaddInputRecorderConfig
163 
164  def __init__(self, *args, **kwargs):
165  pipeBase.Task.__init__(self, *args, **kwargs)
166  self.visitSchemavisitSchema = afwTable.ExposureTable.makeMinimalSchema()
167  if self.config.saveVisitGoodPix:
168  self.visitGoodPixKeyvisitGoodPixKey = self.visitSchemavisitSchema.addField("goodpix", type=numpy.int32,
169  doc="Number of good pixels in the coaddTempExp")
170  self.visitWeightKeyvisitWeightKey = self.visitSchemavisitSchema.addField("weight", type=float,
171  doc="Weight for this visit in the coadd")
172  self.ccdSchemaccdSchema = afwTable.ExposureTable.makeMinimalSchema()
173  self.ccdCcdKeyccdCcdKey = self.ccdSchemaccdSchema.addField("ccd", type=numpy.int32, doc="cameraGeom CCD serial number")
174  self.ccdVisitKeyccdVisitKey = self.ccdSchemaccdSchema.addField("visit", type=numpy.int64,
175  doc="Foreign key for the visits (coaddTempExp) catalog")
176  self.ccdGoodPixKeyccdGoodPixKey = self.ccdSchemaccdSchema.addField("goodpix", type=numpy.int32,
177  doc="Number of good pixels in this CCD")
178  if self.config.saveCcdWeights:
179  self.ccdWeightKeyccdWeightKey = self.ccdSchemaccdSchema.addField("weight", type=float,
180  doc="Weight for this visit in the coadd")
181  self.visitFilterKeyvisitFilterKey = self.visitSchemavisitSchema.addField("filter", type=str, size=32,
182  doc="Physical filter associated with this visit.")
183  self.ccdFilterKeyccdFilterKey = self.ccdSchemaccdSchema.addField("filter", type=str, size=32,
184  doc="Physical filter associated with this visit.")
185 
186  def makeCoaddTempExpRecorder(self, visitId, num=0):
187  """Return a CoaddTempExpInputRecorder instance to help with saving a CoaddTempExp's inputs.
188 
189  The visitId may be any number that is unique for each CoaddTempExp that goes into a coadd,
190  but ideally should be something more meaningful that can be used to reconstruct a data ID.
191  """
192  return CoaddTempExpInputRecorder(self, visitId, num=num)
193 
194  def makeCoaddInputs(self):
195  """Create a CoaddInputs object with schemas defined by the task configuration"""
196  return afwImage.CoaddInputs(self.visitSchemavisitSchema, self.ccdSchemaccdSchema)
197 
198  def addVisitToCoadd(self, coaddInputs, coaddTempExp, weight):
199  """Called by AssembleCoaddTask when adding (a subset of) a coaddTempExp to a coadd. The
200  base class impementation extracts the CoaddInputs from the coaddTempExp and appends
201  them to the given coaddInputs, filling in the weight column(s).
202 
203  Note that the passed coaddTempExp may be a subimage, but that this method will only be
204  called for the first subimage
205 
206  Returns the record for the visit to allow subclasses to fill in additional fields.
207  Warns and returns None if the inputRecorder catalogs for the coaddTempExp are not usable.
208  """
209  tempExpInputs = coaddTempExp.getInfo().getCoaddInputs()
210  if len(tempExpInputs.visits) != 1:
211  self.log.warning("CoaddInputs for coaddTempExp should have exactly one record in visits table "
212  "(found %d). CoaddInputs for this visit will not be saved.",
213  len(tempExpInputs.visits))
214  return None
215  inputVisitRecord = tempExpInputs.visits[0]
216  outputVisitRecord = coaddInputs.visits.addNew()
217  outputVisitRecord.assign(inputVisitRecord)
218  outputVisitRecord.setD(self.visitWeightKeyvisitWeightKey, weight)
219  outputVisitRecord.set(self.visitFilterKeyvisitFilterKey, coaddTempExp.getFilterLabel().physicalLabel)
220  for inputCcdRecord in tempExpInputs.ccds:
221  if inputCcdRecord.getL(self.ccdVisitKeyccdVisitKey) != inputVisitRecord.getId():
222  self.log.warning("CoaddInputs for coaddTempExp with id %d contains CCDs with visit=%d. "
223  "CoaddInputs may be unreliable.",
224  inputVisitRecord.getId(), inputCcdRecord.getL(self.ccdVisitKeyccdVisitKey))
225  outputCcdRecord = coaddInputs.ccds.addNew()
226  outputCcdRecord.assign(inputCcdRecord)
227  if self.config.saveCcdWeights:
228  outputCcdRecord.setD(self.ccdWeightKeyccdWeightKey, weight)
229  outputCcdRecord.set(self.ccdFilterKeyccdFilterKey, coaddTempExp.getFilterLabel().physicalLabel)
230  return inputVisitRecord
A simple Persistable struct containing ExposureCatalogs that record the inputs to a coadd.
Definition: CoaddInputs.h:49
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
Definition: CoaddPsf.h:58
def addVisitToCoadd(self, coaddInputs, coaddTempExp, weight)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
def makeCoaddApCorrMap(catalog, coaddBox, coaddWcs, weightFieldName="weight")
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.