Coverage for python / lsst / analysis / tools / atools / shapes.py: 28%

324 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-01 08:55 +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/>. 

21 

22from __future__ import annotations 

23 

24__all__ = ( 

25 "ShapeSizeFractionalScalars", 

26 "BasePsfResidualCoadd", 

27 "BasePsfResidualVisit", 

28 "E1DiffScatter", 

29 "E1DiffScatterVisit", 

30 "E2DiffScatter", 

31 "E2DiffScatterVisit", 

32 "ShapeSizeFractionalDiffScatter", 

33 "ShapeSizeFractionalDiffScatterVisit", 

34 "E1DiffSky", 

35 "E1DiffSkyVisit", 

36 "E2DiffSky", 

37 "E2DiffSkyVisit", 

38 "ShapeSizeFractionalDiffSky", 

39 "ShapeSizeFractionalDiffSkyVisit", 

40 "RhoStatistics", 

41 "RelativeSizeResidualPlot", 

42 "EBase", 

43 "EScatter", 

44 "E1ScatterVisit", 

45 "E2ScatterVisit", 

46 "ESky", 

47 "E1SkyVisit", 

48 "E2SkyVisit", 

49 "EFocalPlane", 

50 "E1FocalPlane", 

51 "E2FocalPlane", 

52 "ShapeSizeFractionalDiffFocalPlane", 

53) 

54 

55from lsst.pex.config import Field 

56from lsst.pex.config.configurableActions import ConfigurableActionField 

57 

58from ..actions.keyedData import KeyedScalars 

59from ..actions.plot import FocalPlanePlot 

60from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlot 

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

62from ..actions.plot.skyPlot import SkyPlot 

63from ..actions.scalar import CountAction, MedianAction, SigmaMadAction 

64from ..actions.vector import ( 

65 CalcE, 

66 CalcE1, 

67 CalcE2, 

68 CalcEDiff, 

69 CalcMomentSize, 

70 CalcRhoStatistics, 

71 CoaddPlotFlagSelector, 

72 ConvertFluxToMag, 

73 DownselectVector, 

74 FractionalDifference, 

75 LoadVector, 

76 SnSelector, 

77 StarSelector, 

78 VectorSelector, 

79 VisitPlotFlagSelector, 

80) 

81from ..contexts import CoaddContext, VisitContext 

82from ..interfaces import AnalysisTool, KeyedData, VectorAction 

83 

84 

85class ShapeSizeFractionalScalars(KeyedScalars): 

86 vectorKey = Field[str](doc="Column key to compute scalars") 

87 

88 snFluxType = Field[str](doc="column key for the flux type used in SN selection") 

89 

90 selector = ConfigurableActionField[VectorAction](doc="Selector to use before computing Scalars") 

91 

92 def setDefaults(self): 

93 super().setDefaults() 

94 self.scalarActions.median = MedianAction(vectorKey=self.vectorKey) # type: ignore 

95 self.scalarActions.sigmaMad = SigmaMadAction(vectorKey=self.vectorKey) # type: ignore 

96 self.scalarActions.count = CountAction(vectorKey=self.vectorKey) # type: ignore 

97 self.scalarActions.approxMag = MedianAction(vectorKey=self.snFluxType) # type: ignore 

98 

99 def __call__(self, data: KeyedData, **kwargs) -> KeyedData: 

100 mask = kwargs.get("mask") 

101 selection = self.selector(data, **kwargs) 

102 if mask is not None: 

103 mask &= selection 

104 else: 

105 mask = selection 

106 return super().__call__(data, **kwargs | dict(mask=mask)) 

107 

108 

109class BasePsfResidualCoadd(AnalysisTool): 

110 """Shared configuration for `prep` and `process` stages of PSF residuals. 

111 

112 This is a mixin class used by `BasePsfResidualScatterPlot` and 

113 `BasePsfResidualMetric` to share common default configuration. 

114 """ 

115 

116 def setDefaults(self): 

117 super().setDefaults() 

118 

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

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

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

122 

123 self.process.calculateActions.stars = ScatterPlotStatsAction( 

124 vectorKey="yStars", 

125 ) 

