Coverage for tests/test_diaForcedSource.py: 19%

106 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 03:42 -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 

35from lsst.meas.base import IdGenerator 

36import lsst.utils.tests 

37 

38 

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

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

41 

42 Parameters 

43 ---------- 

44 n_points : `int` 

45 Number of DiaObject test points to create. 

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

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

48 startPos : `int` 

49 Start position to iterate from when creating test DiaObjects 

50 

51 Returns 

52 ------- 

53 test_points : `pandas.DataFrame` 

54 Catalog of points to test. 

55 """ 

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

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

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

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

60 

61 objects = pandas.DataFrame({ 

62 "diaObjectId": ids, 

63 "ra": ra, 

64 "dec": dec 

65 }) 

66 

67 return objects 

68 

69 

70class TestDiaForcedSource(unittest.TestCase): 

71 

72 def setUp(self): 

73 # metadata taken from CFHT data 

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

75 self.metadata = dafBase.PropertySet() 

76 

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

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

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

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

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

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

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

84 

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

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

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

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

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

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

91 

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

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

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

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

96 

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

98 

99 self.calibration = 10000 

100 self.calibrationErr = 100 

101 self.exposureId = 1234 

102 self.exposureTime = 200. 

103 self.imageSize = [1024, 1153] 

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

105 

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

107 # variance for each image. 

108 # Direct Image 

109 source_image = afwImage.MaskedImageF( 

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

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

112 source_image.getVariance().set(1) 

113 bbox = lsst.geom.BoxI( 

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

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

116 self.imageSize[1])) 

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

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

119 

120 detector = DetectorWrapper( 

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

122 visit = afwImage.VisitInfo( 

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.info.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.info.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 FWHM = 5 

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

151 self.exposure.setPsf(psf) 

152 self.diffim.setPsf(psf) 

153 

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

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

156 # and calexp visit images. 

157 objects = [ 

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

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

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

161 ] 

162 extra = pandas.DataFrame({ 

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

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

165 "dec": [point.getDec().asDegrees() for id, point in objects] 

166 }) 

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

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

169 # processing. 

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

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

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

173 # above list of ids. 

174 self.expectedDiaForcedSources = 6 

175 

176 self.expected_n_columns = 13 

177 

178 def testRun(self): 

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

180 sensible values. 

181 """ 

182 test_objects = self.testDiaObjects.copy() 

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

184 dfs = DiaForcedSourceTask() 

185 dia_forced_sources = dfs.run( 

186 test_objects, self.updatedTestIds, self.exposure, self.diffim, IdGenerator()) 

187 

188 direct_values = [199854.48417094944, 160097.40719241602, 

189 82299.17897267535, 27148.604434624354, 

190 5746.988388215507] 

191 direct_var = [75240.939811168, 75231.42933749466, 

192 75218.89495113207, 75214.88248249644, 

193 75214.41447602339] 

194 diff_values = [399708.9683418989, 320194.81438483205, 

195 164598.3579453507, 54297.20886924871, 

196 11493.976776431015] 

197 diff_var = [106444.28782374493, 106417.39592887461, 

198 106381.94840437356, 106370.59980584883, 

199 106369.27608815048] 

200 

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

202 # outside of the ccd area. 

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

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

205 self.expected_n_columns) 

206 

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

208 direct_values, 

209 diff_values, 

210 direct_var, 

211 diff_var): 

212 self.assertAlmostEqual(diaFS["psfFlux"] / diffVal, 1.) 

213 self.assertAlmostEqual(diaFS["psfFluxErr"] / diffVar, 1.) 

214 

215 self.assertAlmostEqual(diaFS["scienceFlux"] / dirVal, 1.) 

216 self.assertAlmostEqual(diaFS["scienceFluxErr"] / dirVar, 1.) 

217 

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

219 

220 

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

222 pass 

223 

224 

225def setup_module(module): 

226 lsst.utils.tests.init() 

227 

228 

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

230 lsst.utils.tests.init() 

231 unittest.main()