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

350 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-15 00:23 +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 RmsAction, 

59 SigmaMadAction, 

60 StdevAction, 

61) 

62from ..actions.vector import ( 

63 AngularSeparation, 

64 CoaddPlotFlagSelector, 

65 ConvertFluxToMag, 

66 ConvertUnits, 

67 DownselectVector, 

68 LoadVector, 

69 MagDiff, 

70 RAcosDec, 

71 RangeSelector, 

72 SnSelector, 

73 StarSelector, 

74 SubtractVector, 

75 VisitPlotFlagSelector, 

76) 

77from ..contexts import CoaddContext, RefMatchContext, VisitContext 

78from ..interfaces import AnalysisTool 

79 

80 

81class TargetRefCatDelta(AnalysisTool): 

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

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

84 """ 

85 

86 parameterizedBand = Field[bool]( 

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

88 ) 

89 

90 def coaddContext(self) -> None: 

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

92 Applies the coadd plot flag selector and sets 

93 flux types. 

94 """ 

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

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

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

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

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

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

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

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

103 

104 def visitContext(self) -> None: 

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

106 Applies the visit plot flag selector and sets 

107 the flux types. 

108 """ 

109 self.parameterizedBand = False 

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

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

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

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

114 

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

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

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

118 

119 def setDefaults(self): 

120 super().setDefaults() 

121 

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

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

124 

125 

126class TargetRefCatDeltaScatterAstrom(TargetRefCatDelta): 

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

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

129 a scatter plot. 

130 """ 

131 

132 def setDefaults(self): 

133 super().setDefaults() 

134 

135 self.process.buildActions.yStars = ConvertUnits( 

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

137 ) 

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

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

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

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

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

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

144 

145 self.produce = ScatterPlotWithTwoHists() 

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

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

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

149 self.applyContext(CoaddContext) 

150 self.applyContext(RefMatchContext) 

151 

152 

153class TargetRefCatDeltaScatterAstromVisit(TargetRefCatDelta): 

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

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

156 a scatter plot. 

157 """ 

158 

159 def setDefaults(self): 

160 super().setDefaults() 

161 

162 self.process.buildActions.yStars = ConvertUnits( 

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

164 ) 

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

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

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

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

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

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

171 

172 self.produce = ScatterPlotWithTwoHists() 

173 self.produce.addSummaryPlot = False 

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

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

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

177 self.applyContext(VisitContext) 

178 self.applyContext(RefMatchContext) 

179 

180 

181class TargetRefCatDeltaScatterPhotom(TargetRefCatDelta): 

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

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

184 """ 

185 

186 def setDefaults(self): 

187 super().setDefaults() 

188 

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

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

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

192 

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

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

195 

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

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

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

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

200 

201 self.produce = ScatterPlotWithTwoHists() 

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

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

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

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

206 self.applyContext(CoaddContext) 

207 self.applyContext(RefMatchContext) 

208 

209 

210class TargetRefCatDeltaScatterPhotomVisit(TargetRefCatDelta): 

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

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

213 """ 

214 

215 def setDefaults(self): 

216 super().setDefaults() 

217 

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

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

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

221 

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

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

224 

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

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

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

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

229 

230 self.produce = ScatterPlotWithTwoHists() 

231 self.produce.addSummaryPlot = False 

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

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

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

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

236 self.applyContext(VisitContext) 

237 self.applyContext(RefMatchContext) 

238 

239 

240class TargetRefCatDeltaPsfScatterPlot(TargetRefCatDeltaScatterPhotom): 

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

242 of a target catalog and a reference catalog 

243 """ 

244 

245 def setDefaults(self): 

246 super().setDefaults() 

247 

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

249 

250 

251class TargetRefCatDeltaCModelScatterPlot(TargetRefCatDeltaScatterPhotom): 

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

253 of a target catalog and a reference catalog. 

254 """ 

255 

256 def setDefaults(self): 

257 super().setDefaults() 

258 

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

260 

261 

262class TargetRefCatDeltaPsfScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit): 

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

264 of a target catalog and a reference catalog 

265 """ 

266 

267 def setDefaults(self): 

268 super().setDefaults() 

269 

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

271 

272 

