LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
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='Psf'
61 )
62
63 def setDefaults(self):
64 # Configured to match the deprecated "matcher" selector:
65 # SN > 40, some bad flags, valid centroids.
66 self.sourceSelector["science"].doSignalToNoise = True
67 self.sourceSelector["science"].signalToNoise.minimum = 40
68 self.sourceSelector["science"].signalToNoise.fluxField = f"slot_{self.sourceFluxType}Flux_instFlux"
69 self.sourceSelector["science"].signalToNoise.errField = f"slot_{self.sourceFluxType}Flux_instFluxErr"
70 self.sourceSelector["science"].doFlags = True
71 self.sourceSelector["science"].flags.bad = ["base_PixelFlags_flag_edge",
72 "base_PixelFlags_flag_interpolatedCenter",
73 "base_PixelFlags_flag_saturated",
74 "base_SdssCentroid_flag",
75 ]
76
77
78class RefMatchTask(pipeBase.Task):
79 """Match an input source catalog with objects from a reference catalog.
80
81 Parameters
82 ----------
83 refObjLoader : `lsst.meas.algorithms.ReferenceLoader`
84 A reference object loader object; gen3 pipeline tasks will pass `None`
85 and call `setRefObjLoader` in `runQuantum`.
86 **kwargs
87 Additional keyword arguments for pipe_base `lsst.pipe.base.Task`.
88 """
89 ConfigClass = RefMatchConfig
90 _DefaultName = "calibrationBaseClass"
91
92 def __init__(self, refObjLoader=None, **kwargs):
93 pipeBase.Task.__init__(self, **kwargs)
94 if refObjLoader:
95 self.refObjLoader = refObjLoader
96 else:
97 self.refObjLoader = None
98
99 if self.config.sourceSelector.name == 'matcher':
100 if self.config.sourceSelector['matcher'].sourceFluxType != self.config.sourceFluxType:
101 raise RuntimeError("The sourceFluxType in the sourceSelector['matcher'] must match "
102 "the configured sourceFluxType")
103
104 self.makeSubtask("matcher")
105 self.makeSubtask("sourceSelector")
106 self.makeSubtask("referenceSelector")
107
108 def setRefObjLoader(self, refObjLoader):
109 """Sets the reference object loader for the task.
110
111 Parameters
112 ----------
113 refObjLoader
114 An instance of a reference object loader task or class.
115 """
116 self.refObjLoader = refObjLoader
117
118 @timeMethod
119 def loadAndMatch(self, exposure, sourceCat):
120 """Load reference objects overlapping an exposure and match to sources
121 detected on that exposure.
122
123 Parameters
124 ----------
125 exposure : `lsst.afw.image.Exposure`
126 exposure that the sources overlap
127 sourceCat : `lsst.afw.table.SourceCatalog.`
128 catalog of sources detected on the exposure
129
130 Returns
131 -------
132 result : `lsst.pipe.base.Struct`
133 Result struct with Components:
134
135 - ``refCat`` : reference object catalog of objects that overlap the
136 exposure (`lsst.afw.table.SimpleCatalog`)
137 - ``matches`` : Matched sources and references
138 (`list` of `lsst.afw.table.ReferenceMatch`)
139 - ``matchMeta`` : metadata needed to unpersist matches
140 (`lsst.daf.base.PropertyList`)
141
142 Notes
143 -----
144 ignores config.matchDistanceSigma
145 """
146 if self.refObjLoader is None:
147 raise RuntimeError("Running matcher task with no refObjLoader set in __ini__ or setRefObjLoader")
148 import lsstDebug
149 debug = lsstDebug.Info(__name__)
150
151 epoch = exposure.visitInfo.date.toAstropy()
152
153 sourceSelection = self.sourceSelector.run(sourceCat)
154
155 sourceFluxField = "slot_%sFlux_instFlux" % (self.config.sourceFluxType)
156
157 loadRes = self.refObjLoader.loadPixelBox(
158 bbox=exposure.getBBox(),
159 wcs=exposure.wcs,
160 filterName=exposure.filter.bandLabel,
161 epoch=epoch,
162 )
163
164 refSelection = self.referenceSelector.run(loadRes.refCat)
165
166 matchMeta = self.refObjLoader.getMetadataBox(
167 bbox=exposure.getBBox(),
168 wcs=exposure.wcs,
169 filterName=exposure.filter.bandLabel,
170 epoch=epoch,
171 )
172
173 matchRes = self.matcher.matchObjectsToSources(
174 refCat=refSelection.sourceCat,
175 sourceCat=sourceSelection.sourceCat,
176 wcs=exposure.wcs,
177 sourceFluxField=sourceFluxField,
178 refFluxField=loadRes.fluxField,
179 matchTolerance=None,
180 bbox=exposure.getBBox(),
181 )
182
183 distStats = self._computeMatchStatsOnSky(matchRes.matches)
184 self.log.info(
185 "Found %d matches with scatter = %0.3f +- %0.3f arcsec; ",
186 len(matchRes.matches), distStats.distMean.asArcseconds(), distStats.distStdDev.asArcseconds()
187 )
188
189 if debug.display:
190 frame = int(debug.frame)
191 displayAstrometry(
192 refCat=refSelection.sourceCat,
193 sourceCat=sourceSelection.sourceCat,
194 matches=matchRes.matches,
195 exposure=exposure,
196 bbox=exposure.getBBox(),
197 frame=frame,
198 title="Matches",
199 )
200
201 return pipeBase.Struct(
202 refCat=loadRes.refCat,
203 refSelection=refSelection,
204 sourceSelection=sourceSelection,
205 matches=matchRes.matches,
206 matchMeta=matchMeta,
207 )
208
209 def _computeMatchStatsOnSky(self, matchList):
210 """Compute on-sky radial distance statistics for a match list
211
212 Parameters
213 ----------
214 matchList : `list` of `lsst.afw.table.ReferenceMatch`
215 list of matches between reference object and sources;
216 the distance field is the only field read and it must be set to distance in radians
217
218 Returns
219 -------
220 result : `lsst.pipe.base.Struct`
221 Result struct with components:
222
223 - ``distMean`` : clipped mean of on-sky radial separation (`float`)
224 - ``distStdDev`` : clipped standard deviation of on-sky radial
225 separation (`float`)
226 - ``maxMatchDist`` : distMean + self.config.matchDistanceSigma *
227 distStdDev (`float`)
228 """
229 distStatsInRadians = makeMatchStatistics(matchList, afwMath.MEANCLIP | afwMath.STDEVCLIP)
230 distMean = distStatsInRadians.getValue(afwMath.MEANCLIP)*lsst.geom.radians
231 distStdDev = distStatsInRadians.getValue(afwMath.STDEVCLIP)*lsst.geom.radians
232 return pipeBase.Struct(
233 distMean=distMean,
234 distStdDev=distStdDev,
235 maxMatchDist=distMean + self.config.matchDistanceSigma * distStdDev,
236 )
__init__(self, refObjLoader=None, **kwargs)
Definition ref_match.py:92
loadAndMatch(self, exposure, sourceCat)
Definition ref_match.py:119