Coverage for tests/test_cameraGeom.py: 11%

241 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-22 03:22 -0800

1# This file is part of afw. 

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 unittest 

23import os 

24 

25import numpy as np 

26 

27import lsst.utils.tests 

28import lsst.pex.exceptions as pexExcept 

29import lsst.geom 

30import lsst.afw.image as afwImage 

31import lsst.afw.display as afwDisplay 

32from lsst.afw.cameraGeom import ( 

33 AmplifierIsolator, 

34 assembleAmplifierImage, 

35 assembleAmplifierRawImage, 

36 Camera, 

37 CameraSys, 

38 CameraSysPrefix, 

39 Detector, 

40 DetectorCollection, 

41 FIELD_ANGLE, 

42 FOCAL_PLANE, 

43 makeUpdatedDetector, 

44 PIXELS, 

45) 

46import lsst.afw.cameraGeom.testUtils as testUtils 

47import lsst.afw.cameraGeom.utils as cameraGeomUtils 

48 

49try: 

50 type(display) 

51except NameError: 

52 display = False 

53 

54testPath = os.path.abspath(os.path.dirname(__file__)) 

55 

56 

57class CameraGeomTestCase(lsst.utils.tests.TestCase): 

58 """A test case for camera geometry""" 

59 

60 def setUp(self): 

61 self.lsstCamWrapper = testUtils.CameraWrapper(isLsstLike=True) 

62 self.scCamWrapper = testUtils.CameraWrapper(isLsstLike=False) 

63 self.cameraList = (self.lsstCamWrapper, self.scCamWrapper) 

64 self.assemblyList = {} 

65 self.assemblyList[self.lsstCamWrapper.camera.getName()] =\ 

66 [afwImage.ImageU(os.path.join(testPath, 'test_amp.fits.gz')) 

67 for i in range(8)] 

68 self.assemblyList[self.scCamWrapper.camera.getName()] =\ 

69 [afwImage.ImageU(os.path.join(testPath, 'test.fits.gz'), allowUnsafe=True)] 

70 

71 def tearDown(self): 

72 del self.lsstCamWrapper 

73 del self.scCamWrapper 

74 del self.cameraList 

75 del self.assemblyList 

76 

77 def testConstructor(self): 

78 for cw in self.cameraList: 

79 self.assertIsInstance(cw.camera, Camera) 

80 self.assertEqual(cw.nDetectors, len(cw.camera)) 

81 self.assertEqual(cw.nDetectors, len(cw.ampDataDict)) 

82 self.assertEqual(sorted(cw.detectorNameList), 

83 sorted(cw.camera.getNameIter())) 

84 self.assertEqual(sorted(cw.detectorIdList), 

85 sorted(cw.camera.getIdIter())) 

86 for det in cw.camera: 

87 self.assertIsInstance(det, Detector) 

88 self.assertEqual( 

89 cw.ampDataDict[det.getName()]['namps'], len(det)) 

90 idList = [det.getId() for det in cw.camera] 

91 self.assertEqual(idList, sorted(idList)) 

92 

93 def testCameraSysRepr(self): 

94 """Test CameraSys.__repr__ and CameraSysPrefix.__repr__ 

95 """ 

96 for sysName in ("FocalPlane", "FieldAngle", "Pixels", "foo"): 

97 cameraSys = CameraSys(sysName) 

98 predRepr = f"CameraSys({sysName})" 

99 self.assertEqual(repr(cameraSys), predRepr) 

100 

101 cameraSysPrefix = CameraSysPrefix(sysName) 

102 predCSPRepr = f"CameraSysPrefix({sysName})" 

103 self.assertEqual(repr(cameraSysPrefix), predCSPRepr) 

104 for detectorName in ("Detector 1", "bar"): 

105 cameraSys2 = CameraSys(sysName, detectorName) 

106 predRepr2 = f"CameraSys({sysName}, {detectorName})" 

