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

364 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-13 10:07 +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 "TargetRefCatDeltaRAScatterPlot", 

25 "TargetRefCatDeltaDecScatterPlot", 

26 "TargetRefCatDeltaRASkyPlot", 

27 "TargetRefCatDeltaDecSkyPlot", 

28 "TargetRefCatDeltaScatterPhotom", 

29 "TargetRefCatDeltaScatterAstrom", 

30 "TargetRefCatDeltaSkyPlotPhotom", 

31 "TargetRefCatDeltaPsfScatterPlot", 

32 "TargetRefCatDeltaCModelScatterPlot", 

33 "TargetRefCatDeltaCModelSkyPlot", 

34 "TargetRefCatDeltaPsfSkyPlot", 

35 "TargetRefCatDeltaRASkyVisitPlot", 

36 "TargetRefCatDeltaAp09ScatterVisitPlot", 

37 "TargetRefCatDeltaPsfScatterVisitPlot", 

38 "TargetRefCatDeltaAp09SkyVisitPlot", 

39 "TargetRefCatDeltaPsfSkyVisitPlot", 

40 "TargetRefCatDeltaDecSkyVisitPlot", 

41 "TargetRefCatDeltaRAScatterVisitPlot", 

42 "TargetRefCatDeltaDecScatterVisitPlot", 

43 "TargetRefCatDeltaMetrics", 

44 "TargetRefCatDeltaColorMetrics", 

45 "TargetRefCatDeltaPhotomMetrics", 

46) 

47 

48from lsst.pex.config import Field 

49 

50from ..actions.plot import HistPanel, HistPlot, HistStatsPanel 

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

52from ..actions.plot.skyPlot import SkyPlot 

53from ..actions.scalar.scalarActions import ( 

54 CountAction, 

55 FracThreshold, 

56 MeanAction, 

57 MedianAction, 

58 MedianGradientAction, 

59 RmsAction, 

60 SigmaMadAction, 

61 StdevAction, 

62) 

63from ..actions.vector import ( 

64 AngularSeparation, 

65 CoaddPlotFlagSelector, 

66 ConvertFluxToMag, 

67 ConvertUnits, 

68 DownselectVector, 

69 LoadVector, 

70 MagDiff, 

71 RAcosDec, 

72 RangeSelector, 

73 SnSelector, 

74 StarSelector, 

75 SubtractVector, 

76 VisitPlotFlagSelector, 

77) 

78from ..contexts import CoaddContext, RefMatchContext, VisitContext 

79from ..interfaces import AnalysisTool 

80 

81 

82class TargetRefCatDelta(AnalysisTool): 

83 """Plot the difference between a target catalog and a 

84 reference catalog for the quantity set in `setDefaults`. 

85 """ 

86 

87 parameterizedBand = Field[bool]( 

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

89 ) 

90 

91 def coaddContext(self) -> None: 

92 """Apply coadd options for the ref cat plots. 

93 Applies the coadd plot flag selector and sets 

94 flux types. 

95 """ 

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

97 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target" 

98 self.prep.selectors.snSelector.threshold = 200 

99 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target" 

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

101 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target" 

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

103 self.process.buildActions.patch.vectorKey = "patch_target" 

104 

105 def visitContext(self) -> None: 

106 """Apply visit options for the ref cat plots. 

107 Applies the visit plot flag selector and sets 

108 the flux types. 

109 """ 

110 self.parameterizedBand = False 

111 self.prep.selectors.flagSelector = VisitPlotFlagSelector() 

112 self.prep.selectors.starSelector.vectorKey = "extendedness_target" 

113 self.prep.selectors.snSelector.fluxType = "psfFlux_target" 

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

115 

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

117 self.process.buildActions.starStatMask.fluxType = "psfFlux_target" 

118 self.process.buildActions.starStatMask.threshold = 200 

119 

120 def setDefaults(self): 

121 super().setDefaults() 

122 

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

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

125 

126 

127class TargetRefCatDeltaScatterAstrom(TargetRefCatDelta): 

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

129 reference catalog for the coordinate set in `setDefaults`. Plot it on 

130 a scatter plot. 

