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
processCoadd.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008-2015 AURA/LSST.
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 <https://www.lsstcorp.org/LegalNotices/>.
22 #
23 import lsst.pex.config as pexConfig
24 import lsst.pipe.base as pipeBase
25 import lsst.afw.geom as afwGeom
26 import lsst.afw.math as afwMath
27 import lsst.afw.table as afwTable
28 from lsst.coadd.utils import CoaddDataIdContainer
29 from .coaddBase import getSkyInfo
30 from .processImage import ProcessImageTask
31 
32 class ProcessCoaddConfig(ProcessImageTask.ConfigClass):
33  """Config for ProcessCoadd"""
34  coaddName = pexConfig.Field(
35  doc = "coadd name: typically one of deep or goodSeeing",
36  dtype = str,
37  default = "deep",
38  )
39  doScaleVariance = pexConfig.Field(
40  doc = "Scale variance plane using empirical noise",
41  dtype=bool,
42  default=True,
43  )
44 
45  def setDefaults(self):
46  ProcessImageTask.ConfigClass.setDefaults(self)
47  self.calibrate.background.undersampleStyle = 'REDUCE_INTERP_ORDER'
48  self.calibrate.detection.background.undersampleStyle = 'REDUCE_INTERP_ORDER'
49  self.detection.background.undersampleStyle = 'REDUCE_INTERP_ORDER'
50  self.calibrate.doPsf = False
51  self.calibrate.measureApCorr.inputFilterFlag = "calib_psfCandidate"
52  self.calibrate.astrometry.forceKnownWcs = True
53  self.calibrate.repair.doInterpolate = False
54  self.calibrate.repair.doCosmicRay = False
55  self.calibrate.doPhotoCal = False
56  self.detection.isotropicGrow = True
57  self.detection.returnOriginalFootprints = False
59  self.measurement.doReplaceWithNoise = True
60  # coadds do not yet have ap corr data; once they do, delete the following to enable the
61  # ProcessImageConfig default of yes; meanwhile the warning may help remind us
62  self.measurement.doApplyApCorr = "noButWarn"
63  self.doDeblend = True
64  self.deblend.maxNumberOfPeaks = 20
65 
66 class ProcessCoaddTask(ProcessImageTask):
67  """Process a Coadd image
68  """
69  ConfigClass = ProcessCoaddConfig
70  _DefaultName = "processCoadd"
71 
72  def __init__(self, **kwargs):
73  ProcessImageTask.__init__(self, **kwargs)
74  self.dataPrefix = self.config.coaddName + "Coadd_"
75  self.isPatchInnerKey = self.schema.addField(
76  "detect_isPatchInner", type="Flag",
77  doc="true if source is in the inner region of a coadd patch",
78  )
79  self.isTractInnerKey = self.schema.addField(
80  "detect_isTractInner", type="Flag",
81  doc="true if source is in the inner region of a coadd tract",
82  )
83  self.isPrimaryKey = self.schema.addField(
84  "detect_isPrimary", type="Flag",
85  doc="true if source has no children and is in the inner region of a coadd patch " \
86  + "and is in the inner region of a coadd tract",
87  )
88 
89  @pipeBase.timeMethod
90  def scaleVariance(self, exposure):
92  ctrl.setAndMask(~0x0)
93  var = exposure.getMaskedImage().getVariance()
94  mask = exposure.getMaskedImage().getMask()
95  dstats = afwMath.makeStatistics(exposure.getMaskedImage(), afwMath.VARIANCECLIP, ctrl).getValue(afwMath.VARIANCECLIP)
96  vstats = afwMath.makeStatistics(var, mask, afwMath.MEANCLIP, ctrl).getValue(afwMath.MEANCLIP)
97  vrat = dstats / vstats
98  self.log.info("Renormalising variance by %f" % (vrat))
99  var *= vrat
100 
101  def makeIdFactory(self, dataRef):
102  expBits = dataRef.get(self.config.coaddName + "CoaddId_bits")
103  expId = long(dataRef.get(self.config.coaddName + "CoaddId"))
104  return afwTable.IdFactory.makeSource(expId, 64 - expBits)
105 
106  def getExposureId(self, dataRef):
107  return long(dataRef.get(self.config.coaddName + "CoaddId"))
108 
109  @pipeBase.timeMethod
110  def run(self, dataRef):
111  """Process a coadd image
112 
113  @param dataRef: butler data reference corresponding to coadd patch
114  @return pipe_base Struct containing these fields:
115  - exposure: calibrated exposure (calexp): as computed if config.doCalibrate,
116  else as upersisted and updated if config.doDetection, else None
117  - calib: object returned by calibration process if config.doCalibrate, else None
118  - sources: detected source if config.doDetection, else None
119  """
120  self.log.info("Processing %s" % (dataRef.dataId))
121 
122  # initialize outputs
123  coadd = None
124 
125  skyInfo = getSkyInfo(coaddName=self.config.coaddName, patchRef=dataRef)
126 
127  coadd = dataRef.get(self.config.coaddName + "Coadd")
128  if self.config.doScaleVariance:
129  self.scaleVariance(coadd)
130 
131  # delegate most of the work to ProcessImageTask
132  result = self.process(dataRef, coadd, enableWriteSources=False)
133  result.coadd = coadd
134 
135  if result.sources is not None:
136  self.setIsPrimaryFlag(sources=result.sources, skyInfo=skyInfo)
137 
138  # write sources
139  if self.config.doWriteSources:
140  dataRef.put(result.sources, self.dataPrefix + 'src')
141 
142  return result
143 
144  def setIsPrimaryFlag(self, sources, skyInfo):
145  """Set is-primary and related flags on sources
146 
147  @param[in,out] sources: a SourceTable
148  - reads centroid fields and an nChild field
149  - writes is-patch-inner, is-tract-inner and is-primary flags
150  @param[in] skyInfo: a SkyInfo object as returned by getSkyInfo;
151  reads skyMap, patchInfo, and tractInfo fields
152 
153  @raise RuntimeError if self.config.doDeblend and the nChild key is not found in the table
154  """
155  # Test for the presence of the nchild key instead of checking config.doDeblend because sources
156  # might be unpersisted with deblend info, even if deblending is not run again.
157  nChildKeyName = "deblend_nChild"
158 
159  try:
160  nChildKey = self.schema.find(nChildKeyName).key
161  except Exception:
162  nChildKey = None
163 
164  if self.config.doDeblend and nChildKey is None:
165  # deblending was run but the nChildKey was not found; this suggests a variant deblender
166  # was used that we cannot use the output from, or some obscure error.
167  raise RuntimeError("Ran the deblender but cannot find %r in the source table" % (nChildKeyName,))
168 
169  # set inner flags for each source and set primary flags for sources with no children
170  # (or all sources if deblend info not available)
171  innerFloatBBox = afwGeom.Box2D(skyInfo.patchInfo.getInnerBBox())
172  tractId = skyInfo.tractInfo.getId()
173  for source in sources:
174  if source.getCentroidFlag():
175  # centroid unknown, so leave the inner and primary flags False
176  continue
177 
178  centroidPos = source.getCentroid()
179  isPatchInner = innerFloatBBox.contains(centroidPos)
180  source.setFlag(self.isPatchInnerKey, isPatchInner)
181 
182  skyPos = source.getCoord()
183  sourceInnerTractId = skyInfo.skyMap.findTract(skyPos).getId()
184  isTractInner = sourceInnerTractId == tractId
185  source.setFlag(self.isTractInnerKey, isTractInner)
186 
187  if nChildKey is None or source.get(nChildKey) == 0:
188  source.setFlag(self.isPrimaryKey, isPatchInner and isTractInner)
189 
190  @classmethod
192  parser = pipeBase.ArgumentParser(name=cls._DefaultName)
193  parser.add_id_argument("--id", "deepCoadd", help="data ID, e.g. --id tract=12345 patch=1,2",
194  ContainerClass=CoaddDataIdContainer)
195  return parser
196 
197  def _getConfigName(self):
198  """Return the name of the config dataset
199  """
200  return "%s_processCoadd_config" % (self.config.coaddName,)
201 
202  def _getMetadataName(self):
203  """Return the name of the metadata dataset
204  """
205  return "%s_processCoadd_metadata" % (self.config.coaddName,)
Pass parameters to a Statistics objectA class to pass parameters which control how the stats are calc...
Definition: Statistics.h:92
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1082
def getSkyInfo
Return SkyMap, tract and patch.
Definition: coaddBase.py:212
A floating-point coordinate rectangle geometry.
Definition: Box.h:271