107 self.assertEqual(repr(cameraSys2), predRepr2) 

108 

109 def testAccessor(self): 

110 for cw in self.cameraList: 

111 camera = cw.camera 

112 for name in cw.detectorNameList: 

113 self.assertIsInstance(camera[name], Detector) 

114 for detId in cw.detectorIdList: 

115 self.assertIsInstance(camera[detId], Detector) 

116 

117 def testTransformSlalib(self): 

118 """Test Camera.transform against data computed using SLALIB 

119 

120 These test data come from SLALIB using SLA_PCD with 0.925 and 

121 a plate scale of 20 arcsec/mm 

122 """ 

123 testData = [(-1.84000000, 1.04000000, -331.61689069, 187.43563387), 

124 (-1.64000000, 0.12000000, -295.42491556, 21.61645724), 

125 (-1.44000000, -0.80000000, -259.39818797, -144.11010443), 

126 (-1.24000000, -1.72000000, -223.48275934, -309.99221457), 

127 (-1.08000000, 1.36000000, -194.56520533, 245.00803635), 

128 (-0.88000000, 0.44000000, -158.44320430, 79.22160215), 

129 (-0.68000000, -0.48000000, -122.42389383, -86.41686623), 

130 (-0.48000000, -1.40000000, -86.45332534, -252.15553224), 

131 (-0.32000000, 1.68000000, -57.64746955, 302.64921514), 

132 (-0.12000000, 0.76000000, -21.60360306, 136.82281940), 

133 (0.08000000, -0.16000000, 14.40012984, -28.80025968), 

134 (0.28000000, -1.08000000, 50.41767773, -194.46818554), 

135 (0.48000000, -2.00000000, 86.50298919, -360.42912163), 

136 (0.64000000, 1.08000000, 115.25115701, 194.48632746), 

137 (0.84000000, 0.16000000, 151.23115189, 28.80593369), 

138 (1.04000000, -0.76000000, 187.28751874, -136.86395600), 

139 (1.24000000, -1.68000000, 223.47420612, -302.77150507), 

140 (1.40000000, 1.40000000, 252.27834478, 252.27834478), 

141 (1.60000000, 0.48000000, 288.22644118, 86.46793236), 

142 (1.80000000, -0.44000000, 324.31346653, -79.27662515), ] 

143 

144 for cw in self.cameraList: 

145 camera = cw.camera 

146 for point in testData: 

147 fpGivenPos = lsst.geom.Point2D(point[2], point[3]) 

148 fieldGivenPos = lsst.geom.Point2D( 

149 lsst.geom.degToRad(point[0]), lsst.geom.degToRad(point[1])) 

150 

151 fieldAngleToFocalPlane = camera.getTransform(FIELD_ANGLE, FOCAL_PLANE) 

152 fpComputedPos = fieldAngleToFocalPlane.applyForward(fieldGivenPos) 

153 self.assertPairsAlmostEqual(fpComputedPos, fpGivenPos) 

154 

155 focalPlaneToFieldAngle = camera.getTransform(FOCAL_PLANE, FIELD_ANGLE) 

156 fieldComputedPos = focalPlaneToFieldAngle.applyForward(fpGivenPos) 

157 self.assertPairsAlmostEqual(fieldComputedPos, fieldGivenPos) 

158 

159 def testTransformDet(self): 

160 """Test Camera.getTransform with detector-based coordinate systems (PIXELS) 

161 """ 

162 for cw in self.cameraList: 

163 numOffUsable = 0 # number of points off one detector but on another 

164 camera = cw.camera 

165 detNameList = list(camera.getNameIter()) 

166 for detName in detNameList: 

167 det = camera[detName] 

168 

169 # test transforms using an arbitrary point on the detector 

170 posPixels = lsst.geom.Point2D(10, 10) 

171 pixSys = det.makeCameraSys(PIXELS) 

172 pixelsToFocalPlane = camera.getTransform(pixSys, FOCAL_PLANE) 

