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

65 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-06 04:18 -0700

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 "StellarPhotometricRepeatability", 

25 "StellarPhotometricResidualsFocalPlane", 

26) 

27 

28from lsst.pex.config import Field 

29 

30from ..actions.plot import FocalPlanePlot, HistPanel, HistPlot, HistStatsPanel 

31from ..actions.scalar.scalarActions import CountAction, FracThreshold, MedianAction 

32from ..actions.vector import ( 

33 BandSelector, 

34 CalcSn, 

35 ConvertFluxToMag, 

36 LoadVector, 

37 MultiCriteriaDownselectVector, 

38 PerGroupStatistic, 

39 ResidualWithPerGroupStatistic, 

40 SnSelector, 

41 ThresholdSelector, 

42) 

43from ..interfaces import AnalysisTool 

44 

45 

46class StellarPhotometricRepeatability(AnalysisTool): 

47 """Compute photometric repeatability from multiple measurements of a set of 

48 stars. First, a set of per-source quality criteria are applied. Second, 

49 the individual source measurements are grouped together by object index 

50 and per-group quantities are computed (e.g., a representative S/N for the 

51 group based on the median of associated per-source measurements). Third, 

52 additional per-group criteria are applied. Fourth, summary statistics are 

53 computed for the filtered groups. 

54 """ 

55 

56 fluxType = Field[str](doc="Flux type to calculate repeatability with", default="psfFlux") 

57 PA2Value = Field[float]( 

58 doc="Used to compute the percent of individual measurements that deviate by more than PA2Value" 

59 "from the mean of each measurement (PF1). Units of PA2Value are mmag.", 

60 default=15.0, 

61 ) 

62 

63 def setDefaults(self): 

64 super().setDefaults() 

65 

66 # Apply per-source selection criteria 

67 self.prep.selectors.bandSelector = BandSelector() 

68 

69 # Compute per-group quantities 

70 self.process.buildActions.perGroupSn = PerGroupStatistic() 

71 self.process.buildActions.perGroupSn.buildAction = CalcSn(fluxType=f"{self.fluxType}") 

72 self.process.buildActions.perGroupSn.func = "median" 

73 self.process.buildActions.perGroupExtendedness = PerGroupStatistic() 

74 self.process.buildActions.perGroupExtendedness.buildAction.vectorKey = "extendedness" 

75 self.process.buildActions.perGroupExtendedness.func = "median" 

76 self.process.buildActions.perGroupCount = PerGroupStatistic() 

77 self.process.buildActions.perGroupCount.buildAction.vectorKey = f"{self.fluxType}" 

78 self.process.buildActions.perGroupCount.func = "count" 

79 # Use mmag units 

80 self.process.buildActions.perGroupStdev = PerGroupStatistic() 

81 self.process.buildActions.perGroupStdev.buildAction = ConvertFluxToMag( 

82 vectorKey=f"{self.fluxType}", 

83 returnMillimags=True, 

84 ) 

85 self.process.buildActions.perGroupStdev.func = "std" 

86 

87 # Filter on per-group quantities 

88 self.process.filterActions.perGroupStdevFiltered = MultiCriteriaDownselectVector( 

89 vectorKey="perGroupStdev" 

90 ) 

91 self.process.filterActions.perGroupStdevFiltered.selectors.count = ThresholdSelector( 

92 vectorKey="perGroupCount", 

93 op="ge", 

94 threshold=3, 

95 ) 

96 self.process.filterActions.perGroupStdevFiltered.selectors.sn = ThresholdSelector( 

97 vectorKey="perGroupSn", 

98 op="ge", 

99 threshold=200, 

100 ) 

101 self.process.filterActions.perGroupStdevFiltered.selectors.extendedness = ThresholdSelector( 

102 vectorKey="perGroupExtendedness", 

103 op="le", 

104 threshold=0.5, 

105 ) 

106 

107 # Compute summary statistics on filtered groups 

