lsst.meas.base  16.0-18-ga4d4bcb+9
forcedPhotImage.py
Go to the documentation of this file.
1 # This file is part of meas_base.
2 #
3 # Developed for the LSST Data Management System.
4 # This product includes software developed by the LSST Project
5 # (https://www.lsst.org).
6 # See the COPYRIGHT file at the top-level directory of this distribution
7 # for details of code ownership.
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 GNU General Public License
20 # along with this program. If not, see <https://www.gnu.org/licenses/>.
21 
22 """Base command-line driver task for forced measurement.
23 
24 Must be inherited to specialize for a specific dataset to be used (see
25 `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  # Docstring inherited.
74  # Make catalogCalculation a no-op by default as no modelFlux is setup by default in
75  # ForcedMeasurementTask
76  self.catalogCalculation.plugins.names = []
77 
78 
79 class ForcedPhotImageTask(lsst.pipe.base.CmdLineTask):
80  """A base class for command-line forced measurement drivers.
81 
82  Parameters
83  ----------
84  butler : `lsst.daf.persistence.butler.Butler`, optional
85  A Butler which will be passed to the references subtask to allow it to
86  load its schema from disk. Optional, but must be specified if
87  ``refSchema`` is not; if both are specified, ``refSchema`` takes
88  precedence.
89  refSchema : `lsst.afw.table.Schema`, optional
90  The schema of the reference catalog, passed to the constructor of the
91  references subtask. Optional, but must be specified if ``butler`` is
92  not; if both are specified, ``refSchema`` takes precedence.
93  **kwds
94  Keyword arguments are passed to the supertask constructor.
95 
96  Notes
97  -----
98  This is a an abstract class, which is the common ancestor for
99  `ForcedPhotCcdTask` and `ForcedPhotCoaddTask`. It provides the
100  `runDataRef` method that does most of the work, while delegating a few
101  customization tasks to other methods that are overridden by subclasses.
102 
103  This task is not directly usable as a command line task. Subclasses must:
104 
105  - Set the `_DefaultName` class attribute;
106  - Implement `makeIdFactory`;
107  - Implement `fetchReferences`;
108  - Optionally, implement `attachFootprints`.
109  """
110 
111  ConfigClass = ForcedPhotImageConfig
112  _DefaultName = "processImageForcedTask"
113 
114  def __init__(self, butler=None, refSchema=None, **kwds):
115  super(lsst.pipe.base.CmdLineTask, self).__init__(**kwds)
116  self.makeSubtask("references", butler=butler, schema=refSchema)
117  if refSchema is None:
118  refSchema = self.references.schema
119  self.makeSubtask("measurement", refSchema=refSchema)
120  # It is necessary to get the schema internal to the forced measurement task until such a time
121  # that the schema is not owned by the measurement task, but is passed in by an external caller
122  if self.config.doApCorr:
123  self.makeSubtask("applyApCorr", schema=self.measurement.schema)
124  self.makeSubtask('catalogCalculation', schema=self.measurement.schema)
125 
126  def runDataRef(self, dataRef, psfCache=None):
127  """Perform forced measurement on a single exposure.
128 
129  Parameters
130  ----------
131  dataRef : `lsst.daf.persistence.ButlerDataRef`
132  Passed to the ``references`` subtask to obtain the reference WCS,
133  the ``getExposure`` method (implemented by derived classes) to
134  read the measurment image, and the ``fetchReferences`` method to
135  get the exposure and load the reference catalog (see
136  :lsst-task`lsst.meas.base.references.CoaddSrcReferencesTask`).
137  Refer to derived class documentation for details of the datasets
138  and data ID keys which are used.
139  psfCache : `int`, optional
140  Size of PSF cache, or `None`. The size of the PSF cache can have
141  a significant effect upon the runtime for complicated PSF models.
142 
143  Notes
144  -----
145  Sources are generated with ``generateMeasCat`` in the ``measurement``
146  subtask. These are passed to ``measurement``'s ``run`` method, which
147  fills the source catalog with the forced measurement results. The
148  sources are then passed to the ``writeOutputs`` method (implemented by
149  derived classes) which writes the outputs.
150  """
151  refWcs = self.references.getWcs(dataRef)
152  exposure = self.getExposure(dataRef)
153  if psfCache is not None:
154  exposure.getPsf().setCacheSize(psfCache)
155  refCat = self.fetchReferences(dataRef, exposure)
156 
157  measCat = self.measurement.generateMeasCat(exposure, refCat, refWcs,
158  idFactory=self.makeIdFactory(dataRef))
159  self.log.info("Performing forced measurement on %s" % (dataRef.dataId,))
160  self.attachFootprints(measCat, refCat, exposure, refWcs, dataRef)
161 
162  exposureId = self.getExposureId(dataRef)
163 
164  forcedPhotResult = self.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
165 
166  self.writeOutput(dataRef, forcedPhotResult.measCat)
167 
168  def run(self, measCat, exposure, refCat, refWcs, exposureId=None):
169  """Perform forced measurement on a single exposure.
170 
171  Parameters
172  ----------
173  measCat : `lsst.afw.table.SourceCatalog`
174  The measurement catalog, based on the sources listed in the
175  reference catalog.
176  exposure : `lsst.afw.image.Exposure`
177  The measurement image upon which to perform forced detection.
178  refCat : `lsst.afw.table.SourceCatalog`
179  The reference catalog of sources to measure.
180  refWcs : `lsst.afw.image.SkyWcs`
181  The WCS for the references.
182  exposureId : `int`
183  Optional unique exposureId used for random seed in measurement
184  task.
185 
186  Returns
187  -------
188  result : `lsst.pipe.base.Struct`
189  Structure with fields:
190 
191  ``measCat``
192  Catalog of forced measurement results
193  (`lsst.afw.table.SourceCatalog`).
194  """
195  self.measurement.run(measCat, exposure, refCat, refWcs, exposureId=exposureId)
196  if self.config.doApCorr:
197  self.applyApCorr.run(
198  catalog=measCat,
199  apCorrMap=exposure.getInfo().getApCorrMap()
200  )
201  self.catalogCalculation.run(measCat)
202 
203  return lsst.pipe.base.Struct(measCat=measCat)
204 
205  def makeIdFactory(self, dataRef):
206  """Hook for derived classes to make an ID factory for forced sources.
207 
208  Notes
209  -----
210  That this applies to forced *source* IDs, not object IDs, which are
211  usually handled by the ``measurement.copyColumns`` config option.
212 
213  """
214  raise NotImplementedError()
215 
216  def getExposureId(self, dataRef):
217  raise NotImplementedError()
218 
219  def fetchReferences(self, dataRef, exposure):
220  """Hook for derived classes to define how to get reference objects.
221 
222  Notes
223  -----
224  Derived classes should call one of the ``fetch*`` methods on the
225  ``references`` subtask, but which one they call depends on whether the
226  region to get references for is a easy to describe in patches (as it
227  would be when doing forced measurements on a coadd), or is just an
228  arbitrary box (as it would be for CCD forced measurements).
229  """
230  raise NotImplementedError()
231 
232  def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef):
233  r"""Attach footprints to blank sources prior to measurements.
234 
235  Notes
236  -----
237  `~lsst.afw.detection.Footprint`\ s for forced photometry must be in the
238  pixel coordinate system of the image being measured, while the actual
239  detections may start out in a different coordinate system.
240 
241  Subclasses of this class must implement this method to define how
242  those `~lsst.afw.detection.Footprint`\ s should be generated.
243 
244  This default implementation transforms the
245  `~lsst.afw.detection.Footprint`\ s from the reference catalog from the
246  reference WCS to the exposure's WcS, which downgrades
247  `lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s into regular
248  `~lsst.afw.detection.Footprint`\ s, destroying deblend information.
249  """
250  return self.measurement.attachTransformedFootprints(sources, refCat, exposure, refWcs)
251 
252  def getExposure(self, dataRef):
253  """Read input exposure on which measurement will be performed.
254 
255  Parameters
256  ----------
257  dataRef : `lsst.daf.persistence.ButlerDataRef`
258  Butler data reference.
259  """
260  return dataRef.get(self.dataPrefix + "calexp", immediate=True)
261 
262  def writeOutput(self, dataRef, sources):
263  """Write forced source table
264 
265  Parameters
266  ----------
267  dataRef : `lsst.daf.persistence.ButlerDataRef`
268  Butler data reference. The forced_src dataset (with
269  self.dataPrefix prepended) is all that will be modified.
270  sources : `lsst.afw.table.SourceCatalog`
271  Catalog of sources to save.
272  """
273  dataRef.put(sources, self.dataPrefix + "forced_src", flags=lsst.afw.table.SOURCE_IO_NO_FOOTPRINTS)
274 
275  def getSchemaCatalogs(self):
276  """The schema catalogs that will be used by this task.
277 
278  Returns
279  -------
280  schemaCatalogs : `dict`
281  Dictionary mapping dataset type to schema catalog.
282 
283  Notes
284  -----
285  There is only one schema for each type of forced measurement. The
286  dataset type for this measurement is defined in the mapper.
287  """
288  catalog = lsst.afw.table.SourceCatalog(self.measurement.schema)
289  catalog.getTable().setMetadata(self.measurement.algMetadata)
290  datasetType = self.dataPrefix + "forced_src"
291  return {datasetType: catalog}
292 
293  def _getConfigName(self):
294  # Documented in superclass
295  return self.dataPrefix + "forced_config"
296 
297  def _getMetadataName(self):
298  # Documented in superclass
299  return self.dataPrefix + "forced_metadata"
def run(self, measCat, exposure, refCat, refWcs, exposureId=None)
def __init__(self, butler=None, refSchema=None, kwds)
def attachFootprints(self, sources, refCat, exposure, refWcs, dataRef)