173 pixelsToFieldAngle = camera.getTransform(pixSys, FIELD_ANGLE) 

174 focalPlaneToFieldAngle = camera.getTransform(FOCAL_PLANE, FIELD_ANGLE) 

175 posFocalPlane = pixelsToFocalPlane.applyForward(posPixels) 

176 posFieldAngle = pixelsToFieldAngle.applyForward(posPixels) 

177 posFieldAngle2 = focalPlaneToFieldAngle.applyForward(posFocalPlane) 

178 self.assertPairsAlmostEqual(posFieldAngle, posFieldAngle2) 

179 

180 posFieldAngle3 = camera.transform(posPixels, pixSys, FIELD_ANGLE) 

181 self.assertPairsAlmostEqual(posFieldAngle, posFieldAngle3) 

182 

183 for intermedPos, intermedSys in ( 

184 (posPixels, pixSys), 

185 (posFocalPlane, FOCAL_PLANE), 

186 (posFieldAngle, FIELD_ANGLE), 

187 ): 

188 pixelSys = det.makeCameraSys(PIXELS) 

189 intermedSysToPixels = camera.getTransform(intermedSys, pixelSys) 

190 posPixelsRoundTrip = intermedSysToPixels.applyForward(intermedPos) 

191 self.assertPairsAlmostEqual(posPixels, posPixelsRoundTrip) 

192 

193 posPixelsRoundTrip2 = camera.transform(intermedPos, intermedSys, pixelSys) 

194 self.assertPairsAlmostEqual(posPixels, posPixelsRoundTrip2) 

195 

196 # Test finding detectors for a point off this detector. 

197 # The point off the detector may be on one other detector, 

198 # depending if the detector has neighbor on the correct edge. 

199 pixOffDet = lsst.geom.Point2D(0, -10) 

200 pixCoordSys = det.makeCameraSys(PIXELS) 

201 detList = camera.findDetectors(pixOffDet, pixCoordSys) 

202 self.assertIn(len(detList), (0, 1)) 

203 if len(detList) == 1: 

204 numOffUsable += 1 

205 

206 otherDet = detList[0] 

207 self.assertNotEqual(otherDet, det) 

208 otherCoordPixSys = otherDet.makeCameraSys(PIXELS) 

209 

210 pixelsToOtherPixels = camera.getTransform(pixCoordSys, otherCoordPixSys) 

211 otherPixels = pixelsToOtherPixels.applyForward(pixOffDet) 

212 with self.assertRaises(AssertionError): 

213 self.assertPairsAlmostEqual(otherPixels, pixOffDet) 

214 

215 # convert back 

216 otherPixelsToPixels = camera.getTransform(otherCoordPixSys, pixCoordSys) 

217 pixOffDetRoundTrip = otherPixelsToPixels.applyForward(otherPixels) 

218 self.assertPairsAlmostEqual(pixOffDet, pixOffDetRoundTrip) 

219 self.assertEqual(numOffUsable, 5) 

220 

221 def testFindDetectors(self): 

222 for cw in self.cameraList: 

223 detCtrFocalPlaneList = [] 

224 for det in cw.camera: 

225 # This currently assumes there is only one detector at the center 

226 # position of any detector. That is not enforced and multiple detectors 

227 # at a given FIELD_ANGLE position is supported. Change this if the default 

228 # camera changes. 

229 detCtrFocalPlane = det.getCenter(FOCAL_PLANE) 

230 detCtrFocalPlaneList.append(detCtrFocalPlane) 

231 detList = cw.camera.findDetectors(detCtrFocalPlane, FOCAL_PLANE) 

232 self.assertEqual(len(detList), 1) 

233 self.assertEqual(det.getName(), detList[0].getName()) 

234 detList = cw.camera.findDetectorsList(detCtrFocalPlaneList, FOCAL_PLANE) 

235 self.assertEqual(len(cw.camera), len(detList)) 

