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

91 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-02 11:54 -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) 

32 

33from lsst.pex.config import Field 

34from lsst.pex.config.configurableActions import ConfigurableActionField 

35 

36from ..actions.keyedData import KeyedScalars 

37from ..actions.plot.rhoStatisticsPlot import RhoStatisticsPlotAction 

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

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

40from ..actions.vector import ( 

41 CalcE, 

42 CalcEDiff, 

43 CalcRhoStatistics, 

44 CalcShapeSize, 

45 CoaddPlotFlagSelector, 

46 DownselectVector, 

47 FlagSelector, 

48 FractionalDifference, 

49 LoadVector, 

50 MagColumnNanoJansky, 

51 SnSelector, 

52 StarSelector, 

53 VectorSelector, 

54) 

55from ..interfaces import AnalysisTool, KeyedData, VectorAction 

56 

57 

58class ShapeSizeFractionalScalars(KeyedScalars): 

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

60 

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

62 

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

64 

65 def setDefaults(self): 

66 super().setDefaults() 

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

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

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

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

71 

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

73 mask = kwargs.get("mask") 

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

75 if mask is not None: 

76 mask &= selection 

77 else: 

78 mask = selection 

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

80 

81 

82class BasePsfResidual(AnalysisTool): 

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

84 

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

86 `BasePsfResidualMetric` to share common default configuration. 

87 """ 

88 

89 def setDefaults(self): 

90 super().setDefaults() 

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

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

93 

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

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

96 

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

98 

99 self.process.buildActions.fracDiff = FractionalDifference( 

100 actionA=CalcShapeSize(colXx="{band}_ixx", colYy="{band}_iyy", colXy="{band}_ixy"), 

101 actionB=CalcShapeSize(colXx="{band}_ixxPSF", colYy="{band}_iyyPSF", colXy="{band}_ixyPSF"), 

102 ) 

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

104 # component. 

105 self.process.buildActions.eDiff = CalcEDiff( 

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

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

108 ) 

109 self.process.buildActions.e1Diff = self.process.buildActions.eDiff 

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

111 self.process.buildActions.e2Diff = self.process.buildActions.eDiff 

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

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

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

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

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

117 

118 self.process.filterActions.xStars = DownselectVector( 

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

120 ) 

121 # downselect the psfFlux as well 

122 self.process.filterActions.psfFlux = DownselectVector( 

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

124 ) 

125 self.process.filterActions.psfFluxErr = DownselectVector( 

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

127 ) 

128 

129 self.process.filterActions.patch = DownselectVector( 

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

131 ) 

132 

133 self.process.calculateActions.stars = ScatterPlotStatsAction( 

134 vectorKey="yStars", 

135 ) 

136 # use the downselected psfFlux 

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

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

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

140 

141 self.produce.metric.units = { 

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

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

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

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

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

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

148 } 

149 

150 self.produce.plot = ScatterPlotWithTwoHists() 

151 

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

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

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

155 self.produce.plot.addSummaryPlot = True 

156 

157 

158class ShapeSizeFractionalDiff(BasePsfResidual): 

159 def setDefaults(self): 

160 super().setDefaults() 

161 self.process.filterActions.yStars = DownselectVector( 

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

163 ) 

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

165 

166 

167class E1Diff(BasePsfResidual): 

168 def setDefaults(self): 

169 super().setDefaults() 

170 self.process.filterActions.yStars = DownselectVector( 

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

172 ) 

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

174 

175 

176class E2Diff(BasePsfResidual): 

177 def setDefaults(self): 

178 super().setDefaults() 

179 self.process.filterActions.yStars = DownselectVector( 

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

181 ) 

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

183 

184 

185class RhoStatistics(AnalysisTool): 

186 parameterizedBand: bool = True 

187 

188 def setDefaults(self): 

189 super().setDefaults() 

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

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

192 

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

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

195 

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

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

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

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

200 self.process.buildActions.starSelector = FlagSelector(selectWhenTrue=("{band}_calib_psf_used",)) 

201 

202 self.process.filterActions.xStars = DownselectVector( 

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

204 ) 

205 # downselect the psfFlux as well 

206 self.process.filterActions.psfFlux = DownselectVector( 

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

208 ) 

209 self.process.filterActions.psfFluxErr = DownselectVector( 

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

211 ) 

212 

213 self.process.filterActions.patch = DownselectVector( 

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

215 ) 

216 

217 self.process.calculateActions.stars = CalcRhoStatistics() 

218 

219 self.process.calculateActions.stars.treecorr.nbins = 10 

220 self.process.calculateActions.stars.treecorr.min_sep = 0.1 

221 self.process.calculateActions.stars.treecorr.max_sep = 100.0 

222 self.process.calculateActions.stars.treecorr.sep_units = "arcmin" 

223 

224 self.produce.plot = RhoStatisticsPlotAction()