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