Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of pipe_tasks. 

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 

22 

23import numpy as np 

24from scipy.spatial import cKDTree 

25 

26from lsst.geom import Box2D, radians, SpherePoint 

27import lsst.pex.config as pexConfig 

28from lsst.pipe.base import PipelineTask, PipelineTaskConnections, Struct 

29import lsst.pipe.base.connectionTypes as connTypes 

30from lsst.pipe.tasks.insertFakes import InsertFakesConfig 

31 

32__all__ = ["MatchFakesTask", 

33 "MatchFakesConfig", 

34 "MatchFakesConnections"] 

35 

36 

37class MatchFakesConnections(PipelineTaskConnections, 

38 defaultTemplates={"coaddName": "deep", 

39 "fakesType": "fakes_"}, 

40 dimensions=("tract", 

41 "skymap", 

42 "instrument", 

43 "visit", 

44 "detector")): 

45 fakeCat = connTypes.Input( 

46 doc="Catalog of fake sources to draw inputs from.", 

47 name="{fakesType}fakeSourceCat", 

48 storageClass="DataFrame", 

49 dimensions=("tract", "skymap") 

50 ) 

51 diffIm = connTypes.Input( 

52 doc="Difference image on which the DiaSources were detected.", 

53 name="{fakesType}{coaddName}Diff_differenceExp", 

54 storageClass="ExposureF", 

55 dimensions=("instrument", "visit", "detector"), 

56 ) 

57 associatedDiaSources = connTypes.Input( 

58 doc="Optional output storing the DiaSource catalog after matching and " 

59 "SDMification.", 

60 name="{fakesType}{coaddName}Diff_assocDiaSrc", 

61 storageClass="DataFrame", 

62 dimensions=("instrument", "visit", "detector"), 

63 ) 

64 matchedDiaSources = connTypes.Output( 

65 doc="", 

66 name="{fakesType}{coaddName}Diff_matchDiaSrc", 

67 storageClass="DataFrame", 

68 dimensions=("instrument", "visit", "detector"), 

69 ) 

70 

71 

72class MatchFakesConfig( 

73 InsertFakesConfig, 

74 pipelineConnections=MatchFakesConnections): 

75 """Config for MatchFakesTask. 

76 """ 

77 matchDistanceArcseconds = pexConfig.RangeField( 

78 doc="Distance in arcseconds to ", 

79 dtype=float, 

80 default=0.5, 

81 min=0, 

82 max=10, 

83 ) 

84 

85 

86class MatchFakesTask(PipelineTask): 

87 """Create and store a set of spatially uniform star fakes over the sphere. 

88 Additionally assign random magnitudes to said 

89 fakes and assign them to be inserted into either a visit exposure or 

90 template exposure. 

91 """ 

92 

93 _DefaultName = "matchFakes" 

94 ConfigClass = MatchFakesConfig 

95 

96 def runQuantum(self, butlerQC, inputRefs, outputRefs): 

97 inputs = butlerQC.get(inputRefs) 

98 

99 outputs = self.run(**inputs) 

100 butlerQC.put(outputs, outputRefs) 

101 

102 def run(self, fakeCat, diffIm, associatedDiaSources): 

103 """Match fakes to detected diaSources within a difference image bound. 

104 

105 Parameters 

106 ---------- 

107 fakeCat : `pandas.DataFrame` 

108 Catalog of fakes to match to detected diaSources. 

109 diffIm : `lsst.afw.image.Exposure` 

110 Difference image where ``associatedDiaSources`` were detected in. 

111 associatedDiaSources : `pandas.DataFrame` 

112 Catalog of difference image sources detected in ``diffIm``. 

113 

114 Returns 

115 ------- 

116 result : `lsst.pipe.base.Struct` 

117 Results struct with components. 

118 

119 - ``matchedDiaSources`` : Fakes matched to input diaSources. Has 

120 length of ``fakeCat``. (`pandas.DataFrame`) 

121 """ 

122 trimmedFakes = self._trimFakeCat(fakeCat, diffIm) 

123 nPossibleFakes = len(trimmedFakes) 

124 

125 fakeVects = self._getVectors(trimmedFakes[self.config.raColName], 

126 trimmedFakes[self.config.decColName]) 

127 diaSrcVects = self._getVectors( 

128 np.radians(associatedDiaSources.loc[:, "ra"]), 

129 np.radians(associatedDiaSources.loc[:, "decl"])) 

130 

131 diaSrcTree = cKDTree(diaSrcVects) 

132 dist, idxs = diaSrcTree.query( 

133 fakeVects, 

134 distance_upper_bound=np.radians(self.config.matchDistanceArcseconds / 3600)) 

135 nFakesFound = np.isfinite(dist).sum() 

136 

137 self.log.info("Found %d out of %d possible.", nFakesFound, nPossibleFakes) 

138 diaSrcIds = associatedDiaSources.iloc[np.where(np.isfinite(dist), idxs, 0)]["diaSourceId"].to_numpy() 

139 matchedFakes = trimmedFakes.assign(diaSourceId=np.where(np.isfinite(dist), diaSrcIds, 0)) 

140 

141 return Struct( 

142 matchedDiaSources=matchedFakes.merge( 

143 associatedDiaSources.reset_index(drop=True), on="diaSourceId", how="left") 

144 ) 

145 

146 def _trimFakeCat(self, fakeCat, image): 

147 """Trim the fake cat to about the size of the input image. 

148 

149 Parameters 

150 ---------- 

151 fakeCat : `pandas.core.frame.DataFrame` 

152 The catalog of fake sources to be input 

153 image : `lsst.afw.image.exposure.exposure.ExposureF` 

154 The image into which the fake sources should be added 

155 

156 Returns 

157 ------- 

158 fakeCat : `pandas.core.frame.DataFrame` 

159 The original fakeCat trimmed to the area of the image 

160 """ 

161 wcs = image.getWcs() 

162 

163 bbox = Box2D(image.getBBox()) 

164 

165 def trim(row): 

166 coord = SpherePoint(row[self.config.raColName], 

167 row[self.config.decColName], 

168 radians) 

169 cent = wcs.skyToPixel(coord) 

170 return bbox.contains(cent) 

171 

172 return fakeCat[fakeCat.apply(trim, axis=1)] 

173 

174 def _getVectors(self, ras, decs): 

175 """Convert ra dec to unit vectors on the sphere. 

176 

177 Parameters 

178 ---------- 

179 ras : `numpy.ndarray`, (N,) 

180 RA coordinates in radians. 

181 decs : `numpy.ndarray`, (N,) 

182 Dec coordinates in radians. 

183 

184 Returns 

185 ------- 

186 vectors : `numpy.ndarray`, (N, 3) 

187 Vectors on the unit sphere for the given RA/DEC values. 

188 """ 

189 vectors = np.empty((len(ras), 3)) 

190 

191 vectors[:, 2] = np.sin(decs) 

192 vectors[:, 0] = np.cos(decs) * np.cos(ras) 

193 vectors[:, 1] = np.cos(decs) * np.sin(ras) 

194 

195 return vectors