Coverage for python / lsst / analysis / tools / atools / coaddInputCount.py: 22%

97 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-01 08:55 +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__ = ("CoaddInputCount", "CoaddQualityCheck", "CoaddQualityPlot", "CoaddInputFraction") 

24 

25from lsst.pex.config import ListField 

26 

27from ..actions.plot.calculateRange import MinMax 

28from ..actions.plot.coaddDepthPlot import CoaddDepthPlot 

29from ..actions.plot.skyPlot import SkyPlot 

30from ..actions.scalar.scalarActions import ( 

31 CountAction, 

32 DivideScalar, 

33 MeanAction, 

34 MedianAction, 

35 SigmaMadAction, 

36 StdevAction, 

37 SumAction, 

38) 

39from ..actions.vector import BandSelector, CoaddPlotFlagSelector, DownselectVector, LoadVector, SnSelector 

40from ..interfaces import AnalysisTool 

41 

42 

43class CoaddInputCount(AnalysisTool): 

44 """Tract-wide metrics pertaining to how many exposures have gone into 

45 a deep coadd, per band. 

46 

47 This AnalysisTool is designed to run on an object table, which is only 

48 created for deep coadds, not template coadds. 

49 """ 

50 

51 def setDefaults(self): 

52 super().setDefaults() 

53 self.prep.selectors.flagSelector = CoaddPlotFlagSelector() 

54 # Set this to an empty list to look at the band 

55 # the plot is being made in. 

56 self.prep.selectors.flagSelector.bands = [] 

57 

58 self.prep.selectors.snSelector = SnSelector() 

59 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux" 

60 self.prep.selectors.snSelector.threshold = 5 

61 

62 self.process.buildActions.x = LoadVector() 

63 self.process.buildActions.x.vectorKey = "coord_ra" 

64 self.process.buildActions.y = LoadVector() 

65 self.process.buildActions.y.vectorKey = "coord_dec" 

66 self.process.buildActions.statMask = SnSelector() 

67 self.process.buildActions.statMask.fluxType = "{band}_psfFlux" 

68 self.process.buildActions.statMask.threshold = 5 

69 

70 self.process.buildActions.z = LoadVector() 

71 self.process.buildActions.z.vectorKey = "{band}_inputCount" 

72 

73 self.process.buildActions.patch = LoadVector() 

74 self.process.buildActions.patch.vectorKey = "patch" 

75 

76 self.process.calculateActions.median = MedianAction() 

77 self.process.calculateActions.median.vectorKey = "z" 

78 

79 self.process.calculateActions.mean = MeanAction() 

80 self.process.calculateActions.mean.vectorKey = "z" 

81 

82 self.process.calculateActions.sigmaMad = SigmaMadAction() 

83 self.process.calculateActions.sigmaMad.vectorKey = "z" 

84 

85 # SkyPlot of number of contributing exposures in coad, per tract/band: 

86 self.produce.plot = SkyPlot() 

87 self.produce.plot.plotTypes = ["any"] 

88 self.produce.plot.plotName = "{band}_inputCount" 

89 self.produce.plot.xAxisLabel = "R.A. (deg)" 

90 self.produce.plot.yAxisLabel = "Dec. (deg)" 

91 self.produce.plot.zAxisLabel = "Input Count" 

92 self.produce.plot.plotOutlines = True 

93 self.produce.plot.showExtremeOutliers = False 

94 self.produce.plot.colorbarRange = MinMax() 

95 

96 # Summary metrics for the whole coadd, per tract/band. 

97 self.produce.metric.units = {"median": "ct", "sigmaMad": "ct", "mean": "ct"} 

98 self.produce.metric.newNames = { 

99 "median": "{band}_inputCount_median", 

100 "mean": "{band}_inputCount_mean", 

101 "sigmaMad": "{band}_inputCount_sigmaMad", 

102 } 

103 

104 

105class CoaddQualityCheck(AnalysisTool): 

106 """Compute the percentage of each coadd that has a number of input 

107 exposures exceeding a threshold. 

108 

109 This AnalysisTool is designed to run on any coadd, provided a 

110 coadd_depth_table is created first (via CoaddDepthSummaryAnalysisTask). 

111 

112 For example, if exactly half of a coadd patch contains 15 overlapping 

113 constituent visits and half contains fewer, the value computed for 

114 `depth_above_threshold_12` would be 50. 

115 

116 These values come from the n_image data product, which is an image 

117 identical to the coadd but with pixel values of the number of input 

118 images instead of flux or counts. n_images are persisted during 

119 coadd assembly. 

120 """ 

121 

122 band_list = ListField( 

123 default=["u", "g", "r", "i", "z", "y"], 

124 dtype=str, 

125 doc="Bands for colors.", 

126 ) 

127 

128 threshold_list = ListField( 

129 default=[1, 2, 3, 5, 12], 

130 dtype=int, 

131 doc="The n_image pixel value thresholds, in ascending order.", 

132 ) 

133 

134 quantile_list = ListField( 

135 default=[5, 10, 25, 50, 75, 90, 95], 

136 dtype=int, 

137 doc="The percentiles at which to compute n_image values, in ascending order.", 

138 ) 

139 

140 def setDefaults(self): 

141 super().setDefaults() 

142 

143 self.process.buildActions.patch = LoadVector(vectorKey="patch") 

