24__all__ = [
"BaseSourceSelectorConfig",
"BaseSourceSelectorTask",
"sourceSelectorRegistry",
25 "ColorLimit",
"MagnitudeLimit",
"SignalToNoiseLimit",
"MagnitudeErrorLimit",
26 "RequireFlags",
"RequireUnresolved",
"RequireFiniteRaDec",
"RequirePrimary",
27 "ScienceSourceSelectorConfig",
"ScienceSourceSelectorTask",
28 "ReferenceSourceSelectorConfig",
"ReferenceSourceSelectorTask",
33import astropy.units
as u
46 """Base class for source selectors
48 Source selectors are classes that perform a selection on a catalog
49 object given a set of criteria or cuts. They
return the selected catalog
50 and can optionally set a specified Flag field
in the input catalog to
51 identify
if the source was selected.
53 Register all source selectors
with the sourceSelectorRegistry using:
54 sourceSelectorRegistry.register(name,
class)
59 A boolean variable specify
if the inherited source selector uses
60 matches to an external catalog,
and thus requires the ``matches``
61 argument to ``
run()``.
64 ConfigClass = BaseSourceSelectorConfig
65 _DefaultName = "sourceSelector"
69 pipeBase.Task.__init__(self, **kwargs)
71 def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None):
72 """Select sources and return them.
74 The input catalog must be contiguous in memory.
78 sourceCat : Various table formats
79 Catalog of sources to select
from. Can be
81 `astropy.table.Table`,
82 sourceSelectedField : `str`
or None
83 Name of flag field
in sourceCat to set
for selected sources.
84 If set, will modify sourceCat
in-place.
86 List of matches to use
for source selection.
87 If usesMatches
is set
in source selector this field
is required.
88 If
not, it
is ignored.
90 The exposure the catalog was built
from; used
for debug display.
94 struct : `lsst.pipe.base.Struct`
95 The struct contains the following data:
98 The catalog of sources that were selected.
99 (may
not be memory-contiguous)
101 or `astropy.table.Table`)
103 Boolean array of sources that were selected, same length
as
105 (`numpy.ndarray` of `bool`)
110 Raised
if ``sourceCat``
is not contiguous.
112 if hasattr(sourceCat,
'isContiguous'):
114 if not sourceCat.isContiguous():
115 raise RuntimeError(
"Input catalogs for source selection must be contiguous.")
121 if sourceSelectedField
is not None:
122 sourceCat[sourceSelectedField] = result.selected
124 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
125 selected=result.selected)
129 """Return a selection of sources selected by some criteria.
133 sourceCat : Various table formats
134 Catalog of sources to select from. Supports
136 or `astropy.table.Table`
137 This catalog must be contiguous
in memory.
141 The exposure the catalog was built
from; used
for debug display.
145 struct : `lsst.pipe.base.Struct`
146 The struct contains the following data:
149 Boolean array of sources that were selected, same length
as
151 (`numpy.ndarray` of `bool`)
153 raise NotImplementedError(
"BaseSourceSelectorTask is abstract")
156sourceSelectorRegistry = pexConfig.makeRegistry(
157 doc=
"A registry of source selectors (subclasses of "
158 "BaseSourceSelectorTask)",
163 """Base class for selecting sources by applying a limit
165 This object can be used as a `lsst.pex.config.Config`
for configuring
166 the limit,
and then the `apply` method can be used to identify sources
167 in the catalog that match the configured limit.
169 This provides the `maximum`
and `minimum` fields
in the Config,
and
170 a method to apply the limits to an array of values calculated by the
173 minimum = pexConfig.Field(dtype=float, optional=True, doc=
"Select objects with value greater than this")
174 maximum = pexConfig.Field(dtype=float, optional=
True, doc=
"Select objects with value less than this")
177 """Apply the limits to an array of values
179 Subclasses should calculate the array of values and then
180 return the result of calling this method.
184 values : `numpy.ndarray`
185 Array of values to which to apply limits.
189 selected : `numpy.ndarray`
190 Boolean array indicating
for each source whether it
is selected
191 (
True means selected).
193 selected = np.ones(len(values), dtype=bool)
194 with np.errstate(invalid=
"ignore"):
196 selected &= values > self.
minimum
198 selected &= values < self.
maximum
203 """Select sources using a color limit
205 This object can be used as a `lsst.pex.config.Config`
for configuring
206 the limit,
and then the `apply` method can be used to identify sources
207 in the catalog that match the configured limit.
209 We refer to
'primary' and 'secondary' flux measurements; these are the
210 two components of the color, which
is:
212 instFluxToMag(cat[primary]) - instFluxToMag(cat[secondary])
214 primary = pexConfig.Field(dtype=str, doc="Name of column with primary flux measurement")
215 secondary = pexConfig.Field(dtype=str, doc=
"Name of column with secondary flux measurement")
218 """Apply the color limit to a catalog
222 catalog : Various table formats
223 Catalog of sources to which the limit will be applied.
225 or `astropy.table.Table`
229 selected : `numpy.ndarray`
230 Boolean array indicating
for each source whether it
is selected
231 (
True means selected).
233 primary = _getFieldFromCatalog(catalog, self.primary)
234 secondary = _getFieldFromCatalog(catalog, self.secondary)
236 primary = (primary*u.nJy).to_value(u.ABmag)
237 secondary = (secondary*u.nJy).to_value(u.ABmag)
238 color = primary - secondary
239 return BaseLimit.apply(self, color)
243 """Select sources using a flux limit
245 This object can be used as a `lsst.pex.config.Config`
for configuring
246 the limit,
and then the `apply` method can be used to identify sources
247 in the catalog that match the configured limit.
249 fluxField = pexConfig.Field(dtype=str, default="slot_CalibFlux_instFlux",
250 doc=
"Name of the source flux field to use.")
253 """Apply the flux limits to a catalog
258 Catalog of sources to which the limit will be applied.
262 selected : `numpy.ndarray`
263 Boolean array indicating for each source whether it
is selected
264 (
True means selected).
267 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
268 flux = _getFieldFromCatalog(catalog, self.
fluxField)
270 selected &= BaseLimit.apply(self, flux)
275 """Select sources using a magnitude limit
277 Note that this assumes that a zero-point has already been applied and
278 the fluxes are
in AB fluxes
in Jansky. It
is therefore principally
279 intended
for reference catalogs rather than catalogs extracted
from
282 This object can be used
as a `lsst.pex.config.Config`
for configuring
283 the limit,
and then the `apply` method can be used to identify sources
284 in the catalog that match the configured limit.
286 fluxField = pexConfig.Field(dtype=str, default="flux",
287 doc=
"Name of the source flux field to use.")
290 """Apply the magnitude limits to a catalog
295 Catalog of sources to which the limit will be applied.
299 selected : `numpy.ndarray`
300 Boolean array indicating for each source whether it
is selected
301 (
True means selected).
304 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
305 flux = _getFieldFromCatalog(catalog, self.
fluxField)
307 magnitude = (flux*u.nJy).to_value(u.ABmag)
308 selected &= BaseLimit.apply(self, magnitude)
313 """Select sources using a flux signal-to-noise limit
315 This object can be used as a `lsst.pex.config.Config`
for configuring
316 the limit,
and then the `apply` method can be used to identify sources
317 in the catalog that match the configured limit.
319 fluxField = pexConfig.Field(dtype=str, default="flux",
320 doc=
"Name of the source flux field to use.")
321 errField = pexConfig.Field(dtype=str, default=
"flux_err",
322 doc=
"Name of the source flux error field to use.")
325 """Apply the signal-to-noise limits to a catalog
330 Catalog of sources to which the limit will be applied.
334 selected : `numpy.ndarray`
335 Boolean array indicating for each source whether it
is selected
336 (
True means selected).
339 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
340 flux = _getFieldFromCatalog(catalog, self.
fluxField)
341 err = _getFieldFromCatalog(catalog, self.
errField)
343 signalToNoise = flux/err
344 selected &= BaseLimit.apply(self, signalToNoise)
349 """Select sources using a magnitude error limit
351 Because the magnitude error is the inverse of the signal-to-noise
352 ratio, this also works to select sources by signal-to-noise when
353 you only have a magnitude.
355 This object can be used
as a `lsst.pex.config.Config`
for configuring
356 the limit,
and then the `apply` method can be used to identify sources
357 in the catalog that match the configured limit.
359 magErrField = pexConfig.Field(dtype=str, default="mag_err",
360 doc=
"Name of the source flux error field to use.")
363 """Apply the magnitude error limits to a catalog
368 Catalog of sources to which the limit will be applied.
372 selected : `numpy.ndarray`
373 Boolean array indicating for each source whether it
is selected
374 (
True means selected).
376 return BaseLimit.apply(self, catalog[self.
magErrField])
380 """Select sources using flags
382 This object can be used as a `lsst.pex.config.Config`
for configuring
383 the limit,
and then the `apply` method can be used to identify sources
384 in the catalog that match the configured limit.
386 good = pexConfig.ListField(dtype=str, default=[],
387 doc="List of source flag fields that must be set for a source to be used.")
388 bad = pexConfig.ListField(dtype=str, default=[],
389 doc=
"List of source flag fields that must NOT be set for a source to be used.")
392 """Apply the flag requirements to a catalog
394 Returns whether the source is selected.
399 Catalog of sources to which the requirements will be applied.
403 selected : `numpy.ndarray`
404 Boolean array indicating
for each source whether it
is selected
405 (
True means selected).
407 selected = np.ones(len(catalog), dtype=bool)
408 for flag
in self.
good:
409 selected &= catalog[flag]
410 for flag
in self.
bad:
411 selected &= ~catalog[flag]
416 """Select sources using star/galaxy separation
418 This object can be used as a `lsst.pex.config.Config`
for configuring
419 the limit,
and then the `apply` method can be used to identify sources
420 in the catalog that match the configured limit.
422 name = pexConfig.Field(dtype=str, default="base_ClassificationExtendedness_value",
423 doc=
"Name of column for star/galaxy separation")
428 ``base_ClassificationExtendedness_value < 0.5`` means unresolved.
433 """Apply the flag requirements to a catalog
435 Returns whether the source is selected.
440 Catalog of sources to which the requirements will be applied.
444 selected : `numpy.ndarray`
445 Boolean array indicating
for each source whether it
is selected
446 (
True means selected).
448 value = catalog[self.name]
449 return BaseLimit.apply(self, value)
453 """Select sources based on whether they are isolated
455 This object can be used as a `lsst.pex.config.Config`
for configuring
456 the column names to check
for "parent" and "nChild" keys.
458 Note that this should only be run on a catalog that has had the
459 deblender already run (
or else deblend_nChild does
not exist).
461 parentName = pexConfig.Field(dtype=str, default="parent",
462 doc=
"Name of column for parent")
463 nChildName = pexConfig.Field(dtype=str, default=
"deblend_nChild",
464 doc=
"Name of column for nChild")
467 """Apply the isolation requirements to a catalog
469 Returns whether the source is selected.
474 Catalog of sources to which the requirements will be applied.
478 selected : `numpy.ndarray`
479 Boolean array indicating
for each source whether it
is selected
480 (
True means selected).
488 """Select sources that have finite RA and Dec sky coordinate values
490 This object can be used as a `lsst.pex.config.Config`
for configuring
491 the column names to check
for "coord_ra" and "coord_dec" keys.
493 This will select against objects
for which either the RA
or Dec coordinate
494 entries are
not numpy.isfinite().
496 raColName = pexConfig.Field(dtype=str, default="coord_ra", doc=
"Name of column for RA coordinate")
497 decColName = pexConfig.Field(dtype=str, default=
"coord_dec", doc=
"Name of column for Dec coordinate")
500 """Apply the sky coordinate requirements to a catalog
502 Returns whether the sources were selected.
507 or `astropy.table.Table`
508 Catalog of sources to which the requirements will be applied.
512 selected : `numpy.ndarray`
513 Boolean array indicating
for each source whether it
is selected
514 (
True means selected).
516 selected = (np.isfinite(_getFieldFromCatalog(catalog, self.raColName))
517 & np.isfinite(_getFieldFromCatalog(catalog, self.decColName)))
522 """Select sources that have the detect_isPrimary flag set.
524 This object can be used as a `lsst.pex.config.Config`
for configuring
525 the column names to check
for "detect_isPrimary". For single frame
526 catalogs this will be
True when the source
is not a sky object,
and is
527 either an isolated parent that
is un-modeled
or deblended
from a parent
528 with multiple children. For meas_deblender, this
is equivalent to
529 deblend_nChild=0. For coadd catalogs there
is an additional constraint
530 that the source
is located on the interior of a patch
and tract.
532 primaryColName = pexConfig.Field(
534 default="detect_isPrimary",
535 doc=
"Name of primary flag column",
539 """Apply the primary requirements to a catalog.
541 Returns whether the sources were selected.
546 or `astropy.table.Table`
547 Catalog of sources to which the requirement will be applied.
551 selected : `numpy.ndarray`
552 Boolean array indicating
for each source whether it
is selected
553 (
True means selected).
555 selected = (_getFieldFromCatalog(catalog, self.primaryColName)).astype(bool)
561 """Configuration for selecting science sources"""
562 doFluxLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flux limit?")
563 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
564 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
565 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
566 doIsolated = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply isolated limitation?")
567 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=
False,
568 doc=
"Apply finite sky coordinate check?")
569 doRequirePrimary = pexConfig.Field(dtype=bool, default=
False,
570 doc=
"Apply source is primary check?")
571 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc=
"Flux limit to apply")
572 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
573 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
574 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
575 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc=
"Isolated criteria to apply")
576 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
577 doc=
"Finite sky coordinate criteria to apply")
578 requirePrimary = pexConfig.ConfigField(dtype=RequirePrimary,
579 doc=
"Primary source criteria to apply")
582 pexConfig.Config.setDefaults(self)
583 self.
flags.bad = [
"base_PixelFlags_flag_edge",
"base_PixelFlags_flag_saturated",
"base_PsfFlux_flags"]
588@pexConfig.registerConfigurable("science", sourceSelectorRegistry)
590 """Science source selector
592 By "science" sources, we mean sources that are on images that we
593 are processing,
as opposed to sources
from reference catalogs.
595 This selects (science) sources by (optionally) applying each of a
596 magnitude limit, flag requirements
and star/galaxy separation.
598 ConfigClass = ScienceSourceSelectorConfig
601 """Return a selection of sources selected by specified criteria.
606 Catalog of sources to select from.
607 This catalog must be contiguous
in memory.
609 Ignored
in this SourceSelector.
611 The exposure the catalog was built
from; used
for debug display.
615 struct : `lsst.pipe.base.Struct`
616 The struct contains the following data:
619 Boolean array of sources that were selected, same length
as
621 (`numpy.ndarray` of `bool`)
623 selected = np.ones(len(sourceCat), dtype=bool)
624 if self.config.doFluxLimit:
625 selected &= self.config.fluxLimit.apply(sourceCat)
626 if self.config.doFlags:
627 selected &= self.config.flags.apply(sourceCat)
628 if self.config.doUnresolved:
629 selected &= self.config.unresolved.apply(sourceCat)
630 if self.config.doSignalToNoise:
631 selected &= self.config.signalToNoise.apply(sourceCat)
632 if self.config.doIsolated:
633 selected &= self.config.isolated.apply(sourceCat)
634 if self.config.doRequireFiniteRaDec:
635 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
636 if self.config.doRequirePrimary:
637 selected &= self.config.requirePrimary.apply(sourceCat)
639 self.log.info(
"Selected %d/%d sources", selected.sum(), len(sourceCat))
641 return pipeBase.Struct(selected=selected)
645 doMagLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude limit?")
646 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
647 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
648 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
649 doMagError = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude error limit?")
650 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc=
"Magnitude limit to apply")
651 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
652 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
653 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
654 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc=
"Magnitude error limit to apply")
655 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
656 doc=
"Color limits to apply; key is used as a label only")
659@pexConfig.registerConfigurable("references", sourceSelectorRegistry)
661 """Reference source selector
663 This selects reference sources by (optionally) applying each of a
664 magnitude limit, flag requirements and color limits.
666 ConfigClass = ReferenceSourceSelectorConfig
669 """Return a selection of reference sources selected by some criteria.
674 Catalog of sources to select from.
675 This catalog must be contiguous
in memory.
677 Ignored
in this SourceSelector.
679 The exposure the catalog was built
from; used
for debug display.
683 struct : `lsst.pipe.base.Struct`
684 The struct contains the following data:
687 Boolean array of sources that were selected, same length
as
689 (`numpy.ndarray` of `bool`)
691 selected = np.ones(len(sourceCat), dtype=bool)
692 if self.config.doMagLimit:
693 selected &= self.config.magLimit.apply(sourceCat)
694 if self.config.doFlags:
695 selected &= self.config.flags.apply(sourceCat)
696 if self.config.doUnresolved:
697 selected &= self.config.unresolved.apply(sourceCat)
698 if self.config.doSignalToNoise:
699 selected &= self.config.signalToNoise.apply(sourceCat)
700 if self.config.doMagError:
701 selected &= self.config.magError.apply(sourceCat)
702 for limit
in self.config.colorLimits.values():
703 selected &= limit.apply(sourceCat)
705 self.log.info(
"Selected %d/%d references", selected.sum(), len(sourceCat))
707 return pipeBase.Struct(selected=selected)
710def _getFieldFromCatalog(catalog, field, isFlag=False):
713 `pandas.DataFrame`
or `astropy.table.Table` catalogs.
718 or `astropy.table.Table`
719 Catalog of sources to extract field array
722 isFlag : `bool`, optional
723 Is this a flag column? If it does
not exist,
return array
729 Array of field values
from the catalog.
732 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
733 if field
in catalog.columns:
736 arr = np.array(catalog[field])
738 if field
in catalog.schema:
742 if isFlag
and not found:
743 arr = np.zeros(len(catalog), dtype=bool)
745 raise KeyError(f
"Could not find field {field} in catalog.")
def selectSources(self, sourceCat, matches=None, exposure=None)
def __init__(self, **kwargs)
def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None)
def selectSources(self, sourceCat, matches=None, exposure=None)
def selectSources(self, sourceCat, matches=None, exposure=None)