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

102 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2024-05-16 04:37 -0700

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

27 "ShapeSizeFractionalDiff", 

28 "E1Diff", 

29 "E2Diff", 

30 "RhoStatistics", 

31 "RelativeSizeResidualPlot", 

32) 

33 

34from lsst.pex.config import Field 

35from lsst.pex.config.configurableActions import ConfigurableActionField 

36 

37from ..actions.keyedData import KeyedScalars 

38from ..actions.plot import FocalPlanePlot 

39from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlot 

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

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

42from ..actions.vector import ( 

43 CalcE, 

44 CalcEDiff, 

45 CalcMomentSize, 

46 CalcRhoStatistics, 

47 CoaddPlotFlagSelector, 

48 ConvertFluxToMag, 

49 DownselectVector, 

50 FractionalDifference, 

51 LoadVector, 

52 SnSelector, 

53 StarSelector, 

54 VectorSelector, 

55 VisitPlotFlagSelector, 

56) 

57from ..interfaces import AnalysisTool, KeyedData, VectorAction 

58 

59 

60class ShapeSizeFractionalScalars(KeyedScalars): 

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

62 

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

64 

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

66 

67 def setDefaults(self): 

68 super().setDefaults() 

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

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

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

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

73 

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

75 mask = kwargs.get("mask") 

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

77 if mask is not None: 

78 mask &= selection 

79 else: 

80 mask = selection 

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

82 

83 

84class BasePsfResidual(AnalysisTool): 

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

86 

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

88 `BasePsfResidualMetric` to share common default configuration. 

89 """ 

90 

91 def setDefaults(self): 

92 super().setDefaults() 

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

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

95 

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

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

98 

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

100 

101 self.process.buildActions.fracDiff = FractionalDifference( 

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

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

104 ) 

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

106 # component. 

107 self.process.buildActions.eDiff = CalcEDiff( 

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

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

110 ) 

111 self.process.buildActions.e1Diff = self.process.buildActions.eDiff 

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

113 self.process.buildActions.e2Diff = self.process.buildActions.eDiff 

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

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

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

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

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

119 

120 self.process.filterActions.xStars = DownselectVector( 

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

122 ) 

123 # downselect the psfFlux as well 

124 self.process.filterActions.psfFlux = DownselectVector( 

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

126 ) 

127 self.process.filterActions.psfFluxErr = DownselectVector( 

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

129 ) 

130 

131 self.process.filterActions.patch = DownselectVector( 

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

133 ) 

134 

135 self.process.calculateActions.stars = ScatterPlotStatsAction( 

136 vectorKey="yStars", 

137 ) 

138 # use the downselected psfFlux 

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

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

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

142 

143 self.produce.metric.units = { 

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

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

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

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

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

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

150 } 

151 

152 self.produce.plot = ScatterPlotWithTwoHists() 

153 

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

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

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

157 

158 

159class ShapeSizeFractionalDiff(BasePsfResidual): 

160 def setDefaults(self): 

161 super().setDefaults() 

162 self.process.filterActions.yStars = DownselectVector( 

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

164 ) 

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

166 

167 

168class E1Diff(BasePsfResidual): 

169 def setDefaults(self): 

170 super().setDefaults() 

171 self.process.filterActions.yStars = DownselectVector( 

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

173 ) 

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

175 

176 

177class E2Diff(BasePsfResidual): 

178 def setDefaults(self): 

179 super().setDefaults() 

180 self.process.filterActions.yStars = DownselectVector( 

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

182 ) 

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

184 

185 

186class RhoStatistics(AnalysisTool): 

187 parameterizedBand: bool = True 

188 

189 def setDefaults(self): 

190 super().setDefaults() 

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

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

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

194 

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

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

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

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

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

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

201 

202 self.produce.plot = RhoStatisticsPlot() 

203 

204 

205class RelativeSizeResidualPlot(AnalysisTool): 

206 def setDefaults(self): 

207 super().setDefaults() 

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

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

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

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

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

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

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

215 self.process.buildActions.z = FractionalDifference( 

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

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

218 ) 

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

220 self.produce.plot = FocalPlanePlot() 

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

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

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

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