Loading [MathJax]/extensions/tex2jax.js
LSST Applications g04dff08e69+fafbcb10e2,g0d33ba9806+e09a96fa4e,g0fba68d861+cc01b48236,g1e78f5e6d3+fb95f9dda6,g1ec0fe41b4+f536777771,g1fd858c14a+ae46bc2a71,g35bb328faa+fcb1d3bbc8,g4af146b050+dd94f3aad7,g4d2262a081+7ee6f976aa,g53246c7159+fcb1d3bbc8,g5a012ec0e7+b20b785ecb,g60b5630c4e+e09a96fa4e,g6273192d42+bf8cfc5e62,g67b6fd64d1+4086c0989b,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g87b7deb4dc+831c06c8fc,g8852436030+54b48a5987,g89139ef638+4086c0989b,g9125e01d80+fcb1d3bbc8,g94187f82dc+e09a96fa4e,g989de1cb63+4086c0989b,g9f33ca652e+64be6d9d51,g9f7030ddb1+d11454dffd,ga2b97cdc51+e09a96fa4e,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+23605820ec,gb58c049af0+f03b321e39,gb89ab40317+4086c0989b,gcf25f946ba+54b48a5987,gd6cbbdb0b4+af3c3595f5,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+15f2daff9d,ge278dab8ac+d65b3c2b70,ge410e46f29+4086c0989b,gf67bdafdda+4086c0989b,v29.0.0.rc5
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
30from deprecated.sphinx import deprecated
31
32import numpy as np
33
34import lsst.pex.config as pexConfig
35from lsst.pex.exceptions import NotFoundError
36from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
37from lsst.pipe.base import Struct
38from functools import reduce
39
40
42 badFlags = pexConfig.ListField(
43 doc="List of flags which cause a source to be rejected as bad",
44 dtype=str,
45 default=[
46 "base_PixelFlags_flag_edge",
47 "base_PixelFlags_flag_nodata",
48 "base_PixelFlags_flag_interpolatedCenter",
49 "base_PixelFlags_flag_saturatedCenter",
50 "base_PixelFlags_flag_crCenter",
51 "base_PixelFlags_flag_bad",
52 ],
53 )
54 sourceFluxType = pexConfig.Field(
55 doc="Type of source flux; typically one of Ap or Psf",
56 dtype=str,
57 default="Ap",
58 )
59 minSnr = pexConfig.Field(
60 dtype=float,
61 doc="Minimum allowed signal-to-noise ratio for sources used for matching "
62 "(in the flux specified by sourceFluxType); <= 0 for no limit",
63 default=10,
64 )
65
66
67# remove this file on DM-41146
68@deprecated(reason=("This Task has been replaced by an appropriately configured ScienceSourceSelector."
69 " See `AstrometryConfig.setDefaults` in meas_astrom for an example config that was "
70 "made to closely match this Task. Will be removed after v27."),
71 version="v27.0", category=FutureWarning)
72@pexConfig.registerConfigurable("astrometry", sourceSelectorRegistry)
74 """Select sources that are useful for astrometry.
75
76 Good astrometry sources have high signal/noise, are non-blended, and
77 did not have certain "bad" flags set during source extraction. They need not
78 be PSF sources, just have reliable centroids.
79 """
80 ConfigClass = AstrometrySourceSelectorConfig
81
82 def __init__(self, *args, **kwargs):
83 BaseSourceSelectorTask.__init__(self, *args, **kwargs)
84
85 def selectSources(self, sourceCat, matches=None, exposure=None):
86 """Return a selection of sources that are useful for astrometry.
87
88 Parameters
89 ----------
90 sourceCat : `lsst.afw.table.SourceCatalog`
91 Catalog of sources to select from.
92 This catalog must be contiguous in memory.
93 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
94 Ignored in this SourceSelector.
95 exposure : `lsst.afw.image.Exposure` or None
96 The exposure the catalog was built from; used for debug display.
97
98 Returns
99 -------
100 struct : `lsst.pipe.base.Struct`
101 The struct contains the following data:
102
103 ``selected``
104 Boolean array of sources that were selected, same length as
105 sourceCat. (`numpy.ndarray` of `bool`)
106 """
107 self._getSchemaKeys(sourceCat.schema)
108
109 bad = reduce(lambda x, y: np.logical_or(x, sourceCat[y]), self.config.badFlags, False)
110 good = self._isGood(sourceCat)
111 return Struct(selected=good & ~bad)
112
113 def _getSchemaKeys(self, schema):
114 """Extract and save the necessary keys from schema with asKey.
115 """
116 self.parentKey = schema["parent"].asKey()
117 self.nChildKey = schema["deblend_nChild"].asKey()
118 self.centroidXKey = schema["slot_Centroid_x"].asKey()
119 self.centroidYKey = schema["slot_Centroid_y"].asKey()
120 self.centroidXErrKey = schema["slot_Centroid_xErr"].asKey()
121 self.centroidYErrKey = schema["slot_Centroid_yErr"].asKey()
122 self.centroidFlagKey = schema["slot_Centroid_flag"].asKey()
123 try:
124 self.primaryKey = schema["detect_isPrimary"].asKey()
125 except NotFoundError:
126 self.primaryKey = None
127
128 self.edgeKey = schema["base_PixelFlags_flag_edge"].asKey()
129 self.interpolatedCenterKey = schema["base_PixelFlags_flag_interpolatedCenter"].asKey()
130 self.saturatedKey = schema["base_PixelFlags_flag_saturated"].asKey()
131
132 fluxPrefix = "slot_%sFlux_" % (self.config.sourceFluxType,)
133 self.instFluxKey = schema[fluxPrefix + "instFlux"].asKey()
134 self.fluxFlagKey = schema[fluxPrefix + "flag"].asKey()
135 self.instFluxErrKey = schema[fluxPrefix + "instFluxErr"].asKey()
136
137 def _isMultiple(self, sourceCat):
138 """Return True for each source that is likely multiple sources.
139 """
140 test = (sourceCat[self.parentKey] != 0) | (sourceCat[self.nChildKey] != 0)
141 # have to currently manage footprints on a source-by-source basis.
142 for i, cat in enumerate(sourceCat):
143 footprint = cat.getFootprint()
144 test[i] |= (footprint is not None) and (len(footprint.getPeaks()) > 1)
145 return test
146
147 def _hasCentroid(self, sourceCat):
148 """Return True for each source that has a valid centroid
149 """
150 def checkNonfiniteCentroid():
151 """Return True for sources with non-finite centroids.
152 """
153 return ~np.isfinite(sourceCat[self.centroidXKey]) | \
154 ~np.isfinite(sourceCat[self.centroidYKey])
155 assert ~checkNonfiniteCentroid().any(), \
156 "Centroids not finite for %d unflagged sources." % (checkNonfiniteCentroid().sum())
157 return np.isfinite(sourceCat[self.centroidXErrKey]) \
158 & np.isfinite(sourceCat[self.centroidYErrKey]) \
159 & ~sourceCat[self.centroidFlagKey]
160
161 def _goodSN(self, sourceCat):
162 """Return True for each source that has Signal/Noise > config.minSnr.
163 """
164 if self.config.minSnr <= 0:
165 return True
166 else:
167 with np.errstate(invalid="ignore"): # suppress NAN warnings
168 return sourceCat[self.instFluxKey]/sourceCat[self.instFluxErrKey] > self.config.minSnr
169
170 def _isUsable(self, sourceCat):
171 """Return True for each source that is usable for matching, even if it may
172 have a poor centroid.
173
174 For a source to be usable it must:
175 - have a valid centroid
176 - not be deblended
177 - have a valid flux (of the type specified in this object's constructor)
178 - have adequate signal-to-noise
179 """
180
181 return self._hasCentroid(sourceCat) \
182 & ~self._isMultiple(sourceCat) \
183 & self._goodSN(sourceCat) \
184 & ~sourceCat[self.fluxFlagKey]
185
186 def _isPrimary(self, sourceCat):
187 """Return True if this is a primary source.
188 """
189 if self.primaryKey:
190 return sourceCat[self.primaryKey]
191 else:
192 return np.ones(len(sourceCat), dtype=bool)
193
194 def _isGood(self, sourceCat):
195 """Return True for each source that is usable for matching and likely has a
196 good centroid.
197
198 The additional tests for a good centroid, beyond isUsable, are:
199 - not interpolated in the center
200 - not saturated
201 - not near the edge
202 """
203
204 return self._isUsable(sourceCat) \
205 & self._isPrimary(sourceCat) \
206 & ~sourceCat[self.saturatedKey] \
207 & ~sourceCat[self.interpolatedCenterKey] \
208 & ~sourceCat[self.edgeKey]
209
210 def _isBadFlagged(self, source):
211 """Return True if any of config.badFlags are set for this source.
212 """
213 return any(source[flag] for flag in self.config.badFlags)