131 """ 

132 

133 def setDefaults(self): 

134 super().setDefaults() 

135 

136 self.process.buildActions.yStars = ConvertUnits( 

137 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

138 ) 

139 self.process.buildActions.xStars = ConvertFluxToMag() 

140 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target" 

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

142 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target" 

143 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target" 

144 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target" 

145 

146 # Calculate median, std, sigmaMad, RMS, and number counts: 

147 self.process.calculateActions.ref_offset = MedianAction(vectorKey="yStars") 

148 self.process.calculateActions.ref_offset_mean = MeanAction(vectorKey="yStars") 

149 self.process.calculateActions.ref_offset_stdev = StdevAction(vectorKey="yStars") 

150 self.process.calculateActions.ref_offset_sigmaMad = SigmaMadAction(vectorKey="yStars") 

151 self.process.calculateActions.ref_offset_rms = RmsAction(vectorKey="yStars") 

152 self.process.calculateActions.ref_offset_nstars = CountAction(vectorKey="yStars") 

153 self.process.calculateActions.ref_offset_gradient = MedianGradientAction( 

154 xsVectorKey="xStars", ysVectorKey="yStars" 

155 ) 

156 

157 self.produce.plot = ScatterPlotWithTwoHists() 

158 self.produce.plot.plotTypes = ["stars"] 

159 self.produce.plot.magLabel = "PSF Magnitude (mag$_{{AB}}$)" 

160 self.produce.plot.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)" 

161 self.applyContext(CoaddContext) 

162 self.applyContext(RefMatchContext) 

163 

164 

165class TargetRefCatDeltaScatterAstromVisit(TargetRefCatDelta): 

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

167 reference catalog for the coordinate set in `setDefaults`. Plot it on 

168 a scatter plot. 

169 """ 

170 

171 def setDefaults(self): 

172 super().setDefaults() 

173 

174 self.process.buildActions.yStars = ConvertUnits( 

175 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

176 ) 

177 self.process.buildActions.xStars = ConvertFluxToMag() 

178 self.process.buildActions.xStars.vectorKey = "psfFlux_target" 

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

180 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target" 

181 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target" 

182 self.process.calculateActions.stars.fluxType = "psfFlux_target" 

183 

184 self.produce = ScatterPlotWithTwoHists() 

185 self.produce.addSummaryPlot = False 

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

187 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)" 

188 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)" 

189 self.applyContext(VisitContext) 

190 self.applyContext(RefMatchContext) 

191 

192 

193class TargetRefCatDeltaScatterPhotom(TargetRefCatDelta): 

194 """Plot the difference in millimags between a target catalog and a 

195 reference catalog for the flux type set in `setDefaults`. 

196 """ 

197 

198 def setDefaults(self): 

199 super().setDefaults() 

200 

201 self.process.buildActions.yStars = MagDiff() 

202 self.process.buildActions.yStars.col2 = "{band}_mag_ref" 

203 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)" 

204 

205 self.process.buildActions.xStars = ConvertFluxToMag() 

206 self.process.buildActions.xStars.vectorKey = "{band}_psfFlux_target" 

207 

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

209 self.process.calculateActions.stars.lowSNSelector.fluxType = "{band}_psfFlux_target" 

210 self.process.calculateActions.stars.highSNSelector.fluxType = "{band}_psfFlux_target" 

211 self.process.calculateActions.stars.fluxType = "{band}_psfFlux_target" 

212 

213 self.produce = ScatterPlotWithTwoHists() 

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

215 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)" 

216 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)" 

217 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)" 

218 self.applyContext(CoaddContext) 

219 self.applyContext(RefMatchContext) 

220 

221 

222class TargetRefCatDeltaScatterPhotomVisit(TargetRefCatDelta): 

223 """Plot the difference in millimags between a target catalog and a 

224 reference catalog for the flux type set in `setDefaults`. 

225 """ 

226 

227 def setDefaults(self): 

228 super().setDefaults() 

229 

230 self.process.buildActions.yStars = MagDiff() 

231 self.process.buildActions.yStars.col2 = "mag_ref" 

232 self.process.buildActions.yStars.fluxUnits2 = "mag(AB)" 

233 

234 self.process.buildActions.xStars = ConvertFluxToMag() 

235 self.process.buildActions.xStars.vectorKey = "psfFlux_target" 

236 

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

238 self.process.calculateActions.stars.lowSNSelector.fluxType = "psfFlux_target" 

239 self.process.calculateActions.stars.highSNSelector.fluxType = "psfFlux_target" 

240 self.process.calculateActions.stars.fluxType = "psfFlux_target" 

241 

242 self.produce = ScatterPlotWithTwoHists() 

243 self.produce.addSummaryPlot = False 

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

245 self.produce.magLabel = "PSF Magnitude (mag$_{{AB}}$)" 

246 self.produce.xAxisLabel = "PSF Magnitude (mag$_{{AB}}$)" 

247 self.produce.yAxisLabel = "Output Mag - Ref Mag (mmag)" 