108 self.process.calculateActions.photRepeatStdev = MedianAction(vectorKey="perGroupStdevFiltered") 

109 self.process.calculateActions.photRepeatOutlier = FracThreshold( 

110 vectorKey="perGroupStdevFiltered", 

111 op="ge", 

112 threshold=self.PA2Value, 

113 percent=True, 

114 ) 

115 self.process.calculateActions.photRepeatNsources = CountAction(vectorKey="perGroupStdevFiltered") 

116 

117 self.produce.plot = HistPlot() 

118 

119 self.produce.plot.panels["panel_rms"] = HistPanel() 

120 

121 self.produce.plot.panels["panel_rms"].statsPanel = HistStatsPanel() 

122 self.produce.plot.panels["panel_rms"].statsPanel.statsLabels = ["N", "PA1", "PF1 %"] 

123 self.produce.plot.panels["panel_rms"].statsPanel.stat1 = ["photRepeatNsources"] 

124 self.produce.plot.panels["panel_rms"].statsPanel.stat2 = ["photRepeatStdev"] 

125 self.produce.plot.panels["panel_rms"].statsPanel.stat3 = ["photRepeatOutlier"] 

126 

127 self.produce.plot.panels["panel_rms"].referenceValue = self.PA2Value 

128 self.produce.plot.panels["panel_rms"].label = "rms (mmag)" 

129 self.produce.plot.panels["panel_rms"].hists = dict(perGroupStdevFiltered="Filtered per group rms") 

130 

131 self.produce.metric.units = { # type: ignore 

132 "photRepeatStdev": "mmag", 

133 "photRepeatOutlier": "percent", 

134 "photRepeatNsources": "ct", 

135 } 

136 self.produce.metric.newNames = { 

137 "photRepeatStdev": "{band}_stellarPhotRepeatStdev", 

138 "photRepeatOutlier": "{band}_stellarPhotRepeatOutlierFraction", 

139 "photRepeatNsources": "{band}_ct", 

140 } 

141 

142 

143class StellarPhotometricResidualsFocalPlane(AnalysisTool): 

144 """Plot mean photometric residuals as a function of the position on the 

145 focal plane. 

146 

147 First, a set of per-source quality criteria are applied. Second, the 

148 individual source measurements are grouped together by object index 

149 and the per-group magnitude is computed. The residuals between the 

150 individual sources and these magnitudes are then used to construct a plot 

151 showing the mean residual as a function of the focal-plane position. 

152 """ 

153 

154 fluxType = Field[str](doc="Flux type to calculate repeatability with", default="psfFlux") 

155 

156 def setDefaults(self): 

157 super().setDefaults() 

158 

159 # Apply per-source selection criteria 

160 self.prep.selectors.bandSelector = BandSelector() 

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

162 self.prep.selectors.snSelector.fluxType = "psfFlux" 

163 self.prep.selectors.snSelector.threshold = 50 

164 

165 self.process.buildActions.z = ResidualWithPerGroupStatistic() 

166 self.process.buildActions.z.buildAction = ConvertFluxToMag( 

167 vectorKey=f"{self.fluxType}", 

168 returnMillimags=True, 

169 ) 

170 self.process.buildActions.z.func = "median" 

171 

172 self.process.buildActions.x = LoadVector(vectorKey="x") 

173 self.process.buildActions.y = LoadVector(vectorKey="y") 

174 

175 self.process.buildActions.detector = LoadVector(vectorKey="detector") 

176 

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

178 self.process.buildActions.statMask.threshold = 200 

179 self.process.buildActions.statMask.fluxType = "psfFlux" 

180 

181 self.produce.plot = FocalPlanePlot() 

182 self.produce.plot.xAxisLabel = "x (focal plane)" 

183 self.produce.plot.yAxisLabel = "y (focal plane)" 

184 self.produce.plot.zAxisLabel = "Mag - Mag$_{mean}$ (mmag)"