236 for dets in detList: 

237 self.assertEqual(len(dets), 1) 

238 

239 def testFpBbox(self): 

240 for cw in self.cameraList: 

241 camera = cw.camera 

242 bbox = lsst.geom.Box2D() 

243 for name in cw.detectorNameList: 

244 for corner in camera[name].getCorners(FOCAL_PLANE): 

245 bbox.include(corner) 

246 self.assertEqual(bbox.getMin(), camera.getFpBBox().getMin()) 

247 self.assertEqual(bbox.getMax(), camera.getFpBBox().getMax()) 

248 

249 def testLinearity(self): 

250 """Test if we can set/get Linearity parameters""" 

251 for cw in self.cameraList: 

252 camera = cw.camera 

253 for det in camera: 

254 for amp in det: 

255 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linthresh'], 

256 amp.getLinearityThreshold()) 

257 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linmax'], 

258 amp.getLinearityMaximum()) 

259 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linunits'], 

260 amp.getLinearityUnits()) 

261 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['lintype'], 

262 amp.getLinearityType()) 

263 for c1, c2 in zip(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['lincoeffs'], 

264 amp.getLinearityCoeffs()): 

265 if np.isfinite(c1) and np.isfinite(c2): 

266 self.assertEqual(c1, c2) 

267 

268 def testAssembly(self): 

269 ccdNames = ('R:0,0 S:1,0', 'R:0,0 S:0,1') 

270 detectorImageMap = {True: afwImage.ImageU(os.path.join(testPath, 'test_comp_trimmed.fits.gz'), 

271 allowUnsafe=True), 

272 False: afwImage.ImageU(os.path.join(testPath, 'test_comp.fits.gz'), 

273 allowUnsafe=True)} 

274 for cw in self.cameraList: 

275 camera = cw.camera 

276 imList = self.assemblyList[camera.getName()] 

277 for ccdName in ccdNames: 

278 det = camera[ccdName] 

279 if len(imList) == 1: 

280 # There's one test image because it's the same for all 

281 # amplifiers, not because there's only one amplifier. 

282 imList *= len(det) 

283 # Test going from possibly-separate amp images to detector 

284 # images. 

285 for trim, assemble in ((False, assembleAmplifierRawImage), (True, assembleAmplifierImage)): 

286 if not trim: 

287 outBbox = cameraGeomUtils.calcRawCcdBBox(det) 

288 else: 

289 outBbox = det.getBBox() 

290 outImage = afwImage.ImageU(outBbox) 

291 for amp, im in zip(det, imList): 

292 assemble(outImage, im, amp) 

293 self.assertImagesEqual(outImage, detectorImageMap[trim]) 

294 # Test going from detector images back to single-amplifier 

295 # images. 

296 detector_exposure = afwImage.ExposureU(afwImage.MaskedImageU(detectorImageMap[trim])) 

297 detector_exposure.setDetector(makeUpdatedDetector(det)) 

298 for amp, im in zip(det, imList): 

299 amp_exposure = AmplifierIsolator.apply(detector_exposure, amp) 

300 self.assertEqual(len(amp_exposure.getDetector()), 1) 

301 self.assertEqual(amp_exposure.getDetector().getBBox(), amp.getBBox()) 

302 self.assertAmplifiersEqual(amp, amp_exposure.getDetector()[0]) 

303 if not trim: 

304 self.assertEqual(cameraGeomUtils.calcRawCcdBBox(amp_exposure.getDetector()), 

305 amp_exposure.getBBox()) 

306 self.assertImagesEqual(im[amp.getRawBBox()], amp_exposure.image) 

307 else: 

308 self.assertEqual(amp_exposure.getDetector().getBBox(), 

309 amp_exposure.getBBox()) 

310 self.assertImagesEqual(im[amp.getRawDataBBox()], amp_exposure.image) 

311 

312 @unittest.skipIf(not display, "display variable not set; skipping cameraGeomUtils test") 