273class TargetRefCatDeltaAp09ScatterVisitPlot(TargetRefCatDeltaScatterPhotomVisit): 

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

275 of a target catalog and a reference catalog. 

276 """ 

277 

278 def setDefaults(self): 

279 super().setDefaults() 

280 

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

282 

283 

284class TargetRefCatDeltaRAScatterPlot(TargetRefCatDeltaScatterAstrom): 

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

286 and a reference catalog 

287 """ 

288 

289 def setDefaults(self): 

290 super().setDefaults() 

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

292 raKey="coord_ra_target", decKey="coord_dec_target" 

293 ) 

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

295 

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

297 

298 

299class TargetRefCatDeltaRAScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit): 

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

301 and a reference catalog 

302 """ 

303 

304 def setDefaults(self): 

305 super().setDefaults() 

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

307 raKey="coord_ra_target", decKey="coord_dec_target" 

308 ) 

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

310 

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

312 

313 

314class TargetRefCatDeltaDecScatterVisitPlot(TargetRefCatDeltaScatterAstromVisit): 

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

316 and a reference catalog 

317 """ 

318 

319 def setDefaults(self): 

320 super().setDefaults() 

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

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

323 

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

325 

326 

327class TargetRefCatDeltaDecScatterPlot(TargetRefCatDeltaScatterAstrom): 

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() 

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

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

336 

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

338 

339 

340class TargetRefCatDeltaSkyPlot(TargetRefCatDelta): 

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

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

343 the target and reference catalog as the color. 

344 """ 

345 

346 parameterizedBand = Field[bool]( 

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

348 ) 

349 

350 def setDefaults(self): 

351 super().setDefaults() 

352 

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

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

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

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

357 

358 self.produce = SkyPlot() 

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

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

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

362 self.produce.plotOutlines = False 

363 

364 

365class TargetRefCatDeltaSkyPlotAstrom(TargetRefCatDeltaSkyPlot): 

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

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

368 the color. 

369 """ 

370 

371 def setDefaults(self): 

372 super().setDefaults() 

373 

374 self.process.buildActions.zStars = ConvertUnits( 

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

376 ) 

377 

378 self.applyContext(CoaddContext) 

379 self.applyContext(RefMatchContext) 

380 

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

382 

383 

384class TargetRefCatDeltaSkyPlotAstromVisit(TargetRefCatDeltaSkyPlot): 

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

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

387 the target and reference catalog as the color. 

388 """ 

389 

390 def setDefaults(self): 

391 super().setDefaults() 

392 

393 self.process.buildActions.zStars = ConvertUnits( 

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

395 ) 

396 

397 self.applyContext(VisitContext) 

398 self.applyContext(RefMatchContext) 

399 

400 

401class TargetRefCatDeltaSkyPlotPhotomVisit(TargetRefCatDeltaSkyPlot): 

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

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

404 the color. 

405 """ 

406 

407 def setDefaults(self): 

408 super().setDefaults() 

409 

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

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

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

413 

414 self.produce.plotName = "photomDiffSky" 

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

416 self.applyContext(VisitContext) 

417 self.applyContext(RefMatchContext) 

418 

419 

420class TargetRefCatDeltaSkyPlotPhotom(TargetRefCatDeltaSkyPlot): 

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

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

423 the color. 

424 """ 

425 

426 def setDefaults(self): 

427 super().setDefaults() 

428 

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

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

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

432 

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

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

435 self.applyContext(CoaddContext) 

436 self.applyContext(RefMatchContext) 

437 

438 

439class TargetRefCatDeltaPsfSkyPlot(TargetRefCatDeltaSkyPlotPhotom): 

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

441 difference between the PSF photometry of the target and reference 

442 catalog as the color. 

443 """ 

444 

445 def setDefaults(self): 

446 super().setDefaults() 

447 

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

449 

450 

451class TargetRefCatDeltaPsfSkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit): 

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

453 difference between the PSF photometry of the target and reference 

454 catalog as the color. 

455 """ 

456 

457 def setDefaults(self): 

458 super().setDefaults() 

459 

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

461 

462 

463class TargetRefCatDeltaAp09SkyVisitPlot(TargetRefCatDeltaSkyPlotPhotomVisit): 

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

465 difference between the Ap09 photometry of the target and reference 

466 catalog as the color. 

467 """ 