248 self.applyContext(VisitContext) 

249 self.applyContext(RefMatchContext) 

250 

251 

252class TargetRefCatDeltaPsfScatterPlot(TargetRefCatDeltaScatterPhotom): 

253 """Plot the difference in millimags between the PSF flux 

254 of a target catalog and a reference catalog 

255 """ 

256 

257 def setDefaults(self): 

258 super().setDefaults() 

259 

260 self.process.buildActions.yStars.col1 = "{band}_psfFlux_target" 

261 

262 

263class TargetRefCatDeltaCModelScatterPlot(TargetRefCatDeltaScatterPhotom): 

264 """Plot the difference in millimags between the CModel flux 

265 of a target catalog and a reference catalog. 

266 """ 

267 

268 def setDefaults(self): 

269 super().setDefaults() 

270 

271 self.process.buildActions.yStars.col1 = "{band}_cModelFlux_target" 

272 

273 

274class TargetRefCatDeltaPsfScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit): 

275 """Plot the difference in millimags between the PSF flux 

276 of a target catalog and a reference catalog 

277 """ 

278 

279 def setDefaults(self): 

280 super().setDefaults() 

281 

282 self.process.buildActions.yStars.col1 = "psfFlux_target" 

283 

284 

285class TargetRefCatDeltaAp09ScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit): 

286 """Plot the difference in millimags between the aper 09 flux 

287 of a target catalog and a reference catalog. 

288 """ 

289 

290 def setDefaults(self): 

291 super().setDefaults() 

292 

293 self.process.buildActions.yStars.col1 = "ap09Flux_target" 

294 

295 

296class TargetRefCatDeltaRAScatterPlot(TargetRefCatDeltaScatterAstrom): 

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

298 and a reference catalog 

299 """ 

300 

301 def setDefaults(self): 

302 super().setDefaults() 

303 self.process.buildActions.yStars.buildAction.actionA = RAcosDec( 

304 raKey="coord_ra_target", decKey="coord_dec_target" 

305 ) 

306 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref") 

307 

308 self.produce.plot.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)" 

309 

310 self.produce.metric.units = { 

311 "ref_offset": "mas", 

312 "ref_offset_mean": "mas", 

313 "ref_offset_stdev": "mas", 

314 "ref_offset_sigmaMad": "mas", 

315 "ref_offset_rms": "mas", 

316 "ref_offset_nstars": "", 

317 "ref_offset_gradient": "mas / mag", 

318 } 

319 

320 self.produce.metric.newNames = { 

321 "ref_offset": "{band}_ref_ra_offset_coadd", 

322 "ref_offset_mean": "{band}_ref_ra_offset_mean_coadd", 

323 "ref_offset_stdev": "{band}_ref_ra_offset_stdev_coadd", 

324 "ref_offset_sigmaMad": "{band}_ref_ra_offset_sigmaMad_coadd", 

325 "ref_offset_rms": "{band}_ref_ra_offset_rms_coadd", 

326 "ref_offset_nstars": "{band}_ref_ra_offset_nstars_coadd", 

327 "ref_offset_gradient": "{band}_ref_ra_offset_gradient_coadd", 

328 } 

329 

330 

331class TargetRefCatDeltaRAScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit): 

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

333 and a reference catalog 

334 """ 

335 

336 def setDefaults(self): 

337 super().setDefaults() 

338 self.process.buildActions.yStars.buildAction.actionA = RAcosDec( 

339 raKey="coord_ra_target", decKey="coord_dec_target" 

340 ) 

341 self.process.buildActions.yStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref") 

342 

343 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)" 

344 

345 

346class TargetRefCatDeltaDecScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit): 

347 """Plot the difference in milliseconds between the Decs of a target catalog 

348 and a reference catalog 

349 """ 

350 

351 def setDefaults(self): 

352 super().setDefaults() 

353 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target") 

354 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref") 

355 

356 self.produce.yAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)" 

357 

358 

359class TargetRefCatDeltaDecScatterPlot(TargetRefCatDeltaScatterAstrom): 

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

361 and a reference catalog 

