LSST Applications 26.0.0,g0265f82a02+6660c170cc,g07994bdeae+30b05a742e,g0a0026dc87+17526d298f,g0a60f58ba1+17526d298f,g0e4bf8285c+96dd2c2ea9,g0ecae5effc+c266a536c8,g1e7d6db67d+6f7cb1f4bb,g26482f50c6+6346c0633c,g2bbee38e9b+6660c170cc,g2cc88a2952+0a4e78cd49,g3273194fdb+f6908454ef,g337abbeb29+6660c170cc,g337c41fc51+9a8f8f0815,g37c6e7c3d5+7bbafe9d37,g44018dc512+6660c170cc,g4a941329ef+4f7594a38e,g4c90b7bd52+5145c320d2,g58be5f913a+bea990ba40,g635b316a6c+8d6b3a3e56,g67924a670a+bfead8c487,g6ae5381d9b+81bc2a20b4,g93c4d6e787+26b17396bd,g98cecbdb62+ed2cb6d659,g98ffbb4407+81bc2a20b4,g9ddcbc5298+7f7571301f,ga1e77700b3+99e9273977,gae46bcf261+6660c170cc,gb2715bf1a1+17526d298f,gc86a011abf+17526d298f,gcf0d15dbbd+96dd2c2ea9,gdaeeff99f8+0d8dbea60f,gdb4ec4c597+6660c170cc,ge23793e450+96dd2c2ea9,gf041782ebf+171108ac67
LSST Data Management Base Package
Loading...
Searching...
No Matches
astrometrySourceSelector.py
Go to the documentation of this file.
1# This file is part of meas_algorithms.
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"""Select sources that are useful for astrometry.
23
24Such sources have good signal-to-noise, are well centroided, not blended,
25and not flagged with a handful of "bad" flags.
26"""
27
28__all__ = ["AstrometrySourceSelectorConfig", "AstrometrySourceSelectorTask"]
29
30import numpy as np
31
32import lsst.pex.config as pexConfig
33from lsst.pex.exceptions import NotFoundError
34from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
35from lsst.pipe.base import Struct
36from functools import reduce
37
38
40 badFlags = pexConfig.ListField(
41 doc="List of flags which cause a source to be rejected as bad",
42 dtype=str,
43 default=[
44 "base_PixelFlags_flag_edge",
45 "base_PixelFlags_flag_interpolatedCenter",
46 "base_PixelFlags_flag_saturatedCenter",
47 "base_PixelFlags_flag_crCenter",
48 "base_PixelFlags_flag_bad",
49 ],
50 )
51 sourceFluxType = pexConfig.Field(
52 doc="Type of source flux; typically one of Ap or Psf",
53 dtype=str,
54 default="Ap",
55 )
56 minSnr = pexConfig.Field(
57 dtype=float,
58 doc="Minimum allowed signal-to-noise ratio for sources used for matching "
59 "(in the flux specified by sourceFluxType); <= 0 for no limit",
60 default=10,
61 )
62
63
64@pexConfig.registerConfigurable("astrometry", sourceSelectorRegistry)
66 """Select sources that are useful for astrometry.
67
68 Good astrometry sources have high signal/noise, are non-blended, and
69 did not have certain "bad" flags set during source extraction. They need not
70 be PSF sources, just have reliable centroids.
71 """
72 ConfigClass = AstrometrySourceSelectorConfig
73
74 def __init__(self, *args, **kwargs):
75 BaseSourceSelectorTask.__init__(self, *args, **kwargs)
76
77 def selectSources(self, sourceCat, matches=None, exposure=None):
78 """Return a selection of sources that are useful for astrometry.
79
80 Parameters
81 ----------
83 Catalog of sources to select from.
84 This catalog must be contiguous in memory.
85 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
86 Ignored in this SourceSelector.
87 exposure : `lsst.afw.image.Exposure` or None
88 The exposure the catalog was built from; used for debug display.
89
90 Returns
91 -------
92 struct : `lsst.pipe.base.Struct`
93 The struct contains the following data:
94
95 ``selected``
96 Boolean array of sources that were selected, same length as
97 sourceCat. (`numpy.ndarray` of `bool`)
98 """
99 self._getSchemaKeys(sourceCat.schema)
100
101 bad = reduce(lambda x, y: np.logical_or(x, sourceCat[y]), self.config.badFlags, False)
102 good = self._isGood(sourceCat)
103 return Struct(selected=good & ~bad)
104
105 def _getSchemaKeys(self, schema):
106 """Extract and save the necessary keys from schema with asKey.
107 """
108 self.parentKey = schema["parent"].asKey()
109 self.nChildKey = schema["deblend_nChild"].asKey()
110 self.centroidXKey = schema["slot_Centroid_x"].asKey()
111 self.centroidYKey = schema["slot_Centroid_y"].asKey()
112 self.centroidXErrKey = schema["slot_Centroid_xErr"].asKey()
113 self.centroidYErrKey = schema["slot_Centroid_yErr"].asKey()
114 self.centroidFlagKey = schema["slot_Centroid_flag"].asKey()
115 try:
116 self.primaryKey = schema["detect_isPrimary"].asKey()
117 except NotFoundError:
118 self.primaryKey = None
119
120 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
121 self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
122 self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey()
123
124 fluxPrefix = "slot_%sFlux_" % (self.config.sourceFluxType,)
125 self.instFluxKey = schema[fluxPrefix + "instFlux"].asKey()
126 self.fluxFlagKey = schema[fluxPrefix + "flag"].asKey()
127 self.instFluxErrKey = schema[fluxPrefix + "instFluxErr"].asKey()
128
129 def _isMultiple(self, sourceCat):
130 """Return True for each source that is likely multiple sources.
131 """
132 test = (sourceCat[self.parentKey] != 0) | (sourceCat[self.nChildKey] != 0)
133 # have to currently manage footprints on a source-by-source basis.
134 for i, cat in enumerate(sourceCat):
135 footprint = cat.getFootprint()
136 test[i] |= (footprint is not None) and (len(footprint.getPeaks()) > 1)
137 return test
138
139 def _hasCentroid(self, sourceCat):
140 """Return True for each source that has a valid centroid
141 """
142 def checkNonfiniteCentroid():
143 """Return True for sources with non-finite centroids.
144 """
145 return ~np.isfinite(sourceCat[self.centroidXKey]) | \
146 ~np.isfinite(sourceCat[self.centroidYKey])
147 assert ~checkNonfiniteCentroid().any(), \
148 "Centroids not finite for %d unflagged sources." % (checkNonfiniteCentroid().sum())
149 return np.isfinite(sourceCat[self.centroidXErrKey]) \
150 & np.isfinite(sourceCat[self.centroidYErrKey]) \
151 & ~sourceCat[self.centroidFlagKey]
152
153 def _goodSN(self, sourceCat):
154 """Return True for each source that has Signal/Noise > config.minSnr.
155 """
156 if self.config.minSnr <= 0:
157 return True
158 else:
159 with np.errstate(invalid="ignore"): # suppress NAN warnings
160 return sourceCat[self.instFluxKey]/sourceCat[self.instFluxErrKey] > self.config.minSnr
161
162 def _isUsable(self, sourceCat):
163 """Return True for each source that is usable for matching, even if it may
164 have a poor centroid.
165
166 For a source to be usable it must:
167 - have a valid centroid
168 - not be deblended
169 - have a valid flux (of the type specified in this object's constructor)
170 - have adequate signal-to-noise
171 """
172
173 return self._hasCentroid(sourceCat) \
174 & ~self._isMultiple(sourceCat) \
175 & self._goodSN(sourceCat) \
176 & ~sourceCat[self.fluxFlagKey]
177
178 def _isPrimary(self, sourceCat):
179 """Return True if this is a primary source.
180 """
181 if self.primaryKey:
182 return sourceCat[self.primaryKey]
183 else:
184 return np.ones(len(sourceCat), dtype=bool)
185
186 def _isGood(self, sourceCat):
187 """Return True for each source that is usable for matching and likely has a
188 good centroid.
189
190 The additional tests for a good centroid, beyond isUsable, are:
191 - not interpolated in the center
192 - not saturated
193 - not near the edge
194 """
195
196 return self._isUsable(sourceCat) \
197 & self._isPrimary(sourceCat) \
198 & ~sourceCat[self.saturatedKey] \
199 & ~sourceCat[self.interpolatedCenterKey] \
200 & ~sourceCat[self.edgeKey]
201
202 def _isBadFlagged(self, source):
203 """Return True if any of config.badFlags are set for this source.
204 """
205 return any(source[flag] for flag in self.config.badFlags)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
Lightweight representation of a geometric match between two records.
Definition Match.h:67