24 __all__ = [
"BaseSourceSelectorConfig",
"BaseSourceSelectorTask",
"sourceSelectorRegistry",
25 "ColorLimit",
"MagnitudeLimit",
"SignalToNoiseLimit",
"MagnitudeErrorLimit",
26 "RequireFlags",
"RequireUnresolved",
27 "ScienceSourceSelectorConfig",
"ScienceSourceSelectorTask",
28 "ReferenceSourceSelectorConfig",
"ReferenceSourceSelectorTask",
33 import astropy.units
as u
37 import lsst.pex.config
as pexConfig
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 : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` 79 or `astropy.table.Table` 80 Catalog of sources to select from. 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. 93 struct : `lsst.pipe.base.Struct` 94 The struct contains the following data: 96 - sourceCat : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` 97 or `astropy.table.Table` 98 The catalog of sources that were selected. 99 (may not be memory-contiguous) 100 - selected : `numpy.ndarray` of `bool`` 101 Boolean array of sources that were selected, same length as 107 Raised if ``sourceCat`` is not contiguous. 109 if hasattr(sourceCat,
'isContiguous'):
111 if not sourceCat.isContiguous():
112 raise RuntimeError(
"Input catalogs for source selection must be contiguous.")
118 if sourceSelectedField
is not None:
119 if isinstance(sourceCat, (pandas.DataFrame, astropy.table.Table)):
120 sourceCat[sourceSelectedField] = result.selected
122 source_selected_key = \
123 sourceCat.getSchema()[sourceSelectedField].asKey()
125 for source, flag
in zip(sourceCat, result.selected):
126 source.set(source_selected_key, bool(flag))
127 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
128 selected=result.selected)
132 """Return a selection of sources selected by some criteria. 136 sourceCat : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` 137 or `astropy.table.Table` 138 Catalog of sources to select from. 139 This catalog must be contiguous in memory. 140 matches : `list` of `lsst.afw.table.ReferenceMatch` or None 141 A list of lsst.afw.table.ReferenceMatch objects 142 exposure : `lsst.afw.image.Exposure` or None 143 The exposure the catalog was built from; used for debug display. 147 struct : `lsst.pipe.base.Struct` 148 The struct contains the following data: 150 - selected : `numpy.ndarray` of `bool`` 151 Boolean array of sources that were selected, same length as 154 raise NotImplementedError(
"BaseSourceSelectorTask is abstract")
157 sourceSelectorRegistry = pexConfig.makeRegistry(
158 doc=
"A registry of source selectors (subclasses of " 159 "BaseSourceSelectorTask)",
164 """Base class for selecting sources by applying a limit 166 This object can be used as a `lsst.pex.config.Config` for configuring 167 the limit, and then the `apply` method can be used to identify sources 168 in the catalog that match the configured limit. 170 This provides the `maximum` and `minimum` fields in the Config, and 171 a method to apply the limits to an array of values calculated by the 174 minimum = pexConfig.Field(dtype=float, optional=
True, doc=
"Select objects with value greater than this")
175 maximum = pexConfig.Field(dtype=float, optional=
True, doc=
"Select objects with value less than this")
178 """Apply the limits to an array of values 180 Subclasses should calculate the array of values and then 181 return the result of calling this method. 185 values : `numpy.ndarray` 186 Array of values to which to apply limits. 190 selected : `numpy.ndarray` 191 Boolean array indicating for each source whether it is selected 192 (True means selected). 194 selected = np.ones(len(values), dtype=bool)
195 with np.errstate(invalid=
"ignore"):
197 selected &= values > self.
minimum 199 selected &= values < self.
maximum 204 """Select sources using a color limit 206 This object can be used as a `lsst.pex.config.Config` for configuring 207 the limit, and then the `apply` method can be used to identify sources 208 in the catalog that match the configured limit. 210 We refer to 'primary' and 'secondary' flux measurements; these are the 211 two components of the color, which is: 213 instFluxToMag(cat[primary]) - instFluxToMag(cat[secondary]) 215 primary = pexConfig.Field(dtype=str, doc=
"Name of column with primary flux measurement")
216 secondary = pexConfig.Field(dtype=str, doc=
"Name of column with secondary flux measurement")
219 """Apply the color limit to a catalog 223 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` 224 or `astropy.table.Table` 225 Catalog of sources to which the limit will be applied. 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 257 catalog : `lsst.afw.table.SourceCatalog` 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 294 catalog : `lsst.afw.table.SourceCatalog` 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 329 catalog : `lsst.afw.table.SourceCatalog` 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 367 catalog : `lsst.afw.table.SourceCatalog` 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. 398 catalog : `lsst.afw.table.SourceCatalog` 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. 439 catalog : `lsst.afw.table.SourceCatalog` 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. 473 catalog : `lsst.afw.table.SourceCatalog` 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 """Configuration for selecting science sources""" 489 doFluxLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flux limit?")
490 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
491 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
492 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
493 doIsolated = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply isolated limitation?")
494 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc=
"Flux limit to apply")
495 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
496 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
497 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
498 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc=
"Isolated criteria to apply")
501 pexConfig.Config.setDefaults(self)
502 self.
flags.bad = [
"base_PixelFlags_flag_edge",
"base_PixelFlags_flag_saturated",
"base_PsfFlux_flags"]
507 @pexConfig.registerConfigurable(
"science", sourceSelectorRegistry)
509 """Science source selector 511 By "science" sources, we mean sources that are on images that we 512 are processing, as opposed to sources from reference catalogs. 514 This selects (science) sources by (optionally) applying each of a 515 magnitude limit, flag requirements and star/galaxy separation. 517 ConfigClass = ScienceSourceSelectorConfig
520 """Return a selection of sources selected by specified criteria. 524 sourceCat : `lsst.afw.table.SourceCatalog` 525 Catalog of sources to select from. 526 This catalog must be contiguous in memory. 527 matches : `list` of `lsst.afw.table.ReferenceMatch` or None 528 Ignored in this SourceSelector. 529 exposure : `lsst.afw.image.Exposure` or None 530 The exposure the catalog was built from; used for debug display. 534 struct : `lsst.pipe.base.Struct` 535 The struct contains the following data: 537 - selected : `array` of `bool`` 538 Boolean array of sources that were selected, same length as 541 selected = np.ones(len(sourceCat), dtype=bool)
542 if self.config.doFluxLimit:
543 selected &= self.config.fluxLimit.apply(sourceCat)
544 if self.config.doFlags:
545 selected &= self.config.flags.apply(sourceCat)
546 if self.config.doUnresolved:
547 selected &= self.config.unresolved.apply(sourceCat)
548 if self.config.doSignalToNoise:
549 selected &= self.config.signalToNoise.apply(sourceCat)
550 if self.config.doIsolated:
551 selected &= self.config.isolated.apply(sourceCat)
553 self.log.info(
"Selected %d/%d sources", selected.sum(), len(sourceCat))
555 return pipeBase.Struct(selected=selected)
559 doMagLimit = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude limit?")
560 doFlags = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply flag limitation?")
561 doUnresolved = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply unresolved limitation?")
562 doSignalToNoise = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply signal-to-noise limit?")
563 doMagError = pexConfig.Field(dtype=bool, default=
False, doc=
"Apply magnitude error limit?")
564 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc=
"Magnitude limit to apply")
565 flags = pexConfig.ConfigField(dtype=RequireFlags, doc=
"Flags to require")
566 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc=
"Star/galaxy separation to apply")
567 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc=
"Signal-to-noise limit to apply")
568 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc=
"Magnitude error limit to apply")
569 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
570 doc=
"Color limits to apply; key is used as a label only")
573 @pexConfig.registerConfigurable(
"references", sourceSelectorRegistry)
575 """Reference source selector 577 This selects reference sources by (optionally) applying each of a 578 magnitude limit, flag requirements and color limits. 580 ConfigClass = ReferenceSourceSelectorConfig
583 """Return a selection of reference sources selected by some criteria. 587 sourceCat : `lsst.afw.table.SourceCatalog` 588 Catalog of sources to select from. 589 This catalog must be contiguous in memory. 590 matches : `list` of `lsst.afw.table.ReferenceMatch` or None 591 Ignored in this SourceSelector. 592 exposure : `lsst.afw.image.Exposure` or None 593 The exposure the catalog was built from; used for debug display. 597 struct : `lsst.pipe.base.Struct` 598 The struct contains the following data: 600 - selected : `array` of `bool`` 601 Boolean array of sources that were selected, same length as 604 selected = np.ones(len(sourceCat), dtype=bool)
605 if self.config.doMagLimit:
606 selected &= self.config.magLimit.apply(sourceCat)
607 if self.config.doFlags:
608 selected &= self.config.flags.apply(sourceCat)
609 if self.config.doUnresolved:
610 selected &= self.config.unresolved.apply(sourceCat)
611 if self.config.doSignalToNoise:
612 selected &= self.config.signalToNoise.apply(sourceCat)
613 if self.config.doMagError:
614 selected &= self.config.magError.apply(sourceCat)
615 for limit
in self.config.colorLimits.values():
616 selected &= limit.apply(sourceCat)
618 self.log.info(
"Selected %d/%d references", selected.sum(), len(sourceCat))
620 return pipeBase.Struct(selected=selected)
623 def _getFieldFromCatalog(catalog, field, isFlag=False):
625 Get a field from a catalog, for `lsst.afw.table` catalogs or 626 `pandas.DataFrame` or `astropy.table.Table` catalogs. 630 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame` 631 or `astropy.table.Table` 632 Catalog of sources to extract field array 635 isFlag : `bool`, optional 636 Is this a flag column? If it does not exist, return array 642 Array of field values from the catalog. 645 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
646 if field
in catalog.columns:
649 arr = np.array(catalog[field])
651 if field
in catalog.schema:
655 if isFlag
and not found:
656 arr = np.zeros(len(catalog), dtype=bool)
658 raise KeyError(f
"Could not find field {field} in catalog.")
def selectSources(self, sourceCat, matches=None, exposure=None)
def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None)
def __init__(self, kwargs)
def selectSources(self, sourceCat, matches=None, exposure=None)
def selectSources(self, sourceCat, matches=None, exposure=None)