362 """ 

363 

364 def setDefaults(self): 

365 super().setDefaults() 

366 self.process.buildActions.yStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target") 

367 self.process.buildActions.yStars.buildAction.actionB = LoadVector(vectorKey="dec_ref") 

368 

369 self.produce.plot.yAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)" 

370 

371 self.produce.metric.units = { 

372 "ref_offset": "mas", 

373 "ref_offset_mean": "mas", 

374 "ref_offset_stdev": "mas", 

375 "ref_offset_sigmaMad": "mas", 

376 "ref_offset_rms": "mas", 

377 "ref_offset_nstars": "", 

378 "ref_offset_gradient": "mas / mag", 

379 } 

380 

381 self.produce.metric.newNames = { 

382 "ref_offset": "{band}_ref_dec_offset_coadd", 

383 "ref_offset_mean": "{band}_ref_dec_offset_mean_coadd", 

384 "ref_offset_stdev": "{band}_ref_dec_offset_stdev_coadd", 

385 "ref_offset_sigmaMad": "{band}_ref_dec_offset_sigmaMad_coadd", 

386 "ref_offset_rms": "{band}_ref_dec_offset_rms_coadd", 

387 "ref_offset_nstars": "{band}_ref_dec_offset_nstars_coadd", 

388 "ref_offset_gradient": "{band}_ref_dec_offset_gradient_coadd", 

389 } 

390 

391 

392class TargetRefCatDeltaSkyPlot(TargetRefCatDelta): 

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

394 difference of the quantity defined in the vector key parameter between 

395 the target and reference catalog as the color. 

396 """ 

397 

398 parameterizedBand = Field[bool]( 

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

400 ) 

401 

402 def setDefaults(self): 

403 super().setDefaults() 

404 

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

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

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

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

409 

410 self.produce = SkyPlot() 

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

412 self.produce.xAxisLabel = "R.A. (deg)" 

413 self.produce.yAxisLabel = "Dec. (deg)" 

414 self.produce.plotOutlines = False 

415 

416 

417class TargetRefCatDeltaSkyPlotAstrom(TargetRefCatDeltaSkyPlot): 

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

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

420 the color. 

421 """ 

422 

423 def setDefaults(self): 

424 super().setDefaults() 

425 

426 self.process.buildActions.zStars = ConvertUnits( 

427 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

428 ) 

429 

430 self.applyContext(CoaddContext) 

431 self.applyContext(RefMatchContext) 

432 

433 self.process.buildActions.starStatMask.fluxType = "{band}_psfFlux_target" 

434 

435 

436class TargetRefCatDeltaSkyPlotAstromVisit(TargetRefCatDeltaSkyPlot): 

437 """Base class for plotting the RA/Dec distribution of stars at 

438 the visit level, with the difference between the RA or Dec of 

439 the target and reference catalog as the color. 

440 """ 

441 

442 def setDefaults(self): 

443 super().setDefaults() 

444 

445 self.process.buildActions.zStars = ConvertUnits( 

446 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

447 ) 

448 

449 self.applyContext(VisitContext) 

450 self.applyContext(RefMatchContext) 

451 

452 

453class TargetRefCatDeltaSkyPlotPhotomVisit(TargetRefCatDeltaSkyPlot): 

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

455 difference between the photometry of the target and reference catalog as 

456 the color. 

457 """ 

458 

459 def setDefaults(self): 

460 super().setDefaults() 

461 

462 self.process.buildActions.zStars = MagDiff() 

463 self.process.buildActions.zStars.col2 = "mag_ref" 

464 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)" 

465 

466 self.produce.plotName = "photomDiffSky" 

467 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)" 

468 self.applyContext(VisitContext) 

469 self.applyContext(RefMatchContext) 

470 

471 

472class TargetRefCatDeltaSkyPlotPhotom(TargetRefCatDeltaSkyPlot): 

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

474 difference between the photometry of the target and reference catalog as 

475 the color. 

476 """ 

477 

478 def setDefaults(self): 

479 super().setDefaults() 

480 

481 self.process.buildActions.zStars = MagDiff() 

482 self.process.buildActions.zStars.col2 = "{band}_mag_ref" 

483 self.process.buildActions.zStars.fluxUnits2 = "mag(AB)" 

484 

485 self.produce.plotName = "photomDiffSky_{band}" 

486 self.produce.zAxisLabel = "Output Mag - Ref Mag (mmag)" 

487 self.applyContext(CoaddContext) 

488 self.applyContext(RefMatchContext) 

489 

490 

491class TargetRefCatDeltaPsfSkyPlot(TargetRefCatDeltaSkyPlotPhotom): 

492 """Plot the RA/Dec distribution of stars, with the 

493 difference between the PSF photometry of the target and reference 

494 catalog as the color. 

495 """ 

496 

497 def setDefaults(self): 

498 super().setDefaults() 

499 

500 self.process.buildActions.zStars.col1 = "{band}_psfFlux_target" 

501 

502 

503class TargetRefCatDeltaPsfSkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit): 

504 """Plot the RA/Dec distribution of stars, with the 

