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# 

2# LSST Data Management System 

3# Copyright 2008-2016 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

8# This program is free software: you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation, either version 3 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22""" 

23Tests for lsst.afw.table.CoaddInputs 

24 

25Note: generating the version 1 data requires some complicated machinations 

26because CoaddInputRecorderTask had bugs: 

27- Setup pipe_tasks 12.1, or a similar version old enough to have version 1 CoaddInput/ExposureTable 

28- Edit this file as follows: 

29 - Set SaveCoadd = True 

30 - Set self.version to 2 in CoaddInputsTestCase 

31- Edit CoaddInputRecorderTask as follows: 

32 - Apply all fixes to CoaddInputRecorderTask from DM-7976 

33 - Comment out the line that saves VisitInfo (since version 1 doesn't have it) 

34- Edit ups/pipe_tasks.table as follows: 

35 - Do not import obs_base (since it did not exist at that time) 

36- "setup -r . 'j" to setup this version of pipe_tasks 

37- "python tests/testCoaddInputs.py" to run this test. 

38 You will see some errors, but the needed data file will be written: 

39 tests/data/testCoaddInputs_coadd_with_version_1_data.fits 

40- Save the data file to the repository (but do not save any other changes). 

41""" 

42import os.path 

43import unittest 

44 

45import numpy as np 

46 

47import lsst.utils.tests 

48import lsst.pex.exceptions 

49from lsst.daf.base import DateTime 

50import lsst.afw.cameraGeom.testUtils 

51from lsst.afw.coord import Observatory, Weather 

52import lsst.geom 

53import lsst.afw.geom 

54import lsst.afw.image 

55from lsst.afw.detection import GaussianPsf 

56from lsst.afw.math import ChebyshevBoundedField 

57from lsst.pipe.tasks.coaddInputRecorder import CoaddInputRecorderTask 

58 

59SaveCoadd = False # if True then save coadd even if test passes (always saved if a test fails) 

60 

61 

62class MockExposure: 

63 

64 """Factory to make simple mock exposures suitable to put in a coadd 

65 

66 The metadata is set, but not the pixels 

67 """ 

68 

69 def __init__(self, numExp, coaddInputRecorder, version=2): 

70 """Make a MockExposure 

71 

72 @param[in] numExp total number of exposures that will be made 

73 @param[in] coaddInputRecoder an instance of CoaddInputRecorderTask 

74 @param[in] version desired version of CoaddInput/ExposureTable; 

75 1 for no VisitInfo; this will only produce the desired result 

76 if you run the test with code from before VisitInfo, e.g. version 12.1) 

77 2 to include VisitInfo 

78 """ 

79 self.numExp = int(numExp) 

80 self.coaddInputRecorder = coaddInputRecorder 

81 self.version = int(version) 

82 

83 def makeExposure(self, universalId): 

84 """Make a tiny exposure with exposure info set, but no pixels 

85 

86 In particular, exposure info is set as a record in a table, so it can be recorded in a coadd 