144 self.process.buildActions.band = LoadVector(vectorKey="band") 

145 

146 def finalize(self): 

147 for threshold in self.threshold_list: 

148 # This gives a RuntimeWarning whenever the tract doesn't have 

149 # a particular band. Need to use a band list derived from the 

150 # bands found in the "band" column of a given tract. 

151 for band in self.band_list: 

152 name = f"depth_above_threshold_{threshold}" 

153 setattr(self.process.buildActions, name, LoadVector(vectorKey=name)) 

154 setattr( 

155 self.process.filterActions, 

156 f"{name}_{band}", 

157 DownselectVector(vectorKey=name, selector=BandSelector(bands=[band])), 

158 ) 

159 setattr( 

160 self.process.calculateActions, 

161 f"{name}_{band}_median", 

162 MedianAction(vectorKey=f"{name}_{band}"), 

163 ) 

164 setattr( 

165 self.process.calculateActions, 

166 f"{name}_{band}_mean", 

167 MeanAction(vectorKey=f"{name}_{band}"), 

168 ) 

169 setattr( 

170 self.process.calculateActions, 

171 f"{name}_{band}_stdev", 

172 StdevAction(vectorKey=f"{name}_{band}"), 

173 ) 

174 

175 # The units for the quantity are dimensionless (percentage) 

176 self.produce.metric.units[f"{name}_{band}_median"] = "" 

177 self.produce.metric.units[f"{name}_{band}_mean"] = "" 

178 self.produce.metric.units[f"{name}_{band}_stdev"] = "" 

179 

180 for quantile in self.quantile_list: 

181 for band in self.band_list: 

182 name = f"depth_{quantile}_percentile" 

183 setattr(self.process.buildActions, name, LoadVector(vectorKey=name)) 

184 setattr( 

185 self.process.filterActions, 

186 f"{name}_{band}", 

187 DownselectVector(vectorKey=name, selector=BandSelector(bands=[band])), 

188 ) 

189 setattr( 

190 self.process.calculateActions, 

191 f"{name}_{band}_median", 

192 MedianAction(vectorKey=f"{name}_{band}"), 

193 ) 

194 setattr( 

195 self.process.calculateActions, 

196 f"{name}_{band}_mean", 

197 MeanAction(vectorKey=f"{name}_{band}"), 

198 ) 

199 setattr( 

200 self.process.calculateActions, 

201 f"{name}_{band}_stdev", 

202 StdevAction(vectorKey=f"{name}_{band}"), 

203 ) 

204 

205 # The units for the quantity are dimensionless (percentage) 

206 self.produce.metric.units[f"{name}_{band}_median"] = "" 

207 self.produce.metric.units[f"{name}_{band}_mean"] = "" 

208 self.produce.metric.units[f"{name}_{band}_stdev"] = "" 

209 

210 

211class CoaddQualityPlot(AnalysisTool): 

212 """Make a plot of coadd depth.""" 

213 

214 parameterizedBand: bool = False 

215 

216 def setDefaults(self): 

217 super().setDefaults() 

218 self.process.buildActions.patch = LoadVector(vectorKey="patch") 

219 self.process.buildActions.band = LoadVector(vectorKey="band") 

220 self.process.buildActions.depth = LoadVector(vectorKey="depth") 

221 self.process.buildActions.pixels = LoadVector(vectorKey="pixels") 

222 

223 self.produce.plot = CoaddDepthPlot() 

224 

225 

226class CoaddInputFraction(AnalysisTool): 

227 """Metrics quantifying the number of images that overlap a patch, 

228 the number that actually made it into the coadd, and the ratio 

229 of the two (as a fraction). 

230 """ 

231 

232 parameterizedBand: bool = False 

233 

234 def setDefaults(self): 

235 super().setDefaults() 

236 

237 # The number of entries is the number of potential raws: 

238 self.process.calculateActions.rawCount = CountAction(vectorKey="inCoadd") 

239 self.process.calculateActions.overlapCount = SumAction(vectorKey="patchOverlap") 

240 self.process.calculateActions.inVisitSummaryCount = SumAction(vectorKey="visitSummaryRecord") 

241 self.process.calculateActions.inCoaddCount = SumAction(vectorKey="inCoadd") 

242 

243 self.process.calculateActions.inVisitSummaryFraction = DivideScalar( 

244 actionA=SumAction(vectorKey="visitSummaryRecord"), 

245 actionB=CountAction(vectorKey="inCoadd"), 

246 ) 

247 

248 self.process.calculateActions.overlapFraction = DivideScalar( 

249 actionA=SumAction(vectorKey="patchOverlap"), 

250 actionB=CountAction(vectorKey="inCoadd"), 

251 ) 

252 

253 self.process.calculateActions.inCoaddFraction = DivideScalar( 

254 actionA=SumAction(vectorKey="inCoadd"), 

255 actionB=CountAction(vectorKey="inCoadd"), 

256 ) 

257 

258 self.produce.metric.units = { 

259 "rawCount": "ct", 

260 "inVisitSummaryCount": "ct", 

261 "overlapCount": "ct", 

262 "inCoaddCount": "ct", 

263 "inVisitSummaryFraction": "", 

264 "overlapFraction": "", 

265 "inCoaddFraction": "", 

266 }