lsst.meas.base  14.0-13-gd9a51ef+9
references.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 """
24 Subtasks for creating the reference catalogs used in forced measurement.
25 """
26 
27 import lsst.afw.geom
28 import lsst.pex.config
29 import lsst.pipe.base
30 
31 __all__ = ("BaseReferencesTask", "CoaddSrcReferencesTask")
32 
33 
34 class BaseReferencesConfig(lsst.pex.config.Config):
35  removePatchOverlaps = lsst.pex.config.Field(
36  doc="Only include reference sources for each patch that lie within the patch's inner bbox",
37  dtype=bool,
38  default=True
39  )
40  filter = lsst.pex.config.Field(
41  doc="Bandpass for reference sources; None indicates chi-squared detections.",
42  dtype=str,
43  optional=True
44  )
45 
46 
47 class BaseReferencesTask(lsst.pipe.base.Task):
48  """!
49  Base class for forced photometry subtask that retrieves reference sources.
50 
51  BaseReferencesTask defines the required API for the references task, which includes:
52  - getSchema(butler)
53  - fetchInPatches(butler, tract, filter, patchList)
54  - fetchInBox(self, butler, tract, filter, bbox, wcs)
55  - the removePatchOverlaps config option
56 
57  It also provides the subset() method, which may be of use to derived classes when
58  reimplementing fetchInBox.
59  """
60 
61  ConfigClass = BaseReferencesConfig
62 
63  def __init__(self, butler=None, schema=None, **kwargs):
64  """!Initialize the task.
65 
66  BaseReferencesTask and its subclasses take two keyword arguments beyond the usual Task arguments:
67  - schema: the Schema of the reference catalog
68  - butler: a butler that will allow the task to load its Schema from disk.
69  At least one of these arguments must be present; if both are, schema takes precedence.
70  """
71  lsst.pipe.base.Task.__init__(self, **kwargs)
72 
73  def getSchema(self, butler):
74  """!
75  Return the schema for the reference sources.
76 
77  Must be available even before any data has been processed.
78  """
79  raise NotImplementedError("BaseReferencesTask is pure abstract, and cannot be used directly.")
80 
81  def getWcs(self, dataRef):
82  """!
83  Return the WCS for reference sources. The given dataRef must include the tract in its dataId.
84  """
85  raise NotImplementedError("BaseReferencesTask is pure abstract, and cannot be used directly.")
86 
87  def fetchInBox(self, dataRef, bbox, wcs):
88  """!
89  Return reference sources that overlap a region defined by a pixel-coordinate bounding box
90  and corresponding Wcs.
91 
92  @param[in] dataRef ButlerDataRef; the implied data ID must contain the 'tract' key.
93  @param[in] bbox a afw.geom.Box2I or Box2D that defines the region in pixel coordinates
94  @param[in] wcs afw.image.Wcs that maps the bbox to sky coordinates
95 
96  @return an iterable of reference sources
97 
98  It is not required that the returned object be a SourceCatalog; it may be any Python iterable
99  containing SourceRecords (including a lazy iterator).
100 
101  The returned set of sources should be complete and close to minimal.
102  """
103  raise NotImplementedError("BaseReferencesTask is pure abstract, and cannot be used directly.")
104 
105  def fetchInPatches(self, dataRef, patchList):
106  """!
107  Return reference sources that overlap a region defined by one or more SkyMap patches.
108 
109  @param[in] dataRef ButlerDataRef; the implied data ID must contain the 'tract' key.
110  @param[in] patchList list of skymap.PatchInfo instances for which to fetch reference sources
111 
112  @return an iterable of reference sources
113 
114  It is not required that the returned object be a SourceCatalog; it may be any Python sequence
115  containing SourceRecords (including a lazy iterator).
116 
117  The returned set of sources should be complete and close to minimal. If
118  config.removePatchOverlaps is True, only sources within each patch's "inner" bounding box
119  should be returned.
120  """
121  raise NotImplementedError("BaseReferencesTask is pure abstract, and cannot be used directly.")
122 
123  def subset(self, sources, bbox, wcs):
124  """!
125  Filter sources to contain only those within the given box, defined in the coordinate system
126  defined by the given Wcs.
127 
128  @param[in] sources input iterable of SourceRecords
129  @param[in] bbox bounding box with which to filter reference sources (Box2I or Box2D)
130  @param[in] wcs afw.image.Wcs that defines the coordinate system of bbox
131 
132  Instead of filtering sources directly via their positions, we filter based on the positions
133  of parent objects, then include or discard all children based on their parent's status. This
134  is necessary to support ReplaceWithNoise in measurement, which requires all child sources have
135  their parent present.
136 
137  @return an iterable of filtered reference sources
138 
139  This is not a part of the required BaseReferencesTask interface; it's a convenience function
140  used in implementing fetchInBox that may be of use to subclasses.
141  """
142  boxD = lsst.afw.geom.Box2D(bbox)
143  # We're passed an arbitrary iterable, but we need a catalog so we can iterate
144  # over parents and then children.
145  catalog = lsst.afw.table.SourceCatalog(self.schema)
146  catalog.extend(sources)
147  # catalog must be sorted by parent ID for lsst.afw.table.getChildren to work
149  # Iterate over objects that have no parent.
150  for parent in catalog.getChildren(0):
151  pixel = wcs.skyToPixel(parent.getCoord())
152  if boxD.contains(pixel):
153  yield parent
154  for child in catalog.getChildren(parent.getId()):
155  yield child
156 
157 
158 class CoaddSrcReferencesConfig(BaseReferencesTask.ConfigClass):
159  coaddName = lsst.pex.config.Field(
160  doc="Coadd name: typically one of deep or goodSeeing.",
161  dtype=str,
162  default="deep",
163  )
164  skipMissing = lsst.pex.config.Field(
165  doc="Silently skip patches where the reference catalog does not exist.",
166  dtype=bool,
167  default=False
168  )
169 
170  def validate(self):
171  if (self.coaddName == "chiSquared") != (self.filter is None):
172  raise lsst.pex.config.FieldValidationError(
173  field=CoaddSrcReferencesConfig.coaddName,
174  config=self,
175  msg="filter may be None if and only if coaddName is chiSquared"
176  )
177 
178 
180  """!
181  A references task implementation that loads the coadd_datasetSuffix dataset directly from
182  disk using the butler.
183  """
184 
185  ConfigClass = CoaddSrcReferencesConfig
186  datasetSuffix = "src" # Suffix to add to "Coadd_" for dataset name
187 
188  def __init__(self, butler=None, schema=None, **kwargs):
189  """! Initialize the task.
190  Additional keyword arguments (forwarded to BaseReferencesTask.__init__):
191  - schema: the schema of the detection catalogs used as input to this one
192  - butler: a butler used to read the input schema from disk, if schema is None
193  The task will set its own self.schema attribute to the schema of the output merged catalog.
194  """
195  BaseReferencesTask.__init__(self, butler=butler, schema=schema, **kwargs)
196  if schema is None:
197  assert butler is not None, "No butler nor schema provided"
198  schema = butler.get("{}Coadd_{}_schema".format(self.config.coaddName, self.datasetSuffix),
199  immediate=True).getSchema()
200  self.schema = schema
201 
202  def getWcs(self, dataRef):
203  """Return the WCS for reference sources. The given dataRef must include the tract in its dataId.
204  """
205  skyMap = dataRef.get(self.config.coaddName + "Coadd_skyMap", immediate=True)
206  return skyMap[dataRef.dataId["tract"]].getWcs()
207 
208  def fetchInPatches(self, dataRef, patchList):
209  """!
210  An implementation of BaseReferencesTask.fetchInPatches that loads 'coadd_' + datasetSuffix
211  catalogs using the butler.
212 
213  The given dataRef must include the tract in its dataId.
214  """
215  dataset = "{}Coadd_{}".format(self.config.coaddName, self.datasetSuffix)
216  tract = dataRef.dataId["tract"]
217  butler = dataRef.butlerSubset.butler
218  for patch in patchList:
219  dataId = {'tract': tract, 'patch': "%d,%d" % patch.getIndex()}
220  if self.config.filter is not None:
221  dataId['filter'] = self.config.filter
222 
223  if not butler.datasetExists(dataset, dataId):
224  if self.config.skipMissing:
225  continue
226  raise lsst.pipe.base.TaskError("Reference %s doesn't exist" % (dataId,))
227  self.log.info("Getting references in %s" % (dataId,))
228  catalog = butler.get(dataset, dataId, immediate=True)
229  if self.config.removePatchOverlaps:
230  bbox = lsst.afw.geom.Box2D(patch.getInnerBBox())
231  for source in catalog:
232  if bbox.contains(source.getCentroid()):
233  yield source
234  else:
235  for source in catalog:
236  yield source
237 
238  def fetchInBox(self, dataRef, bbox, wcs, pad=0):
239  """!
240  Return reference sources that overlap a region defined by a pixel-coordinate bounding box
241  and corresponding Wcs.
242 
243  @param[in] dataRef ButlerDataRef; the implied data ID must contain the 'tract' key.
244  @param[in] bbox a afw.geom.Box2I or Box2D that defines the region in pixel coordinates
245  @param[in] wcs afw.image.Wcs that maps the bbox to sky coordinates
246  @param[in] pad a buffer to grow the bounding box by after catalogs have been loaded, but
247  before filtering them to include just the given bounding box.
248 
249  @return an iterable of reference sources
250  """
251  skyMap = dataRef.get(self.config.coaddName + "Coadd_skyMap", immediate=True)
252  tract = skyMap[dataRef.dataId["tract"]]
253  coordList = [wcs.pixelToSky(corner) for corner in lsst.afw.geom.Box2D(bbox).getCorners()]
254  self.log.info("Getting references in region with corners %s [degrees]" %
255  ", ".join("(%s)" % (coord.getPosition(lsst.afw.geom.degrees),) for coord in coordList))
256  patchList = tract.findPatchList(coordList)
257  # After figuring out which patch catalogs to read from the bbox, pad out the bbox if desired
258  # But don't add any new patches while padding
259  if pad:
260  bbox.grow(pad)
261  return self.subset(self.fetchInPatches(dataRef, patchList), bbox, wcs)
262 
263 
264 class MultiBandReferencesConfig(CoaddSrcReferencesTask.ConfigClass):
265 
266  def validate(self):
267  if self.filter is not None:
268  raise lsst.pex.config.FieldValidationError(
269  field=MultiBandReferencesConfig.filter,
270  config=self,
271  msg="Filter should not be set for the multiband processing scheme")
272  # Delegate to ultimate base class, because the direct one has a check we don't want.
273  BaseReferencesTask.ConfigClass.validate(self)
274 
275 
277  """Loads references from the multiband processing scheme"""
278  ConfigClass = MultiBandReferencesConfig
279  datasetSuffix = "ref"
def fetchInBox(self, dataRef, bbox, wcs, pad=0)
Return reference sources that overlap a region defined by a pixel-coordinate bounding box and corresp...
Definition: references.py:238
def getSchema(self, butler)
Return the schema for the reference sources.
Definition: references.py:73
def getWcs(self, dataRef)
Return the WCS for reference sources.
Definition: references.py:81
Base class for forced photometry subtask that retrieves reference sources.
Definition: references.py:47
A references task implementation that loads the coadd_datasetSuffix dataset directly from disk using ...
Definition: references.py:179
def fetchInBox(self, dataRef, bbox, wcs)
Return reference sources that overlap a region defined by a pixel-coordinate bounding box and corresp...
Definition: references.py:87
def fetchInPatches(self, dataRef, patchList)
An implementation of BaseReferencesTask.fetchInPatches that loads &#39;coadd_&#39; + datasetSuffix catalogs u...
Definition: references.py:208
def __init__(self, butler=None, schema=None, kwargs)
Initialize the task.
Definition: references.py:188
def __init__(self, butler=None, schema=None, kwargs)
Initialize the task.
Definition: references.py:63
static Key< RecordId > getParentKey()
def fetchInPatches(self, dataRef, patchList)
Return reference sources that overlap a region defined by one or more SkyMap patches.
Definition: references.py:105
def subset(self, sources, bbox, wcs)
Filter sources to contain only those within the given box, defined in the coordinate system defined b...
Definition: references.py:123