23 """Select sources that are useful for astrometry. 
   25 Such sources have good signal-to-noise, are well centroided, not blended, 
   26 and not flagged with a handful of "bad" flags. 
   31 import lsst.pex.config 
as pexConfig
 
   32 from .sourceSelector 
import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
 
   34 from functools 
import reduce
 
   38     badFlags = pexConfig.ListField(
 
   39         doc=
"List of flags which cause a source to be rejected as bad",
 
   42             "base_PixelFlags_flag_edge",
 
   43             "base_PixelFlags_flag_interpolatedCenter",
 
   44             "base_PixelFlags_flag_saturatedCenter",
 
   45             "base_PixelFlags_flag_crCenter",
 
   46             "base_PixelFlags_flag_bad",
 
   49     sourceFluxType = pexConfig.Field(
 
   50         doc=
"Type of source flux; typically one of Ap or Psf",
 
   54     minSnr = pexConfig.Field(
 
   56         doc=
"Minimum allowed signal-to-noise ratio for sources used for matching " 
   57         "(in the flux specified by sourceFluxType); <= 0 for no limit",
 
   62 @pexConfig.registerConfigurable(
"astrometry", sourceSelectorRegistry)
 
   64     """Select sources that are useful for astrometry. 
   66     Good astrometry sources have high signal/noise, are non-blended, and 
   67     did not have certain "bad" flags set during source extraction. They need not 
   68     be PSF sources, just have reliable centroids. 
   70     ConfigClass = AstrometrySourceSelectorConfig
 
   73         BaseSourceSelectorTask.__init__(self, *args, **kwargs)
 
   76         """Return a selection of sources that are useful for astrometry. 
   80         sourceCat : `lsst.afw.table.SourceCatalog` 
   81             Catalog of sources to select from. 
   82             This catalog must be contiguous in memory. 
   83         matches : `list` of `lsst.afw.table.ReferenceMatch` or None 
   84             Ignored in this SourceSelector. 
   85         exposure : `lsst.afw.image.Exposure` or None 
   86             The exposure the catalog was built from; used for debug display. 
   90         struct : `lsst.pipe.base.Struct` 
   91             The struct contains the following data: 
   93             - selected : `array` of `bool`` 
   94                 Boolean array of sources that were selected, same length as 
   99         bad = reduce(
lambda x, y: np.logical_or(x, sourceCat.get(y)), self.config.badFlags, 
False)
 
  101         return Struct(selected=good & ~bad)
 
  103     def _getSchemaKeys(self, schema):
 
  104         """Extract and save the necessary keys from schema with asKey.""" 
  113         self.
edgeKey = schema[
"base_PixelFlags_flag_edge"].asKey()
 
  117         fluxPrefix = 
"slot_%sFlux_" % (self.config.sourceFluxType,)
 
  122     def _isMultiple(self, sourceCat):
 
  123         """Return True for each source that is likely multiple sources.""" 
  126         for i, cat 
in enumerate(sourceCat):
 
  127             footprint = cat.getFootprint()
 
  128             test[i] |= (footprint 
is not None) 
and (len(footprint.getPeaks()) > 1)
 
  131     def _hasCentroid(self, sourceCat):
 
  132         """Return True for each source that has a valid centroid""" 
  133         def checkNonfiniteCentroid():
 
  134             """Return True for sources with non-finite centroids.""" 
  135             return ~np.isfinite(sourceCat.get(self.
centroidXKey)) | \
 
  137         assert ~checkNonfiniteCentroid().
any(), \
 
  138             "Centroids not finite for %d unflagged sources." % (checkNonfiniteCentroid().sum())
 
  143     def _goodSN(self, sourceCat):
 
  144         """Return True for each source that has Signal/Noise > config.minSnr.""" 
  145         if self.config.minSnr <= 0:
 
  148             with np.errstate(invalid=
"ignore"):  
 
  151     def _isUsable(self, sourceCat):
 
  153         Return True for each source that is usable for matching, even if it may 
  154         have a poor centroid. 
  156         For a source to be usable it must: 
  157         - have a valid centroid 
  159         - have a valid flux (of the type specified in this object's constructor) 
  160         - have adequate signal-to-noise 
  168     def _isGood(self, sourceCat):
 
  170         Return True for each source that is usable for matching and likely has a 
  173         The additional tests for a good centroid, beyond isUsable, are: 
  174         - not interpolated in the center 
  184     def _isBadFlagged(self, source):
 
  185         """Return True if any of config.badFlags are set for this source.""" 
  186         return any(source.get(flag) 
for flag 
in self.config.badFlags)