Coverage for tests/test_diaForcedSource.py: 17%

116 statements  

« prev     ^ index     » next       coverage.py v7.2.1, created at 2023-03-12 10:45 +0000

1# This file is part of ap_association. 

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 

23import unittest 

24import unittest.mock 

25 

26from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

27import lsst.afw.geom as afwGeom 

28import lsst.afw.image as afwImage 

29import lsst.afw.table as afwTable 

30import lsst.daf.base as dafBase 

31import lsst.meas.algorithms as measAlg 

32from lsst.ap.association import \ 

33 DiaForcedSourceTask, \ 

34 make_dia_object_schema 

35import lsst.utils.tests 

36 

37 

38def create_test_dia_objects(n_points, wcs, startPos=100): 

39 """Create dummy DIASources or DIAObjects for use in our tests. 

40 

41 Parameters 

42 ---------- 

43 n_points : `int` 

44 Number of DiaObject test points to create. 

45 wcs : `lsst.afw.geom.SkyWcs` 

46 Wcs to convert RA/Dec to pixel x/y. 

47 startPos : `int` 

48 Start position to iterate from when creating test DiaObjects 

49 

50 Returns 

51 ------- 

52 test_points : `lsst.afw.table.SourceCatalog` 

53 Catalog of points to test. 

54 """ 

55 objects = afwTable.SourceCatalog(make_dia_object_schema()) 

56 

57 for src_idx in range(n_points): 

58 src = objects.addNew() 

59 src['id'] = src_idx 

60 src.setCoord(wcs.pixelToSky(startPos + src_idx, 

61 startPos + src_idx)) 

62 

63 return objects 

64 

65 

66class TestDiaForcedSource(unittest.TestCase): 

67 

68 def setUp(self): 

69 # metadata taken from CFHT data 

70 # v695856-e0/v695856-e0-c000-a00.sci_img.fits 

71 self.metadata = dafBase.PropertySet() 

72 

73 self.metadata.set("SIMPLE", "T") 

74 self.metadata.set("BITPIX", -32) 

75 self.metadata.set("NAXIS", 2) 

76 self.metadata.set("NAXIS1", 1024) 

77 self.metadata.set("NAXIS2", 1153) 

78 self.metadata.set("RADECSYS", 'FK5') 

79 self.metadata.set("EQUINOX", 2000.) 

80 

81 self.metadata.setDouble("CRVAL1", 215.604025685476) 

82 self.metadata.setDouble("CRVAL2", 53.1595451514076) 

83 self.metadata.setDouble("CRPIX1", 1109.99981456774) 

84 self.metadata.setDouble("CRPIX2", 560.018167811613) 

85 self.metadata.set("CTYPE1", 'RA---SIN') 

86 self.metadata.set("CTYPE2", 'DEC--SIN') 

87 

88 self.metadata.setDouble("CD1_1", 5.10808596133527E-05) 

89 self.metadata.setDouble("CD1_2", 1.85579539217196E-07) 

90 self.metadata.setDouble("CD2_2", -5.10281493481982E-05) 

91 self.metadata.setDouble("CD2_1", -8.27440751733828E-07) 

92 

93 self.wcs = afwGeom.makeSkyWcs(self.metadata) 

94 

95 self.calibration = 10000 

96 self.calibrationErr = 100 

97 self.exposureId = 1234 

98 self.exposureTime = 200. 

99 self.imageSize = [1024, 1153] 

100 self.dateTime = "2014-05-13T17:00:00.000000000" 

101 

102 # Make images with one source in them and distinct values and 

103 # variance for each image. 

104 # Direct Image 

105 source_image = afwImage.MaskedImageF( 

106 lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1)) 

107 source_image.image[100, 100, afwImage.LOCAL] = 10 

108 source_image.getVariance().set(1) 

109 bbox = lsst.geom.BoxI( 

110 lsst.geom.PointI(1, 1), 

111 lsst.geom.ExtentI(self.imageSize[0], 

112 self.imageSize[1])) 

113 masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL) 

114 self.exposure = afwImage.makeExposure(masked_image, self.wcs) 

115 

116 detector = DetectorWrapper( 

117 id=23, bbox=self.exposure.getBBox()).detector 

118 visit = afwImage.VisitInfo( 

119 exposureId=self.exposureId, 

120 exposureTime=self.exposureTime, 

121 date=dafBase.DateTime(self.dateTime, 

122 dafBase.DateTime.Timescale.TAI)) 

123 self.exposure.setDetector(detector) 

124 self.exposure.getInfo().setVisitInfo(visit) 

125 self.exposure.setFilterLabel(afwImage.FilterLabel(band='g', physical='g.MP9401')) 

126 self.exposure.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr)) 

127 

128 # Difference Image 

129 source_image = afwImage.MaskedImageF( 

130 lsst.geom.ExtentI(self.imageSize[0] + 1, self.imageSize[1] + 1)) 

131 source_image.image[100, 100, afwImage.LOCAL] = 20 

132 source_image.getVariance().set(2) 

133 bbox = lsst.geom.BoxI( 

134 lsst.geom.PointI(1, 1), 

135 lsst.geom.ExtentI(self.imageSize[0], 

136 self.imageSize[1])) 

137 masked_image = afwImage.MaskedImageF(source_image, bbox, afwImage.LOCAL) 

138 self.diffim = afwImage.makeExposure(masked_image, self.wcs) 

