Coverage for python/lsst/analysis/tools/atools/astrometricRepeatability.py: 25%

105 statements  

« prev     ^ index     » next       coverage.py v7.4.3, created at 2024-02-24 11:17 +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 "AstrometricRelativeRepeatability", 

25 "StellarAstrometricResidualsRAFocalPlanePlot", 

26 "StellarAstrometricResidualsDecFocalPlanePlot", 

27 "StellarAstrometricResidualStdDevRAFocalPlanePlot", 

28 "StellarAstrometricResidualStdDevDecFocalPlanePlot", 

29 "StellarAstrometricResidualsRASkyPlot", 

30 "StellarAstrometricResidualsDecSkyPlot", 

31) 

32 

33from lsst.pex.config import Field 

34 

35from ..actions.keyedData import CalcRelativeDistances 

36from ..actions.plot import FocalPlanePlot, HistPanel, HistPlot, SkyPlot 

37from ..actions.vector import ( 

38 BandSelector, 

39 ConvertFluxToMag, 

40 ConvertUnits, 

41 DownselectVector, 

42 LoadVector, 

43 RAcosDec, 

44 RangeSelector, 

45 ResidualWithPerGroupStatistic, 

46 SnSelector, 

47 ThresholdSelector, 

48) 

49from ..interfaces import AnalysisTool 

50 

51 

52class StellarAstrometricResidualsBase(AnalysisTool): 

53 """Plot mean astrometric residuals. 

54 

55 The individual source measurements are grouped together by object index 

56 and the per-group centroid is computed. The residuals between the 

57 individual sources and these centroids are then used to construct a plot 

58 showing the mean residual as a function of the focal-plane or sky position. 

59 """ 

60 

61 def setDefaults(self): 

62 super().setDefaults() 

63 

64 # Apply per-source selection criteria 

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

66 

67 self.process.buildActions.mags = ConvertFluxToMag(vectorKey="psfFlux") 

68 self.process.buildActions.residual = ConvertUnits() 

69 self.process.buildActions.residual.inUnit = "degree" 

70 self.process.buildActions.residual.outUnit = "marcsec" 

71 

72 self.process.buildActions.residual.buildAction = ResidualWithPerGroupStatistic() 

73 

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

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

76 

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

78 

79 self.process.filterActions.x = DownselectVector(vectorKey="x") 

80 self.process.filterActions.x.selector = ThresholdSelector( 

81 vectorKey="mags", 

82 op="le", 

83 threshold=24, 

84 ) 

85 self.process.filterActions.y = DownselectVector( 

86 vectorKey="y", selector=self.process.filterActions.x.selector 

87 ) 

88 self.process.filterActions.z = DownselectVector( 

89 vectorKey="residual", selector=self.process.filterActions.x.selector 

90 ) 

91 self.process.filterActions.detector = DownselectVector( 

92 vectorKey="detector", selector=self.process.filterActions.x.selector 

93 ) 

94 

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

96 self.process.buildActions.statMask.threshold = 0 

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

98 

99 

100class StellarAstrometricResidualsRASkyPlot(StellarAstrometricResidualsBase): 

101 """Plot mean astrometric residuals in RA as a function of the position in 

102 RA and Dec. 

103 """ 

104 

105 def setDefaults(self): 

106 super().setDefaults() 

107 

108 # Compute per-group quantities 

109 self.process.buildActions.residual.buildAction.buildAction = RAcosDec() 

110 self.process.buildActions.x = LoadVector(vectorKey="coord_ra") 

111 self.process.buildActions.y = LoadVector(vectorKey="coord_dec") 

112 

113 self.produce = SkyPlot() 

114 

115 self.produce.plotTypes = ["any"] 

116 self.produce.plotName = "ra_residuals" 

117 self.produce.xAxisLabel = "R.A. (degrees)" 

118 self.produce.yAxisLabel = "Dec. (degrees)" 

119 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$" 

120 

121 

122class StellarAstrometricResidualsDecSkyPlot(StellarAstrometricResidualsBase): 

123 """Plot mean astrometric residuals in RA as a function of the position in 

124 RA and Dec. 

125 """ 

126 

127 def setDefaults(self): 

128 super().setDefaults() 

129 

130 # Compute per-group quantities 

131 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec" 

132 self.process.buildActions.x = LoadVector(vectorKey="coord_ra") 

133 self.process.buildActions.y = LoadVector(vectorKey="coord_dec") 

134 

135 self.produce = SkyPlot() 

136 

137 self.produce.plotTypes = ["any"] 

138 self.produce.plotName = "ra_residuals" 

139 self.produce.xAxisLabel = "R.A. (degrees)" 

140 self.produce.yAxisLabel = "Dec. (degrees)" 

141 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$" 

142 

143 

144class StellarAstrometricResidualsRAFocalPlanePlot(StellarAstrometricResidualsBase): 

145 """Plot mean astrometric residuals in RA as a function of the focal plane 

146 position. 

147 """ 

148 

149 def setDefaults(self): 

