Coverage for python/lsst/analysis/tools/analysisPlots/analysisPlots.py: 29%

218 statements  

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

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

25 "E2DiffScatterPlot", 

26 "ShapeSizeFractionalDiffScatterPlot", 

27 "Ap12PsfSkyPlot", 

28 "WPerpPSFPlot", 

29 "WPerpCModelPlot", 

30 "XPerpPSFPlot", 

31 "XPerpCModelPlot", 

32 "YPerpPSFPlot", 

33 "YPerpCModelPlot", 

34 "TargetRefCatDeltaRAScatterPlot", 

35 "TargetRefCatDeltaDecScatterPlot", 

36 "TargetRefCatDeltaRASkyPlot", 

37 "TargetRefCatDeltaDecSkyPlot", 

38 "SourcesPlot", 

39 "RhoStatisticsPlot", 

40) 

41 

42from lsst.pex.config import Field 

43 

44from ..actions.plot.barPlots import BarPanel, BarPlot 

45from ..actions.plot.colorColorFitPlot import ColorColorFitPlot 

46from ..actions.plot.histPlot import HistPanel, HistPlot 

47from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlotAction 

48from ..actions.plot.scatterplotWithTwoHists import ScatterPlotStatsAction, ScatterPlotWithTwoHists 

49from ..actions.plot.skyPlot import SkyPlot 

50from ..actions.scalar import CountAction 

51from ..actions.vector import ( 

52 AstromDiff, 

53 CoaddPlotFlagSelector, 

54 DownselectVector, 

55 ExtinctionCorrectedMagDiff, 

56 LoadVector, 

57 MagColumnNanoJansky, 

58 SnSelector, 

59 StarSelector, 

60 VectorSelector, 

61) 

62from ..analysisParts.baseFluxRatio import BasePsfApRatio 

63from ..analysisParts.baseSources import BaseSources 

64from ..analysisParts.genericPrep import CoaddPrep, VisitPrep 

65from ..analysisParts.rhoStatistics import RhoStatisticsMixin 

66from ..analysisParts.shapeSizeFractional import BasePsfResidualMixin 

67from ..analysisParts.stellarLocus import WPerpCModel, WPerpPSF, XPerpCModel, XPerpPSF, YPerpCModel, YPerpPSF 

68from ..interfaces import AnalysisPlot 

69 

70 

71class BasePsfResidualScatterPlot(AnalysisPlot, BasePsfResidualMixin): 

72 """Base class for scatter plots of PSF residuals. 

73 

74 This is shared by size and ellipticity plots. 

75 """ 

76 

77 def setDefaults(self): 

78 super().setDefaults() 

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

80 self.prep.selectors.snSelector = SnSelector(fluxType="{band}_psfFlux", threshold=100) 

81 

82 self.process.buildActions.patchWhole = LoadVector() 

83 self.process.buildActions.patchWhole.vectorKey = "patch" 

84 

85 self.process.buildActions.mags = MagColumnNanoJansky(vectorKey="{band}_psfFlux") 

86 # pre-compute a stellar selector mask so it can be used in the filter 

87 # actions while only being computed once, alternatively the stellar 

88 # selector could be calculated and applied twice in the filter stage 

89 self.process.buildActions.starSelector = StarSelector() 

90 

91 self.process.filterActions.xStars = DownselectVector( 

92 vectorKey="mags", selector=VectorSelector(vectorKey="starSelector") 

93 ) 

94 # downselect the psfFlux as well 

95 self.process.filterActions.psfFlux = DownselectVector( 

96 vectorKey="{band}_psfFlux", selector=VectorSelector(vectorKey="starSelector") 

97 ) 

98 self.process.filterActions.psfFluxErr = DownselectVector( 

99 vectorKey="{band}_psfFluxErr", selector=VectorSelector(vectorKey="starSelector") 

100 ) 

101 

102 self.process.filterActions.patch = DownselectVector( 

103 vectorKey="patchWhole", selector=VectorSelector(vectorKey="starSelector") 

104 ) 

105 

106 self.process.calculateActions.stars = ScatterPlotStatsAction( 

107 vectorKey="yStars", 

108 ) 

109 # use the downselected psfFlux 

110 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux" 

111 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux" 

112 self.process.calculateActions.stars.fluxType = "psfFlux" 

113 

114 self.produce = ScatterPlotWithTwoHists() 

115 

116 self.produce.plotTypes = ["stars"] 

117 self.produce.xAxisLabel = "PSF Magnitude (mag)" 

