Coverage for python / lsst / analysis / tools / atools / diaSourceMetrics.py: 47%

36 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-07 08:53 +0000

1# This file is part of analysis_tools. 

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/>. 

21from __future__ import annotations 

22 

23__all__ = ( 

24 "NumGoodDiaSourcesMetrics", 

25 "NumDipolesMetric", 

26 "NumDiaSourcesSelectionMetric", 

27 "DiaSourcesGoodVsBadRatioMetric", 

28) 

29 

30from lsst.pex.config import Field 

31 

32from ..actions.scalar import CountAction, DivideScalar 

33from ..actions.vector import DownselectVector, FlagSelector, GoodDiaSourceSelector 

34from ..interfaces import AnalysisTool 

35 

36 

37class NumGoodDiaSourcesMetrics(AnalysisTool): 

38 """Calculate the number of DIA Sources that do not have known 

39 bad/quality flags set to true, and also calculate the ratio of 

40 counts of non-flagged sources to all sources. 

41 """ 

42 

43 parameterizedBand: bool = False 

44 

45 def setDefaults(self): 

46 super().setDefaults() 

47 

48 # filter for and count the number of dia sources that don't have flags 

49 self.process.filterActions.goodDiaSources = DownselectVector( 

50 vectorKey="parentDiaSourceId", selector=GoodDiaSourceSelector() 

51 ) 

52 self.process.calculateActions.numGoodDiaSources = CountAction(vectorKey="goodDiaSources") 

53 

54 # Count the total number of dia sources: 

55 self.process.calculateActions.numAllDiaSources = CountAction(vectorKey="parentDiaSourceId") 

56 

57 # And calculate the ratio of good-to-all counts 

58 self.process.calculateActions.ratioGoodToAllDiaSources = DivideScalar( 

59 actionA=self.process.calculateActions.numGoodDiaSources, 

60 actionB=self.process.calculateActions.numAllDiaSources, 

61 ) 

62 

63 # the units for the quantity (count, an astropy quantity) 

64 self.produce.metric.units = { 

65 "numAllDiaSources": "ct", 

66 "numGoodDiaSources": "ct", 

67 "ratioGoodToAllDiaSources": "", 

68 } 

69 

70 

71class NumDipolesMetric(AnalysisTool): 

72 """Calculate the number of dipoles with NaN values excluded.""" 

73 

74 def setDefaults(self): 

75 super().setDefaults() 

76 

77 # select all diaSources flagged as dipole 

78 self.prep.selectors.flags = FlagSelector(selectWhenTrue=["isDipole"]) 

79 

80 # count the number of dipoles 

81 self.process.buildActions.numDipoles = CountAction(vectorKey="isDipole") 

82 

83 # the units for the quantity (count, an astropy quantity) 

84 self.produce.metric.units = {"numDipoles": "ct"} 

85 

86 

87class NumDiaSourcesSelectionMetric(AnalysisTool): 

88 """Count the number of DIA Sources for a given threshold.""" 

89 

90 metricName = Field[str](doc="Name to use for output metric") 

91 

92 def setDefaults(self): 

93 super().setDefaults() 

94 

95 # Count dia sources with reliability lower than the threshold 

96 self.process.calculateActions.countingAction = CountAction 

97 

98 # The units for the quantity (count, an astropy quantity) 

99 self.produce.metric.units = {"countingAction": "ct"} 

100 

101 def finalize(self): 

102 self.produce.metric.newNames = {"countingAction": self.metricName} 

103 

104 

105class DiaSourcesGoodVsBadRatioMetric(AnalysisTool): 

106 """Calculate the ratio of 'good' vs 'bad' DIA Sources.""" 

107 

108 def setDefaults(self): 

109 super().setDefaults() 

110 

111 # Count dia sources with reliability higher than the threshold 

112 self.process.buildActions.numDiaSourcesHighReliability = CountAction( 

113 op="gt", threshold=0.9, vectorKey="reliability" 

114 ) 

115 

116 # Count dia sources with reliability lower than the threshold 

117 self.process.buildActions.numDiaSourcesLowReliability = CountAction( 

118 op="lt", threshold=0.1, vectorKey="reliability" 

119 ) 

120 

121 # Calculate ratio of good vs bad DIA Sources 

122 self.process.calculateActions.DiaSourcesGoodVsBadRatio = DivideScalar( 

123 actionA=self.process.buildActions.numDiaSourcesHighReliability, 

124 actionB=self.process.buildActions.numDiaSourcesLowReliability, 

125 ) 

126 

127 # The units for the quantity (dimensionless, an astropy quantity) 

128 self.produce.metric.units = {"DiaSourcesGoodVsBadRatio": ""}