lsst.meas.base  16.0-17-g7e0e4ff+6
forcedPhotImage.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # LSST Data Management System
4 # Copyright 2008, 2009, 2010, 2014 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 """Base command-line driver task for forced measurement. Must be inherited to specialize for
25 a specific dataset to be used (see ForcedPhotCcdTask, ForcedPhotCoaddTask).
26 """
27 
28 import lsst.afw.table
29 import lsst.pex.config
30 import lsst.daf.base
31 import lsst.pipe.base
32 import lsst.pex.config
33 
34 from .references import MultiBandReferencesTask
35 from .forcedMeasurement import ForcedMeasurementTask
36 from .applyApCorr import ApplyApCorrTask
37 from .catalogCalculation import CatalogCalculationTask
38 
39 __all__ = ("ForcedPhotImageConfig", "ForcedPhotImageTask")
40 
41 
42 class ForcedPhotImageConfig(lsst.pex.config.Config):
43  """!Config class for forced measurement driver task."""
44 
45  references = lsst.pex.config.ConfigurableField(
46  target=MultiBandReferencesTask,
47  doc="subtask to retrieve reference source catalog"
48  )
49  measurement = lsst.pex.config.ConfigurableField(
50  target=ForcedMeasurementTask,
51  doc="subtask to do forced measurement"
52  )
53  coaddName = lsst.pex.config.Field(
54  doc="coadd name: typically one of deep or goodSeeing",
55  dtype=str,
56  default="deep",
57  )
58  doApCorr = lsst.pex.config.Field(
59  dtype=bool,
60  default=True,
61  doc="Run subtask to apply aperture corrections"
62  )
63  applyApCorr = lsst.pex.config.ConfigurableField(
64  target=ApplyApCorrTask,
65  doc="Subtask to apply aperture corrections"
66  )
67  catalogCalculation = lsst.pex.config.ConfigurableField(
68  target=CatalogCalculationTask,
69  doc="Subtask to run catalogCalculation plugins on catalog"
70  )
71 
72  def setDefaults(self):
73  # Make catalogCalculation a no-op by default as no modelFlux is setup by default in
74  # ForcedMeasurementTask
75  self.catalogCalculation.plugins.names = []
76 
77 
83 
84 
85 class ForcedPhotImageTask(lsst.pipe.base.CmdLineTask):
86  """!A base class for command-line forced measurement drivers.
87 
88  This is a an abstract class, which is the common ancestor for ForcedPhotCcdTask
89  and ForcedPhotCoaddTask. It provides the runDataRef() method that does most of
90  the work, while delegating a few customization tasks to other methods that are
91  overridden by subclasses.
92 
93  This task is not directly usable as a CmdLineTask; subclasses must:
94  - Set the _DefaultName class attribute
95  - Implement makeIdFactory
96  - Implement fetchReferences
97  - (optional) Implement attachFootprints
98  """
99  ConfigClass = ForcedPhotImageConfig
100  _DefaultName = "processImageForcedTask"
101 
102  def __init__(self, butler=None, refSchema=None, **kwds):
103  """Initialize the task.
104 
105  ForcedPhotImageTask takes two keyword arguments beyond the usual CmdLineTask arguments:
106  - refSchema: the Schema of the reference catalog, passed to the constructor of the references
107  subtask
108  - butler: a butler that will be passed to the references subtask to allow it to load its Schema
109  from disk
110  At least one of these arguments must be present; if both are, schema takes precedence.
111  """
112  super(lsst.pipe.base.CmdLineTask, self).__init__(**kwds)
113  self.makeSubtask("references", butler=butler, schema=refSchema)
114  if refSchema is None:
115  refSchema = self.references.schema
116  self.makeSubtask("measurement", refSchema=refSchema)
117  # It is necessary to get the schema internal to the forced measurement task until such a time
118  # that the schema is not owned by the measurement task, but is passed in by an external caller
119  if self.config.doApCorr:
120  self.makeSubtask("applyApCorr", schema=self.measurement.schema)
121  self.makeSubtask('catalogCalculation', schema=self.measurement.schema)
122 
123  def runDataRef(self, dataRef, psfCache=None):
124  """!Measure a single exposure for forced detection for a reference catalog using a dataRef.
125 
126  @param[in] dataRef An lsst.daf.persistence.ButlerDataRef. It is passed to the
127  references subtask to obtain the reference WCS, the getExposure()
128  method (implemented by derived classes) to read the measurement
129  image, and the fetchReferences() method (implemented by derived
130  classes) to get the exposure and load the reference catalog (see
131  the CoaddSrcReferencesTask for more information). The sources are then
132  passed to the writeOutputs() method (implemented by derived classes)
133  which writes the outputs. See derived class documentation for which
134  datasets and data ID keys are used.
135  @param[in] psfCache Size of PSF cache, or None. The size of the PSF cache can have
136  a significant effect upon the runtime for complicated PSF models.
137  """
138  refWcs = self.references.getWcs(dataRef)
139  exposure = self.getExposure(dataRef)
140  if psfCache is not None:
141  exposure.getPsf().setCacheSize(psfCache)
142  refCat = self.fetchReferences(dataRef, exposure)
143 
144  measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
145  idFactory=self.makeIdFactory(dataRef))
146  self.log.info("Performing forced measurement on %s" % (dataRef.dataId,))
147  self.attachFootprints(measCat, refCat, exposure, refWcs, dataRef)
148 
149  exposureId = self.getExposureId(dataRef)
150 
151  forcedPhotResult = self.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
152 
153  self.writeOutput(dataRef, forcedPhotResult.measCat)
154 
155  def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
156  """!Measure a single exposure with forced detection for a reference catalog.
157 
158  @param[in] measCat The measurement catalog generated by measurement.generateMeasCat(),
159  based on the sources listed in the reference catalog.
160  @param[in] exposure The measurement image upon which to perform forced detection.
161  @param[in] refCat The reference catalog of sources to measure.
162  @param[in] refWcs The WCS for the references.
163  @param[in] exposureId Optional unique exposureId used for random seed in measurement task.
164 
165  @return result An lsst.pipe.base.Struct containing fields:
166  measCat Source catalog of forced measurement results from measurement.run().
167  """
168  self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
169  if self.config.doApCorr:
170  self.applyApCorr.run(
171  catalog=measCat,
172  apCorrMap=exposure.getInfo().getApCorrMap()
173  )
174  self.catalogCalculation.run(measCat)
175 
176  return lsst.pipe.base.Struct(measCat=measCat)
177 
178  def makeIdFactory(self, dataRef):
179  """!Hook for derived classes to define how to make an IdFactory for forced sources.
180 
181  Note that this is for forced source IDs, not object IDs, which are usually handled by
182  the measurement.copyColumns config option.
183  """
184  raise NotImplementedError()
185 
186  def getExposureId(self, dataRef):
187  raise NotImplementedError()
188 
189  def fetchReferences(self, dataRef, exposure):
190  """!Hook for derived classes to define how to get references objects.
191 
192  Derived classes should call one of the fetch* methods on the references subtask,
193  but which one they call depends on whether the region to get references for is a
194  easy to describe in patches (as it would be when doing forced measurements on a
195  coadd), or is just an arbitrary box (as it would be for CCD forced measurements).
196  """
197  raise NotImplementedError()
198 
199  def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef):
200  """!Hook for derived classes to define how to attach Footprints to blank sources prior to measurement
201 
202  Footprints for forced photometry must be in the pixel coordinate system of the image being
203  measured, while the actual detections may start out in a different coordinate system.
204 
205  Subclasses for ForcedPhotImageTask must implement this method to define how those Footprints
206  should be generated.
207 
208  The default implementation (defined in forcedMeasurement.py) transforms the Footprints from
209  the reference catalog from the refWcs to the exposure's Wcs, which downgrades HeavyFootprints
210  into regular Footprints, destroying deblend information.
211  """
212  return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs)
213 
214  def getExposure(self, dataRef):
215  """!Read input exposure on which to perform the measurements
216 
217  @param dataRef Data reference from butler.
218  """
219  return dataRef.get(self.dataPrefix + "calexp", immediate=True)
220 
221  def writeOutput(self, dataRef, sources):
222  """!Write forced source table
223 
224  @param dataRef Data reference from butler; the forced_src dataset (with self.dataPrefix included)
225  is all that will be modified.
226  @param sources SourceCatalog to save
227  """
228  dataRef.put(sources, self.dataPrefix + "forced_src", flags=lsst.afw.table.SOURCE_IO_NO_FOOTPRINTS)
229 
230  def getSchemaCatalogs(self):
231  """!Get a dict of Schema catalogs that will be used by this Task.
232 
233  In the case of forced taks, there is only one schema for each type of forced measurement.
234  The dataset type for this measurement is defined in the mapper.
235  """
236  catalog = lsst.afw.table.SourceCatalog(self.measurement.schema)
237  catalog.getTable().setMetadata(self.measurement.algMetadata)
238  datasetType = self.dataPrefix + "forced_src"
239  return {datasetType: catalog}
240 
241  def _getConfigName(self):
242  """!Return the name of the config dataset. Forces config comparison from run-to-run
243  """
244  return self.dataPrefix + "forced_config"
245 
246  def _getMetadataName(self):
247  """!Return the name of the metadata dataset. Forced metadata to be saved
248  """
249  return self.dataPrefix + "forced_metadata"
A base class for command-line forced measurement drivers.
def getExposure(self, dataRef)
Read input exposure on which to perform the measurements.
def runDataRef(self, dataRef, psfCache=None)
Measure a single exposure for forced detection for a reference catalog using a dataRef.
def fetchReferences(self, dataRef, exposure)
Hook for derived classes to define how to get references objects.
def run(self, measCat, exposure, refCat, refWcs, exposureId=None)
Measure a single exposure with forced detection for a reference catalog.
def makeIdFactory(self, dataRef)
Hook for derived classes to define how to make an IdFactory for forced sources.
def __init__(self, butler=None, refSchema=None, kwds)
def getSchemaCatalogs(self)
Get a dict of Schema catalogs that will be used by this Task.
def writeOutput(self, dataRef, sources)
Write forced source table.
def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef)
Hook for derived classes to define how to attach Footprints to blank sources prior to measurement...
Config class for forced measurement driver task.