118 self.produce.magLabel = "PSF Magnitude (mag)" 

119 self.produce.addSummaryPlot = True 

120 

121 

122class ShapeSizeFractionalDiffScatterPlot(BasePsfResidualScatterPlot): 

123 def setDefaults(self): 

124 super().setDefaults() 

125 self.process.filterActions.yStars = DownselectVector( 

126 vectorKey="fracDiff", selector=VectorSelector(vectorKey="starSelector") 

127 ) 

128 self.produce.yAxisLabel = "Fractional size residuals (S/S_PSF - 1)" 

129 

130 

131class E1DiffScatterPlot(BasePsfResidualScatterPlot): 

132 def setDefaults(self): 

133 super().setDefaults() 

134 self.process.filterActions.yStars = DownselectVector( 

135 vectorKey="e1Diff", selector=VectorSelector(vectorKey="starSelector") 

136 ) 

137 self.produce.yAxisLabel = "Ellipticty residuals (e1 - e1_PSF)" 

138 

139 

140class E2DiffScatterPlot(BasePsfResidualScatterPlot): 

141 def setDefaults(self): 

142 super().setDefaults() 

143 self.process.filterActions.yStars = DownselectVector( 

144 vectorKey="e2Diff", selector=VectorSelector(vectorKey="starSelector") 

145 ) 

146 self.produce.yAxisLabel = "Ellipticty residuals (e2 - e2_PSF)" 

147 

148 

149class RhoStatisticsPlot(AnalysisPlot, RhoStatisticsMixin): 

150 def setDefaults(self): 

151 super().setDefaults() 

152 self.produce = RhoStatisticsPlotAction() 

153 

154 

155class WPerpPSFPlot(WPerpPSF, AnalysisPlot): 

156 def setDefaults(self): 

157 super().setDefaults() 

158 

159 self.produce = ColorColorFitPlot() 

160 self.produce.xAxisLabel = "g - r (PSF) [mags]" 

161 self.produce.yAxisLabel = "r - i (PSF) [mags]" 

162 self.produce.magLabel = "PSF Mag" 

163 self.produce.plotName = "wPerp_psfFlux" 

164 

165 

166class WPerpCModelPlot(WPerpCModel, AnalysisPlot): 

167 def setDefaults(self): 

168 super().setDefaults() 

169 

170 self.produce = ColorColorFitPlot() 

171 self.produce.xAxisLabel = "g - r (CModel) [mags]" 

172 self.produce.yAxisLabel = "r - i (CModel) [mags]" 

173 self.produce.magLabel = "CModel Mag" 

174 self.produce.plotName = "wPerp_cmodelFlux" 

175 

176 

177class XPerpPSFPlot(XPerpPSF, AnalysisPlot): 

178 def setDefaults(self): 

179 super().setDefaults() 

180 

181 self.produce = ColorColorFitPlot() 

182 self.produce.xAxisLabel = "g - r (PSF) [mags]" 

183 self.produce.yAxisLabel = "r - i (PSF) [mags]" 

184 self.produce.magLabel = "PSF Mag" 

185 self.produce.plotName = "xPerp_psfFlux" 

186 

187 

188class XPerpCModelPlot(XPerpCModel, AnalysisPlot): 

189 def setDefaults(self): 

190 super().setDefaults() 

191 

192 self.produce = ColorColorFitPlot() 

193 self.produce.xAxisLabel = "g - r (CModel) [mags]" 

194 self.produce.yAxisLabel = "r - i (CModel) [mags]" 

195 self.produce.magLabel = "CModel Mag" 

196 self.produce.plotName = "xPerp_cmodelFlux" 

197 

198 

199class YPerpPSFPlot(YPerpPSF, AnalysisPlot): 

200 def setDefaults(self): 

201 super().setDefaults() 

202 

203 self.produce = ColorColorFitPlot() 

204 self.produce.xAxisLabel = "r - i (PSF) [mags]" 

205 self.produce.yAxisLabel = "i - z (PSF) [mags]" 

206 self.produce.magLabel = "PSF Mag" 

207 self.produce.plotName = "yPerp_psfFlux" 

208 

209 

210class YPerpCModelPlot(YPerpCModel, AnalysisPlot): 

211 def setDefaults(self): 

212 super().setDefaults() 

213 

214 self.produce = ColorColorFitPlot() 

215 self.produce.xAxisLabel = "r - i (CModel) [mags]" 

216 self.produce.yAxisLabel = "i - z (CModel) [mags]" 

