Coverage for python/lsst/ip/diffim/metrics.py: 53%

39 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-08 02:06 -0800

1# This file is part of ip_diffim. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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 <http://www.gnu.org/licenses/>. 

21# 

22 

23__all__ = [ 

24 "NumberSciSourcesMetricTask", "NumberSciSourcesMetricConfig", 

25 "FractionDiaSourcesToSciSourcesMetricTask", "FractionDiaSourcesToSciSourcesMetricConfig", 

26] 

27 

28 

29import numpy as np 

30import astropy.units as u 

31 

32from lsst.pipe.base import Struct, connectionTypes 

33from lsst.verify import Measurement 

34from lsst.verify.tasks import MetricTask, MetricConfig, MetricConnections, \ 

35 MetricComputationError 

36 

37 

38class NumberSciSourcesMetricConnections( 

39 MetricConnections, 

40 defaultTemplates={"package": "ip_diffim", 

41 "metric": "numSciSources"}, 

42 dimensions={"instrument", "visit", "detector"}, 

43): 

44 sources = connectionTypes.Input( 

45 doc="The catalog of science sources.", 

46 name="src", 

47 storageClass="SourceCatalog", 

48 dimensions={"instrument", "visit", "detector"}, 

49 ) 

50 

51 

52class NumberSciSourcesMetricConfig( 

53 MetricConfig, 

54 pipelineConnections=NumberSciSourcesMetricConnections): 

55 pass 

56 

57 

58class NumberSciSourcesMetricTask(MetricTask): 

59 """Task that computes the number of cataloged non-primary science sources. 

60 

61 Notes 

62 ----- 

63 The task excludes any non-primary sources in the catalog, but it does 

64 not require that the catalog include a ``detect_isPrimary`` or 

65 ``sky_sources`` column. 

66 """ 

67 _DefaultName = "numSciSources" 

68 ConfigClass = NumberSciSourcesMetricConfig 

69 

70 def run(self, sources): 

71 """Count the number of primary science sources. 

72 

73 Parameters 

74 ---------- 

75 sources : `lsst.afw.table.SourceCatalog` 

76 A science source catalog, which may be empty. 

77 

78 Returns 

79 ------- 

80 result : `lsst.pipe.base.Struct` 

81 A `~lsst.pipe.base.Struct` containing the following component: 

82 

83 ``measurement`` 

84 the total number of non-primary science sources 

85 (`lsst.verify.Measurement` or `None`) 

86 """ 

87 nSciSources = _countRealSources(sources) 

88 meas = Measurement(self.config.metricName, nSciSources * u.count) 

89 return Struct(measurement=meas) 

90 

91 

92class FractionDiaSourcesToSciSourcesMetricConnections( 

93 MetricTask.ConfigClass.ConnectionsClass, 

94 dimensions={"instrument", "visit", "detector"}, 

95 defaultTemplates={"coaddName": "deep", 

96 "fakesType": "", 

97 "package": "ip_diffim", 

98 "metric": "fracDiaSourcesToSciSources"}): 

99 sciSources = connectionTypes.Input( 

100 doc="The catalog of science sources.", 

101 name="src", 

102 storageClass="SourceCatalog", 

103 dimensions={"instrument", "visit", "detector"}, 

104 ) 

105 diaSources = connectionTypes.Input( 

106 doc="The catalog of DIASources.", 

107 name="{fakesType}{coaddName}Diff_diaSrc", 

108 storageClass="SourceCatalog", 

109 dimensions={"instrument", "visit", "detector"}, 

110 ) 

111 

112 

113class FractionDiaSourcesToSciSourcesMetricConfig( 

114 MetricTask.ConfigClass, 

115 pipelineConnections=FractionDiaSourcesToSciSourcesMetricConnections): 

116 pass 

117 

118 

119class FractionDiaSourcesToSciSourcesMetricTask(MetricTask): 

120 """Task that computes the ratio of difference image sources to science 

121 sources in an image, visit, etc. 

122 

123 Notes 

124 ----- 

125 The task excludes any non-primary sources in the catalog, but it does 

126 not require that the catalog include a ``detect_isPrimary`` or 

127 ``sky_sources`` column. 

128 """ 

129 _DefaultName = "fracDiaSourcesToSciSources" 

130 ConfigClass = FractionDiaSourcesToSciSourcesMetricConfig 

131 

132 def run(self, sciSources, diaSources): 

133 """Compute the ratio of DIASources to non-primary science sources. 

134 

135 Parameters 

136 ---------- 

137 sciSources : `lsst.afw.table.SourceCatalog` 

138 A science source catalog, which may be empty. 

139 diaSources : `lsst.afw.table.SourceCatalog` 

140 A DIASource catalog for the same unit of processing 

141 as ``sciSources``. 

142 

143 Returns 

144 ------- 

145 result : `lsst.pipe.base.Struct` 

146 A `~lsst.pipe.base.Struct` containing the following component: 

147 

148 ``measurement`` 

149 the ratio (`lsst.verify.Measurement` or `None`) 

150 """ 

151 nSciSources = _countRealSources(sciSources) 

152 nDiaSources = _countRealSources(diaSources) 

153 metricName = self.config.metricName 

154 if nSciSources <= 0: 

155 raise MetricComputationError( 

156 "No science sources found; ratio of DIASources to science sources ill-defined.") 

157 else: 

158 meas = Measurement(metricName, nDiaSources / nSciSources * u.dimensionless_unscaled) 

159 return Struct(measurement=meas) 

160 

161 

162def _countRealSources(catalog): 

163 """Return the number of valid sources in a catalog. 

164 

165 At present, this definition includes only primary sources. If a catalog 

166 does not have a ``detect_isPrimary`` flag, this function counts non-sky 

167 sources. If it does not have a ``sky_source`` flag, either, all sources 

168 are counted. 

169 

170 Parameters 

171 ---------- 

172 `catalog` : `lsst.afw.table.SourceCatalog` 

173 The catalog of sources to count. 

174 

175 Returns 

176 ------- 

177 count : `int` 

178 The number of sources that satisfy the criteria. 

179 """ 

180 # E712 is not applicable, because afw.table.SourceRecord.ColumnView 

181 # is not a bool. 

182 if "detect_isPrimary" in catalog.schema: 

183 return np.count_nonzero(catalog["detect_isPrimary"] == True) # noqa: E712 

184 elif "sky_source" in catalog.schema: 

185 return np.count_nonzero(catalog["sky_source"] == False) # noqa: E712 

186 else: 

187 return len(catalog)