126 self.process.buildActions.mags = ConvertFluxToMag(vectorKey="{band}_psfFlux") 

127 

128 self.process.buildActions.fracDiff = FractionalDifference( 

129 actionA=CalcMomentSize(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"), 

130 actionB=CalcMomentSize(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"), 

131 ) 

132 # Define an eDiff action and let e1Diff and e2Diff differ only in 

133 # component. 

134 self.process.buildActions.eDiff = CalcEDiff( 

135 colA=CalcE(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"), 

136 colB=CalcE(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"), 

137 ) 

138 self.process.buildActions.e1Diff = self.process.buildActions.eDiff 

139 self.process.buildActions.e1Diff.component = "1" 

140 self.process.buildActions.e2Diff = self.process.buildActions.eDiff 

141 self.process.buildActions.e2Diff.component = "2" 

142 

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

144 self.process.buildActions.patch.vectorKey = "patch" 

145 

146 self.process.buildActions.xStars = ConvertFluxToMag(vectorKey="{band}_psfFlux") 

147 self.process.buildActions.psfFlux = LoadVector(vectorKey="{band}_psfFlux") 

148 self.process.buildActions.psfFluxErr = LoadVector(vectorKey="{band}_psfFluxErr") 

149 

150 

151class BasePsfResidualVisit(AnalysisTool): 

152 """Shared configuration for `prep` and `process` stages of PSF residuals. 

153 

154 This is a mixin class used by `BasePsfResidualScatterPlot` and 

155 `BasePsfResidualMetric` to share common default configuration. 

156 """ 

157 

158 parameterizedBand: bool = False 

159 

160 def setDefaults(self): 

161 super().setDefaults() 

162 

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

164 self.prep.selectors.snSelector = SnSelector(fluxType="psfFlux", threshold=50) 

165 self.prep.selectors.starSelector = StarSelector(vectorKey="extendedness") 

166 

167 self.process.buildActions.xStars = ConvertFluxToMag(vectorKey="psfFlux") 

168 

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

170 

171 self.process.buildActions.fracDiff = FractionalDifference( 

172 actionA=CalcMomentSize(colXx="ixx", colYy="iyy", colXy="ixy"), 

173 actionB=CalcMomentSize(colXx="ixxPSF", colYy="iyyPSF", colXy="ixyPSF"), 

174 ) 

175 # Define an eDiff action and let e1Diff and e2Diff differ only in 

176 # component. 

177 self.process.buildActions.eDiff = CalcEDiff( 

178 colA=CalcE(colXx="ixx", colYy="iyy", colXy="ixy"), 

179 colB=CalcE(colXx="ixxPSF", colYy="iyyPSF", colXy="ixyPSF"), 

180 ) 

181 self.process.buildActions.e1Diff = self.process.buildActions.eDiff 

182 self.process.buildActions.e1Diff.component = "1" 

183 self.process.buildActions.e2Diff = self.process.buildActions.eDiff 

184 self.process.buildActions.e2Diff.component = "2" 

185 

186 

187class SkyCoadd(BasePsfResidualCoadd): 

188 """A base class for coadd level sky plots.""" 

189 

190 def setDefaults(self): 

191 super().setDefaults() 

192 

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

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

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

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

197 

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

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

200 

201 self.produce.plot = SkyPlot() 

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

203 self.produce.plot.xAxisLabel = "R.A. (deg)" 

204 self.produce.plot.yAxisLabel = "Dec. (deg)" 

205 

206 

207class SkyVisit(BasePsfResidualVisit): 

208 """A base class for visit level sky plots.""" 

209 

210 def setDefaults(self): 

211 super().setDefaults() 

212 

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

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

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

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

217 

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

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

220 

221 self.produce.plot = SkyPlot() 

222 

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

224 self.produce.plot.xAxisLabel = "R.A. (deg)" 

225 self.produce.plot.yAxisLabel = "Dec. (deg)" 

226 

227 self.produce.plot.plotOutlines = False 

228 

229 

230class ScatterCoadd(BasePsfResidualCoadd): 

231 """A base class for coadd level scatter plots.""" 

232 

233 def setDefaults(self): 

234 super().setDefaults() 

235 self.process.calculateActions.stars = ScatterPlotStatsAction( 

236 vectorKey="yStars", 

237 ) 

238 # use the downselected psfFlux 

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

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

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

242 

243 self.produce.metric.units = { 

244 "{band}_highSNStars_median": "pixel", 

245 "{band}_highSNStars_sigmaMad": "pixel", 

246 "{band}_highSNStars_count": "count", 

247 "{band}_lowSNStars_median": "pixel", 

248 "{band}_lowSNStars_sigmaMad": "pixel", 

249 "{band}_lowSNStars_count": "count", 

250 } 

251 

252 self.produce.plot = ScatterPlotWithTwoHists() 

253 

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

255 self.produce.plot.xAxisLabel = "PSF Magnitude (mag)" 

256 self.produce.plot.magLabel = "PSF Magnitude (mag)" 

257 

258 

259class ScatterVisit(BasePsfResidualVisit): 

260 """A base class for visit level sky plots.""" 

261 

262 def setDefaults(self): 

263 super().setDefaults() 

264 self.process.calculateActions.stars = ScatterPlotStatsAction( 

265 vectorKey="yStars", 

266 ) 

267 

268 # use the downselected psfFlux 

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

270 self.process.calculateActions.stars.highSNSelector.threshold = 500 

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

272 self.process.calculateActions.stars.lowSNSelector.threshold = 100 

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

274 

275 self.produce.metric.units = { 

276 "{band}_highSNStars_median": "pixel", 

277 "{band}_highSNStars_sigmaMad": "pixel", 

278 "{band}_highSNStars_count": "count", 

279 "{band}_lowSNStars_median": "pixel", 

280 "{band}_lowSNStars_sigmaMad": "pixel", 

281 "{band}_lowSNStars_count": "count", 

282 } 

283 

284 self.produce.metric.newNames = { 

285 "{band}_highSNStars_median": "highSNStars_median", 

286 "{band}_highSNStars_sigmaMad": "highSNStars_sigmaMad", 

287 "{band}_highSNStars_count": "highSNStars_count", 

288 "{band}_lowSNStars_median": "lowSNStars_median", 

289 "{band}_lowSNStars_sigmaMad": "lowSNStars_sigmaMad", 

290 "{band}_lowSNStars_count": "lowSNStars_count", 

291 } 

292 

293 self.produce.plot = ScatterPlotWithTwoHists() 

294 

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

296 self.produce.plot.xAxisLabel = "PSF Magnitude (mag)" 

297 self.produce.plot.magLabel = "PSF Magnitude (mag)" 

298 

299 self.produce.plot.addSummaryPlot = False 

300 

301 

302class E1DiffScatter(ScatterCoadd): 

303 """The difference between E1 and E1 PSF plotted on 

304 a scatter plot. Used in coaddQualityCore. 

305 """ 

306 

307 def setDefaults(self): 

308 super().setDefaults() 

309 self.applyContext(CoaddContext) 

310 self.process.filterActions.yStars = LoadVector(vectorKey="e1Diff") 

311 self.produce.plot.yAxisLabel = "Ellipticity residuals (e1 - e1_PSF)" 

312 

313 

314class E1DiffScatterVisit(ScatterVisit): 

315 """The difference between E1 and E1 PSF plotted on 

316 a scatter plot for visit level data. 

317 Used in debugPSF. 

318 """ 

319 

320 def setDefaults(self): 

321 super().setDefaults() 

322 self.process.filterActions.yStars = LoadVector(vectorKey="e1Diff") 

323 self.applyContext(VisitContext) 

324 self.produce.plot.yAxisLabel = "Ellipticity residuals (e1 - e1_PSF)" 

325 

326 

327class E2DiffScatter(ScatterCoadd): 

328 """The difference between E2 and E2 PSF plotted on 

329 a scatter plot for coadd level data. 

330 Used in coaddQualityCore. 

331 """ 

332 

333 def setDefaults(self): 

334 super().setDefaults() 

335 self.process.filterActions.yStars = LoadVector(vectorKey="e2Diff") 

336 self.produce.plot.yAxisLabel = "Ellipticity residuals (e2 - e2_PSF)" 

337 self.applyContext(CoaddContext) 

338 

339 

340class E2DiffScatterVisit(ScatterVisit): 

341 """The difference between E2 and E2 PSF plotted on 

342 a scatter plot for visit level data. 

343 Used in debugPSF. 

344 """ 

345 

346 def setDefaults(self): 

347 super().setDefaults() 

348 self.process.filterActions.yStars = LoadVector(vectorKey="e2Diff") 

349 self.applyContext(VisitContext) 

350 self.produce.plot.yAxisLabel = "Ellipticity residuals (e2 - e2_PSF)" 

351 

352 

353class ShapeSizeFractionalDiffScatter(ScatterCoadd): 

354 """The difference between shape and shape PSF plotted on 

355 a scatter plot for coadd level data. 

356 Used in coaddQualityCore. 

357 """ 

358 

359 def setDefaults(self): 

360 super().setDefaults() 

361 self.applyContext(CoaddContext) 

362 self.process.filterActions.yStars = LoadVector(vectorKey="fracDiff") 

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

364 

365 

366class ShapeSizeFractionalDiffScatterVisit(ScatterVisit): 

367 """The difference between shape and shape PSF plotted on 

368 a scatter plot for visit level data. 

369 Used in debugPsf. 

370 """ 

371 

372 def setDefaults(self): 

373 super().setDefaults() 

374 self.process.filterActions.yStars = LoadVector(vectorKey="fracDiff") 

375 self.applyContext(VisitContext) 

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

377 

378 

379class E1DiffSky(SkyCoadd): 

380 """The difference between E1 and E1 PSF plotted on 

381 a sky plot for coadd level data. 

382 Used in debugPSF. 

383 """ 

384 

385 def setDefaults(self): 

386 super().setDefaults() 

387 self.process.filterActions.zStars = LoadVector(vectorKey="e1Diff") 

388 self.produce.plot.zAxisLabel = "E1 Diff" 

389 self.produce.plot.plotName = "E1 Diff Sky" 

390 self.applyContext(CoaddContext) 

391 

392 

393class E1DiffSkyVisit(SkyVisit): 

394 """The difference between E1 and E1 PSF plotted on 

395 a sky plot for visit level data. 

396 Used in debugPSF. 

397 """ 

398 

399 def setDefaults(self): 

400 super().setDefaults() 

401 self.process.filterActions.zStars = LoadVector(vectorKey="e1Diff") 

402 self.produce.plot.zAxisLabel = "E1 Diff" 

403 self.produce.plot.plotName = "E1 Diff Sky" 

404 self.applyContext(VisitContext) 

405 

406 

407class E2DiffSky(SkyCoadd): 

408 """The difference between E2 and E2 PSF plotted on 

409 a sky plot for coadd level data. 

410 Used in debugPSF. 

411 """ 

412 

413 def setDefaults(self): 

414 super().setDefaults() 

415 self.process.filterActions.zStars = LoadVector(vectorKey="e2Diff") 

416 self.produce.plot.zAxisLabel = "E2 Diff" 

417 self.produce.plot.plotName = "E2 Diff Sky" 

418 self.applyContext(CoaddContext) 

419 

420 

421class E2DiffSkyVisit(SkyVisit): 

422 """The difference between E2 and E2 PSF plotted on 

423 a sky plot for visit level data. 

424 Used in debugPSF. 

425 """ 

426 

427 def setDefaults(self): 

428 super().setDefaults() 

429 self.process.filterActions.zStars = LoadVector(vectorKey="e2Diff") 

430 self.produce.plot.zAxisLabel = "E2 Diff" 

431 self.produce.plot.plotName = "E2 Diff Sky" 

432 self.applyContext(VisitContext) 

433 

434 

435class ShapeSizeFractionalDiffSky(SkyCoadd): 

436 """The difference between shape and shape PSF plotted on 

437 a sky plot for coadd level data. 

438 Used in debugPSF. 

439 """ 

440 

441 def setDefaults(self): 

442 super().setDefaults() 

443 self.applyContext(CoaddContext) 

444 self.process.filterActions.zStars = LoadVector(vectorKey="fracDiff") 

445 self.produce.plot.zAxisLabel = "E1 Diff" 

446 self.produce.plot.plotName = "Fractional Diff Sky" 

447 

448 

449class ShapeSizeFractionalDiffSkyVisit(SkyVisit): 

450 """The difference between shape and shape PSF plotted on 

451 a sky plot for visit level data. 

452 Used in debugPSF. 

453 """ 

454 

455 def setDefaults(self): 

456 super().setDefaults() 

457 self.process.filterActions.zStars = LoadVector(vectorKey="fracDiff") 

458 self.produce.plot.zAxisLabel = "E1 Diff" 

459 self.produce.plot.plotName = "Fractional Diff Sky" 

460 

461 self.applyContext(VisitContext) 

462 

463 

464class RhoStatistics(AnalysisTool): 

465 parameterizedBand: bool = True 

466 

467 def setDefaults(self): 

468 super().setDefaults() 

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

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

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

472 

473 self.process.calculateActions.rho = CalcRhoStatistics() 

474 self.process.calculateActions.rho.treecorr.nbins = 21 

475 self.process.calculateActions.rho.treecorr.min_sep = 0.01 

476 self.process.calculateActions.rho.treecorr.max_sep = 100.0 

477 self.process.calculateActions.rho.treecorr.sep_units = "arcmin" 

478 self.process.calculateActions.rho.treecorr.metric = "Arc" 

479 

480 self.produce.plot = RhoStatisticsPlot() 

481 

482 

483class RelativeSizeResidualPlot(AnalysisTool): 

484 def setDefaults(self): 

485 super().setDefaults() 

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

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

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

489 self.process.buildActions.x = LoadVector() 

490 self.process.buildActions.x.vectorKey = "x" 

491 self.process.buildActions.y = LoadVector() 

492 self.process.buildActions.y.vectorKey = "y" 

493 self.process.buildActions.z = FractionalDifference( 

494 actionA=CalcMomentSize(colXx="ixx", colYy="iyy", colXy="ixy", sizeType="trace"), 

495 actionB=CalcMomentSize(colXx="ixxPSF", colYy="iyyPSF", colXy="ixyPSF", sizeType="trace"), 

496 ) 

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

498 self.produce.plot = FocalPlanePlot() 

499 self.produce.plot.zAxisLabel = "Residuals" 

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

501 self.process.buildActions.statMask.threshold = 20 

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

503 

504 

505class EBase(AnalysisTool): 

506 """A base class for plotting ellipticity plots.""" 

507 

508 def setDefaults(self): 

509 super().setDefaults() 

510 

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

512 

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

514 

515 def visitContext(self): 

516 self.parameterizedBand = False 

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

518 

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

520 self.prep.selectors.snSelector.threshold = 100 

521 

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

523 

524 self.process.buildActions.xStars.vectorKey = "psfFlux" 

525 

526 

527class EScatter(EBase): 

528 """Base class for ellipticity scatter plots.""" 

529 

530 def setDefaults(self): 

531 super().setDefaults() 

532 

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

534 

535 self.process.calculateActions.median = MedianAction() 

536 self.process.calculateActions.median.vectorKey = "yStars" 

537 self.process.calculateActions.sigmaMad = SigmaMadAction() 

538 self.process.calculateActions.sigmaMad.vectorKey = "yStars" 

539 

540 self.produce.plot = ScatterPlotWithTwoHists() 

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

542 self.produce.plot.xAxisLabel = "PSF Magnitude (mag)" 

543 self.produce.plot.magLabel = "PSF Magnitude (mag)" 

544 

545 self.produce.metric.units = {"median": "pix", "sigmaMad": "pix"} 

546 

547 

548class E1ScatterVisit(EScatter): 

549 """Visit level scatter plot for E1.""" 

550 

551 parameterizedBand: bool = False 

552 

553 def setDefaults(self): 

554 super().setDefaults() 

555 

556 self.process.buildActions.yStarsAll = CalcE1(colXx="ixx", colYy="iyy", colXy="ixy") 

557 self.process.buildActions.yStars = DownselectVector( 

558 vectorKey="yStarsAll", selector=VectorSelector(vectorKey="starSelector") 

559 ) 

560 

561 self.produce.plot.yAxisLabel = "e1" 

562 

563 self.produce.metric.newNames = { 

564 "median": "e1_median", 

565 "sigmaMad": "e1_sigmaMad", 

566 } 

567 self.applyContext(VisitContext) 

568 

569 

570class E2ScatterVisit(EScatter): 

571 """Visit level scatter plot for E2.""" 

572 

573 parameterizedBand: bool = False 

574 

575 def setDefaults(self): 

576 super().setDefaults() 

577 

578 self.process.buildActions.yStarsAll = CalcE2(colXx="ixx", colYy="iyy", colXy="ixy") 

579 self.process.buildActions.yStars = DownselectVector( 

580 vectorKey="yStarsAll", selector=VectorSelector(vectorKey="starSelector") 

581 ) 

582 

583 self.produce.plot.yAxisLabel = "e2" 

584 

585 self.produce.metric.newNames = { 

586 "median": "e2_median", 

587 "sigmaMad": "e2_sigmaMad", 

588 } 

589 self.applyContext(VisitContext) 

590 

591 

592class ESky(EBase): 

593 """Base class for ellipticity sky plots.""" 

594 

595 def setDefaults(self): 

596 super().setDefaults() 

597 

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

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

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

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

602 

603 self.produce.plot = SkyPlot() 

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

605 self.produce.plot.xAxisLabel = "R.A. (deg)" 

606 self.produce.plot.yAxisLabel = "Dec. (deg)" 

607 

608 

609class E1SkyVisit(ESky): 

610 """A sky plot of E1 for visit level data.""" 

611 

612 parameterizedBand: bool = False 

613 

614 def setDefaults(self): 

615 super().setDefaults() 

616 

617 self.process.buildActions.zStars = CalcE1(colXx="ixx", colYy="iyy", colXy="ixy") 

618 

619 self.produce.plot.zAxisLabel = "e1" 

620 

621 self.applyContext(VisitContext) 

622 

623 

624class E2SkyVisit(ESky): 

625 """A visit level sky plot for E2.""" 

626 

627 parameterizedBand: bool = False 

628 

629 def setDefaults(self): 

630 super().setDefaults() 

631 

632 self.process.buildActions.zStars = CalcE2(colXx="ixx", colYy="iyy", colXy="ixy") 

633 

634 self.produce.plot.zAxisLabel = "e2" 

635 

636 self.applyContext(VisitContext) 

637 

638 

639class EFocalPlane(EBase): 

640 """Base class for ellipticity focal plane plots.""" 

641 

642 def setDefaults(self): 

643 super().setDefaults() 

644 

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

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

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

648 

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

650 self.process.buildActions.statMask.threshold = 20 

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

652 

653 self.produce.plot = FocalPlanePlot() 

654 

655 

656class E1FocalPlane(EFocalPlane): 

657 """A focal plane plot of E1.""" 

658 

659 parameterizedBand: bool = False 

660 

661 def setDefaults(self): 

662 super().setDefaults() 

663 

664 self.process.buildActions.z = CalcE1(colXx="ixx", colYy="iyy", colXy="ixy") 

665 self.produce.plot.zAxisLabel = "e1" 

666 

667 

668class E2FocalPlane(EFocalPlane): 

669 """A focal plane plot of E2.""" 

670 

671 parameterizedBand: bool = False 

672 

673 def setDefaults(self): 

674 super().setDefaults() 

675 

676 self.process.buildActions.z = CalcE2(colXx="ixx", colYy="iyy", colXy="ixy") 

677 self.produce.plot.zAxisLabel = "e2" 

678 

679 

680class ShapeSizeFractionalDiffFocalPlane(EFocalPlane): 

681 """A focal plane plot of shape - psf shape.""" 

682 

683 parameterizedBand: bool = False 

684 

685 def setDefaults(self): 

686 super().setDefaults() 

687 self.process.buildActions.z = FractionalDifference( 

688 actionA=CalcMomentSize(colXx="ixx", colYy="iyy", colXy="ixy"), 

689 actionB=CalcMomentSize(colXx="ixxPSF", colYy="iyyPSF", colXy="ixyPSF"), 

690 ) 

691 self.produce.plot.zAxisLabel = "Fractional size residuals (S/S_PSF - 1)"