217 self.produce.magLabel = "CModel Mag" 

218 self.produce.plotName = "yPerp_cmodelFlux" 

219 

220 

221class Ap12PsfSkyPlot(AnalysisPlot): 

222 def setDefaults(self): 

223 super().setDefaults() 

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

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

226 # the plot is being made in. 

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

228 

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

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

231 self.prep.selectors.snSelector.threshold = 300 

232 

233 self.prep.selectors.starSelector = StarSelector() 

234 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness" 

235 

236 # TODO: Can we make these defaults somewhere? 

237 self.process.buildActions.xStars = LoadVector() 

238 self.process.buildActions.xStars.vectorKey = "coord_ra" 

239 self.process.buildActions.yStars = LoadVector() 

240 self.process.buildActions.yStars.vectorKey = "coord_dec" 

241 self.process.buildActions.starStatMask = SnSelector() 

242 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux" 

243 

244 self.process.buildActions.zStars = ExtinctionCorrectedMagDiff() 

245 self.process.buildActions.zStars.magDiff.col1 = "{band}_ap12Flux" 

246 self.process.buildActions.zStars.magDiff.col2 = "{band}_psfFlux" 

247 

248 self.produce = SkyPlot() 

249 self.produce.plotTypes = ["stars"] 

250 self.produce.plotName = "ap12-psf_{band}" 

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

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

253 self.produce.zAxisLabel = "Ap 12 - PSF [mag]" 

254 self.produce.plotOutlines = False 

255 

256 

257class TargetRefCatDelta(AnalysisPlot): 

258 """Plot the difference in milliseconds between a target catalog and a 

259 reference catalog for the coordinate set in `setDefaults`. 

260 """ 

261 

262 parameterizedBand = Field[bool]( 

263 doc="Does this AnalysisTool support band as a name parameter", default=True 

264 ) 

265 

266 def coaddContext(self) -> None: 

267 self.prep = CoaddPrep() 

268 self.process.buildActions.starSelector.vectorKey = "{band}_extendedness" 

269 self.process.buildActions.mags = MagColumnNanoJansky(vectorKey="{band}_psfFlux") 

270 self.process.filterActions.psfFlux = DownselectVector( 

271 vectorKey="{band}_psfFlux", selector=VectorSelector(vectorKey="starSelector") 

272 ) 

273 self.process.filterActions.psfFluxErr = DownselectVector( 

274 vectorKey="{band}_psfFluxErr", selector=VectorSelector(vectorKey="starSelector") 

275 ) 

276 

277 def visitContext(self) -> None: 

278 self.parameterizedBand = False 

279 self.prep = VisitPrep() 

280 self.process.buildActions.starSelector.vectorKey = "extendedness" 

281 self.process.buildActions.mags = MagColumnNanoJansky(vectorKey="psfFlux") 

282 self.process.filterActions.psfFlux = DownselectVector( 

283 vectorKey="psfFlux", selector=VectorSelector(vectorKey="starSelector") 

284 ) 

285 self.process.filterActions.psfFluxErr = DownselectVector( 

286 vectorKey="psfFluxErr", selector=VectorSelector(vectorKey="starSelector") 

287 ) 

288 

289 def setDefaults(self, coordinate): 

290 super().setDefaults() 

291 

292 self.process.buildActions.starSelector = StarSelector() 

293 coordStr = coordinate.lower() 

294 self.process.buildActions.astromDiff = AstromDiff( 

295 col1=f"coord_{coordStr}_target", col2=f"coord_{coordStr}_ref" 

296 ) 

297 

298 self.process.filterActions.xStars = DownselectVector( 

299 vectorKey="mags", selector=VectorSelector(vectorKey="starSelector") 

300 ) 

301 self.process.filterActions.yStars = DownselectVector( 

302 vectorKey="astromDiff", selector=VectorSelector(vectorKey="starSelector") 

303 ) 

304 

305 self.process.calculateActions.stars = ScatterPlotStatsAction(vectorKey="yStars") 

306 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux" 

307 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux" 

308 self.process.calculateActions.stars.fluxType = "psfFlux" 

309 

310 self.produce = ScatterPlotWithTwoHists() 

311 

312 self.produce.plotTypes = ["stars"] 

313 self.produce.xAxisLabel = "PSF Magnitude (mag)" 

314 self.produce.yAxisLabel = f"${coordinate}_{{target}} - {coordinate}_{{ref}}$ (marcsec)" 

315 self.produce.magLabel = "PSF Magnitude (mag)" 