139 self.diffim.setDetector(detector) 

140 self.diffim.getInfo().setVisitInfo(visit) 

141 self.diffim.setFilterLabel(afwImage.FilterLabel(band='g', physical='g.MP9401')) 

142 self.diffim.setPhotoCalib(afwImage.PhotoCalib(self.calibration, self.calibrationErr)) 

143 

144 self.expIdBits = 16 

145 

146 FWHM = 5 

147 psf = measAlg.DoubleGaussianPsf(15, 15, FWHM/(2*np.sqrt(2*np.log(2)))) 

148 self.exposure.setPsf(psf) 

149 self.diffim.setPsf(psf) 

150 

151 self.testDiaObjects = create_test_dia_objects(5, self.wcs) 

152 # Add additional diaObjects that are outside of the above difference 

153 # and calexp visit images. 

154 # xy outside 

155 src = self.testDiaObjects.addNew() 

156 src['id'] = 10000000 

157 src.setCoord(self.wcs.pixelToSky(-100000, 

158 -100000)) 

159 # y outside 

160 src = self.testDiaObjects.addNew() 

161 src['id'] = 10000001 

162 src.setCoord(self.wcs.pixelToSky(100, 

163 -100000)) 

164 # x outside 

165 src = self.testDiaObjects.addNew() 

166 src['id'] = 10000002 

167 src.setCoord(self.wcs.pixelToSky(-100000, 

168 100)) 

169 # Ids of objects that were "updated" during "ap_association" 

170 # processing. 

171 self.updatedTestIds = np.array([1, 2, 3, 4, 10000001], dtype=np.uint64) 

172 # Expecdted number of sources is the number of updated ids plus 

173 # any that are within the CCD footprint but are not in the 

174 # above list of ids. 

175 self.expectedDiaForcedSources = 6 

176 

177 self.expected_n_columns = 11 

178 

179 def testRun(self): 

180 """Test that forced source catalogs are successfully created and have 

181 sensible values. 

182 """ 

183 test_objects = self._convert_to_pandas(self.testDiaObjects) 

184 test_objects.rename(columns={"id": "diaObjectId"}, 

185 inplace=True) 

186 test_objects.set_index("diaObjectId", inplace=True, drop=False) 

187 dfs = DiaForcedSourceTask() 

188 dia_forced_sources = dfs.run( 

189 test_objects, self.updatedTestIds, self.expIdBits, self.exposure, self.diffim) 

190 

191 direct_values = [199854.48417094944, 160097.40719241602, 

192 82299.17897267535, 27148.604434624354, 

193 5746.988388215507] 

194 direct_var = [75240.939811168, 75231.42933749466, 

195 75218.89495113207, 75214.88248249644, 

196 75214.41447602339] 

197 diff_values = [399708.9683418989, 320194.81438483205, 

198 164598.3579453507, 54297.20886924871, 

199 11493.976776431015] 

200 diff_var = [106444.28782374493, 106417.39592887461, 

201 106381.94840437356, 106370.59980584883, 

202 106369.27608815048] 

203 

204 # Should be number of test objects minus one as one object is purposely 

205 # outside of the ccd area. 

206 self.assertEqual(len(dia_forced_sources), self.expectedDiaForcedSources) 

207 self.assertEqual(len(dia_forced_sources.columns), 

208 self.expected_n_columns) 

209 

210 for (diaFS_id, diaFS), testObj, dirVal, diffVal, dirVar, diffVar in zip(dia_forced_sources.iterrows(), 

211 self.testDiaObjects, 

212 direct_values, 

213 diff_values, 

214 direct_var, 

215 diff_var): 

216 self.assertAlmostEqual(diaFS["psFlux"] / diffVal, 1.) 

217 self.assertAlmostEqual(diaFS["psFluxErr"] / diffVar, 1.) 

218 

219 self.assertAlmostEqual(diaFS["totFlux"] / dirVal, 1.) 

220 self.assertAlmostEqual(diaFS["totFluxErr"] / dirVar, 1.) 

221 

222 self.assertEqual(diaFS["ccdVisitId"], self.exposureId) 

223 

224 def _convert_to_pandas(self, dia_objects): 

225 """Convert input afw table to pandas. 

226 

227 Parameters 

228 ---------- 

229 dia_objects : `lsst.afw.table.SourceCatalog` 

230 Catalog to convert 

231 

232 Returns 

233 ------- 

234 output_catalog : `pandas.DataFrame` 

235 Converted catalog 

236 """ 

237 output_catalog = dia_objects.asAstropy().to_pandas() 

238 output_catalog.rename(columns={"id": "diaObjectId", 

239 "coord_ra": "ra", 

240 "coord_dec": "decl"}, 

241 inplace=True) 

242 

243 output_catalog.loc[:, "ra"] = np.degrees(output_catalog["ra"]) 

244 output_catalog.loc[:, "decl"] = np.degrees(output_catalog["decl"]) 

245 

246 return output_catalog 

247 

248 

249class MemoryTester(lsst.utils.tests.MemoryTestCase): 

250 pass 

251 

252 

253def setup_module(module): 

254 lsst.utils.tests.init() 

255 

256 

257if __name__ == "__main__": 257 ↛ 258line 257 didn't jump to line 258, because the condition on line 257 was never true

258 lsst.utils.tests.init() 

259 unittest.main()