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