LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
ref_match.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2016 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ['RefMatchConfig', 'RefMatchTask']
24 
25 import astropy.time
26 
27 import lsst.geom
28 from lsst.daf.base import DateTime
29 import lsst.afw.math as afwMath
30 import lsst.pex.config as pexConfig
31 import lsst.pipe.base as pipeBase
32 from lsst.meas.algorithms import ScienceSourceSelectorTask, ReferenceSourceSelectorTask
33 from .matchPessimisticB import MatchPessimisticBTask
34 from .display import displayAstrometry
35 from . import makeMatchStatistics
36 
37 
38 class RefMatchConfig(pexConfig.Config):
39  matcher = pexConfig.ConfigurableField(
40  target=MatchPessimisticBTask,
41  doc="reference object/source matcher",
42  )
43  matchDistanceSigma = pexConfig.RangeField(
44  doc="the maximum match distance is set to "
45  " mean_match_distance + matchDistanceSigma*std_dev_match_distance; "
46  "ignored if not fitting a WCS",
47  dtype=float,
48  default=2,
49  min=0,
50  )
51  sourceSelection = pexConfig.ConfigurableField(target=ScienceSourceSelectorTask,
52  doc="Selection of science sources")
53  referenceSelection = pexConfig.ConfigurableField(target=ReferenceSourceSelectorTask,
54  doc="Selection of reference sources")
55 
56 
57 class RefMatchTask(pipeBase.Task):
58  """Match an input source catalog with objects from a reference catalog.
59 
60  Parameters
61  ----------
62  refObjLoader : `lsst.meas.algorithms.ReferenceLoader`
63  A reference object loader object
64  **kwargs
65  additional keyword arguments for pipe_base `lsst.pipe.base.Task`
66  """
67  ConfigClass = RefMatchConfig
68  _DefaultName = "calibrationBaseClass"
69 
70  def __init__(self, refObjLoader, **kwargs):
71  pipeBase.Task.__init__(self, **kwargs)
72  if refObjLoader:
73  self.refObjLoader = refObjLoader
74  else:
75  self.refObjLoader = None
76  self.makeSubtask("matcher")
77  self.makeSubtask("sourceSelection")
78  self.makeSubtask("referenceSelection")
79 
80  def setRefObjLoader(self, refObjLoader):
81  """Sets the reference object loader for the task
82 
83  Parameters
84  ----------
85  refObjLoader
86  An instance of a reference object loader task or class
87  """
88  self.refObjLoader = refObjLoader
89 
90  @pipeBase.timeMethod
91  def loadAndMatch(self, exposure, sourceCat):
92  """Load reference objects overlapping an exposure and match to sources
93  detected on that exposure.
94 
95  Parameters
96  ----------
97  exposure : `lsst.afw.image.Exposure`
98  exposure that the sources overlap
99  sourceCat : `lsst.afw.table.SourceCatalog.`
100  catalog of sources detected on the exposure
101 
102  Returns
103  -------
104  result : `lsst.pipe.base.Struct`
105  Result struct with Components:
106 
107  - ``refCat`` : reference object catalog of objects that overlap the
108  exposure (`lsst.afw.table.SimpleCatalog`)
109  - ``matches`` : Matched sources and references
110  (`list` of `lsst.afw.table.ReferenceMatch`)
111  - ``matchMeta`` : metadata needed to unpersist matches
112  (`lsst.daf.base.PropertyList`)
113 
114  Notes
115  -----
116  ignores config.matchDistanceSigma
117  """
118  if self.refObjLoader is None:
119  raise RuntimeError("Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
120  import lsstDebug
121  debug = lsstDebug.Info(__name__)
122 
123  expMd = self._getExposureMetadata(exposure)
124 
125  sourceSelection = self.sourceSelection.run(sourceCat)
126 
127  loadRes = self.refObjLoader.loadPixelBox(
128  bbox=expMd.bbox,
129  wcs=expMd.wcs,
130  filterName=expMd.filterName,
131  calib=expMd.calib,
132  )
133 
134  refSelection = self.referenceSelection.run(loadRes.refCat)
135 
136  matchMeta = self.refObjLoader.getMetadataBox(
137  bbox=expMd.bbox,
138  wcs=expMd.wcs,
139  filterName=expMd.filterName,
140  calib=expMd.calib,
141  )
142 
143  matchRes = self.matcher.matchObjectsToSources(
144  refCat=refSelection.sourceCat,
145  sourceCat=sourceSelection.sourceCat,
146  wcs=expMd.wcs,
147  refFluxField=loadRes.fluxField,
148  match_tolerance=None,
149  )
150 
151  distStats = self._computeMatchStatsOnSky(matchRes.matches)
152  self.log.info(
153  "Found %d matches with scatter = %0.3f +- %0.3f arcsec; " %
154  (len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds())
155  )
156 
157  if debug.display:
158  frame = int(debug.frame)
160  refCat=refSelection.sourceCat,
161  sourceCat=sourceSelection.sourceCat,
162  matches=matchRes.matches,
163  exposure=exposure,
164  bbox=expMd.bbox,
165  frame=frame,
166  title="Matches",
167  )
168 
169  return pipeBase.Struct(
170  refCat=loadRes.refCat,
171  refSelection=refSelection,
172  sourceSelection=sourceSelection,
173  matches=matchRes.matches,
174  matchMeta=matchMeta,
175  )
176 
177  def _computeMatchStatsOnSky(self, matchList):
178  """Compute on-sky radial distance statistics for a match list
179 
180  Parameters
181  ----------
182  matchList : `list` of `lsst.afw.table.ReferenceMatch`
183  list of matches between reference object and sources;
184  the distance field is the only field read and it must be set to distance in radians
185 
186  Returns
187  -------
188  result : `lsst.pipe.base.Struct`
189  Result struct with components:
190 
191  - ``distMean`` : clipped mean of on-sky radial separation (`float`)
192  - ``distStdDev`` : clipped standard deviation of on-sky radial
193  separation (`float`)
194  - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
195  distStdDev (`float`)
196  """
197  distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
198  distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
199  distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
200  return pipeBase.Struct(
201  distMean=distMean,
202  distStdDev=distStdDev,
203  maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
204  )
205 
206  def _getExposureMetadata(self, exposure):
207  """Extract metadata from an exposure.
208 
209  Parameters
210  ----------
211  exposure : `lsst.afw.image.Exposure`
212 
213  Returns
214  -------
215  result : `lsst.pipe.base.Struct`
216  Result struct with components:
217 
218  - ``bbox`` : parent bounding box (`lsst.geom.Box2I`)
219  - ``wcs`` : exposure WCS (`lsst.afw.geom.SkyWcs`)
220  - ``calib`` : calibration (`lsst.afw.image.Calib`)
221  - ``filterName`` : name of filter (`str`)
222  - ``epoch`` : date of exposure (`astropy.time.Time`)
223 
224  """
225  exposureInfo = exposure.getInfo()
226  filterName = exposureInfo.getFilter().getName() or None
227  if filterName == "_unknown_":
228  filterName = None
229  epoch = None
230  if exposure.getInfo().hasVisitInfo():
231  epochTaiMjd = exposure.getInfo().getVisitInfo().getDate().get(system=DateTime.MJD,
232  scale=DateTime.TAI)
233  epoch = astropy.time.Time(epochTaiMjd, scale="tai", format="mjd")
234 
235  return pipeBase.Struct(
236  bbox=exposure.getBBox(),
237  wcs=exposureInfo.getWcs(),
238  calib=exposureInfo.getCalib() if exposureInfo.hasCalib() else None,
239  filterName=filterName,
240  epoch=epoch,
241  )
def _computeMatchStatsOnSky(self, matchList)
Definition: ref_match.py:177
def _getExposureMetadata(self, exposure)
Definition: ref_match.py:206
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def displayAstrometry(refCat=None, sourceCat=None, distortedCentroidKey=None, bbox=None, exposure=None, matches=None, frame=1, title="", pause=True)
Definition: display.py:35
def setRefObjLoader(self, refObjLoader)
Definition: ref_match.py:80
def loadAndMatch(self, exposure, sourceCat)
Definition: ref_match.py:91
def __init__(self, refObjLoader, kwargs)
Definition: ref_match.py:70