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

45 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-14 03:07 -0700

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 non-primary science sources. 

72 

73 Parameters 

74 ---------- 

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

76 A science source catalog, which may be empty or `None`. 

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 if sources is not None: 

88 nSciSources = _countRealSources(sources) 

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

90 else: 

91 self.log.info("Nothing to do: no catalogs found.") 

92 meas = None 

93 return Struct(measurement=meas) 

94 

95 

96class FractionDiaSourcesToSciSourcesMetricConnections( 

97 MetricTask.ConfigClass.ConnectionsClass, 

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

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

100 "fakesType": "", 

101 "package": "ip_diffim", 

102 "metric": "fracDiaSourcesToSciSources"}): 

103 sciSources = connectionTypes.Input( 

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

105 name="src", 

106 storageClass="SourceCatalog", 

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

108 ) 

109 diaSources = connectionTypes.Input( 

110 doc="The catalog of DIASources.", 

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

112 storageClass="SourceCatalog", 

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

114 ) 

115 

116 

117class FractionDiaSourcesToSciSourcesMetricConfig( 

118 MetricTask.ConfigClass, 

119 pipelineConnections=FractionDiaSourcesToSciSourcesMetricConnections): 

120 pass 

121 

122 

123class FractionDiaSourcesToSciSourcesMetricTask(MetricTask): 

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

125 sources in an image, visit, etc. 

126 

127 Notes 

128 ----- 

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

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

131 ``sky_sources`` column. 

132 """ 

133 _DefaultName = "fracDiaSourcesToSciSources" 

134 ConfigClass = FractionDiaSourcesToSciSourcesMetricConfig 

135 

136 def run(self, sciSources, diaSources): 

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

138 

139 Parameters 

140 ---------- 

141 sciSources : `lsst.afw.table.SourceCatalog` or `None` 

142 A science source catalog, which may be empty or `None`. 

143 diaSources : `lsst.afw.table.SourceCatalog` or `None` 

144 A DIASource catalog for the same unit of processing 

145 as ``sciSources``. 

146 

147 Returns 

148 ------- 

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

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

151 

152 ``measurement`` 

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

154 """ 

155 if diaSources is not None and sciSources is not None: 

156 nSciSources = _countRealSources(sciSources) 

157 nDiaSources = _countRealSources(diaSources) 

158 metricName = self.config.metricName 

159 if nSciSources <= 0: 

160 raise MetricComputationError( 

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

162 else: 

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

164 else: 

165 self.log.info("Nothing to do: no catalogs found.") 

166 meas = None 

167 return Struct(measurement=meas) 

168 

169 

170def _countRealSources(catalog): 

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

172 

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

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

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

176 are counted. 

177 

178 Parameters 

179 ---------- 

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

181 The catalog of sources to count. 

182 

183 Returns 

184 ------- 

185 count : `int` 

186 The number of sources that satisfy the criteria. 

187 """ 

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

189 # is not a bool. 

190 if "detect_isPrimary" in catalog.schema: 

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

192 elif "sky_source" in catalog.schema: 

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

194 else: 

195 return len(catalog)