468 

469 def setDefaults(self): 

470 super().setDefaults() 

471 

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

473 

474 

475class TargetRefCatDeltaCModelSkyPlot(TargetRefCatDeltaSkyPlotPhotom): 

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

477 difference between the CModel photometry of the target and reference 

478 catalog as the color. 

479 """ 

480 

481 def setDefaults(self): 

482 super().setDefaults() 

483 

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

485 

486 

487class TargetRefCatDeltaRASkyPlot(TargetRefCatDeltaSkyPlotAstrom): 

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

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

490 the color. 

491 """ 

492 

493 def setDefaults(self): 

494 super().setDefaults() 

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

496 raKey="coord_ra_target", decKey="coord_dec_target" 

497 ) 

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

499 

500 self.produce.plotName = "astromDiffSky_RA" 

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

502 

503 

504class TargetRefCatDeltaRASkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit): 

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

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

507 the color. 

508 """ 

509 

510 def setDefaults(self): 

511 super().setDefaults() 

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

513 raKey="coord_ra_target", decKey="coord_dec_target" 

514 ) 

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

516 self.produce.plotName = "astromDiffSky_RA" 

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

518 

519 

520class TargetRefCatDeltaDecSkyVisitPlot(TargetRefCatDeltaSkyPlotAstromVisit): 

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

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

523 the color. 

524 """ 

525 

526 def setDefaults(self): 

527 super().setDefaults() 

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

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

530 

531 self.produce.plotName = "astromDiffSky_Dec" 

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

533 

534 

535class TargetRefCatDeltaDecSkyPlot(TargetRefCatDeltaSkyPlotAstrom): 

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

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

538 the color. 

539 """ 

540 

541 def setDefaults(self): 

542 super().setDefaults() 

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

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

545 

546 self.produce.plotName = "astromDiffSky_Dec" 

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

548 

549 

550class TargetRefCatDeltaMetrics(AnalysisTool): 

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

552 the target and reference catalog coordinates. 

553 """ 

554 

555 parameterizedBand = Field[bool]( 

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

557 ) 

558 

559 def coaddContext(self) -> None: 

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

561 selector and sets flux types. 

562 """ 

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

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

565 

566 self.applyContext(RefMatchContext) 

567 

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

569 

570 self.produce.metric.newNames = { 

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

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

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

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

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

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

577 } 

578 

579 def visitContext(self) -> None: 

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

581 selector and sets flux types. 

582 """ 

583 self.parameterizedBand = False 

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

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

586 

587 self.applyContext(RefMatchContext) 

588 

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

590 

591 def setDefaults(self): 

592 super().setDefaults() 

593 

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

595 

596 # Calculate difference in RA 

597 self.process.buildActions.astromDiffRA = ConvertUnits( 

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

599 ) 

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

601 raKey="coord_ra_target", decKey="dec_ref" 

602 ) 

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

604 raKey="ra_ref", decKey="dec_ref" 

605 ) 

606 # Calculate difference in Dec 

607 self.process.buildActions.astromDiffDec = ConvertUnits( 

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

609 ) 

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

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

612 # Calculate total difference (using astropy) 

613 self.process.buildActions.astromDiffTot = AngularSeparation( 

614 raKey_A="coord_ra_target", 

615 decKey_A="coord_dec_target", 

616 raKey_B="ra_ref", 

617 decKey_B="dec_ref", 

618 ) 

619 

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

621 

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

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

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

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

626 ) 

627 

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

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

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

631 ) 

632 

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

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

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

636 ) 

637 

638 # Calculate median and sigmaMad 

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

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

641 

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

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

644 

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

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

647 

648 self.produce.metric.units = { 

649 "AA1_RA": "mas", 

650 "AA1_sigmaMad_RA": "mas", 

651 "AA1_Dec": "mas", 

652 "AA1_sigmaMad_Dec": "mas", 

653 "AA1_tot": "mas", 

654 "AA1_sigmaMad_tot": "mas", 

655 } 

656 

657 

658class TargetRefCatDeltaColorMetrics(AnalysisTool): 

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

