LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
ref_match.py
Go to the documentation of this file.
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
25import astropy.time
26
27import lsst.geom
28from lsst.daf.base import DateTime
29import lsst.afw.math as afwMath
30import lsst.pex.config as pexConfig
31import lsst.pipe.base as pipeBase
32from lsst.meas.algorithms import ReferenceSourceSelectorTask
33from lsst.meas.algorithms.sourceSelector import sourceSelectorRegistry
34from lsst.utils.timer import timeMethod
35from .matchPessimisticB import MatchPessimisticBTask
36from .display import displayAstrometry
37from . import makeMatchStatistics
38
39
40class 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
77class 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
135 - ``matches`` : Matched sources and references
137 - ``matchMeta`` : metadata needed to unpersist matches
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 epoch=expMd.epoch,
160 )
161
162 refSelection = self.referenceSelector.run(loadRes.refCat)
163
164 matchMeta = self.refObjLoaderrefObjLoader.getMetadataBox(
165 bbox=expMd.bbox,
166 wcs=expMd.wcs,
167 filterName=expMd.filterName,
168 epoch=expMd.epoch,
169 )
170
171 matchRes = self.matcher.matchObjectsToSources(
172 refCat=refSelection.sourceCat,
173 sourceCat=sourceSelection.sourceCat,
174 wcs=expMd.wcs,
175 sourceFluxField=sourceFluxField,
176 refFluxField=loadRes.fluxField,
177 match_tolerance=None,
178 )
179
180 distStats = self._computeMatchStatsOnSky_computeMatchStatsOnSky(matchRes.matches)
181 self.log.info(
182 "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
183 len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
184 )
185
186 if debug.display:
187 frame = int(debug.frame)
189 refCat=refSelection.sourceCat,
190 sourceCat=sourceSelection.sourceCat,
191 matches=matchRes.matches,
192 exposure=exposure,
193 bbox=expMd.bbox,
194 frame=frame,
195 title="Matches",
196 )
197
198 return pipeBase.Struct(
199 refCat=loadRes.refCat,
200 refSelection=refSelection,
201 sourceSelection=sourceSelection,
202 matches=matchRes.matches,
203 matchMeta=matchMeta,
204 )
205
206 def _computeMatchStatsOnSky(self, matchList):
207 """Compute on-sky radial distance statistics for a match list
208
209 Parameters
210 ----------
211 matchList : `list` of `lsst.afw.table.ReferenceMatch`
212 list of matches between reference object and sources;
213 the distance field is the only field read and it must be set to distance in radians
214
215 Returns
216 -------
217 result : `lsst.pipe.base.Struct`
218 Result struct with components:
219
220 - ``distMean`` : clipped mean of on-sky radial separation (`float`)
221 - ``distStdDev`` : clipped standard deviation of on-sky radial
222 separation (`float`)
223 - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
224 distStdDev (`float`)
225 """
226 distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
227 distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
228 distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
229 return pipeBase.Struct(
230 distMean=distMean,
231 distStdDev=distStdDev,
232 maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
233 )
234
235 def _getExposureMetadata(self, exposure):
236 """Extract metadata from an exposure.
237
238 Parameters
239 ----------
240 exposure : `lsst.afw.image.Exposure`
241
242 Returns
243 -------
244 result : `lsst.pipe.base.Struct`
245 Result struct with components:
246
247 - ``bbox`` : parent bounding box (`lsst.geom.Box2I`)
248 - ``wcs`` : exposure WCS (`lsst.afw.geom.SkyWcs`)
249 - ``photoCalib`` : photometric calibration (`lsst.afw.image.PhotoCalib`)
250 - ``filterName`` : name of filter band (`str`)
251 - ``epoch`` : date of exposure (`astropy.time.Time`)
252
253 """
254 exposureInfo = exposure.getInfo()
255 filterLabel = exposureInfo.getFilter()
256 filterName = filterLabel.bandLabel if filterLabel is not None else None
257 epoch = None
258 if exposure.getInfo().hasVisitInfo():
259 epochTaiMjd = exposure.getInfo().getVisitInfo().getDate().get(system=DateTime.MJD,
260 scale=DateTime.TAI)
261 epoch = astropy.time.Time(epochTaiMjd, scale="tai", format="mjd")
262
263 return pipeBase.Struct(
264 bbox=exposure.getBBox(),
265 wcs=exposureInfo.getWcs(),
266 photoCalib=exposureInfo.getPhotoCalib(),
267 filterName=filterName,
268 epoch=epoch,
269 )
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition: SkyWcs.h:117
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition: Exposure.h:72
The photometric calibration of an exposure.
Definition: PhotoCalib.h:114
Custom catalog class for record/table subclasses that are guaranteed to have an ID,...
Definition: SortedCatalog.h:42
Class for storing ordered metadata with comments.
Definition: PropertyList.h:68
An integer coordinate rectangle.
Definition: Box.h:55
def _computeMatchStatsOnSky(self, matchList)
Definition: ref_match.py:206
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:235
def setRefObjLoader(self, refObjLoader)
Definition: ref_match.py:106
def run(self, coaddExposures, bbox, wcs, dataIds, **kwargs)
Definition: getTemplate.py:596
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
Lightweight representation of a geometric match between two records.
Definition: Match.h:67