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

84 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-27 04:18 -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 RhoStatisticsPlot 

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

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

40from ..actions.vector import ( 

41 CalcE, 

42 CalcEDiff, 

43 CalcMomentSize, 

44 CalcRhoStatistics, 

45 CoaddPlotFlagSelector, 

46 ConvertFluxToMag, 

47 DownselectVector, 

48 FractionalDifference, 

49 LoadVector, 

50 SnSelector, 

51 StarSelector, 

52 VectorSelector, 

53) 

54from ..interfaces import AnalysisTool, KeyedData, VectorAction 

55 

56 

57class ShapeSizeFractionalScalars(KeyedScalars): 

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

59 

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

61 

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

63 

64 def setDefaults(self): 

65 super().setDefaults() 

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

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

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

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

70 

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

72 mask = kwargs.get("mask") 

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

74 if mask is not None: 

75 mask &= selection 

76 else: 

77 mask = selection 

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

79 

80 

81class BasePsfResidual(AnalysisTool): 

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

83 

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

85 `BasePsfResidualMetric` to share common default configuration. 

86 """ 

87 

88 def setDefaults(self): 

89 super().setDefaults() 

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

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

92 

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

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

95 

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

97 

98 self.process.buildActions.fracDiff = FractionalDifference( 

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

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

101 ) 

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

103 # component. 

104 self.process.buildActions.eDiff = CalcEDiff( 

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

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

107 ) 

108 self.process.buildActions.e1Diff = self.process.buildActions.eDiff 

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

110 self.process.buildActions.e2Diff = self.process.buildActions.eDiff 

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

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

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

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

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

116 

117 self.process.filterActions.xStars = DownselectVector( 

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

119 ) 

120 # downselect the psfFlux as well 

121 self.process.filterActions.psfFlux = DownselectVector( 

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

123 ) 

124 self.process.filterActions.psfFluxErr = DownselectVector( 

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

126 ) 

127 

128 self.process.filterActions.patch = DownselectVector( 

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

130 ) 

131 

132 self.process.calculateActions.stars = ScatterPlotStatsAction( 

133 vectorKey="yStars", 

134 ) 

135 # use the downselected psfFlux 

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

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

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

139 

140 self.produce.metric.units = { 

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

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

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

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

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

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

147 } 

148 

149 self.produce.plot = ScatterPlotWithTwoHists() 

150 

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

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

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

154 

155 

156class ShapeSizeFractionalDiff(BasePsfResidual): 

157 def setDefaults(self): 

158 super().setDefaults() 

159 self.process.filterActions.yStars = DownselectVector( 

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

161 ) 

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

163 

164 

165class E1Diff(BasePsfResidual): 

166 def setDefaults(self): 

167 super().setDefaults() 

168 self.process.filterActions.yStars = DownselectVector( 

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

170 ) 

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

172 

173 

174class E2Diff(BasePsfResidual): 

175 def setDefaults(self): 

176 super().setDefaults() 

177 self.process.filterActions.yStars = DownselectVector( 

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

179 ) 

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

181 

182 

183class RhoStatistics(AnalysisTool): 

184 parameterizedBand: bool = True 

185 

186 def setDefaults(self): 

187 super().setDefaults() 

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

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

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

191 

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

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

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

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

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

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

198 

199 self.produce.plot = RhoStatisticsPlot()