Coverage for python/lsst/ip/diffim/diaCatalogSourceSelector.py: 19%

93 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-10-26 16:44 +0000

1# This file is part of ip_diffim. 

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 

22import numpy as np 

23 

24from lsst.pipe.base import Struct 

25import lsst.pex.config as pexConfig 

26import lsst.afw.display as afwDisplay 

27import lsst.meas.algorithms as measAlg 

28 

29__all__ = ["DiaCatalogSourceSelectorConfig", "DiaCatalogSourceSelectorTask"] 

30 

31 

32class DiaCatalogSourceSelectorConfig(measAlg.BaseStarSelectorConfig): 

33 # Selection cuts on the input source catalog 

34 fluxLim = pexConfig.Field( 34 ↛ exitline 34 didn't jump to the function exit

35 doc="specify the minimum psfFlux for good Kernel Candidates", 

36 dtype=float, 

37 default=0.0, 

38 check=lambda x: x >= 0.0, 

39 ) 

40 fluxMax = pexConfig.Field( 40 ↛ exitline 40 didn't jump to the function exit

41 doc="specify the maximum psfFlux for good Kernel Candidates (ignored if == 0)", 

42 dtype=float, 

43 default=0.0, 

44 check=lambda x: x >= 0.0, 

45 ) 

46 # Selection cuts on the reference catalog 

47 selectStar = pexConfig.Field( 

48 doc="Select objects that are flagged as stars", 

49 dtype=bool, 

50 default=True 

51 ) 

52 selectGalaxy = pexConfig.Field( 

53 doc="Select objects that are flagged as galaxies", 

54 dtype=bool, 

55 default=False 

56 ) 

57 includeVariable = pexConfig.Field( 

58 doc="Include objects that are known to be variable", 

59 dtype=bool, 

60 default=False 

61 ) 

62 grMin = pexConfig.Field( 

63 doc="Minimum g-r color for selection (inclusive)", 

64 dtype=float, 

65 default=0.0 

66 ) 

67 grMax = pexConfig.Field( 

68 doc="Maximum g-r color for selection (inclusive)", 

69 dtype=float, 

70 default=3.0 

71 ) 

72 

73 def setDefaults(self): 

74 measAlg.BaseStarSelectorConfig.setDefaults(self) 

75 self.badFlags = [ 

76 "base_PixelFlags_flag_edge", 

77 "base_PixelFlags_flag_interpolatedCenter", 

78 "base_PixelFlags_flag_saturatedCenter", 

79 "slot_Centroid_flag", 

80 ] 

81 

82 

83class CheckSource(object): 

84 """A functor to check whether a source has any flags set that should cause it to be labeled bad.""" 

85 

86 def __init__(self, table, fluxLim, fluxMax, badFlags): 

87 self.keys = [table.getSchema().find(name).key for name in badFlags] 

88 self.fluxLim = fluxLim 

89 self.fluxMax = fluxMax 

90 

91 def __call__(self, source): 

92 for k in self.keys: 

93 if source.get(k): 

94 return False 

95 if self.fluxLim is not None and source.getPsfInstFlux() < self.fluxLim: # ignore faint objects 

96 return False 

97 if self.fluxMax != 0.0 and source.getPsfInstFlux() > self.fluxMax: # ignore bright objects 

98 return False 

99 return True 

100 

101 

102@pexConfig.registerConfigurable("diaCatalog", measAlg.sourceSelectorRegistry) 

103class DiaCatalogSourceSelectorTask(measAlg.BaseSourceSelectorTask): 

104 """A task that selects sources for Kernel candidates. 

105 

106 A naive star selector based on second moments. Use with caution. 

107 

108 Notes 

109 ----- 

110 Debug Variables 

111 

112 DiaCatalogSourceSelectorTask has a debug dictionary with the following keys: 

113 

114 display : `bool` 

115 if True display debug information 

116 displayExposure : `bool` 

117 if True display exposure 

118 pauseAtEnd `bool` 

119 if True wait after displaying everything and wait for user input 

120 

121 Examples 

122 -------- 

123 For example, put something like: 

124 

125 .. code-block:: py 

126 

127 import lsstDebug 

128 def DebugInfo(name): 

129 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 

130 if name.endswith("diaCatalogSourceSelector"): 

131 di.display = True 

132 

133 return di 

134 

135 lsstDebug.Info = DebugInfo 

136 

137 into your `debug.py` file and run your task with the `--debug` flag. 

138 """ 