87 """ 

88 inputRecorder = self.coaddInputRecorder.makeCoaddTempExpRecorder(universalId, self.numExp) 

89 bbox = lsst.geom.Box2I(lsst.geom.Point2I(100, 100), lsst.geom.Extent2I(10, 10)) 

90 

91 detectorName = "detector {}".format(universalId) 

92 detector = lsst.afw.cameraGeom.testUtils.DetectorWrapper(name=detectorName, id=universalId).detector 

93 

94 exp = lsst.afw.image.ExposureF(bbox) 

95 exp.setDetector(detector) 

96 

97 expInfo = exp.getInfo() 

98 scale = 5.1e-5*lsst.geom.degrees 

99 cdMatrix = lsst.afw.geom.makeCdMatrix(scale=scale) 

100 wcs = lsst.afw.geom.makeSkyWcs( 

101 crpix=lsst.geom.Point2D(5, 5), 

102 crval=lsst.geom.SpherePoint(10, 45, lsst.geom.degrees), 

103 cdMatrix=cdMatrix, 

104 ) 

105 expInfo.setWcs(wcs) 

106 expInfo.setPsf(GaussianPsf(5, 5, 2.5)) 

107 expInfo.setPhotoCalib(lsst.afw.image.makePhotoCalibFromCalibZeroPoint(1.1e12, 2.2e10)) 

108 expInfo.setApCorrMap(self.makeApCorrMap()) 

109 expInfo.setValidPolygon(lsst.afw.geom.Polygon(lsst.geom.Box2D(bbox).getCorners())) 

110 if self.version > 1: 110 ↛ 113line 110 didn't jump to line 113, because the condition on line 110 was never false

111 expInfo.setVisitInfo(self.makeVisitInfo()) 

112 

113 inputRecorder.addCalExp(calExp=exp, ccdId=universalId, nGoodPix=100) 

114 inputRecorder.finish(coaddTempExp=exp, nGoodPix=100) 

115 

116 return exp 

117 

118 @staticmethod 

119 def makeWcs(): 

120 scale = 5.1e-5*lsst.geom.degrees 

121 cdMatrix = lsst.afw.geom.makeCdMatrix(scale=scale) 

122 return lsst.afw.geom.makeSkyWcs( 

123 crpix=lsst.geom.Point2D(5, 5), 

124 crval=lsst.geom.SpherePoint(10, 45, lsst.geom.degrees), 

125 cdMatrix=cdMatrix, 

126 ) 

127 

128 @staticmethod 

129 def makeVisitInfo(): 

130 return lsst.afw.image.VisitInfo( 

131 10313423, 

132 10.01, 

133 11.02, 

134 DateTime(65321.1, DateTime.MJD, DateTime.TAI), 

135 12345.1, 

136 45.1*lsst.geom.degrees, 

137 lsst.geom.SpherePoint(23.1, 73.2, lsst.geom.degrees), 

138 lsst.geom.SpherePoint(134.5, 33.3, lsst.geom.degrees), 

139 1.73, 

140 73.2*lsst.geom.degrees, 

141 lsst.afw.image.RotType.SKY, 

142 Observatory(11.1*lsst.geom.degrees, 22.2*lsst.geom.degrees, 0.333), 

143 Weather(1.1, 2.2, 34.5), 

144 ) 

145 

146 @staticmethod 

147 def makeApCorrMap(): 

148 """Make a trivial ApCorrMap with three elements""" 

149 bbox = lsst.geom.Box2I(lsst.geom.Point2I(-5, -5), lsst.geom.Point2I(5, 5)) 

150 apCorrMap = lsst.afw.image.ApCorrMap() 

151 for name in ("a", "b", "c"): 

152 apCorrMap.set(name, ChebyshevBoundedField(bbox, np.zeros((3, 3), dtype=float))) 

153 return apCorrMap 

154 

155 

156class MockCoadd: 

157 

158 """Class to make a mock coadd 

159 """ 

160 

161 def __init__(self, numExp, version=2): 

162 """Create a coadd with the specified number of exposures. 

163 

164 @param[in] numExp total number of exposures that will be made 

165 @param[in] version desired version of CoaddInput/ExposureTable; see MockExposure for details 

166 

167 Useful fields include: 

168 - coadd 

169 - exposures a list of exposures that went into the coadd 

