LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
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 
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 
165  def validate(self):
166  if (self.coaddName == "chiSquared") != (self.filter is None):
167  raise lsst.pex.config.FieldValidationError(
168  field=CoaddSrcReferencesConfig.coaddName,
169  config=self,
170  msg="filter may be None if and only if coaddName is chiSquared"
171  )
172 
173 
175  """!
176  A references task implementation that loads the coadd_datasetSuffix dataset directly from
177  disk using the butler.
178  """
179 
180  ConfigClass = CoaddSrcReferencesConfig
181  datasetSuffix = "src" # Suffix to add to "Coadd_" for dataset name
182 
183  def __init__(self, butler=None, schema=None, **kwargs):
184  """! Initialize the task.
185  Additional keyword arguments (forwarded to BaseReferencesTask.__init__):
186  - schema: the schema of the detection catalogs used as input to this one
187  - butler: a butler used to read the input schema from disk, if schema is None
188  The task will set its own self.schema attribute to the schema of the output merged catalog.
189  """
190  BaseReferencesTask.__init__(self, butler=butler, schema=schema, **kwargs)
191  if schema is None:
192  assert butler is not None, "No butler nor schema provided"
193  schema = butler.get("{}Coadd_{}_schema".format(self.config.coaddName, self.datasetSuffix),
194  immediate=True).getSchema()
195  self.schema = schema
196 
197  def getWcs(self, dataRef):
198  """Return the WCS for reference sources. The given dataRef must include the tract in its dataId.
199  """
200  skyMap = dataRef.get(self.config.coaddName + "Coadd_skyMap", immediate=True)
201  return skyMap[dataRef.dataId["tract"]].getWcs()
202 
203  def fetchInPatches(self, dataRef, patchList):
204  """!
205  An implementation of BaseReferencesTask.fetchInPatches that loads 'coadd_' + datasetSuffix
206  catalogs using the butler.
207 
208  The given dataRef must include the tract in its dataId.
209  """
210  dataset = "{}Coadd_{}".format(self.config.coaddName, self.datasetSuffix)
211  tract = dataRef.dataId["tract"]
212  butler = dataRef.butlerSubset.butler
213  for patch in patchList:
214  dataId = {'tract': tract, 'patch': "%d,%d" % patch.getIndex()}
215  if self.config.filter is not None:
216  dataId['filter'] = self.config.filter
217 
218  if not butler.datasetExists(dataset, dataId):
219  raise lsst.pipe.base.TaskError("Reference %s doesn't exist" % (dataId,))
220  self.log.info("Getting references in %s" % (dataId,))
221  catalog = butler.get(dataset, dataId, immediate=True)
222  if self.config.removePatchOverlaps:
223  bbox = lsst.afw.geom.Box2D(patch.getInnerBBox())
224  for source in catalog:
225  if bbox.contains(source.getCentroid()):
226  yield source
227  else:
228  for source in catalog:
229  yield source
230 
231  def fetchInBox(self, dataRef, bbox, wcs, pad=0):
232  """!
233  Return reference sources that overlap a region defined by a pixel-coordinate bounding box
234  and corresponding Wcs.
235 
236  @param[in] dataRef ButlerDataRef; the implied data ID must contain the 'tract' key.
237  @param[in] bbox a afw.geom.Box2I or Box2D that defines the region in pixel coordinates
238  @param[in] wcs afw.image.Wcs that maps the bbox to sky coordinates
239  @param[in] pad a buffer to grow the bounding box by after catalogs have been loaded, but
240  before filtering them to include just the given bounding box.
241 
242  @return an iterable of reference sources
243  """
244  skyMap = dataRef.get(self.config.coaddName + "Coadd_skyMap", immediate=True)
245  tract = skyMap[dataRef.dataId["tract"]]
246  coordList = [wcs.pixelToSky(corner) for corner in lsst.afw.geom.Box2D(bbox).getCorners()]
247  self.log.info("Getting references in region with corners %s [degrees]" %
248  ", ".join("(%s)" % (coord.getPosition(lsst.afw.geom.degrees),) for coord in coordList))
249  patchList = tract.findPatchList(coordList)
250  # After figuring out which patch catalogs to read from the bbox, pad out the bbox if desired
251  # But don't add any new patches while padding
252  if pad:
253  bbox.grow(pad)
254  return self.subset(self.fetchInPatches(dataRef, patchList), bbox, wcs)
255 
256 
257 class MultiBandReferencesConfig(CoaddSrcReferencesTask.ConfigClass):
258 
259  def validate(self):
260  if self.filter is not None:
261  raise lsst.pex.config.FieldValidationError(
262  field=MultiBandReferencesConfig.filter,
263  config=self,
264  msg="Filter should not be set for the multiband processing scheme")
265  # Delegate to ultimate base class, because the direct one has a check we don't want.
266  BaseReferencesTask.ConfigClass.validate(self)
267 
268 
270  """Loads references from the multiband processing scheme"""
271  ConfigClass = MultiBandReferencesConfig
272  datasetSuffix = "ref"
def getSchema
Return the schema for the reference sources.
Definition: references.py:73
def getWcs
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:174
def subset
Filter sources to contain only those within the given box, defined in the coordinate system defined b...
Definition: references.py:123
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:231
def fetchInBox
Return reference sources that overlap a region defined by a pixel-coordinate bounding box and corresp...
Definition: references.py:87
def fetchInPatches
Return reference sources that overlap a region defined by one or more SkyMap patches.
Definition: references.py:105
static Key< RecordId > getParentKey()
Key for the parent ID.
Definition: Source.h:258
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:203