Coverage for tests/test_diaForcedSource.py: 18%

106 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-05 04:23 -0700

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 pandas as pd 

24import unittest 

25import unittest.mock 

26 

27import pandas 

28 

29from lsst.afw.cameraGeom.testUtils import DetectorWrapper 

30import lsst.afw.geom as afwGeom 

31import lsst.afw.image as afwImage 

32import lsst.daf.base as dafBase 

33import lsst.meas.algorithms as measAlg 

34from lsst.ap.association import DiaForcedSourceTask 

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 : `pandas.DataFrame` 

53 Catalog of points to test. 

54 """ 

55 ids = np.arange(n_points, dtype=np.int64) 

56 points = [wcs.pixelToSky(startPos + src_idx, startPos + src_idx) for src_idx in ids] 

57 ra = np.array([point.getRa().asDegrees() for point in points], dtype=float) 

58 decl = np.array([point.getDec().asDegrees() for point in points], dtype=float) 

59 

60 objects = pandas.DataFrame({ 

61 "diaObjectId": ids, 

62 "ra": ra, 

63 "decl": decl 

64 }) 

65 

66 return objects 

67 

68 

69class TestDiaForcedSource(unittest.TestCase): 

70 

71 def setUp(self): 

72 # metadata taken from CFHT data 

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

74 self.metadata = dafBase.PropertySet() 

75 

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

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

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

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

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

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

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

83 

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

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

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

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

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

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

90 

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

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

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

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

95 

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

97 

98 self.calibration = 10000 

99 self.calibrationErr = 100 

100 self.exposureId = 1234 

101 self.exposureTime = 200. 

102 self.imageSize = [1024, 1153] 

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

104 

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

106 # variance for each image. 

107 # Direct Image 

108 source_image = afwImage.MaskedImageF( 

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

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

111 source_image.getVariance().set(1) 

112 bbox = lsst.geom.BoxI( 

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

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

115 self.imageSize[1])) 

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

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

118 

119 detector = DetectorWrapper( 

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

121 visit = afwImage.VisitInfo( 

122 exposureId=self.exposureId, 

123 exposureTime=self.exposureTime, 

124 date=dafBase.DateTime(self.dateTime, 

125 dafBase.DateTime.Timescale.TAI)) 

126 self.exposure.info.id = self.exposureId 

127 self.exposure.setDetector(detector) 

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

129 self.exposure.setFilter(afwImage.FilterLabel(band='g', physical='g.MP9401')) 

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

131 

132 # Difference Image 

133 source_image = afwImage.MaskedImageF( 

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

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

136 source_image.getVariance().set(2) 

137 bbox = lsst.geom.BoxI( 

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

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

140 self.imageSize[1])) 

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

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

143 self.diffim.info.id = self.exposureId 

144 self.diffim.setDetector(detector) 

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

146 self.diffim.setFilter(afwImage.FilterLabel(band='g', physical='g.MP9401')) 

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

148 

149 self.expIdBits = 16 

150 

151 FWHM = 5 

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

153 self.exposure.setPsf(psf) 

154 self.diffim.setPsf(psf) 

155 

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

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

158 # and calexp visit images. 

159 objects = [ 

160 (10000000, self.wcs.pixelToSky(-100000, -100000)), # xy outside 

161 (10000001, self.wcs.pixelToSky(100, -100000)), # y outside 

162 (10000002, self.wcs.pixelToSky(-100000, 100)), # x outside 

163 ] 

164 extra = pandas.DataFrame({ 

165 "diaObjectId": np.array([id for id, point in objects], dtype=np.int64), 

166 "ra": [point.getRa().asDegrees() for id, point in objects], 

167 "decl": [point.getDec().asDegrees() for id, point in objects] 

168 }) 

169 self.testDiaObjects = pd.concat([self.testDiaObjects, extra], ignore_index=True) 

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

171 # processing. 

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

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

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

175 # above list of ids. 

176 self.expectedDiaForcedSources = 6 

177 

178 self.expected_n_columns = 11 

179 

180 def testRun(self): 

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

182 sensible values. 

183 """ 

184 test_objects = self.testDiaObjects.copy() 

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

186 dfs = DiaForcedSourceTask() 

187 dia_forced_sources = dfs.run( 

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

189 

190 direct_values = [199854.48417094944, 160097.40719241602, 

191 82299.17897267535, 27148.604434624354, 

192 5746.988388215507] 

193 direct_var = [75240.939811168, 75231.42933749466, 

194 75218.89495113207, 75214.88248249644, 

195 75214.41447602339] 

196 diff_values = [399708.9683418989, 320194.81438483205, 

197 164598.3579453507, 54297.20886924871, 

198 11493.976776431015] 

199 diff_var = [106444.28782374493, 106417.39592887461, 

200 106381.94840437356, 106370.59980584883, 

201 106369.27608815048] 

202 

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

204 # outside of the ccd area. 

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

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

207 self.expected_n_columns) 

208 

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

210 direct_values, 

211 diff_values, 

212 direct_var, 

213 diff_var): 

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

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

216 

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

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

219 

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

221 

222 

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

224 pass 

225 

226 

227def setup_module(module): 

228 lsst.utils.tests.init() 

229 

230 

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

232 lsst.utils.tests.init() 

233 unittest.main()