LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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 ReferenceSourceSelectorTask
33 from lsst.meas.algorithms.sourceSelector import sourceSelectorRegistry
34 from lsst.utils.timer import timeMethod
35 from .matchPessimisticB import MatchPessimisticBTask
36 from .display import displayAstrometry
37 from . import makeMatchStatistics
38 
39 
40 class RefMatchConfig(pexConfig.Config):
41  matcher = pexConfig.ConfigurableField(
42  target=MatchPessimisticBTask,
43  doc="reference object/source matcher",
44  )
45  matchDistanceSigma = pexConfig.RangeField(
46  doc="the maximum match distance is set to "
47  " mean_match_distance + matchDistanceSigma*std_dev_match_distance; "
48  "ignored if not fitting a WCS",
49  dtype=float,
50  default=2,
51  min=0,
52  )
53  sourceSelector = sourceSelectorRegistry.makeField(
54  doc="How to select sources for cross-matching.",
55  default="science",
56  )
57  referenceSelector = pexConfig.ConfigurableField(
58  target=ReferenceSourceSelectorTask,
59  doc="How to select reference objects for cross-matching."
60  )
61  sourceFluxType = pexConfig.Field(
62  dtype=str,
63  doc="Source flux type to use in source selection.",
64  default='Calib'
65  )
66 
67  def setDefaults(self):
68  self.sourceSelectorsourceSelector.name = "science"
69  self.sourceSelectorsourceSelector['science'].fluxLimit.fluxField = \
70  'slot_%sFlux_instFlux' % (self.sourceFluxTypesourceFluxType)
71  self.sourceSelectorsourceSelector['science'].signalToNoise.fluxField = \
72  'slot_%sFlux_instFlux' % (self.sourceFluxTypesourceFluxType)
73  self.sourceSelectorsourceSelector['science'].signalToNoise.errField = \
74  'slot_%sFlux_instFluxErr' % (self.sourceFluxTypesourceFluxType)
75 
76 
77 class RefMatchTask(pipeBase.Task):
78  """Match an input source catalog with objects from a reference catalog.
79 
80  Parameters
81  ----------
82  refObjLoader : `lsst.meas.algorithms.ReferenceLoader`
83  A reference object loader object
84  **kwargs
85  additional keyword arguments for pipe_base `lsst.pipe.base.Task`
86  """
87  ConfigClass = RefMatchConfig
88  _DefaultName = "calibrationBaseClass"
89 
90  def __init__(self, refObjLoader, **kwargs):
91  pipeBase.Task.__init__(self, **kwargs)
92  if refObjLoader:
93  self.refObjLoaderrefObjLoader = refObjLoader
94  else:
95  self.refObjLoaderrefObjLoader = None
96 
97  if self.config.sourceSelector.name == 'matcher':
98  if self.config.sourceSelector['matcher'].sourceFluxType != self.config.sourceFluxType:
99  raise RuntimeError("The sourceFluxType in the sourceSelector['matcher'] must match "
100  "the configured sourceFluxType")
101 
102  self.makeSubtask("matcher")
103  self.makeSubtask("sourceSelector")
104  self.makeSubtask("referenceSelector")
105 
106  def setRefObjLoader(self, refObjLoader):
107  """Sets the reference object loader for the task
108 
109  Parameters
110  ----------
111  refObjLoader
112  An instance of a reference object loader task or class
113  """
114  self.refObjLoaderrefObjLoader = refObjLoader
115 
116  @timeMethod
117  def loadAndMatch(self, exposure, sourceCat):
118  """Load reference objects overlapping an exposure and match to sources
119  detected on that exposure.
120 
121  Parameters
122  ----------
123  exposure : `lsst.afw.image.Exposure`
124  exposure that the sources overlap
125  sourceCat : `lsst.afw.table.SourceCatalog.`
126  catalog of sources detected on the exposure
127 
128  Returns
129  -------
130  result : `lsst.pipe.base.Struct`
131  Result struct with Components:
132 
133  - ``refCat`` : reference object catalog of objects that overlap the
134  exposure (`lsst.afw.table.SimpleCatalog`)
135  - ``matches`` : Matched sources and references
136  (`list` of `lsst.afw.table.ReferenceMatch`)
137  - ``matchMeta`` : metadata needed to unpersist matches
138  (`lsst.daf.base.PropertyList`)
139 
140  Notes
141  -----
142  ignores config.matchDistanceSigma
143  """
144  if self.refObjLoaderrefObjLoader is None:
145  raise RuntimeError("Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
146  import lsstDebug
147  debug = lsstDebug.Info(__name__)
148 
149  expMd = self._getExposureMetadata_getExposureMetadata(exposure)
150 
151  sourceSelection = self.sourceSelector.run(sourceCat)
152 
153  sourceFluxField = "slot_%sFlux_instFlux" % (self.config.sourceFluxType)
154 
155  loadRes = self.refObjLoaderrefObjLoader.loadPixelBox(
156  bbox=expMd.bbox,
157  wcs=expMd.wcs,
158  filterName=expMd.filterName,
159  photoCalib=expMd.photoCalib,
160  epoch=expMd.epoch,
161  )
162 
163  refSelection = self.referenceSelector.run(loadRes.refCat)
164 
165  matchMeta = self.refObjLoaderrefObjLoader.getMetadataBox(
166  bbox=expMd.bbox,
167  wcs=expMd.wcs,
168  filterName=expMd.filterName,
169  photoCalib=expMd.photoCalib,
170  epoch=expMd.epoch,
171  )
172 
173  matchRes = self.matcher.matchObjectsToSources(
174  refCat=refSelection.sourceCat,
175  sourceCat=sourceSelection.sourceCat,
176  wcs=expMd.wcs,
177  sourceFluxField=sourceFluxField,
178  refFluxField=loadRes.fluxField,
179  match_tolerance=None,
180  )
181 
182  distStats = self._computeMatchStatsOnSky_computeMatchStatsOnSky(matchRes.matches)
183  self.log.info(
184  "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
185  len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
186  )
187 
188  if debug.display:
189  frame = int(debug.frame)
191  refCat=refSelection.sourceCat,
192  sourceCat=sourceSelection.sourceCat,
193  matches=matchRes.matches,
194  exposure=exposure,
195  bbox=expMd.bbox,
196  frame=frame,
197  title="Matches",
198  )
199 
200  return pipeBase.Struct(
201  refCat=loadRes.refCat,
202  refSelection=refSelection,
203  sourceSelection=sourceSelection,
204  matches=matchRes.matches,
205  matchMeta=matchMeta,
206  )
207 
208  def _computeMatchStatsOnSky(self, matchList):
209  """Compute on-sky radial distance statistics for a match list
210 
211  Parameters
212  ----------
213  matchList : `list` of `lsst.afw.table.ReferenceMatch`
214  list of matches between reference object and sources;
215  the distance field is the only field read and it must be set to distance in radians
216 
217  Returns
218  -------
219  result : `lsst.pipe.base.Struct`
220  Result struct with components:
221 
222  - ``distMean`` : clipped mean of on-sky radial separation (`float`)
223  - ``distStdDev`` : clipped standard deviation of on-sky radial
224  separation (`float`)
225  - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
226  distStdDev (`float`)
227  """
228  distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
229  distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
230  distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
231  return pipeBase.Struct(
232  distMean=distMean,
233  distStdDev=distStdDev,
234  maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
235  )
236 
237  def _getExposureMetadata(self, exposure):
238  """Extract metadata from an exposure.
239 
240  Parameters
241  ----------
242  exposure : `lsst.afw.image.Exposure`
243 
244  Returns
245  -------
246  result : `lsst.pipe.base.Struct`
247  Result struct with components:
248 
249  - ``bbox`` : parent bounding box (`lsst.geom.Box2I`)
250  - ``wcs`` : exposure WCS (`lsst.afw.geom.SkyWcs`)
251  - ``photoCalib`` : photometric calibration (`lsst.afw.image.PhotoCalib`)
252  - ``filterName`` : name of filter band (`str`)
253  - ``epoch`` : date of exposure (`astropy.time.Time`)
254 
255  """
256  exposureInfo = exposure.getInfo()
257  filterLabel = exposureInfo.getFilterLabel()
258  filterName = filterLabel.bandLabel if filterLabel is not None else None
259  epoch = None
260  if exposure.getInfo().hasVisitInfo():
261  epochTaiMjd = exposure.getInfo().getVisitInfo().getDate().get(system=DateTime.MJD,
262  scale=DateTime.TAI)
263  epoch = astropy.time.Time(epochTaiMjd, scale="tai", format="mjd")
264 
265  return pipeBase.Struct(
266  bbox=exposure.getBBox(),
267  wcs=exposureInfo.getWcs(),
268  photoCalib=exposureInfo.getPhotoCalib(),
269  filterName=filterName,
270  epoch=epoch,
271  )
def _computeMatchStatsOnSky(self, matchList)
Definition: ref_match.py:208
def loadAndMatch(self, exposure, sourceCat)
Definition: ref_match.py:117
def __init__(self, refObjLoader, **kwargs)
Definition: ref_match.py:90
def _getExposureMetadata(self, exposure)
Definition: ref_match.py:237
def setRefObjLoader(self, refObjLoader)
Definition: ref_match.py:106
def run(self, coaddExposures, bbox, wcs)
Definition: getTemplate.py:603
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:34