660 target and reference catalog coordinates. 

661 """ 

662 

663 parameterizedBand: bool = False 

664 

665 AB2 = Field[float]( 

666 doc=( 

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

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

669 ), 

670 default=20, 

671 ) 

672 

673 def setDefaults(self): 

674 super().setDefaults() 

675 

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

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

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

679 

680 # Calculate difference in RA 

681 self.process.buildActions.astromDiffRA = ConvertUnits( 

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

683 ) 

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

685 raKey="coord_ra", decKey="coord_dec" 

686 ) 

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

688 # Calculate difference in Dec 

689 self.process.buildActions.astromDiffDec = ConvertUnits( 

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

691 ) 

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

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

694 

695 # Calculate total difference (using astropy) 

696 self.process.buildActions.astromDiffTot = AngularSeparation( 

697 raKey_A="coord_ra", 

698 decKey_A="coord_dec", 

699 raKey_B="r_ra", 

700 decKey_B="r_dec", 

701 ) 

702 

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

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

705 

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

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

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

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

710 ) 

711 

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

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

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

715 ) 

716 

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

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

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

720 ) 

721 

722 # Calculate RMS annd number of sources 

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

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

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

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

727 

728 self.produce.metric.units = { 

729 "AB1_RA": "mas", 

730 "ABF1_RA": "percent", 

731 "AB1_Dec": "mas", 

732 "ABF1_Dec": "percent", 

733 "AB1_tot": "mas", 

734 "ABF1_tot": "percent", 

735 } 

736 

737 self.produce.plot = HistPlot() 

738 

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

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

741 brightStarsRA="Separations in RA", 

742 brightStarsDec="Separations in Dec", 

743 brightStarsTot="Total separations", 

744 ) 

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

746 

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

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

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

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

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

752 

753 def finalize(self): 

754 super().finalize() 

755 

756 # Calculate the percentage of outliers above the AB2 threshold 

757 self.process.calculateActions.ABF1_RA = FracThreshold( 

758 vectorKey="brightStarsRA", 

759 op="gt", 

760 threshold=self.AB2, 

761 relative_to_median=True, 

762 percent=True, 

763 use_absolute_value=True, 

764 ) 

765 self.process.calculateActions.ABF1_Dec = FracThreshold( 

766 vectorKey="brightStarsDec", 

767 threshold=self.AB2, 

768 op="gt", 

769 relative_to_median=True, 

770 percent=True, 

771 use_absolute_value=True, 

772 ) 

773 self.process.calculateActions.ABF1_tot = FracThreshold( 

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

775 ) 

776 

777 

778class TargetRefCatDeltaPhotomMetrics(TargetRefCatDeltaMetrics): 

779 """Calculate statistics from the differences between 

780 the target and reference catalog magnitudes. 

781 """ 

782 

783 parameterizedBand = Field[bool]( 

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

785 ) 

786 

787 def coaddContext(self) -> None: 

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

789 selector and sets flux types. 

790 """ 

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

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

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

794 

795 # Calculate magnitude difference 

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

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

798 

799 self.applyContext(RefMatchContext) 

800 

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

802 

803 self.produce.metric.newNames = { 

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

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

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

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

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

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

810 } 

811 

812 def visitContext(self) -> None: 

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

814 selector and sets flux types. 

815 """ 

816 self.parameterizedBand = False 

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

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

819 

820 self.applyContext(RefMatchContext) 

821 

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

823 

824 def setDefaults(self): 

825 super().setDefaults() 

826 

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

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

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

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

831 

832 # Calculate magnitude difference 

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

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

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

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

837 

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

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

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

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

842 self.process.calculateActions.ref_photom_offset_sigmaMad = SigmaMadAction( 

843 vectorKey="srcRefMagdiffStars" 

844 ) 

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

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

847 

848 self.produce.metric.units = { 

849 "ref_photom_offset": "mmag", 

850 "ref_photom_offset_mean": "mmag", 

851 "ref_photom_offset_stdev": "mmag", 

852 "ref_photom_offset_sigmaMad": "mmag", 

853 "ref_photom_offset_rms": "mmag", 

854 "ref_photom_offset_nstars": "ct", 

855 }