LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
matcherSourceSelector.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 from __future__ import absolute_import, division, print_function
23 
24 import numpy as np
25 
26 from lsst.afw import table
27 import lsst.pex.config as pexConfig
28 from .sourceSelector import BaseSourceSelectorConfig, BaseSourceSelectorTask, sourceSelectorRegistry
29 from lsst.pipe.base import Struct
30 from functools import reduce
31 
32 
33 class MatcherSourceSelectorConfig(BaseSourceSelectorConfig):
34  sourceFluxType = pexConfig.Field(
35  doc="Type of source flux; typically one of Ap or Psf",
36  dtype=str,
37  default="Ap",
38  )
39  minSnr = pexConfig.Field(
40  dtype=float,
41  doc="Minimum allowed signal-to-noise ratio for sources used for matching "
42  "(in the flux specified by sourceFluxType); <= 0 for no limit",
43  default=40,
44  )
45 
46 
47 class MatcherSourceSelectorTask(BaseSourceSelectorTask):
48  """
49  !Select sources that are useful for matching.
50 
51  Good matching sources have high signal/noise, are non-blended. They need not
52  be PSF sources, just have reliable centroids.
53  """
54  ConfigClass = MatcherSourceSelectorConfig
55 
56  def __init__(self, *args, **kwargs):
57  BaseSourceSelectorTask.__init__(self, *args, **kwargs)
58 
59  def selectSources(self, sourceCat, matches=None):
60  """
61  !Return a catalog of sources: a subset of sourceCat.
62 
63  If sourceCat is cotiguous in memory, will use vectorized tests for ~100x
64  execution speed advantage over non-contiguous catalogs. This would be
65  even faster if we didn't have to check footprints for multiple peaks.
66 
67  @param[in] sourceCat catalog of sources that may be sources
68  (an lsst.afw.table.SourceCatalog)
69 
70  @return a pipeBase.Struct containing:
71  - sourceCat a catalog of sources
72  """
73  self._getSchemaKeys(sourceCat.schema)
74 
75  if sourceCat.isContiguous():
76  good = self._isUsable_vector(sourceCat)
77  result = sourceCat[good]
78  else:
79  result = table.SourceCatalog(sourceCat.table)
80  for i, source in enumerate(sourceCat):
81  if self._isUsable(source):
82  result.append(source)
83  return Struct(sourceCat=result)
84 
85  def _getSchemaKeys(self, schema):
86  """Extract and save the necessary keys from schema with asKey."""
87  self.parentKey = schema["parent"].asKey()
88  self.centroidXKey = schema["slot_Centroid_x"].asKey()
89  self.centroidYKey = schema["slot_Centroid_y"].asKey()
90  self.centroidFlagKey = schema["slot_Centroid_flag"].asKey()
91 
92  fluxPrefix = "slot_%sFlux_" % (self.config.sourceFluxType,)
93  self.fluxField = fluxPrefix + "flux"
94  self.fluxKey = schema[fluxPrefix + "flux"].asKey()
95  self.fluxFlagKey = schema[fluxPrefix + "flag"].asKey()
96  self.fluxSigmaKey = schema[fluxPrefix + "fluxSigma"].asKey()
97 
98  def _isParent_vector(self, sourceCat):
99  """Return True for each source that is the parent source."""
100  test = (sourceCat.get(self.parentKey) == 0)
101  return test
102 
103  def _isParent(self, source):
104  """Return True if source is the parent source."""
105  if (source.get(self.parentKey) == 0):
106  return True
107  return False
108 
109  def _hasCentroid_vector(self, sourceCat):
110  """Return True for each source that has a valid centroid"""
111  return np.isfinite(sourceCat.get(self.centroidXKey)) \
112  & np.isfinite(sourceCat.get(self.centroidYKey)) \
113  & ~sourceCat.get(self.centroidFlagKey)
114 
115  def _hasCentroid(self, source):
116  """Return True if the source has a valid centroid"""
117  centroid = source.getCentroid()
118  return np.all(np.isfinite(centroid)) and not source.getCentroidFlag()
119 
120  def _goodSN_vector(self, sourceCat):
121  """Return True for each source that has Signal/Noise > config.minSnr."""
122  if self.config.minSnr <= 0:
123  return True
124  else:
125  return sourceCat.get(self.fluxKey)/sourceCat.get(self.fluxSigmaKey) > self.config.minSnr
126 
127  def _goodSN(self, source):
128  """Return True if source has Signal/Noise > config.minSnr."""
129  return (self.config.minSnr <= 0 or
130  (source.get(self.fluxKey)/source.get(self.fluxSigmaKey) > self.config.minSnr))
131 
132  def _isUsable_vector(self, sourceCat):
133  """
134  Return True for each source that is usable for matching, even if it may
135  have a poor centroid.
136 
137  For a source to be usable it must:
138  - have a valid centroid
139  - not be deblended
140  - have a valid flux (of the type specified in this object's constructor)
141  - have adequate signal-to-noise
142  """
143  return self._hasCentroid_vector(sourceCat) \
144  & self._isParent_vector(sourceCat) \
145  & self._goodSN_vector(sourceCat) \
146  & ~sourceCat.get(self.fluxFlagKey)
147 
148  def _isUsable(self, source):
149  """
150  Return True if the source is usable for matching, even if it may have a
151  poor centroid.
152 
153  For a source to be usable it must:
154  - have a valid centroid
155  - not be deblended
156  - have a valid flux (of the type specified in this object's constructor)
157  - have adequate signal-to-noise
158  """
159  return self._hasCentroid(source) \
160  and self._isParent(source) \
161  and not source.get(self.fluxFlagKey) \
162  and self._goodSN(source)
163 
164 sourceSelectorRegistry.register("matcher", MatcherSourceSelectorTask)