505 difference between the PSF photometry of the target and reference 

506 catalog as the color. 

507 """ 

508 

509 def setDefaults(self): 

510 super().setDefaults() 

511 

512 self.process.buildActions.zStars.col1 = "psfFlux_target" 

513 

514 

515class TargetRefCatDeltaAp09SkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit): 

516 """Plot the RA/Dec distribution of stars, with the 

517 difference between the Ap09 photometry of the target and reference 

518 catalog as the color. 

519 """ 

520 

521 def setDefaults(self): 

522 super().setDefaults() 

523 

524 self.process.buildActions.zStars.col1 = "ap09Flux_target" 

525 

526 

527class TargetRefCatDeltaCModelSkyPlot(TargetRefCatDeltaSkyPlotPhotom): 

528 """Plot the RA/Dec distribution of stars, with the 

529 difference between the CModel photometry of the target and reference 

530 catalog as the color. 

531 """ 

532 

533 def setDefaults(self): 

534 super().setDefaults() 

535 

536 self.process.buildActions.zStars.col1 = "{band}_cModelFlux_target" 

537 

538 

539class TargetRefCatDeltaRASkyPlot(TargetRefCatDeltaSkyPlotAstrom): 

540 """Plot the RA/Dec distribution of stars, with the 

541 difference between the RA of the target and reference catalog as 

542 the color. 

543 """ 

544 

545 def setDefaults(self): 

546 super().setDefaults() 

547 self.process.buildActions.zStars.buildAction.actionA = RAcosDec( 

548 raKey="coord_ra_target", decKey="coord_dec_target" 

549 ) 

550 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref") 

551 

552 self.produce.plotName = "astromDiffSky_RA" 

553 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)" 

554 

555 

556class TargetRefCatDeltaRASkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit): 

557 """Plot the RA/Dec distribution of stars, with the 

558 difference between the RA of the target and reference catalog as 

559 the color. 

560 """ 

561 

562 def setDefaults(self): 

563 super().setDefaults() 

564 self.process.buildActions.zStars.buildAction.actionA = RAcosDec( 

565 raKey="coord_ra_target", decKey="coord_dec_target" 

566 ) 

567 self.process.buildActions.zStars.buildAction.actionB = RAcosDec(raKey="ra_ref", decKey="dec_ref") 

568 self.produce.plotName = "astromDiffSky_RA" 

569 self.produce.zAxisLabel = "RA$_{{target}}$ - RA$_{{ref}}$ (mas)" 

570 

571 

572class TargetRefCatDeltaDecSkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit): 

573 """Plot the RA/Dec distribution of stars, with the 

574 difference between the RA of the target and reference catalog as 

575 the color. 

576 """ 

577 

578 def setDefaults(self): 

579 super().setDefaults() 

580 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target") 

581 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref") 

582 

583 self.produce.plotName = "astromDiffSky_Dec" 

584 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)" 

585 

586 

587class TargetRefCatDeltaDecSkyPlot(TargetRefCatDeltaSkyPlotAstrom): 

588 """Plot the RA/Dec distribution of stars, with the 

589 difference between the Dec of the target and reference catalog as 

590 the color. 

591 """ 

592 

593 def setDefaults(self): 

594 super().setDefaults() 

595 self.process.buildActions.zStars.buildAction.actionA = LoadVector(vectorKey="coord_dec_target") 

596 self.process.buildActions.zStars.buildAction.actionB = LoadVector(vectorKey="dec_ref") 

597 

598 self.produce.plotName = "astromDiffSky_Dec" 

599 self.produce.zAxisLabel = "Dec$_{{target}}$ - Dec$_{{ref}}$ (mas)" 

600 

601 

602class TargetRefCatDeltaMetrics(AnalysisTool): 

603 """Calculate the AA1 metric and the sigma MAD from the difference between 

604 the target and reference catalog coordinates. 

605 """ 

606 

607 parameterizedBand = Field[bool]( 

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

609 ) 

610 

611 def coaddContext(self) -> None: 

612 """Apply coadd options for the metrics. Applies the coadd plot flag 

613 selector and sets flux types. 

