Coverage for python/lsst/meas/algorithms/sourceSelector.py: 40%
210 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-09 11:21 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-09 11:21 +0000
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/>.
22__all__ = ["BaseSourceSelectorConfig", "BaseSourceSelectorTask", "sourceSelectorRegistry",
23 "ColorLimit", "MagnitudeLimit", "SignalToNoiseLimit", "MagnitudeErrorLimit",
24 "RequireFlags", "RequireUnresolved", "RequireFiniteRaDec", "RequirePrimary",
25 "ScienceSourceSelectorConfig", "ScienceSourceSelectorTask",
26 "ReferenceSourceSelectorConfig", "ReferenceSourceSelectorTask",
27 "NullSourceSelectorTask"
28 ]
30import abc
31import numpy as np
32import astropy.units as u
33import pandas
34import astropy.table
36import lsst.pex.config as pexConfig
37import lsst.pipe.base as pipeBase
40class BaseSourceSelectorConfig(pexConfig.Config):
41 pass
44class BaseSourceSelectorTask(pipeBase.Task, metaclass=abc.ABCMeta):
45 """Base class for source selectors
47 Source selectors are classes that perform a selection on a catalog
48 object given a set of criteria or cuts. They return the selected catalog
49 and can optionally set a specified Flag field in the input catalog to
50 identify if the source was selected.
52 Register all source selectors with the sourceSelectorRegistry using:
53 sourceSelectorRegistry.register(name, class)
55 Attributes
56 ----------
57 usesMatches : `bool`
58 A boolean variable specify if the inherited source selector uses
59 matches to an external catalog, and thus requires the ``matches``
60 argument to ``run()``.
61 """
63 ConfigClass = BaseSourceSelectorConfig
64 _DefaultName = "sourceSelector"
65 usesMatches = False
67 def __init__(self, **kwargs):
68 pipeBase.Task.__init__(self, **kwargs)
70 def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None):
71 """Select sources and return them.
73 The input catalog must be contiguous in memory.
75 Parameters
76 ----------
77 sourceCat : Various table formats
78 Catalog of sources to select from. Can be
79 `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` or
80 `astropy.table.Table`,
81 sourceSelectedField : `str` or None
82 Name of flag field in sourceCat to set for selected sources.
83 If set, will modify sourceCat in-place.
84 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
85 List of matches to use for source selection.
86 If usesMatches is set in source selector this field is required.
87 If not, it is ignored.
88 exposure : `lsst.afw.image.Exposure` or None
89 The exposure the catalog was built from; used for debug display.
91 Returns
92 -------
93 struct : `lsst.pipe.base.Struct`
94 The struct contains the following data:
96 ``sourceCat``
97 The catalog of sources that were selected.
98 (may not be memory-contiguous)
99 (`lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
100 or `astropy.table.Table`)
101 ``selected``
102 Boolean array of sources that were selected, same length as
103 sourceCat.
104 (`numpy.ndarray` of `bool`)
106 Raises
107 ------
108 RuntimeError
109 Raised if ``sourceCat`` is not contiguous.
110 """
111 if hasattr(sourceCat, 'isContiguous'):
112 # Check for continuity on afwTable catalogs
113 if not sourceCat.isContiguous():
114 raise RuntimeError("Input catalogs for source selection must be contiguous.")
116 result = self.selectSources(sourceCat=sourceCat,
117 exposure=exposure,
118 matches=matches)
120 if sourceSelectedField is not None:
121 sourceCat[sourceSelectedField] = result.selected
123 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
124 selected=result.selected)
126 @abc.abstractmethod
127 def selectSources(self, sourceCat, matches=None, exposure=None):
128 """Return a selection of sources selected by some criteria.
130 Parameters
131 ----------
132 sourceCat : Various table formats
133 Catalog of sources to select from. Supports
134 `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
135 or `astropy.table.Table`
136 This catalog must be contiguous in memory.
137 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
138 A list of lsst.afw.table.ReferenceMatch objects
139 exposure : `lsst.afw.image.Exposure` or None
140 The exposure the catalog was built from; used for debug display.
142 Returns
143 -------
144 struct : `lsst.pipe.base.Struct`
145 The struct contains the following data:
147 ``selected``
148 Boolean array of sources that were selected, same length as
149 sourceCat.
150 (`numpy.ndarray` of `bool`)
151 """
152 raise NotImplementedError("BaseSourceSelectorTask is abstract")
155sourceSelectorRegistry = pexConfig.makeRegistry(
156 doc="A registry of source selectors (subclasses of "
157 "BaseSourceSelectorTask)",
158)
161class BaseLimit(pexConfig.Config):
162 """Base class for selecting sources by applying a limit
164 This object can be used as a `lsst.pex.config.Config` for configuring
165 the limit, and then the `apply` method can be used to identify sources
166 in the catalog that match the configured limit.
168 This provides the `maximum` and `minimum` fields in the Config, and
169 a method to apply the limits to an array of values calculated by the
170 subclass.
171 """
172 minimum = pexConfig.Field(dtype=float, optional=True, doc="Select objects with value greater than this")
173 maximum = pexConfig.Field(dtype=float, optional=True, doc="Select objects with value less than this")
175 def apply(self, values):
176 """Apply the limits to an array of values
178 Subclasses should calculate the array of values and then
179 return the result of calling this method.
181 Parameters
182 ----------
183 values : `numpy.ndarray`
184 Array of values to which to apply limits.
186 Returns
187 -------
188 selected : `numpy.ndarray`
189 Boolean array indicating for each source whether it is selected
190 (True means selected).
191 """
192 selected = np.ones(len(values), dtype=bool)
193 with np.errstate(invalid="ignore"): # suppress NAN warnings
194 if self.minimum is not None:
195 selected &= values > self.minimum
196 if self.maximum is not None:
197 selected &= values < self.maximum
198 return selected
201class ColorLimit(BaseLimit):
202 """Select sources using a color limit
204 This object can be used as a `lsst.pex.config.Config` for configuring
205 the limit, and then the `apply` method can be used to identify sources
206 in the catalog that match the configured limit.
208 We refer to 'primary' and 'secondary' flux measurements; these are the
209 two components of the color, which is:
211 instFluxToMag(cat[primary]) - instFluxToMag(cat[secondary])
212 """
213 primary = pexConfig.Field(dtype=str, doc="Name of column with primary flux measurement")
214 secondary = pexConfig.Field(dtype=str, doc="Name of column with secondary flux measurement")
216 def apply(self, catalog):
217 """Apply the color limit to a catalog
219 Parameters
220 ----------
221 catalog : Various table formats
222 Catalog of sources to which the limit will be applied.
223 Supports `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
224 or `astropy.table.Table`
226 Returns
227 -------
228 selected : `numpy.ndarray`
229 Boolean array indicating for each source whether it is selected
230 (True means selected).
231 """
232 primary = _getFieldFromCatalog(catalog, self.primary)
233 secondary = _getFieldFromCatalog(catalog, self.secondary)
235 primary = (primary*u.nJy).to_value(u.ABmag)
236 secondary = (secondary*u.nJy).to_value(u.ABmag)
237 color = primary - secondary
238 return BaseLimit.apply(self, color)
241class FluxLimit(BaseLimit):
242 """Select sources using a flux limit
244 This object can be used as a `lsst.pex.config.Config` for configuring
245 the limit, and then the `apply` method can be used to identify sources
246 in the catalog that match the configured limit.
247 """
248 fluxField = pexConfig.Field(dtype=str, default="slot_CalibFlux_instFlux",
249 doc="Name of the source flux field to use.")
251 def apply(self, catalog):
252 """Apply the flux limits to a catalog
254 Parameters
255 ----------
256 catalog : `lsst.afw.table.SourceCatalog`
257 Catalog of sources to which the limit will be applied.
259 Returns
260 -------
261 selected : `numpy.ndarray`
262 Boolean array indicating for each source whether it is selected
263 (True means selected).
264 """
265 flagField = self.fluxField + "_flag"
266 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
267 flux = _getFieldFromCatalog(catalog, self.fluxField)
269 selected &= BaseLimit.apply(self, flux)
270 return selected
273class MagnitudeLimit(BaseLimit):
274 """Select sources using a magnitude limit
276 Note that this assumes that a zero-point has already been applied and
277 the fluxes are in AB fluxes in Jansky. It is therefore principally
278 intended for reference catalogs rather than catalogs extracted from
279 science images.
281 This object can be used as a `lsst.pex.config.Config` for configuring
282 the limit, and then the `apply` method can be used to identify sources
283 in the catalog that match the configured limit.
284 """
285 fluxField = pexConfig.Field(dtype=str, default="flux",
286 doc="Name of the source flux field to use.")
288 def apply(self, catalog):
289 """Apply the magnitude limits to a catalog
291 Parameters
292 ----------
293 catalog : `lsst.afw.table.SourceCatalog`
294 Catalog of sources to which the limit will be applied.
296 Returns
297 -------
298 selected : `numpy.ndarray`
299 Boolean array indicating for each source whether it is selected
300 (True means selected).
301 """
302 flagField = self.fluxField + "_flag"
303 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
304 flux = _getFieldFromCatalog(catalog, self.fluxField)
306 magnitude = (flux*u.nJy).to_value(u.ABmag)
307 selected &= BaseLimit.apply(self, magnitude)
308 return selected
311class SignalToNoiseLimit(BaseLimit):
312 """Select sources using a flux signal-to-noise limit
314 This object can be used as a `lsst.pex.config.Config` for configuring
315 the limit, and then the `apply` method can be used to identify sources
316 in the catalog that match the configured limit.
317 """
318 fluxField = pexConfig.Field(dtype=str, default="flux",
319 doc="Name of the source flux field to use.")
320 errField = pexConfig.Field(dtype=str, default="flux_err",
321 doc="Name of the source flux error field to use.")
323 def apply(self, catalog):
324 """Apply the signal-to-noise limits to a catalog
326 Parameters
327 ----------
328 catalog : `lsst.afw.table.SourceCatalog`
329 Catalog of sources to which the limit will be applied.
331 Returns
332 -------
333 selected : `numpy.ndarray`
334 Boolean array indicating for each source whether it is selected
335 (True means selected).
336 """
337 flagField = self.fluxField + "_flag"
338 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=True))
339 flux = _getFieldFromCatalog(catalog, self.fluxField)
340 err = _getFieldFromCatalog(catalog, self.errField)
342 signalToNoise = flux/err
343 selected &= BaseLimit.apply(self, signalToNoise)
344 return selected
347class MagnitudeErrorLimit(BaseLimit):
348 """Select sources using a magnitude error limit
350 Because the magnitude error is the inverse of the signal-to-noise
351 ratio, this also works to select sources by signal-to-noise when
352 you only have a magnitude.
354 This object can be used as a `lsst.pex.config.Config` for configuring
355 the limit, and then the `apply` method can be used to identify sources
356 in the catalog that match the configured limit.
357 """
358 magErrField = pexConfig.Field(dtype=str, default="mag_err",
359 doc="Name of the source flux error field to use.")
361 def apply(self, catalog):
362 """Apply the magnitude error limits to a catalog
364 Parameters
365 ----------
366 catalog : `lsst.afw.table.SourceCatalog`
367 Catalog of sources to which the limit will be applied.
369 Returns
370 -------
371 selected : `numpy.ndarray`
372 Boolean array indicating for each source whether it is selected
373 (True means selected).
374 """
375 return BaseLimit.apply(self, catalog[self.magErrField])
378class RequireFlags(pexConfig.Config):
379 """Select sources using flags
381 This object can be used as a `lsst.pex.config.Config` for configuring
382 the limit, and then the `apply` method can be used to identify sources
383 in the catalog that match the configured limit.
384 """
385 good = pexConfig.ListField(dtype=str, default=[],
386 doc="List of source flag fields that must be set for a source to be used.")
387 bad = pexConfig.ListField(dtype=str, default=[],
388 doc="List of source flag fields that must NOT be set for a source to be used.")
390 def apply(self, catalog):
391 """Apply the flag requirements to a catalog
393 Returns whether the source is selected.
395 Parameters
396 ----------
397 catalog : `lsst.afw.table.SourceCatalog`
398 Catalog of sources to which the requirements will be applied.
400 Returns
401 -------
402 selected : `numpy.ndarray`
403 Boolean array indicating for each source whether it is selected
404 (True means selected).
405 """
406 selected = np.ones(len(catalog), dtype=bool)
407 for flag in self.good:
408 selected &= catalog[flag]
409 for flag in self.bad:
410 selected &= ~catalog[flag]
411 return selected
414class RequireUnresolved(BaseLimit):
415 """Select sources using star/galaxy separation
417 This object can be used as a `lsst.pex.config.Config` for configuring
418 the limit, and then the `apply` method can be used to identify sources
419 in the catalog that match the configured limit.
420 """
421 name = pexConfig.Field(dtype=str, default="base_ClassificationExtendedness_value",
422 doc="Name of column for star/galaxy separation")
424 def setDefaults(self):
425 """Set default
427 ``base_ClassificationExtendedness_value < 0.5`` means unresolved.
428 """
429 self.maximum = 0.5
431 def apply(self, catalog):
432 """Apply the flag requirements to a catalog
434 Returns whether the source is selected.
436 Parameters
437 ----------
438 catalog : `lsst.afw.table.SourceCatalog`
439 Catalog of sources to which the requirements will be applied.
441 Returns
442 -------
443 selected : `numpy.ndarray`
444 Boolean array indicating for each source whether it is selected
445 (True means selected).
446 """
447 value = catalog[self.name]
448 return BaseLimit.apply(self, value)
451class RequireIsolated(pexConfig.Config):
452 """Select sources based on whether they are isolated
454 This object can be used as a `lsst.pex.config.Config` for configuring
455 the column names to check for "parent" and "nChild" keys.
457 Note that this should only be run on a catalog that has had the
458 deblender already run (or else deblend_nChild does not exist).
459 """
460 parentName = pexConfig.Field(dtype=str, default="parent",
461 doc="Name of column for parent")
462 nChildName = pexConfig.Field(dtype=str, default="deblend_nChild",
463 doc="Name of column for nChild")
465 def apply(self, catalog):
466 """Apply the isolation requirements to a catalog
468 Returns whether the source is selected.
470 Parameters
471 ----------
472 catalog : `lsst.afw.table.SourceCatalog`
473 Catalog of sources to which the requirements will be applied.
475 Returns
476 -------
477 selected : `numpy.ndarray`
478 Boolean array indicating for each source whether it is selected
479 (True means selected).
480 """
481 selected = ((catalog[self.parentName] == 0)
482 & (catalog[self.nChildName] == 0))
483 return selected
486class RequireFiniteRaDec(pexConfig.Config):
487 """Select sources that have finite RA and Dec sky coordinate values
489 This object can be used as a `lsst.pex.config.Config` for configuring
490 the column names to check for "coord_ra" and "coord_dec" keys.
492 This will select against objects for which either the RA or Dec coordinate
493 entries are not numpy.isfinite().
494 """
495 raColName = pexConfig.Field(dtype=str, default="coord_ra", doc="Name of column for RA coordinate")
496 decColName = pexConfig.Field(dtype=str, default="coord_dec", doc="Name of column for Dec coordinate")
498 def apply(self, catalog):
499 """Apply the sky coordinate requirements to a catalog
501 Returns whether the sources were selected.
503 Parameters
504 ----------
505 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
506 or `astropy.table.Table`
507 Catalog of sources to which the requirements will be applied.
509 Returns
510 -------
511 selected : `numpy.ndarray`
512 Boolean array indicating for each source whether it is selected
513 (True means selected).
514 """
515 selected = (np.isfinite(_getFieldFromCatalog(catalog, self.raColName))
516 & np.isfinite(_getFieldFromCatalog(catalog, self.decColName)))
517 return selected
520class RequirePrimary(pexConfig.Config):
521 """Select sources that have the detect_isPrimary flag set.
523 This object can be used as a `lsst.pex.config.Config` for configuring
524 the column names to check for "detect_isPrimary". For single frame
525 catalogs this will be True when the source is not a sky object, and is
526 either an isolated parent that is un-modeled or deblended from a parent
527 with multiple children. For meas_deblender, this is equivalent to
528 deblend_nChild=0. For coadd catalogs there is an additional constraint
529 that the source is located on the interior of a patch and tract.
530 """
531 primaryColName = pexConfig.Field(
532 dtype=str,
533 default="detect_isPrimary",
534 doc="Name of primary flag column",
535 )
537 def apply(self, catalog):
538 """Apply the primary requirements to a catalog.
540 Returns whether the sources were selected.
542 Parameters
543 ----------
544 catalog : lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
545 or `astropy.table.Table`
546 Catalog of sources to which the requirement will be applied.
548 Returns
549 -------
550 selected : `numpy.ndarray`
551 Boolean array indicating for each source whether it is selected
552 (True means selected).
553 """
554 selected = (_getFieldFromCatalog(catalog, self.primaryColName)).astype(bool)
556 return selected
559class ScienceSourceSelectorConfig(pexConfig.Config):
560 """Configuration for selecting science sources"""
561 doFluxLimit = pexConfig.Field(dtype=bool, default=False, doc="Apply flux limit?")
562 doFlags = pexConfig.Field(dtype=bool, default=False, doc="Apply flag limitation?")
563 doUnresolved = pexConfig.Field(dtype=bool, default=False, doc="Apply unresolved limitation?")
564 doSignalToNoise = pexConfig.Field(dtype=bool, default=False, doc="Apply signal-to-noise limit?")
565 doIsolated = pexConfig.Field(dtype=bool, default=False, doc="Apply isolated limitation?")
566 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=False,
567 doc="Apply finite sky coordinate check?")
568 doRequirePrimary = pexConfig.Field(dtype=bool, default=False,
569 doc="Apply source is primary check?")
570 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc="Flux limit to apply")
571 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
572 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
573 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
574 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc="Isolated criteria to apply")
575 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
576 doc="Finite sky coordinate criteria to apply")
577 requirePrimary = pexConfig.ConfigField(dtype=RequirePrimary,
578 doc="Primary source criteria to apply")
580 def setDefaults(self):
581 pexConfig.Config.setDefaults(self)
582 self.flags.bad = ["base_PixelFlags_flag_edge", "base_PixelFlags_flag_saturated", "base_PsfFlux_flag"]
583 self.signalToNoise.fluxField = "base_PsfFlux_instFlux"
584 self.signalToNoise.errField = "base_PsfFlux_instFluxErr"
587@pexConfig.registerConfigurable("science", sourceSelectorRegistry)
588class ScienceSourceSelectorTask(BaseSourceSelectorTask):
589 """Science source selector
591 By "science" sources, we mean sources that are on images that we
592 are processing, as opposed to sources from reference catalogs.
594 This selects (science) sources by (optionally) applying each of a
595 magnitude limit, flag requirements and star/galaxy separation.
596 """
597 ConfigClass = ScienceSourceSelectorConfig
599 def selectSources(self, sourceCat, matches=None, exposure=None):
600 """Return a selection of sources selected by specified criteria.
602 Parameters
603 ----------
604 sourceCat : `lsst.afw.table.SourceCatalog`
605 Catalog of sources to select from.
606 This catalog must be contiguous in memory.
607 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
608 Ignored in this SourceSelector.
609 exposure : `lsst.afw.image.Exposure` or None
610 The exposure the catalog was built from; used for debug display.
612 Returns
613 -------
614 struct : `lsst.pipe.base.Struct`
615 The struct contains the following data:
617 ``selected``
618 Boolean array of sources that were selected, same length as
619 sourceCat.
620 (`numpy.ndarray` of `bool`)
621 """
622 selected = np.ones(len(sourceCat), dtype=bool)
623 if self.config.doFluxLimit:
624 selected &= self.config.fluxLimit.apply(sourceCat)
625 if self.config.doFlags:
626 selected &= self.config.flags.apply(sourceCat)
627 if self.config.doUnresolved:
628 selected &= self.config.unresolved.apply(sourceCat)
629 if self.config.doSignalToNoise:
630 selected &= self.config.signalToNoise.apply(sourceCat)
631 if self.config.doIsolated:
632 selected &= self.config.isolated.apply(sourceCat)
633 if self.config.doRequireFiniteRaDec:
634 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
635 if self.config.doRequirePrimary:
636 selected &= self.config.requirePrimary.apply(sourceCat)
638 self.log.info("Selected %d/%d sources", selected.sum(), len(sourceCat))
640 return pipeBase.Struct(selected=selected)
643class ReferenceSourceSelectorConfig(pexConfig.Config):
644 doMagLimit = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude limit?")
645 doFlags = pexConfig.Field(dtype=bool, default=False, doc="Apply flag limitation?")
646 doUnresolved = pexConfig.Field(dtype=bool, default=False, doc="Apply unresolved limitation?")
647 doSignalToNoise = pexConfig.Field(dtype=bool, default=False, doc="Apply signal-to-noise limit?")
648 doMagError = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude error limit?")
649 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=True,
650 doc="Apply finite sky coordinate check?")
651 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc="Magnitude limit to apply")
652 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
653 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
654 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
655 doc="Finite sky coordinate criteria to apply")
656 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
657 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc="Magnitude error limit to apply")
658 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
659 doc="Color limits to apply; key is used as a label only")
662@pexConfig.registerConfigurable("references", sourceSelectorRegistry)
663class ReferenceSourceSelectorTask(BaseSourceSelectorTask):
664 """Reference source selector
666 This selects reference sources by (optionally) applying each of a
667 magnitude limit, flag requirements and color limits.
668 """
669 ConfigClass = ReferenceSourceSelectorConfig
671 def selectSources(self, sourceCat, matches=None, exposure=None):
672 """Return a selection of reference sources selected by some criteria.
674 Parameters
675 ----------
676 sourceCat : `lsst.afw.table.SourceCatalog`
677 Catalog of sources to select from.
678 This catalog must be contiguous in memory.
679 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
680 Ignored in this SourceSelector.
681 exposure : `lsst.afw.image.Exposure` or None
682 The exposure the catalog was built from; used for debug display.
684 Returns
685 -------
686 struct : `lsst.pipe.base.Struct`
687 The struct contains the following data:
689 ``selected``
690 Boolean array of sources that were selected, same length as
691 sourceCat.
692 (`numpy.ndarray` of `bool`)
693 """
694 selected = np.ones(len(sourceCat), dtype=bool)
695 if self.config.doMagLimit:
696 selected &= self.config.magLimit.apply(sourceCat)
697 if self.config.doFlags:
698 selected &= self.config.flags.apply(sourceCat)
699 if self.config.doUnresolved:
700 selected &= self.config.unresolved.apply(sourceCat)
701 if self.config.doSignalToNoise:
702 selected &= self.config.signalToNoise.apply(sourceCat)
703 if self.config.doMagError:
704 selected &= self.config.magError.apply(sourceCat)
705 if self.config.doRequireFiniteRaDec:
706 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
707 for limit in self.config.colorLimits.values():
708 selected &= limit.apply(sourceCat)
710 self.log.info("Selected %d/%d references", selected.sum(), len(sourceCat))
712 return pipeBase.Struct(selected=selected)
715@pexConfig.registerConfigurable("null", sourceSelectorRegistry)
716class NullSourceSelectorTask(BaseSourceSelectorTask):
717 """Source selector that returns true for all sources.
719 Use this when you do not want any sub-selection on your inputs.
720 """
721 ConfigClass = BaseSourceSelectorConfig
723 def selectSources(self, sourceCat, **kwargs):
724 # docstring inherited
725 return pipeBase.Struct(selected=np.ones(len(sourceCat), dtype=bool))
728def _getFieldFromCatalog(catalog, field, isFlag=False):
729 """
730 Get a field from a catalog, for `lsst.afw.table` catalogs or
731 `pandas.DataFrame` or `astropy.table.Table` catalogs.
733 Parameters
734 ----------
735 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
736 or `astropy.table.Table`
737 Catalog of sources to extract field array
738 field : `str`
739 Name of field
740 isFlag : `bool`, optional
741 Is this a flag column? If it does not exist, return array
742 of False.
744 Returns
745 -------
746 array : `np.ndarray`
747 Array of field values from the catalog.
748 """
749 found = False
750 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
751 if field in catalog.columns:
752 found = True
753 # Sequences must be converted to numpy arrays
754 arr = np.array(catalog[field])
755 else:
756 if field in catalog.schema:
757 found = True
758 arr = catalog[field]
760 if isFlag and not found:
761 arr = np.zeros(len(catalog), dtype=bool)
762 elif not found:
763 raise KeyError(f"Could not find field {field} in catalog.")
765 return arr