LSST Applications 27.0.0,g0265f82a02+469cd937ee,g02d81e74bb+21ad69e7e1,g1470d8bcf6+cbe83ee85a,g2079a07aa2+e67c6346a6,g212a7c68fe+04a9158687,g2305ad1205+94392ce272,g295015adf3+81dd352a9d,g2bbee38e9b+469cd937ee,g337abbeb29+469cd937ee,g3939d97d7f+72a9f7b576,g487adcacf7+71499e7cba,g50ff169b8f+5929b3527e,g52b1c1532d+a6fc98d2e7,g591dd9f2cf+df404f777f,g5a732f18d5+be83d3ecdb,g64a986408d+21ad69e7e1,g858d7b2824+21ad69e7e1,g8a8a8dda67+a6fc98d2e7,g99cad8db69+f62e5b0af5,g9ddcbc5298+d4bad12328,ga1e77700b3+9c366c4306,ga8c6da7877+71e4819109,gb0e22166c9+25ba2f69a1,gb6a65358fc+469cd937ee,gbb8dafda3b+69d3c0e320,gc07e1c2157+a98bf949bb,gc120e1dc64+615ec43309,gc28159a63d+469cd937ee,gcf0d15dbbd+72a9f7b576,gdaeeff99f8+a38ce5ea23,ge6526c86ff+3a7c1ac5f1,ge79ae78c31+469cd937ee,gee10cc3b42+a6fc98d2e7,gf1cff7945b+21ad69e7e1,gfbcc870c63+9a11dc8c8f
LSST Data Management Base Package
Loading...
Searching...
No Matches
sourceSelector.py
Go to the documentation of this file.
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/>.
21
22__all__ = ["BaseSourceSelectorConfig", "BaseSourceSelectorTask", "sourceSelectorRegistry",
23 "ColorLimit", "MagnitudeLimit", "SignalToNoiseLimit", "MagnitudeErrorLimit",
24 "RequireFlags", "RequireUnresolved", "RequireFiniteRaDec", "RequirePrimary",
25 "ScienceSourceSelectorConfig", "ScienceSourceSelectorTask",
26 "ReferenceSourceSelectorConfig", "ReferenceSourceSelectorTask",
27 "NullSourceSelectorTask"
28 ]
29
30import abc
31import numpy as np
32import astropy.units as u
33import pandas
34import astropy.table
35
36import lsst.pex.config as pexConfig
37import lsst.pipe.base as pipeBase
38
39
40class BaseSourceSelectorConfig(pexConfig.Config):
41 pass
42
43
44class BaseSourceSelectorTask(pipeBase.Task, metaclass=abc.ABCMeta):
45 """Base class for source selectors
46
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.
51
52 Register all source selectors with the sourceSelectorRegistry using:
53 sourceSelectorRegistry.register(name, class)
54
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 """
62
63 ConfigClass = BaseSourceSelectorConfig
64 _DefaultName = "sourceSelector"
65 usesMatches = False
66
67 def __init__(self, **kwargs):
68 pipeBase.Task.__init__(self, **kwargs)
69
70 def run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None):
71 """Select sources and return them.
72
73 The input catalog must be contiguous in memory.
74
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.
90
91 Returns
92 -------
93 struct : `lsst.pipe.base.Struct`
94 The struct contains the following data:
95
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`)
105
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.")
115
116 result = self.selectSources(sourceCat=sourceCat,
117 exposure=exposure,
118 matches=matches)
119
120 if sourceSelectedField is not None:
121 sourceCat[sourceSelectedField] = result.selected
122
123 return pipeBase.Struct(sourceCat=sourceCat[result.selected],
124 selected=result.selected)
125
126 @abc.abstractmethod
127 def selectSources(self, sourceCat, matches=None, exposure=None):
128 """Return a selection of sources selected by some criteria.
129
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.
141
142 Returns
143 -------
144 struct : `lsst.pipe.base.Struct`
145 The struct contains the following data:
146
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")
153
154
155sourceSelectorRegistry = pexConfig.makeRegistry(
156 doc="A registry of source selectors (subclasses of "
157 "BaseSourceSelectorTask)",
158)
159
160
161class BaseLimit(pexConfig.Config):
162 """Base class for selecting sources by applying a limit
163
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.
167
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")
174
175 def apply(self, values):
176 """Apply the limits to an array of values
177
178 Subclasses should calculate the array of values and then
179 return the result of calling this method.
180
181 Parameters
182 ----------
183 values : `numpy.ndarray`
184 Array of values to which to apply limits.
185
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
199
200
202 """Select sources using a color limit
203
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.
207
208 We refer to 'primary' and 'secondary' flux measurements; these are the
209 two components of the color, which is:
210
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")
215
216 def apply(self, catalog):
217 """Apply the color limit to a catalog
218
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`
225
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)
234
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)
239
240
242 """Select sources using a flux limit
243
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.")
250
251 def apply(self, catalog):
252 """Apply the flux limits to a catalog
253
254 Parameters
255 ----------
256 catalog : `lsst.afw.table.SourceCatalog`
257 Catalog of sources to which the limit will be applied.
258
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)
268
269 selected &= BaseLimit.apply(self, flux)
270 return selected
271
272
274 """Select sources using a magnitude limit
275
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.
280
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.")
287
288 def apply(self, catalog):
289 """Apply the magnitude limits to a catalog
290
291 Parameters
292 ----------
293 catalog : `lsst.afw.table.SourceCatalog`
294 Catalog of sources to which the limit will be applied.
295
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)
305
306 magnitude = (flux*u.nJy).to_value(u.ABmag)
307 selected &= BaseLimit.apply(self, magnitude)
308 return selected
309
310
312 """Select sources using a flux signal-to-noise limit
313
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.")
322
323 def apply(self, catalog):
324 """Apply the signal-to-noise limits to a catalog
325
326 Parameters
327 ----------
328 catalog : `lsst.afw.table.SourceCatalog`
329 Catalog of sources to which the limit will be applied.
330
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)
341
342 signalToNoise = flux/err
343 selected &= BaseLimit.apply(self, signalToNoise)
344 return selected
345
346
348 """Select sources using a magnitude error limit
349
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.
353
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.")
360
361 def apply(self, catalog):
362 """Apply the magnitude error limits to a catalog
363
364 Parameters
365 ----------
366 catalog : `lsst.afw.table.SourceCatalog`
367 Catalog of sources to which the limit will be applied.
368
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])
376
377
378class RequireFlags(pexConfig.Config):
379 """Select sources using flags
380
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.")
389
390 def apply(self, catalog):
391 """Apply the flag requirements to a catalog
392
393 Returns whether the source is selected.
394
395 Parameters
396 ----------
397 catalog : `lsst.afw.table.SourceCatalog`
398 Catalog of sources to which the requirements will be applied.
399
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
412
413
415 """Select sources using star/galaxy separation
416
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_ClassificationSizeExtendedness_value",
422 doc="Name of column for star/galaxy separation")
423
424 def setDefaults(self):
425 """Set default
426
427 Values below the threshold are unresolved.
428 """
429 self.maximummaximum = 0.1
430
431 def apply(self, catalog):
432 """Apply the flag requirements to a catalog
433
434 Returns whether the source is selected.
435
436 Parameters
437 ----------
438 catalog : `lsst.afw.table.SourceCatalog`
439 Catalog of sources to which the requirements will be applied.
440
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)
449
450
451class RequireIsolated(pexConfig.Config):
452 """Select sources based on whether they are isolated
453
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.
456
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")
464
465 def apply(self, catalog):
466 """Apply the isolation requirements to a catalog
467
468 Returns whether the source is selected.
469
470 Parameters
471 ----------
472 catalog : `lsst.afw.table.SourceCatalog`
473 Catalog of sources to which the requirements will be applied.
474
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
484
485
486class RequireFiniteRaDec(pexConfig.Config):
487 """Select sources that have finite RA and Dec sky coordinate values
488
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.
491
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")
497
498 def apply(self, catalog):
499 """Apply the sky coordinate requirements to a catalog
500
501 Returns whether the sources were selected.
502
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.
508
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
518
519
520class RequirePrimary(pexConfig.Config):
521 """Select sources that have the detect_isPrimary flag set.
522
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 )
536
537 def apply(self, catalog):
538 """Apply the primary requirements to a catalog.
539
540 Returns whether the sources were selected.
541
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.
547
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)
555
556 return selected
557
558
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 doSkySources = pexConfig.Field(dtype=bool, default=False,
571 doc="Include sky sources, unioned with all other criteria?")
572 fluxLimit = pexConfig.ConfigField(dtype=FluxLimit, doc="Flux limit to apply")
573 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
574 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
575 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
576 isolated = pexConfig.ConfigField(dtype=RequireIsolated, doc="Isolated criteria to apply")
577 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
578 doc="Finite sky coordinate criteria to apply")
579 requirePrimary = pexConfig.ConfigField(dtype=RequirePrimary,
580 doc="Primary source criteria to apply")
581 skyFlag = pexConfig.ConfigField(dtype=RequireFlags, doc="Sky source flag to include")
582
583 def setDefaults(self):
584 pexConfig.Config.setDefaults(self)
585 self.flags.bad = ["base_PixelFlags_flag_edge", "base_PixelFlags_flag_saturated", "base_PsfFlux_flag"]
586 self.signalToNoise.fluxField = "base_PsfFlux_instFlux"
587 self.signalToNoise.errField = "base_PsfFlux_instFluxErr"
588 self.skyFlag.good = ["sky_source"]
589
590
591@pexConfig.registerConfigurable("science", sourceSelectorRegistry)
593 """Science source selector
594
595 By "science" sources, we mean sources that are on images that we
596 are processing, as opposed to sources from reference catalogs.
597
598 This selects (science) sources by (optionally) applying each of a
599 magnitude limit, flag requirements and star/galaxy separation.
600 """
601 ConfigClass = ScienceSourceSelectorConfig
602
603 def selectSources(self, sourceCat, matches=None, exposure=None):
604 """Return a selection of sources selected by specified criteria.
605
606 Parameters
607 ----------
608 sourceCat : `lsst.afw.table.SourceCatalog`
609 Catalog of sources to select from.
610 This catalog must be contiguous in memory.
611 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
612 Ignored in this SourceSelector.
613 exposure : `lsst.afw.image.Exposure` or None
614 The exposure the catalog was built from; used for debug display.
615
616 Returns
617 -------
618 struct : `lsst.pipe.base.Struct`
619 The struct contains the following data:
620
621 ``selected``
622 Boolean array of sources that were selected, same length as
623 sourceCat.
624 (`numpy.ndarray` of `bool`)
625 """
626 selected = np.ones(len(sourceCat), dtype=bool)
627 if self.config.doFluxLimit:
628 selected &= self.config.fluxLimit.apply(sourceCat)
629 if self.config.doFlags:
630 selected &= self.config.flags.apply(sourceCat)
631 if self.config.doUnresolved:
632 selected &= self.config.unresolved.apply(sourceCat)
633 if self.config.doSignalToNoise:
634 selected &= self.config.signalToNoise.apply(sourceCat)
635 if self.config.doIsolated:
636 selected &= self.config.isolated.apply(sourceCat)
637 if self.config.doRequireFiniteRaDec:
638 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
639 if self.config.doRequirePrimary:
640 selected &= self.config.requirePrimary.apply(sourceCat)
641 if self.config.doSkySources:
642 selected |= self.config.skyFlag.apply(sourceCat)
643
644 self.log.info("Selected %d/%d sources", selected.sum(), len(sourceCat))
645
646 return pipeBase.Struct(selected=selected)
647
648
649class ReferenceSourceSelectorConfig(pexConfig.Config):
650 doMagLimit = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude limit?")
651 doFlags = pexConfig.Field(dtype=bool, default=False, doc="Apply flag limitation?")
652 doUnresolved = pexConfig.Field(dtype=bool, default=False, doc="Apply unresolved limitation?")
653 doSignalToNoise = pexConfig.Field(dtype=bool, default=False, doc="Apply signal-to-noise limit?")
654 doMagError = pexConfig.Field(dtype=bool, default=False, doc="Apply magnitude error limit?")
655 doRequireFiniteRaDec = pexConfig.Field(dtype=bool, default=True,
656 doc="Apply finite sky coordinate check?")
657 magLimit = pexConfig.ConfigField(dtype=MagnitudeLimit, doc="Magnitude limit to apply")
658 flags = pexConfig.ConfigField(dtype=RequireFlags, doc="Flags to require")
659 unresolved = pexConfig.ConfigField(dtype=RequireUnresolved, doc="Star/galaxy separation to apply")
660 requireFiniteRaDec = pexConfig.ConfigField(dtype=RequireFiniteRaDec,
661 doc="Finite sky coordinate criteria to apply")
662 signalToNoise = pexConfig.ConfigField(dtype=SignalToNoiseLimit, doc="Signal-to-noise limit to apply")
663 magError = pexConfig.ConfigField(dtype=MagnitudeErrorLimit, doc="Magnitude error limit to apply")
664 colorLimits = pexConfig.ConfigDictField(keytype=str, itemtype=ColorLimit, default={},
665 doc="Color limits to apply; key is used as a label only")
666
667
668@pexConfig.registerConfigurable("references", sourceSelectorRegistry)
670 """Reference source selector
671
672 This selects reference sources by (optionally) applying each of a
673 magnitude limit, flag requirements and color limits.
674 """
675 ConfigClass = ReferenceSourceSelectorConfig
676
677 def selectSources(self, sourceCat, matches=None, exposure=None):
678 """Return a selection of reference sources selected by some criteria.
679
680 Parameters
681 ----------
682 sourceCat : `lsst.afw.table.SourceCatalog`
683 Catalog of sources to select from.
684 This catalog must be contiguous in memory.
685 matches : `list` of `lsst.afw.table.ReferenceMatch` or None
686 Ignored in this SourceSelector.
687 exposure : `lsst.afw.image.Exposure` or None
688 The exposure the catalog was built from; used for debug display.
689
690 Returns
691 -------
692 struct : `lsst.pipe.base.Struct`
693 The struct contains the following data:
694
695 ``selected``
696 Boolean array of sources that were selected, same length as
697 sourceCat.
698 (`numpy.ndarray` of `bool`)
699 """
700 selected = np.ones(len(sourceCat), dtype=bool)
701 if self.config.doMagLimit:
702 selected &= self.config.magLimit.apply(sourceCat)
703 if self.config.doFlags:
704 selected &= self.config.flags.apply(sourceCat)
705 if self.config.doUnresolved:
706 selected &= self.config.unresolved.apply(sourceCat)
707 if self.config.doSignalToNoise:
708 selected &= self.config.signalToNoise.apply(sourceCat)
709 if self.config.doMagError:
710 selected &= self.config.magError.apply(sourceCat)
711 if self.config.doRequireFiniteRaDec:
712 selected &= self.config.requireFiniteRaDec.apply(sourceCat)
713 for limit in self.config.colorLimits.values():
714 selected &= limit.apply(sourceCat)
715
716 self.log.info("Selected %d/%d references", selected.sum(), len(sourceCat))
717
718 return pipeBase.Struct(selected=selected)
719
720
721@pexConfig.registerConfigurable("null", sourceSelectorRegistry)
723 """Source selector that returns true for all sources.
724
725 Use this when you do not want any sub-selection on your inputs.
726 """
727 ConfigClass = BaseSourceSelectorConfig
728
729 def selectSources(self, sourceCat, **kwargs):
730 # docstring inherited
731 return pipeBase.Struct(selected=np.ones(len(sourceCat), dtype=bool))
732
733
734def _getFieldFromCatalog(catalog, field, isFlag=False):
735 """
736 Get a field from a catalog, for `lsst.afw.table` catalogs or
737 `pandas.DataFrame` or `astropy.table.Table` catalogs.
738
739 Parameters
740 ----------
741 catalog : `lsst.afw.table.SourceCatalog` or `pandas.DataFrame`
742 or `astropy.table.Table`
743 Catalog of sources to extract field array
744 field : `str`
745 Name of field
746 isFlag : `bool`, optional
747 Is this a flag column? If it does not exist, return array
748 of False.
749
750 Returns
751 -------
752 array : `np.ndarray`
753 Array of field values from the catalog.
754 """
755 found = False
756 if isinstance(catalog, (pandas.DataFrame, astropy.table.Table)):
757 if field in catalog.columns:
758 found = True
759 # Sequences must be converted to numpy arrays
760 arr = np.array(catalog[field])
761 else:
762 if field in catalog.schema:
763 found = True
764 arr = catalog[field]
765
766 if isFlag and not found:
767 arr = np.zeros(len(catalog), dtype=bool)
768 elif not found:
769 raise KeyError(f"Could not find field {field} in catalog.")
770
771 return arr
run(self, sourceCat, sourceSelectedField=None, matches=None, exposure=None)
selectSources(self, sourceCat, matches=None, exposure=None)
selectSources(self, sourceCat, matches=None, exposure=None)
selectSources(self, sourceCat, matches=None, exposure=None)
_getFieldFromCatalog(catalog, field, isFlag=False)