139 ConfigClass = DiaCatalogSourceSelectorConfig 

140 usesMatches = True # selectStars uses (requires) its matches argument 

141 

142 def selectSources(self, sourceCat, matches=None, exposure=None): 

143 """Return a selection of sources for Kernel candidates. 

144 

145 Parameters 

146 ---------- 

147 sourceCat : `lsst.afw.table.SourceCatalog` 

148 Catalog of sources to select from. 

149 This catalog must be contiguous in memory. 

150 matches : `list` of `lsst.afw.table.ReferenceMatch` 

151 A match vector as produced by meas_astrom. 

152 exposure : `lsst.afw.image.Exposure` or None 

153 The exposure the catalog was built from; used for debug display. 

154 

155 Returns 

156 ------- 

157 struct : `lsst.pipe.base.Struct` 

158 The struct contains the following data: 

159 

160 - selected : `array` of `bool`` 

161 Boolean array of sources that were selected, same length as 

162 sourceCat. 

163 """ 

164 import lsstDebug 

165 display = lsstDebug.Info(__name__).display 

166 displayExposure = lsstDebug.Info(__name__).displayExposure 

167 pauseAtEnd = lsstDebug.Info(__name__).pauseAtEnd 

168 

169 if matches is None: 

170 raise RuntimeError("DiaCatalogSourceSelector requires matches") 

171 

172 mi = exposure.getMaskedImage() 

173 

174 if display and displayExposure: 

175 disp = afwDisplay.Display(frame=lsstDebug.frame) 

176 disp.mtv(mi, title="Kernel candidates") 

177 # 

178 # Look for flags in each Source 

179 # 

180 isGoodSource = CheckSource(sourceCat, self.config.fluxLim, self.config.fluxMax, self.config.badFlags) 

181 

182 # Go through and find all the acceptable candidates in the catalogue 

183 selected = np.zeros(len(sourceCat), dtype=bool) 

184 

185 if display and displayExposure: 

186 symbs = [] 

187 ctypes = [] 

188 

189 doColorCut = True 

190 

191 refSchema = matches[0][0].schema 

192 rRefFluxField = measAlg.getRefFluxField(refSchema, "r") 

193 gRefFluxField = measAlg.getRefFluxField(refSchema, "g") 

194 for i, (ref, source, d) in enumerate(matches): 

195 if not isGoodSource(source): 

196 if display and displayExposure: 

197 symbs.append("+") 

198 ctypes.append(afwDisplay.RED) 

199 else: 

200 isStar = not ref.get("resolved") 

201 isVar = not ref.get("photometric") 

202 gMag = None 

203 rMag = None 

204 if doColorCut: 

205 try: 

206 gMag = -2.5 * np.log10(ref.get(gRefFluxField)) 

207 rMag = -2.5 * np.log10(ref.get(rRefFluxField)) 

208 except KeyError: 

209 self.log.warning("Cannot cut on color info; fields 'g' and 'r' do not exist") 

210 doColorCut = False 

211 isRightColor = True 

212 else: 

213 isRightColor = (gMag-rMag) >= self.config.grMin and (gMag-rMag) <= self.config.grMax 

214 

215 isRightType = (self.config.selectStar and isStar) or (self.config.selectGalaxy and not isStar) 

216 isRightVar = (self.config.includeVariable) or (self.config.includeVariable is isVar) 

217 if isRightType and isRightVar and isRightColor: 

218 selected[i] = True 

219 if display and displayExposure: 

220 symbs.append("+") 

221 ctypes.append(afwDisplay.GREEN) 

222 elif display and displayExposure: 

223 symbs.append("o") 

224 ctypes.append(afwDisplay.BLUE) 

225 

226 if display and displayExposure: 

227 disp = afwDisplay.Display(frame=lsstDebug.frame) 

228 with disp.Buffering(): 

229 for (ref, source, d), symb, ctype in zip(matches, symbs, ctypes): 

230 disp.dot(symb, source.getX() - mi.getX0(), source.getY() - mi.getY0(), 

231 size=4, ctype=ctype) 

232 

233 if display: 

234 lsstDebug.frame += 1 

235 if pauseAtEnd: 

236 input("Continue? y[es] p[db] ") 

237 

238 return Struct(selected=selected)