150 super().setDefaults() 

151 

152 # Compute per-group quantities 

153 self.process.buildActions.residual.buildAction.buildAction = RAcosDec() 

154 

155 self.produce = FocalPlanePlot() 

156 self.produce.zAxisLabel = "RAcos(Dec) - RAcos(Dec)$_{mean}$ (mArcsec)" 

157 

158 

159class StellarAstrometricResidualStdDevRAFocalPlanePlot(StellarAstrometricResidualsBase): 

160 """Plot mean astrometric residuals in RA as a function of the focal plane 

161 position. 

162 """ 

163 

164 def setDefaults(self): 

165 super().setDefaults() 

166 

167 # Compute per-group quantities 

168 self.process.buildActions.residual.buildAction.buildAction = RAcosDec() 

169 

170 self.produce = FocalPlanePlot() 

171 self.produce.statistic = "std" 

172 self.produce.zAxisLabel = "Std(RAcos(Dec) - RAcos(Dec)$_{mean}$) (mArcsec)" 

173 

174 

175class StellarAstrometricResidualsDecFocalPlanePlot(StellarAstrometricResidualsBase): 

176 """Plot mean astrometric residuals in RA as a function of the focal plane 

177 position. 

178 """ 

179 

180 def setDefaults(self): 

181 super().setDefaults() 

182 

183 # Compute per-group quantities 

184 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec" 

185 

186 self.produce = FocalPlanePlot() 

187 self.produce.zAxisLabel = "Dec - Dec$_{mean}$ (mArcsec)" 

188 

189 

190class StellarAstrometricResidualStdDevDecFocalPlanePlot(StellarAstrometricResidualsBase): 

191 """Plot mean astrometric residuals in RA as a function of the focal plane 

192 position. 

193 """ 

194 

195 def setDefaults(self): 

196 super().setDefaults() 

197 

198 # Compute per-group quantities 

199 self.process.buildActions.residual.buildAction.buildAction.vectorKey = "coord_dec" 

200 

201 self.produce = FocalPlanePlot() 

202 self.produce.statistic = "std" 

203 self.produce.zAxisLabel = "Std(Dec - Dec$_{mean}$) (mArcsec)" 

204 

205 

206class AstrometricRelativeRepeatability(AnalysisTool): 

207 """Calculate the AMx, ADx, AFx metrics and make histograms showing the data 

208 used to compute the metrics. 

209 """ 

210 

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

212 xValue = Field[int](doc="Metric suffix corresponding to annulus size (1, 2, or 3)", default=1) 

213 

214 def setDefaults(self): 

215 super().setDefaults() 

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

217 # Following what was done in faro, only sources with S/N between 50 

218 # and 50000 are included. The other filtering that was done in faro 

219 # is now covered by only including sources from isolated_star_sources. 

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

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

222 self.prep.selectors.snSelector.maxSN = 50000 

223 

224 # Select only sources with magnitude between 17 and 21.5 

225 self.process.filterActions.coord_ra = DownselectVector(vectorKey="coord_ra") 

226 self.process.filterActions.coord_ra.selector = RangeSelector( 

227 vectorKey="mags", minimum=17, maximum=21.5 

228 ) 

229 self.process.filterActions.coord_dec = DownselectVector( 

230 vectorKey="coord_dec", selector=self.process.filterActions.coord_ra.selector 

231 ) 

232 self.process.filterActions.obj_index = DownselectVector( 

233 vectorKey="obj_index", selector=self.process.filterActions.coord_ra.selector 

234 ) 

235 self.process.filterActions.visit = DownselectVector( 

236 vectorKey="visit", selector=self.process.filterActions.coord_ra.selector 

237 ) 

238 

239 self.process.calculateActions.rms = CalcRelativeDistances() 

240 

241 self.produce.metric.units = { 

242 "AMx": "mas", 

243 "AFx": "percent", 

244 "ADx": "mas", 

245 } 

246 

247 self.produce.plot = HistPlot() 

248 

249 self.produce.plot.panels["panel_sep"] = HistPanel() 

250 self.produce.plot.panels["panel_sep"].hists = dict(separationResiduals="Source separations") 

251 self.produce.plot.panels["panel_sep"].label = "Separation Distances (marcsec)" 

252 

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

254 self.produce.plot.panels["panel_rms"].hists = dict(rmsDistances="Object RMS") 

255 self.produce.plot.panels["panel_rms"].label = "Per-Object RMS (marcsec)" 

256 # TODO: DM-39163 add reference lines for ADx, AMx, and AFx. 

257 

258 def finalize(self): 

259 super().finalize() 

260 self.prep.selectors.snSelector.fluxType = self.fluxType 

261 self.process.buildActions.mags = ConvertFluxToMag(vectorKey=self.fluxType) 

262 

263 self.produce.metric.newNames = { 

264 "AMx": f"{{band}}_AM{self.xValue}", 

265 "AFx": f"{{band}}_AF{self.xValue}", 

266 "ADx": f"{{band}}_AD{self.xValue}", 

267 }