614 """ 

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

616 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target" 

617 

618 self.applyContext(RefMatchContext) 

619 

620 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target" 

621 

622 self.produce.metric.newNames = { 

623 "AA1_RA": "{band}_AA1_RA_coadd", 

624 "AA1_sigmaMad_RA": "{band}_AA1_sigmaMad_RA_coadd", 

625 "AA1_Dec": "{band}_AA1_Dec_coadd", 

626 "AA1_sigmaMad_Dec": "{band}_AA1_sigmaMad_Dec_coadd", 

627 "AA1_tot": "{band}_AA1_tot_coadd", 

628 "AA1_sigmaMad_tot": "{band}_AA1_sigmaMad_tot_coadd", 

629 } 

630 

631 def visitContext(self) -> None: 

632 """Apply visit options for the metrics. Applies the visit plot flag 

633 selector and sets flux types. 

634 """ 

635 self.parameterizedBand = False 

636 self.prep.selectors.flagSelector = VisitPlotFlagSelector() 

637 self.prep.selectors.starSelector.vectorKey = "extendedness_target" 

638 

639 self.applyContext(RefMatchContext) 

640 

641 self.process.buildActions.mags.vectorKey = "psfFlux_target" 

642 

643 def setDefaults(self): 

644 super().setDefaults() 

645 

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

647 

648 # Calculate difference in RA 

649 self.process.buildActions.astromDiffRA = ConvertUnits( 

650 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

651 ) 

652 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec( 

653 raKey="coord_ra_target", decKey="dec_ref" 

654 ) 

655 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec( 

656 raKey="ra_ref", decKey="dec_ref" 

657 ) 

658 # Calculate difference in Dec 

659 self.process.buildActions.astromDiffDec = ConvertUnits( 

660 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

661 ) 

662 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="coord_dec_target") 

663 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="dec_ref") 

664 # Calculate total difference (using astropy) 

665 self.process.buildActions.astromDiffTot = AngularSeparation( 

666 raKey_A="coord_ra_target", 

667 decKey_A="coord_dec_target", 

668 raKey_B="ra_ref", 

669 decKey_B="dec_ref", 

670 ) 

671 

672 self.process.buildActions.mags = ConvertFluxToMag() 

673 

674 # Filter down to only objects with mag 17-21.5 

675 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA") 

676 self.process.filterActions.brightStarsRA.selector = RangeSelector( 

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

678 ) 

679 

680 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec") 

681 self.process.filterActions.brightStarsDec.selector = RangeSelector( 

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

683 ) 

684 

685 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot") 

686 self.process.filterActions.brightStarsTot.selector = RangeSelector( 

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

688 ) 

689 

690 # Calculate median and sigmaMad 

691 self.process.calculateActions.AA1_RA = MedianAction(vectorKey="brightStarsRA") 

692 self.process.calculateActions.AA1_sigmaMad_RA = SigmaMadAction(vectorKey="brightStarsRA") 

693 

694 self.process.calculateActions.AA1_Dec = MedianAction(vectorKey="brightStarsDec") 

695 self.process.calculateActions.AA1_sigmaMad_Dec = SigmaMadAction(vectorKey="brightStarsDec") 

696 

697 self.process.calculateActions.AA1_tot = MedianAction(vectorKey="brightStarsTot") 

698 self.process.calculateActions.AA1_sigmaMad_tot = SigmaMadAction(vectorKey="brightStarsTot") 

699 

700 self.produce.metric.units = { 

701 "AA1_RA": "mas", 

702 "AA1_sigmaMad_RA": "mas", 

703 "AA1_Dec": "mas", 

704 "AA1_sigmaMad_Dec": "mas", 

705 "AA1_tot": "mas", 

706 "AA1_sigmaMad_tot": "mas", 

707 } 

708 

709 

710class TargetRefCatDeltaColorMetrics(AnalysisTool): 

711 """Calculate the AB1 and ABF1 metrics from the difference between the 

712 target and reference catalog coordinates. 