316 

317 

318class TargetRefCatDeltaRAScatterPlot(TargetRefCatDelta): 

319 """Plot the difference in milliseconds between the RA of a target catalog 

320 and a reference catalog 

321 """ 

322 

323 def setDefaults(self): 

324 super().setDefaults(coordinate="RA") 

325 

326 

327class TargetRefCatDeltaDecScatterPlot(TargetRefCatDelta): 

328 """Plot the difference in milliseconds between the Dec of a target catalog 

329 and a reference catalog 

330 """ 

331 

332 def setDefaults(self): 

333 super().setDefaults(coordinate="Dec") 

334 

335 

336class TargetRefCatDeltaSkyPlot(AnalysisPlot): 

337 """Base class for plotting the RA/Dec distribution of stars, with the 

338 difference between the RA or Dec of the target and reference catalog as 

339 the color. 

340 """ 

341 

342 parameterizedBand = Field[bool]( 

343 doc="Does this AnalysisTool support band as a name parameter", default=True 

344 ) 

345 

346 def coaddContext(self) -> None: 

347 self.prep = CoaddPrep() 

348 

349 self.process.buildActions.starStatMask = SnSelector() 

350 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux" 

351 

352 def visitContext(self) -> None: 

353 self.parameterizedBand = False 

354 self.prep = VisitPrep() 

355 

356 self.process.buildActions.starStatMask = SnSelector() 

357 self.process.buildActions.starStatMask.fluxType = "psfFlux" 

358 

359 def setDefaults(self, coordinate): 

360 super().setDefaults() 

361 

362 coordStr = coordinate.lower() 

363 self.process.buildActions.zStars = AstromDiff( 

364 col1=f"coord_{coordStr}_target", col2=f"coord_{coordStr}_ref" 

365 ) 

366 self.process.buildActions.xStars = LoadVector() 

367 self.process.buildActions.xStars.vectorKey = "coord_ra_target" 

368 self.process.buildActions.yStars = LoadVector() 

369 self.process.buildActions.yStars.vectorKey = "coord_dec_target" 

370 

371 self.produce = SkyPlot() 

372 self.produce.plotTypes = ["stars"] 

373 self.produce.plotName = f"astromDiffSky_{coordinate}" 

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

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

376 self.produce.zAxisLabel = f"${coordinate}_{{target}} - {coordinate}_{{ref}}$ (marcsec)" 

377 self.produce.plotOutlines = False 

378 

379 

380class TargetRefCatDeltaRASkyPlot(TargetRefCatDeltaSkyPlot): 

381 """Plot the difference in milliseconds between the RA of a target catalog 

382 and a reference catalog as a function of RA and Dec. 

383 """ 

384 

385 def setDefaults(self): 

386 super().setDefaults(coordinate="RA") 

387 

388 

389class TargetRefCatDeltaDecSkyPlot(TargetRefCatDeltaSkyPlot): 

390 """Plot the difference in milliseconds between the Dec of a target catalog 

391 and a reference catalog as a function of RA and Dec. 

392 """ 

393 

394 def setDefaults(self): 

395 super().setDefaults(coordinate="Dec") 

396 

397 

398class FluxRatioPlot(AnalysisPlot, BasePsfApRatio): 

399 """Plot a histogram of the PSF and AP flux ratios of sources.""" 

400 

401 def setDefaults(self): 

402 super().setDefaults() 

403 

404 self.produce = HistPlot() 

405 

406 self.produce.panels["panel_flux"] = HistPanel() 

407 self.produce.panels["panel_flux"].label = "Psf/Ap Ratio" 

408 self.produce.panels["panel_flux"].hists = dict(fluxRatioMetric="Ratio") 

409 

410 

411class SourcesPlot(AnalysisPlot, BaseSources): 

412 """Plot a histogram of the associated and unassociated sources.""" 

413 

414 def setDefaults(self, **kwargs): 

415 super().setDefaults() 

416 

417 self.process.calculateActions.associatedCount = CountAction(vectorKey="associatedVector") 

418 self.process.calculateActions.unassociatedCount = CountAction(vectorKey="unassociatedVector") 

419 

420 self.produce = BarPlot() 

421 self.produce.panels["panel_source"] = BarPanel() 

422 self.produce.panels["panel_source"].label = "N Assoc and Unassoc Sources" 

423 self.produce.panels["panel_source"].bars = dict( 

424 associatedVector="Associated Sources", 

425 unassociatedVector="Unassociated Sources", 

426 )