LSST Applications g180d380827+0f66a164bb,g2079a07aa2+86d27d4dc4,g2305ad1205+7d304bc7a0,g29320951ab+500695df56,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+e42ea45bea,g48712c4677+36a86eeaa5,g487adcacf7+2dd8f347ac,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+c70619cc9d,g5a732f18d5+53520f316c,g5ea96fc03c+341ea1ce94,g64a986408d+f7cd9c7162,g858d7b2824+f7cd9c7162,g8a8a8dda67+585e252eca,g99cad8db69+469ab8c039,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+c92fc63c7e,gbd866b1f37+f7cd9c7162,gc120e1dc64+02c66aa596,gc28159a63d+0e5473021a,gc3e9b769f7+b0068a2d9f,gcf0d15dbbd+e42ea45bea,gdaeeff99f8+f9a426f77a,ge6526c86ff+84383d05b3,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+f7cd9c7162,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
ref_match.py
Go to the documentation of this file.
1# This file is part of meas_astrom.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
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 GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22__all__ = ['RefMatchConfig', 'RefMatchTask']
23
24import lsst.geom
25import lsst.afw.math as afwMath
26import lsst.pex.config as pexConfig
27import lsst.pipe.base as pipeBase
28from lsst.meas.algorithms import ReferenceSourceSelectorTask
29from lsst.meas.algorithms.sourceSelector import sourceSelectorRegistry
30from lsst.utils.timer import timeMethod
31from .matchPessimisticB import MatchPessimisticBTask
32from .display import displayAstrometry
33from . import makeMatchStatistics
34
35
36class RefMatchConfig(pexConfig.Config):
37 matcher = pexConfig.ConfigurableField(
38 target=MatchPessimisticBTask,
39 doc="reference object/source matcher",
40 )
41 matchDistanceSigma = pexConfig.RangeField(
42 doc="the maximum match distance is set to "
43 " mean_match_distance + matchDistanceSigma*std_dev_match_distance; "
44 "ignored if not fitting a WCS",
45 dtype=float,
46 default=2,
47 min=0,
48 )
49 sourceSelector = sourceSelectorRegistry.makeField(
50 doc="How to select sources for cross-matching.",
51 default="science",
52 )
53 referenceSelector = pexConfig.ConfigurableField(
54 target=ReferenceSourceSelectorTask,
55 doc="How to select reference objects for cross-matching."
56 )
57 sourceFluxType = pexConfig.Field(
58 dtype=str,
59 doc="Source flux type to use in source selection.",
60 default='Calib'
61 )
62
63 def setDefaults(self):
64 self.sourceSelector['science'].fluxLimit.fluxField = \
65 'slot_%sFlux_instFlux' % (self.sourceFluxType)
66 self.sourceSelector['science'].signalToNoise.fluxField = \
67 'slot_%sFlux_instFlux' % (self.sourceFluxType)
68 self.sourceSelector['science'].signalToNoise.errField = \
69 'slot_%sFlux_instFluxErr' % (self.sourceFluxType)
70
71
72class RefMatchTask(pipeBase.Task):
73 """Match an input source catalog with objects from a reference catalog.
74
75 Parameters
76 ----------
77 refObjLoader : `lsst.meas.algorithms.ReferenceLoader`
78 A reference object loader object; gen3 pipeline tasks will pass `None`
79 and call `setRefObjLoader` in `runQuantum`.
80 **kwargs
81 Additional keyword arguments for pipe_base `lsst.pipe.base.Task`.
82 """
83 ConfigClass = RefMatchConfig
84 _DefaultName = "calibrationBaseClass"
85
86 def __init__(self, refObjLoader=None, **kwargs):
87 pipeBase.Task.__init__(self, **kwargs)
88 if refObjLoader:
89 self.refObjLoader = refObjLoader
90 else:
91 self.refObjLoader = None
92
93 if self.config.sourceSelector.name == 'matcher':
94 if self.config.sourceSelector['matcher'].sourceFluxType != self.config.sourceFluxType:
95 raise RuntimeError("The sourceFluxType in the sourceSelector['matcher'] must match "
96 "the configured sourceFluxType")
97
98 self.makeSubtask("matcher")
99 self.makeSubtask("sourceSelector")
100 self.makeSubtask("referenceSelector")
101
102 def setRefObjLoader(self, refObjLoader):
103 """Sets the reference object loader for the task.
104
105 Parameters
106 ----------
107 refObjLoader
108 An instance of a reference object loader task or class.
109 """
110 self.refObjLoader = refObjLoader
111
112 @timeMethod
113 def loadAndMatch(self, exposure, sourceCat):
114 """Load reference objects overlapping an exposure and match to sources
115 detected on that exposure.
116
117 Parameters
118 ----------
119 exposure : `lsst.afw.image.Exposure`
120 exposure that the sources overlap
121 sourceCat : `lsst.afw.table.SourceCatalog.`
122 catalog of sources detected on the exposure
123
124 Returns
125 -------
126 result : `lsst.pipe.base.Struct`
127 Result struct with Components:
128
129 - ``refCat`` : reference object catalog of objects that overlap the
130 exposure (`lsst.afw.table.SimpleCatalog`)
131 - ``matches`` : Matched sources and references
132 (`list` of `lsst.afw.table.ReferenceMatch`)
133 - ``matchMeta`` : metadata needed to unpersist matches
134 (`lsst.daf.base.PropertyList`)
135
136 Notes
137 -----
138 ignores config.matchDistanceSigma
139 """
140 if self.refObjLoader is None:
141 raise RuntimeError("Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
142 import lsstDebug
143 debug = lsstDebug.Info(__name__)
144
145 epoch = exposure.visitInfo.date.toAstropy()
146
147 sourceSelection = self.sourceSelector.run(sourceCat)
148
149 sourceFluxField = "slot_%sFlux_instFlux" % (self.config.sourceFluxType)
150
151 loadRes = self.refObjLoader.loadPixelBox(
152 bbox=exposure.getBBox(),
153 wcs=exposure.wcs,
154 filterName=exposure.filter.bandLabel,
155 epoch=epoch,
156 )
157
158 refSelection = self.referenceSelector.run(loadRes.refCat)
159
160 matchMeta = self.refObjLoader.getMetadataBox(
161 bbox=exposure.getBBox(),
162 wcs=exposure.wcs,
163 filterName=exposure.filter.bandLabel,
164 epoch=epoch,
165 )
166
167 matchRes = self.matcher.matchObjectsToSources(
168 refCat=refSelection.sourceCat,
169 sourceCat=sourceSelection.sourceCat,
170 wcs=exposure.wcs,
171 sourceFluxField=sourceFluxField,
172 refFluxField=loadRes.fluxField,
173 matchTolerance=None,
174 )
175
176 distStats = self._computeMatchStatsOnSky(matchRes.matches)
177 self.log.info(
178 "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
179 len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
180 )
181
182 if debug.display:
183 frame = int(debug.frame)
184 displayAstrometry(
185 refCat=refSelection.sourceCat,
186 sourceCat=sourceSelection.sourceCat,
187 matches=matchRes.matches,
188 exposure=exposure,
189 bbox=exposure.getBBox(),
190 frame=frame,
191 title="Matches",
192 )
193
194 return pipeBase.Struct(
195 refCat=loadRes.refCat,
196 refSelection=refSelection,
197 sourceSelection=sourceSelection,
198 matches=matchRes.matches,
199 matchMeta=matchMeta,
200 )
201
202 def _computeMatchStatsOnSky(self, matchList):
203 """Compute on-sky radial distance statistics for a match list
204
205 Parameters
206 ----------
207 matchList : `list` of `lsst.afw.table.ReferenceMatch`
208 list of matches between reference object and sources;
209 the distance field is the only field read and it must be set to distance in radians
210
211 Returns
212 -------
213 result : `lsst.pipe.base.Struct`
214 Result struct with components:
215
216 - ``distMean`` : clipped mean of on-sky radial separation (`float`)
217 - ``distStdDev`` : clipped standard deviation of on-sky radial
218 separation (`float`)
219 - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
220 distStdDev (`float`)
221 """
222 distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
223 distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
224 distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
225 return pipeBase.Struct(
226 distMean=distMean,
227 distStdDev=distStdDev,
228 maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
229 )
__init__(self, refObjLoader=None, **kwargs)
Definition ref_match.py:86
loadAndMatch(self, exposure, sourceCat)
Definition ref_match.py:113