313 def testCameraGeomUtils(self): 

314 for cw in self.cameraList: 

315 camera = cw.camera 

316 disp = afwDisplay.Display() 

317 cameraGeomUtils.showCamera(camera, display=disp) 

318 disp.incrDefaultFrame() 

319 for det in (camera[10], camera[20]): 

320 cameraGeomUtils.showCcd(det, inCameraCoords=False) 

321 disp.incrDefaultFrame() 

322 cameraGeomUtils.showCcd(det, inCameraCoords=True) 

323 disp.incrDefaultFrame() 

324 cameraGeomUtils.showCcd(det, inCameraCoords=False) 

325 disp.incrDefaultFrame() 

326 cameraGeomUtils.showCcd(det, inCameraCoords=True) 

327 disp.incrDefaultFrame() 

328 for amp in det: 

329 cameraGeomUtils.showAmp(amp, display=disp, imageFactory=afwImage.ImageF) 

330 disp.incrDefaultFrame() 

331 

332 def testCameraRaises(self): 

333 for cw in self.cameraList: 

334 camera = cw.camera 

335 point = lsst.geom.Point2D(0, 0) 

336 # non-existant source camera system 

337 with self.assertRaises(pexExcept.InvalidParameterError): 

338 camera.getTransform(CameraSys("badSystem"), FOCAL_PLANE) 

339 with self.assertRaises(pexExcept.InvalidParameterError): 

340 camera.transform(point, CameraSys("badSystem"), FOCAL_PLANE) 

341 # non-existant destination camera system 

342 with self.assertRaises(pexExcept.InvalidParameterError): 

343 camera.getTransform(FOCAL_PLANE, CameraSys("badSystem")) 

344 with self.assertRaises(pexExcept.InvalidParameterError): 

345 camera.transform(point, FOCAL_PLANE, CameraSys("badSystem")) 

346 # non-existent source detector 

347 with self.assertRaises(pexExcept.InvalidParameterError): 

348 camera.getTransform(CameraSys("pixels", "invalid"), FOCAL_PLANE) 

349 with self.assertRaises(pexExcept.InvalidParameterError): 

350 camera.transform(point, CameraSys("pixels", "invalid"), FOCAL_PLANE) 

351 # non-existent destination detector 

352 with self.assertRaises(pexExcept.InvalidParameterError): 

353 camera.getTransform(FOCAL_PLANE, CameraSys("pixels", "invalid")) 

354 with self.assertRaises(pexExcept.InvalidParameterError): 

355 camera.transform(point, FOCAL_PLANE, CameraSys("pixels", "invalid")) 

356 

357 def testDetectorCollectionPersistence(self): 

358 """Test that we can round-trip a DetectorCollection through FITS I/O. 

359 """ 

360 for wrapper in self.cameraList: 

361 camera = wrapper.camera 

362 detectors = list(camera) 

363 collectionIn = DetectorCollection(detectors) 

364 with lsst.utils.tests.getTempFilePath(".fits") as filename: 

365 collectionIn.writeFits(filename) 

366 collectionOut = DetectorCollection.readFits(filename) 

367 self.assertDetectorCollectionsEqual(collectionIn, collectionOut) 

368 

369 def testCameraPersistence(self): 

370 """Test that we can round-trip a Camera through FITS I/O. 

371 """ 

372 for wrapper in self.cameraList: 

373 cameraIn = wrapper.camera 

374 with lsst.utils.tests.getTempFilePath(".fits") as filename: 

375 cameraIn.writeFits(filename) 

376 cameraOut = Camera.readFits(filename) 

377 self.assertCamerasEqual(cameraIn, cameraOut) 

378 

379 

380class TestMemory(lsst.utils.tests.MemoryTestCase): 

381 pass 

382 

383 

384def setup_module(module): 

385 lsst.utils.tests.init() 

386 

387 

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

389 lsst.utils.tests.init() 

390 unittest.main()