170 """ 

171 coaddInputRecorder = CoaddInputRecorderTask(name="coaddInputRecorder") 

172 mockExposure = MockExposure(numExp=numExp, coaddInputRecorder=coaddInputRecorder, version=version) 

173 self.exposures = [mockExposure.makeExposure(i) for i in range(numExp)] 

174 

175 exp0 = self.exposures[0] 

176 self.coadd = lsst.afw.image.ExposureF(exp0.getBBox()) 

177 self.coadd.setWcs(exp0.getWcs()) 

178 

179 coaddInputs = coaddInputRecorder.makeCoaddInputs() 

180 for exp in self.exposures: 

181 coaddInputRecorder.addVisitToCoadd(coaddInputs=coaddInputs, coaddTempExp=exp, weight=1.0/numExp) 

182 self.coadd.getInfo().setCoaddInputs(coaddInputs) 

183 

184 

185class CoaddInputsTestCase(lsst.utils.tests.TestCase): 

186 

187 def setUp(self): 

188 self.version = 2 # desired version of ExposureTable/CoaddInput 

189 self.numExp = 3 

190 mockCoadd = MockCoadd(numExp=self.numExp, version=self.version) 

191 self.coadd = mockCoadd.coadd 

192 self.exposures = mockCoadd.exposures 

193 self.dataDir = os.path.join(os.path.dirname(__file__), "data") 

194 

195 def tearDown(self): 

196 del self.coadd 

197 del self.exposures 

198 

199 def assertPsfsAlmostEqual(self, psf1, psf2): 

200 im1 = psf1.computeImage() 

201 im2 = psf2.computeImage() 

202 self.assertImagesAlmostEqual(im1, im2) 

203 

204 def getCoaddPath(self, version): 

205 return os.path.join(self.dataDir, 

206 "testCoaddInputs_coadd_with_version_{}_data.fits".format(version)) 

207 

208 def testPersistence(self): 

209 """Read and write a coadd and check the CoaddInputs""" 

210 coaddPath = self.getCoaddPath(version=self.version) 

211 self.coadd.writeFits(coaddPath) 

212 coadd = lsst.afw.image.ExposureF(coaddPath) 

213 coaddInputs = coadd.getInfo().getCoaddInputs() 

214 self.assertCoaddInputsOk(coaddInputs, version=self.version) 

215 if not SaveCoadd: 215 ↛ 218line 215 didn't jump to line 218, because the condition on line 215 was never false

216 os.unlink(coaddPath) 

217 else: 

218 print("SaveCoadd true; saved coadd as: %r" % (coaddPath,)) 

219 

220 def testReadV1Coadd(self): 

221 """Read a coadd that contains version 1 CoaddInputs 

222 

223 The test code in question has FK5 WCS 

224 """ 

225 coaddPath = self.getCoaddPath(version=1) 

226 print("coaddPath=", coaddPath) 

227 coadd = lsst.afw.image.ExposureF(coaddPath) 

228 coaddWcs = coadd.getWcs() 

229 # the exposure in question uses FK5 for its WCS so update the exposures 

230 for exposure in self.exposures: 

231 exposure.setWcs(coaddWcs) 

232 coaddInputs = coadd.getInfo().getCoaddInputs() 

233 self.assertCoaddInputsOk(coaddInputs, version=1) 

234 

235 def assertCoaddInputsOk(self, coaddInputs, version): 

236 self.assertIsNotNone(coaddInputs) 

237 for expTable in (coaddInputs.ccds, coaddInputs.visits): 

238 self.assertEqual(len(expTable), 3) 

239 for i, expRec in enumerate(expTable): 

240 exp = self.exposures[i] 

241 expInfo = exp.getInfo() 

242 self.assertEqual(expRec.getId(), i) 

243 self.assertEqual(expRec.getBBox(), exp.getBBox()) 

244 self.assertWcsAlmostEqualOverBBox(expRec.getWcs(), expInfo.getWcs(), expRec.getBBox()) 

245 self.assertPsfsAlmostEqual(expRec.getPsf(), exp.getPsf()) 

246 self.assertEqual(expRec.getPhotoCalib(), expInfo.getPhotoCalib()) 

247 self.assertEqual(len(expRec.getApCorrMap()), 3) 

248 self.assertEqual(set(expRec.getApCorrMap().keys()), set(expInfo.getApCorrMap().keys())) 

249 self.assertFloatsAlmostEqual(np.array(expRec.getValidPolygon().getVertices()), 

250 np.array(expInfo.getValidPolygon().getVertices())) 

251 if version > 1: 

252 self.assertEqual(expRec.getVisitInfo(), expInfo.getVisitInfo()) 

253 else: 

254 self.assertIsNone(expRec.getVisitInfo()) 

255 

256 

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

258 pass 

259 

260 

261def setup_module(module): 

262 lsst.utils.tests.init() 

263 

264 

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

266 lsst.utils.tests.init() 

267 unittest.main()