713 """ 

714 

715 parameterizedBand: bool = False 

716 

717 AB2 = Field[float]( 

718 doc=( 

719 "Separation in milliarcseconds used to calculate the ABF1 metric. ABF1 is the percentage of" 

720 " sources whose distance from the reference is greater than AB2 mas from the mean." 

721 ), 

722 default=20, 

723 ) 

724 

725 def setDefaults(self): 

726 super().setDefaults() 

727 

728 self.prep.selectors.flagSelector = VisitPlotFlagSelector() 

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

730 self.prep.selectors.starSelector.vectorKey = "extendedness" 

731 

732 # Calculate difference in RA 

733 self.process.buildActions.astromDiffRA = ConvertUnits( 

734 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

735 ) 

736 self.process.buildActions.astromDiffRA.buildAction.actionA = RAcosDec( 

737 raKey="coord_ra", decKey="coord_dec" 

738 ) 

739 self.process.buildActions.astromDiffRA.buildAction.actionB = RAcosDec(raKey="r_ra", decKey="r_dec") 

740 # Calculate difference in Dec 

741 self.process.buildActions.astromDiffDec = ConvertUnits( 

742 buildAction=SubtractVector, inUnit="degree", outUnit="milliarcsecond" 

743 ) 

744 self.process.buildActions.astromDiffDec.buildAction.actionA = LoadVector(vectorKey="dec") 

745 self.process.buildActions.astromDiffDec.buildAction.actionB = LoadVector(vectorKey="r_dec") 

746 

747 # Calculate total difference (using astropy) 

748 self.process.buildActions.astromDiffTot = AngularSeparation( 

749 raKey_A="coord_ra", 

750 decKey_A="coord_dec", 

751 raKey_B="r_ra", 

752 decKey_B="r_dec", 

753 ) 

754 

755 self.process.buildActions.mags = ConvertFluxToMag() 

756 self.process.buildActions.mags.vectorKey = "psfFlux" 

757 

758 # Filter down to only objects with mag 17-21.5 

759 self.process.filterActions.brightStarsRA = DownselectVector(vectorKey="astromDiffRA") 

760 self.process.filterActions.brightStarsRA.selector = RangeSelector( 

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

762 ) 

763 

764 self.process.filterActions.brightStarsDec = DownselectVector(vectorKey="astromDiffDec") 

765 self.process.filterActions.brightStarsDec.selector = RangeSelector( 

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

767 ) 

768 

769 self.process.filterActions.brightStarsTot = DownselectVector(vectorKey="astromDiffTot") 

770 self.process.filterActions.brightStarsTot.selector = RangeSelector( 

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

772 ) 

773 

774 # Calculate RMS annd number of sources 

775 self.process.calculateActions.AB1_RA = RmsAction(vectorKey="brightStarsRA") 

776 self.process.calculateActions.AB1_Dec = RmsAction(vectorKey="brightStarsDec") 

777 self.process.calculateActions.AB1_tot = RmsAction(vectorKey="brightStarsTot") 

778 self.process.calculateActions.nSources = CountAction(vectorKey="brightStarsTot") 

779 

780 self.produce.metric.units = { 

781 "AB1_RA": "mas", 

782 "ABF1_RA": "percent", 

783 "AB1_Dec": "mas", 

784 "ABF1_Dec": "percent", 

785 "AB1_tot": "mas", 

786 "ABF1_tot": "percent", 

787 } 

788 

789 self.produce.plot = HistPlot() 

790 

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

792 self.produce.plot.panels["panel_sep"].hists = dict( 

793 brightStarsRA="Separations in RA", 

794 brightStarsDec="Separations in Dec", 

795 brightStarsTot="Total separations", 

796 ) 

797 self.produce.plot.panels["panel_sep"].label = "Separation Distances (mas)" 

798 

799 self.produce.plot.panels["panel_sep"].statsPanel = HistStatsPanel() 

800 self.produce.plot.panels["panel_sep"].statsPanel.statsLabels = ["N", "AB1", "ABF1 %"] 

801 self.produce.plot.panels["panel_sep"].statsPanel.stat1 = ["nSources", "nSources", "nSources"] 

802 self.produce.plot.panels["panel_sep"].statsPanel.stat2 = ["AB1_RA", "AB1_Dec", "AB1_tot"] 

803 self.produce.plot.panels["panel_sep"].statsPanel.stat3 = ["ABF1_RA", "ABF1_Dec", "ABF1_tot"] 

804 

805 def finalize(self): 

806 super().finalize() 

807 

808 # Calculate the percentage of outliers above the AB2 threshold 

809 self.process.calculateActions.ABF1_RA = FracThreshold( 

810 vectorKey="brightStarsRA", 

811 op="gt", 

812 threshold=self.AB2, 

813 relative_to_median=True, 

814 percent=True, 

815 use_absolute_value=True, 

816 ) 

817 self.process.calculateActions.ABF1_Dec = FracThreshold( 

818 vectorKey="brightStarsDec", 

819 threshold=self.AB2, 

820 op="gt", 

821 relative_to_median=True, 

822 percent=True, 

823 use_absolute_value=True, 

824 ) 

825 self.process.calculateActions.ABF1_tot = FracThreshold( 

826 vectorKey="brightStarsTot", threshold=self.AB2, op="gt", percent=True 

827 ) 

828 

829 

830class TargetRefCatDeltaPhotomMetrics(TargetRefCatDeltaMetrics): 

831 """Calculate statistics from the differences between 

832 the target and reference catalog magnitudes. 

