22__all__ = [
"BaseSourceSelectorConfig",
"BaseSourceSelectorTask",
"sourceSelectorRegistry",
23 "ColorLimit",
"MagnitudeLimit",
"SignalToNoiseLimit",
"MagnitudeErrorLimit",
24 "RequireFlags",
"RequireUnresolved",
"RequireFiniteRaDec",
"RequirePrimary",
25 "ScienceSourceSelectorConfig",
"ScienceSourceSelectorTask",
26 "ReferenceSourceSelectorConfig",
"ReferenceSourceSelectorTask",
31import astropy.units
as u
44 """Base class for source selectors
46 Source selectors are classes that perform a selection on a catalog
47 object given a set of criteria or cuts. They
return the selected catalog
48 and can optionally set a specified Flag field
in the input catalog to
49 identify
if the source was selected.
51 Register all source selectors
with the sourceSelectorRegistry using:
52 sourceSelectorRegistry.register(name,
class)
57 A boolean variable specify
if the inherited source selector uses
58 matches to an external catalog,
and thus requires the ``matches``
59 argument to ``
run()``.
62 ConfigClass = BaseSourceSelectorConfig
63 _DefaultName = "sourceSelector"
67 pipeBase.Task.__init__(self, **kwargs)
69 def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None):
70 """Select sources and return them.
72 The input catalog must be contiguous in memory.
76 sourceCat : Various table formats
77 Catalog of sources to select
from. Can be
79 `astropy.table.Table`,
80 sourceSelectedField : `str`
or None
81 Name of flag field
in sourceCat to set
for selected sources.
82 If set, will modify sourceCat
in-place.
84 List of matches to use
for source selection.
85 If usesMatches
is set
in source selector this field
is required.
86 If
not, it
is ignored.
88 The exposure the catalog was built
from; used
for debug display.
92 struct : `lsst.pipe.base.Struct`
93 The struct contains the following data:
96 The catalog of sources that were selected.
97 (may
not be memory-contiguous)
99 or `astropy.table.Table`)
101 Boolean array of sources that were selected, same length
as
103 (`numpy.ndarray` of `bool`)
108 Raised
if ``sourceCat``
is not contiguous.
110 if hasattr(sourceCat,
'isContiguous'):
112 if not sourceCat.isContiguous():
113 raise RuntimeError(
"Input catalogs for source selection must be contiguous.")
119 if sourceSelectedField
is not None:
120 sourceCat[sourceSelectedField] = result.selected
122 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
123 selected=result.selected)
127 """Return a selection of sources selected by some criteria.
131 sourceCat : Various table formats
132 Catalog of sources to select from. Supports
134 or `astropy.table.Table`
135 This catalog must be contiguous
in memory.
139 The exposure the catalog was built
from; used
for debug display.
143 struct : `lsst.pipe.base.Struct`
144 The struct contains the following data:
147 Boolean array of sources that were selected, same length
as
149 (`numpy.ndarray` of `bool`)
151 raise NotImplementedError(
"BaseSourceSelectorTask is abstract")
154sourceSelectorRegistry = pexConfig.makeRegistry(
155 doc=
"A registry of source selectors (subclasses of "
156 "BaseSourceSelectorTask)",
161 """Base class for selecting sources by applying a limit
163 This object can be used as a `lsst.pex.config.Config`
for configuring
164 the limit,
and then the `apply` method can be used to identify sources
165 in the catalog that match the configured limit.
167 This provides the `maximum`
and `minimum` fields
in the Config,
and
168 a method to apply the limits to an array of values calculated by the
171 minimum = pexConfig.Field(dtype=float, optional=True, doc=
"Select objects with value greater than this")
172 maximum = pexConfig.Field(dtype=float, optional=
True, doc=
"Select objects with value less than this")
175 """Apply the limits to an array of values
177 Subclasses should calculate the array of values and then
178 return the result of calling this method.
182 values : `numpy.ndarray`
183 Array of values to which to apply limits.
187 selected : `numpy.ndarray`
188 Boolean array indicating
for each source whether it
is selected
189 (
True means selected).
191 selected = np.ones(len(values), dtype=bool)
192 with np.errstate(invalid=
"ignore"):
194 selected &= values > self.
minimum
196 selected &= values < self.
maximum
201 """Select sources using a color limit
203 This object can be used as a `lsst.pex.config.Config`
for configuring
204 the limit,
and then the `apply` method can be used to identify sources
205 in the catalog that match the configured limit.
207 We refer to
'primary' and 'secondary' flux measurements; these are the
208 two components of the color, which
is:
210 instFluxToMag(cat[primary]) - instFluxToMag(cat[secondary])
212 primary = pexConfig.Field(dtype=str, doc="Name of column with primary flux measurement")
213 secondary = pexConfig.Field(dtype=str, doc=
"Name of column with secondary flux measurement")
216 """Apply the color limit to a catalog
220 catalog : Various table formats
221 Catalog of sources to which the limit will be applied.
223 or `astropy.table.Table`
227 selected : `numpy.ndarray`
228 Boolean array indicating
for each source whether it
is selected
229 (
True means selected).
231 primary = _getFieldFromCatalog(catalog, self.primary)
232 secondary = _getFieldFromCatalog(catalog, self.secondary)
234 primary = (primary*u.nJy).to_value(u.ABmag)
235 secondary = (secondary*u.nJy).to_value(u.ABmag)
236 color = primary - secondary
237 return BaseLimit.apply(self, color)
241 """Select sources using a flux limit
243 This object can be used as a `lsst.pex.config.Config`
for configuring
244 the limit,
and then the `apply` method can be used to identify sources
245 in the catalog that match the configured limit.
247 fluxField = pexConfig.Field(dtype=str, default="slot_CalibFlux_instFlux",
248 doc=
"Name of the source flux field to use.")
251 """Apply the flux limits to a catalog
256 Catalog of sources to which the limit will be applied.
260 selected : `numpy.ndarray`
261 Boolean array indicating for each source whether it
is selected
262 (
True means selected).
265 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
266 flux = _getFieldFromCatalog(catalog, self.
fluxField)
268 selected &= BaseLimit.apply(self, flux)
273 """Select sources using a magnitude limit
275 Note that this assumes that a zero-point has already been applied and
276 the fluxes are
in AB fluxes
in Jansky. It
is therefore principally
277 intended
for reference catalogs rather than catalogs extracted
from
280 This object can be used
as a `lsst.pex.config.Config`
for configuring
281 the limit,
and then the `apply` method can be used to identify sources
282 in the catalog that match the configured limit.
284 fluxField = pexConfig.Field(dtype=str, default="flux",
285 doc=
"Name of the source flux field to use.")
288 """Apply the magnitude limits to a catalog
293 Catalog of sources to which the limit will be applied.
297 selected : `numpy.ndarray`
298 Boolean array indicating for each source whether it
is selected
299 (
True means selected).
302 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
303 flux = _getFieldFromCatalog(catalog, self.
fluxField)
305 magnitude = (flux*u.nJy).to_value(u.ABmag)
306 selected &= BaseLimit.apply(self, magnitude)
311 """Select sources using a flux signal-to-noise limit
313 This object can be used as a `lsst.pex.config.Config`
for configuring
314 the limit,
and then the `apply` method can be used to identify sources
315 in the catalog that match the configured limit.
317 fluxField = pexConfig.Field(dtype=str, default="flux",
318 doc=
"Name of the source flux field to use.")
319 errField = pexConfig.Field(dtype=str, default=
"flux_err",
320 doc=
"Name of the source flux error field to use.")
323 """Apply the signal-to-noise limits to a catalog
328 Catalog of sources to which the limit will be applied.
332 selected : `numpy.ndarray`
333 Boolean array indicating for each source whether it
is selected
334 (
True means selected).
337 selected = np.logical_not(_getFieldFromCatalog(catalog, flagField, isFlag=
True))
338 flux = _getFieldFromCatalog(catalog, self.
fluxField)
339 err = _getFieldFromCatalog(catalog, self.
errField)
341 signalToNoise = flux/err
342 selected &= BaseLimit.apply(self, signalToNoise)
347 """Select sources using a magnitude error limit
349 Because the magnitude error is the inverse of the signal-to-noise
350 ratio, this also works to select sources by signal-to-noise when
351 you only have a magnitude.
353 This object can be used
as a `lsst.pex.config.Config`
for configuring
354 the limit,
and then the `apply` method can be used to identify sources
355 in the catalog that match the configured limit.
357 magErrField = pexConfig.Field(dtype=str, default="mag_err",
358 doc=
"Name of the source flux error field to use.")
361 """Apply the magnitude error limits to a catalog
366 Catalog of sources to which the limit will be applied.
370 selected : `numpy.ndarray`
371 Boolean array indicating for each source whether it
is selected
372 (
True means selected).
374 return BaseLimit.apply(self, catalog[self.
magErrField])
378 """Select sources using flags
380 This object can be used as a `lsst.pex.config.Config`
for configuring
381 the limit,
and then the `apply` method can be used to identify sources
382 in the catalog that match the configured limit.
384 good = pexConfig.ListField(dtype=str, default=[],
385 doc="List of source flag fields that must be set for a source to be used.")
386 bad = pexConfig.ListField(dtype=str, default=[],
387 doc=
"List of source flag fields that must NOT be set for a source to be used.")
390 """Apply the flag requirements to a catalog
392 Returns whether the source is selected.
397 Catalog of sources to which the requirements will be applied.
401 selected : `numpy.ndarray`
402 Boolean array indicating
for each source whether it
is selected
403 (
True means selected).
405 selected = np.ones(len(catalog), dtype=bool)
406 for flag
in self.
good:
407 selected &= catalog[flag]
408 for flag
in self.
bad:
409 selected &= ~catalog[flag]
414 """Select sources using star/galaxy separation
416 This object can be used as a `lsst.pex.config.Config`
for configuring
417 the limit,
and then the `apply` method can be used to identify sources
418 in the catalog that match the configured limit.
420 name = pexConfig.Field(dtype=str, default="base_ClassificationExtendedness_value",
421 doc=
"Name of column for star/galaxy separation")
426 ``base_ClassificationExtendedness_value < 0.5`` means unresolved.
431 """Apply the flag requirements to a catalog
433 Returns whether the source is selected.
438 Catalog of sources to which the requirements will be applied.
442 selected : `numpy.ndarray`
443 Boolean array indicating
for each source whether it
is selected
444 (
True means selected).
446 value = catalog[self.name]
447 return BaseLimit.apply(self, value)
451 """Select sources based on whether they are isolated
453 This object can be used as a `lsst.pex.config.Config`
for configuring
454 the column names to check
for "parent" and "nChild" keys.
456 Note that this should only be run on a catalog that has had the
457 deblender already run (
or else deblend_nChild does
not exist).
459 parentName = pexConfig.Field(dtype=str, default="parent",
460 doc=
"Name of column for parent")
461 nChildName = pexConfig.Field(dtype=str, default=
"deblend_nChild",
462 doc=
"Name of column for nChild")
465 """Apply the isolation requirements to a catalog
467 Returns whether the source is selected.
472 Catalog of sources to which the requirements will be applied.
476 selected : `numpy.ndarray`
477 Boolean array indicating
for each source whether it
is selected
478 (
True means selected).
486 """Select sources that have finite RA and Dec sky coordinate values
488 This object can be used as a `lsst.pex.config.Config`
for configuring
489 the column names to check
for "coord_ra" and "coord_dec" keys.
491 This will select against objects
for which either the RA
or Dec coordinate
492 entries are
not numpy.isfinite().
494 raColName = pexConfig.Field(dtype=str, default="coord_ra", doc=
"Name of column for RA coordinate")
495 decColName = pexConfig.Field(dtype=str, default=
"coord_dec", doc=
"Name of column for Dec coordinate")
498 """Apply the sky coordinate requirements to a catalog
500 Returns whether the sources were selected.
505 or `astropy.table.Table`
506 Catalog of sources to which the requirements will be applied.
510 selected : `numpy.ndarray`
511 Boolean array indicating
for each source whether it
is selected
512 (
True means selected).
514 selected = (np.isfinite(_getFieldFromCatalog(catalog, self.raColName))
515 & np.isfinite(_getFieldFromCatalog(catalog, self.decColName)))
520 """Select sources that have the detect_isPrimary flag set.
522 This object can be used as a `lsst.pex.config.Config`
for configuring
523 the column names to check
for "detect_isPrimary". For single frame
524 catalogs this will be
True when the source
is not a sky object,
and is
525 either an isolated parent that
is un-modeled
or deblended
from a parent
526 with multiple children. For meas_deblender, this
is equivalent to
527 deblend_nChild=0. For coadd catalogs there
is an additional constraint
528 that the source
is located on the interior of a patch
and tract.
530 primaryColName = pexConfig.Field(
532 default="detect_isPrimary",
533 doc=
"Name of primary flag column",
537 """Apply the primary requirements to a catalog.
539 Returns whether the sources were selected.
544 or `astropy.table.Table`
545 Catalog of sources to which the requirement will be applied.
549 selected : `numpy.ndarray`
550 Boolean array indicating
for each source whether it
is selected
551 (
True means selected).
553 selected = (_getFieldFromCatalog(catalog, self.primaryColName)).astype(bool)
559 """Configuration for selecting science sources"""
560 doFluxLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flux limit?")
561 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
562 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
563 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
564 doIsolated = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply isolated limitation?")
565 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=
False,
566 doc=
"Apply finite sky coordinate check?")
567 doRequirePrimary = pexConfig.Field(dtype=bool, default=
False,
568 doc=
"Apply source is primary check?")
569 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc=
"Flux limit to apply")
570 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
571 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
572 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
573 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc=
"Isolated criteria to apply")
574 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
575 doc=
"Finite sky coordinate criteria to apply")
576 requirePrimary = pexConfig.ConfigField(dtype=RequirePrimary,
577 doc=
"Primary source criteria to apply")
580 pexConfig.Config.setDefaults(self)
581 self.
flags.bad = [
"base_PixelFlags_flag_edge",
"base_PixelFlags_flag_saturated",
"base_PsfFlux_flags"]
586@pexConfig.registerConfigurable("science", sourceSelectorRegistry)
588 """Science source selector
590 By "science" sources, we mean sources that are on images that we
591 are processing,
as opposed to sources
from reference catalogs.
593 This selects (science) sources by (optionally) applying each of a
594 magnitude limit, flag requirements
and star/galaxy separation.
596 ConfigClass = ScienceSourceSelectorConfig
599 """Return a selection of sources selected by specified criteria.
604 Catalog of sources to select from.
605 This catalog must be contiguous
in memory.
607 Ignored
in this SourceSelector.
609 The exposure the catalog was built
from; used
for debug display.
613 struct : `lsst.pipe.base.Struct`
614 The struct contains the following data:
617 Boolean array of sources that were selected, same length
as
619 (`numpy.ndarray` of `bool`)
621 selected = np.ones(len(sourceCat), dtype=bool)
622 if self.config.doFluxLimit:
623 selected &= self.config.fluxLimit.apply(sourceCat)
624 if self.config.doFlags:
625 selected &= self.config.flags.apply(sourceCat)
626 if self.config.doUnresolved:
627 selected &= self.config.unresolved.apply(sourceCat)
628 if self.config.doSignalToNoise:
629 selected &= self.config.signalToNoise.apply(sourceCat)
630 if self.config.doIsolated:
631 selected &= self.config.isolated.apply(sourceCat)
632 if self.config.doRequireFiniteRaDec:
633 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
634 if self.config.doRequirePrimary:
635 selected &= self.config.requirePrimary.apply(sourceCat)
637 self.log.info(
"Selected %d/%d sources", selected.sum(), len(sourceCat))
639 return pipeBase.Struct(selected=selected)
643 doMagLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude limit?")
644 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
645 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
646 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
647 doMagError = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude error limit?")
648 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc=
"Magnitude limit to apply")
649 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
650 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
651 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
652 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc=
"Magnitude error limit to apply")
653 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
654 doc=
"Color limits to apply; key is used as a label only")
657@pexConfig.registerConfigurable("references", sourceSelectorRegistry)
659 """Reference source selector
661 This selects reference sources by (optionally) applying each of a
662 magnitude limit, flag requirements and color limits.
664 ConfigClass = ReferenceSourceSelectorConfig
667 """Return a selection of reference sources selected by some criteria.
672 Catalog of sources to select from.
673 This catalog must be contiguous
in memory.
675 Ignored
in this SourceSelector.
677 The exposure the catalog was built
from; used
for debug display.
681 struct : `lsst.pipe.base.Struct`
682 The struct contains the following data:
685 Boolean array of sources that were selected, same length
as
687 (`numpy.ndarray` of `bool`)
689 selected = np.ones(len(sourceCat), dtype=bool)
690 if self.config.doMagLimit:
691 selected &= self.config.magLimit.apply(sourceCat)
692 if self.config.doFlags:
693 selected &= self.config.flags.apply(sourceCat)
694 if self.config.doUnresolved:
695 selected &= self.config.unresolved.apply(sourceCat)
696 if self.config.doSignalToNoise:
697 selected &= self.config.signalToNoise.apply(sourceCat)
698 if self.config.doMagError:
699 selected &= self.config.magError.apply(sourceCat)
700 for limit
in self.config.colorLimits.values():
701 selected &= limit.apply(sourceCat)
703 self.log.info(
"Selected %d/%d references", selected.sum(), len(sourceCat))
705 return pipeBase.Struct(selected=selected)
708def _getFieldFromCatalog(catalog, field, isFlag=False):
711 `pandas.DataFrame`
or `astropy.table.Table` catalogs.
716 or `astropy.table.Table`
717 Catalog of sources to extract field array
720 isFlag : `bool`, optional
721 Is this a flag column? If it does
not exist,
return array
727 Array of field values
from the catalog.
730 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
731 if field
in catalog.columns:
734 arr = np.array(catalog[field])
736 if field
in catalog.schema:
740 if isFlag
and not found:
741 arr = np.zeros(len(catalog), dtype=bool)
743 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)