833 """ 

834 

835 parameterizedBand = Field[bool]( 

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

837 ) 

838 

839 def coaddContext(self) -> None: 

840 """Apply coadd options for the metrics. Applies the coadd plot flag 

841 selector and sets flux types. 

842 """ 

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

844 self.prep.selectors.starSelector.vectorKey = "{band}_extendedness_target" 

845 self.prep.selectors.snSelector.fluxType = "{band}_psfFlux_target" 

846 

847 # Calculate magnitude difference 

848 self.process.buildActions.srcRefMagdiffStars.col1 = "{band}_psfFlux_target" 

849 self.process.buildActions.srcRefMagdiffStars.col2 = "{band}_mag_ref" 

850 self.process.buildActions.psfMag = ConvertFluxToMag( 

851 vectorKey="{band}_psfFlux_target", returnMillimags=True 

852 ) 

853 

854 self.applyContext(RefMatchContext) 

855 

856 self.process.buildActions.mags.vectorKey = "{band}_psfFlux_target" 

857 

858 self.produce.metric.newNames = { 

859 "ref_photom_offset": "{band}_ref_photom_offset_coadd", 

860 "ref_photom_offset_mean": "{band}_ref_photom_offset_mean_coadd", 

861 "ref_photom_offset_stdev": "{band}_ref_photom_offset_stdev_coadd", 

862 "ref_photom_offset_sigmaMad": "{band}_ref_photom_offset_sigmaMad_coadd", 

863 "ref_photom_offset_rms": "{band}_ref_photom_offset_rms_coadd", 

864 "ref_photom_offset_nstars": "{band}_ref_photom_offset_nstars_coadd", 

865 "ref_photom_offset_gradient": "{band}_ref_photom_offset_gradient_coadd", 

866 } 

867 

868 def visitContext(self) -> None: 

869 """Apply visit options for the metrics. Applies the visit plot flag 

870 selector and sets flux types. 

871 """ 

872 self.parameterizedBand = False 

873 self.prep.selectors.flagSelector = VisitPlotFlagSelector() 

874 self.prep.selectors.starSelector.vectorKey = "extendedness_target" 

875 

876 self.applyContext(RefMatchContext) 

877 

878 self.process.buildActions.mags.vectorKey = "psfFlux_target" 

879 

880 def setDefaults(self): 

881 super().setDefaults() 

882 

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

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

885 self.prep.selectors.snSelector.fluxType = "psfFlux_target" 

886 self.prep.selectors.snSelector.threshold = 200 

887 

888 # Calculate magnitude difference 

889 self.process.buildActions.srcRefMagdiffStars = MagDiff() 

890 self.process.buildActions.srcRefMagdiffStars.col1 = "psfFlux_target" 

891 self.process.buildActions.srcRefMagdiffStars.col2 = "mag_ref" 

892 self.process.buildActions.srcRefMagdiffStars.fluxUnits2 = "mag(AB)" 

893 

894 self.process.buildActions.psfMag = ConvertFluxToMag(vectorKey="psfFlux_target", returnMillimags=True) 

895 

896 # Calculate median, std, sigmaMad, RMS, and number counts: 

897 self.process.calculateActions.ref_photom_offset = MedianAction(vectorKey="srcRefMagdiffStars") 

898 self.process.calculateActions.ref_photom_offset_mean = MeanAction(vectorKey="srcRefMagdiffStars") 

899 self.process.calculateActions.ref_photom_offset_stdev = StdevAction(vectorKey="srcRefMagdiffStars") 

900 self.process.calculateActions.ref_photom_offset_sigmaMad = SigmaMadAction( 

901 vectorKey="srcRefMagdiffStars" 

902 ) 

903 self.process.calculateActions.ref_photom_offset_rms = RmsAction(vectorKey="srcRefMagdiffStars") 

904 self.process.calculateActions.ref_photom_offset_nstars = CountAction(vectorKey="srcRefMagdiffStars") 

905 self.process.calculateActions.ref_photom_offset_gradient = MedianGradientAction( 

906 xsVectorKey="psfMag", ysVectorKey="srcRefMagdiffStars" 

907 ) 

908 

909 self.produce.metric.units = { 

910 "ref_photom_offset": "mmag", 

911 "ref_photom_offset_mean": "mmag", 

912 "ref_photom_offset_stdev": "mmag", 

913 "ref_photom_offset_sigmaMad": "mmag", 

914 "ref_photom_offset_rms": "mmag", 

915 "ref_photom_offset_nstars": "ct", 

916 "ref_photom_offset_gradient": "", 

917 }