Coverage for tests/test_cameraGeom.py : 12%

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# 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/>.
22import unittest
23import os
25import numpy as np
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 PIXELS, FIELD_ANGLE, FOCAL_PLANE, CameraSys, CameraSysPrefix, \
33 Camera, Detector, assembleAmplifierImage, assembleAmplifierRawImage, DetectorCollection
34import lsst.afw.cameraGeom.testUtils as testUtils
35import lsst.afw.cameraGeom.utils as cameraGeomUtils
37try:
38 type(display)
39except NameError:
40 display = False
42testPath = os.path.abspath(os.path.dirname(__file__))
45class CameraGeomTestCase(lsst.utils.tests.TestCase):
46 """A test case for camera geometry"""
48 def setUp(self):
49 self.lsstCamWrapper = testUtils.CameraWrapper(isLsstLike=True)
50 self.scCamWrapper = testUtils.CameraWrapper(isLsstLike=False)
51 self.cameraList = (self.lsstCamWrapper, self.scCamWrapper)
52 self.assemblyList = {}
53 self.assemblyList[self.lsstCamWrapper.camera.getName()] =\
54 [afwImage.ImageU(os.path.join(testPath, 'test_amp.fits.gz'))
55 for i in range(8)]
56 self.assemblyList[self.scCamWrapper.camera.getName()] =\
57 [afwImage.ImageU(os.path.join(testPath, 'test.fits.gz'), allowUnsafe=True)]
59 def tearDown(self):
60 del self.lsstCamWrapper
61 del self.scCamWrapper
62 del self.cameraList
63 del self.assemblyList
65 def testConstructor(self):
66 for cw in self.cameraList:
67 self.assertIsInstance(cw.camera, Camera)
68 self.assertEqual(cw.nDetectors, len(cw.camera))
69 self.assertEqual(cw.nDetectors, len(cw.ampDataDict))
70 self.assertEqual(sorted(cw.detectorNameList),
71 sorted(cw.camera.getNameIter()))
72 self.assertEqual(sorted(cw.detectorIdList),
73 sorted(cw.camera.getIdIter()))
74 for det in cw.camera:
75 self.assertIsInstance(det, Detector)
76 self.assertEqual(
77 cw.ampDataDict[det.getName()]['namps'], len(det))
78 idList = [det.getId() for det in cw.camera]
79 self.assertEqual(idList, sorted(idList))
81 def testCameraSysRepr(self):
82 """Test CameraSys.__repr__ and CameraSysPrefix.__repr__
83 """
84 for sysName in ("FocalPlane", "FieldAngle", "Pixels", "foo"):
85 cameraSys = CameraSys(sysName)
86 predRepr = f"CameraSys({sysName})"
87 self.assertEqual(repr(cameraSys), predRepr)
89 cameraSysPrefix = CameraSysPrefix(sysName)
90 predCSPRepr = f"CameraSysPrefix({sysName})"
91 self.assertEqual(repr(cameraSysPrefix), predCSPRepr)
92 for detectorName in ("Detector 1", "bar"):
93 cameraSys2 = CameraSys(sysName, detectorName)
94 predRepr2 = f"CameraSys({sysName}, {detectorName})"
95 self.assertEqual(repr(cameraSys2), predRepr2)
97 def testAccessor(self):
98 for cw in self.cameraList:
99 camera = cw.camera
100 for name in cw.detectorNameList:
101 self.assertIsInstance(camera[name], Detector)
102 for detId in cw.detectorIdList:
103 self.assertIsInstance(camera[detId], Detector)
105 def testTransformSlalib(self):
106 """Test Camera.transform against data computed using SLALIB
108 These test data come from SLALIB using SLA_PCD with 0.925 and
109 a plate scale of 20 arcsec/mm
110 """
111 testData = [(-1.84000000, 1.04000000, -331.61689069, 187.43563387),
112 (-1.64000000, 0.12000000, -295.42491556, 21.61645724),
113 (-1.44000000, -0.80000000, -259.39818797, -144.11010443),
114 (-1.24000000, -1.72000000, -223.48275934, -309.99221457),
115 (-1.08000000, 1.36000000, -194.56520533, 245.00803635),
116 (-0.88000000, 0.44000000, -158.44320430, 79.22160215),
117 (-0.68000000, -0.48000000, -122.42389383, -86.41686623),
118 (-0.48000000, -1.40000000, -86.45332534, -252.15553224),
119 (-0.32000000, 1.68000000, -57.64746955, 302.64921514),
120 (-0.12000000, 0.76000000, -21.60360306, 136.82281940),
121 (0.08000000, -0.16000000, 14.40012984, -28.80025968),
122 (0.28000000, -1.08000000, 50.41767773, -194.46818554),
123 (0.48000000, -2.00000000, 86.50298919, -360.42912163),
124 (0.64000000, 1.08000000, 115.25115701, 194.48632746),
125 (0.84000000, 0.16000000, 151.23115189, 28.80593369),
126 (1.04000000, -0.76000000, 187.28751874, -136.86395600),
127 (1.24000000, -1.68000000, 223.47420612, -302.77150507),
128 (1.40000000, 1.40000000, 252.27834478, 252.27834478),
129 (1.60000000, 0.48000000, 288.22644118, 86.46793236),
130 (1.80000000, -0.44000000, 324.31346653, -79.27662515), ]
132 for cw in self.cameraList:
133 camera = cw.camera
134 for point in testData:
135 fpGivenPos = lsst.geom.Point2D(point[2], point[3])
136 fieldGivenPos = lsst.geom.Point2D(
137 lsst.geom.degToRad(point[0]), lsst.geom.degToRad(point[1]))
139 fieldAngleToFocalPlane = camera.getTransform(FIELD_ANGLE, FOCAL_PLANE)
140 fpComputedPos = fieldAngleToFocalPlane.applyForward(fieldGivenPos)
141 self.assertPairsAlmostEqual(fpComputedPos, fpGivenPos)
143 focalPlaneToFieldAngle = camera.getTransform(FOCAL_PLANE, FIELD_ANGLE)
144 fieldComputedPos = focalPlaneToFieldAngle.applyForward(fpGivenPos)
145 self.assertPairsAlmostEqual(fieldComputedPos, fieldGivenPos)
147 def testTransformDet(self):
148 """Test Camera.getTransform with detector-based coordinate systems (PIXELS)
149 """
150 for cw in self.cameraList:
151 numOffUsable = 0 # number of points off one detector but on another
152 camera = cw.camera
153 detNameList = list(camera.getNameIter())
154 for detName in detNameList:
155 det = camera[detName]
157 # test transforms using an arbitrary point on the detector
158 posPixels = lsst.geom.Point2D(10, 10)
159 pixSys = det.makeCameraSys(PIXELS)
160 pixelsToFocalPlane = camera.getTransform(pixSys, FOCAL_PLANE)
161 pixelsToFieldAngle = camera.getTransform(pixSys, FIELD_ANGLE)
162 focalPlaneToFieldAngle = camera.getTransform(FOCAL_PLANE, FIELD_ANGLE)
163 posFocalPlane = pixelsToFocalPlane.applyForward(posPixels)
164 posFieldAngle = pixelsToFieldAngle.applyForward(posPixels)
165 posFieldAngle2 = focalPlaneToFieldAngle.applyForward(posFocalPlane)
166 self.assertPairsAlmostEqual(posFieldAngle, posFieldAngle2)
168 posFieldAngle3 = camera.transform(posPixels, pixSys, FIELD_ANGLE)
169 self.assertPairsAlmostEqual(posFieldAngle, posFieldAngle3)
171 for intermedPos, intermedSys in (
172 (posPixels, pixSys),
173 (posFocalPlane, FOCAL_PLANE),
174 (posFieldAngle, FIELD_ANGLE),
175 ):
176 pixelSys = det.makeCameraSys(PIXELS)
177 intermedSysToPixels = camera.getTransform(intermedSys, pixelSys)
178 posPixelsRoundTrip = intermedSysToPixels.applyForward(intermedPos)
179 self.assertPairsAlmostEqual(posPixels, posPixelsRoundTrip)
181 posPixelsRoundTrip2 = camera.transform(intermedPos, intermedSys, pixelSys)
182 self.assertPairsAlmostEqual(posPixels, posPixelsRoundTrip2)
184 # Test finding detectors for a point off this detector.
185 # The point off the detector may be on one other detector,
186 # depending if the detector has neighbor on the correct edge.
187 pixOffDet = lsst.geom.Point2D(0, -10)
188 pixCoordSys = det.makeCameraSys(PIXELS)
189 detList = camera.findDetectors(pixOffDet, pixCoordSys)
190 self.assertIn(len(detList), (0, 1))
191 if len(detList) == 1:
192 numOffUsable += 1
194 otherDet = detList[0]
195 self.assertNotEqual(otherDet, det)
196 otherCoordPixSys = otherDet.makeCameraSys(PIXELS)
198 pixelsToOtherPixels = camera.getTransform(pixCoordSys, otherCoordPixSys)
199 otherPixels = pixelsToOtherPixels.applyForward(pixOffDet)
200 with self.assertRaises(AssertionError):
201 self.assertPairsAlmostEqual(otherPixels, pixOffDet)
203 # convert back
204 otherPixelsToPixels = camera.getTransform(otherCoordPixSys, pixCoordSys)
205 pixOffDetRoundTrip = otherPixelsToPixels.applyForward(otherPixels)
206 self.assertPairsAlmostEqual(pixOffDet, pixOffDetRoundTrip)
207 self.assertEqual(numOffUsable, 5)
209 def testFindDetectors(self):
210 for cw in self.cameraList:
211 detCtrFocalPlaneList = []
212 for det in cw.camera:
213 # This currently assumes there is only one detector at the center
214 # position of any detector. That is not enforced and multiple detectors
215 # at a given FIELD_ANGLE position is supported. Change this if the default
216 # camera changes.
217 detCtrFocalPlane = det.getCenter(FOCAL_PLANE)
218 detCtrFocalPlaneList.append(detCtrFocalPlane)
219 detList = cw.camera.findDetectors(detCtrFocalPlane, FOCAL_PLANE)
220 self.assertEqual(len(detList), 1)
221 self.assertEqual(det.getName(), detList[0].getName())
222 detList = cw.camera.findDetectorsList(detCtrFocalPlaneList, FOCAL_PLANE)
223 self.assertEqual(len(cw.camera), len(detList))
224 for dets in detList:
225 self.assertEqual(len(dets), 1)
227 def testFpBbox(self):
228 for cw in self.cameraList:
229 camera = cw.camera
230 bbox = lsst.geom.Box2D()
231 for name in cw.detectorNameList:
232 for corner in camera[name].getCorners(FOCAL_PLANE):
233 bbox.include(corner)
234 self.assertEqual(bbox.getMin(), camera.getFpBBox().getMin())
235 self.assertEqual(bbox.getMax(), camera.getFpBBox().getMax())
237 def testLinearity(self):
238 """Test if we can set/get Linearity parameters"""
239 for cw in self.cameraList:
240 camera = cw.camera
241 for det in camera:
242 for amp in det:
243 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linthresh'],
244 amp.getLinearityThreshold())
245 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linmax'],
246 amp.getLinearityMaximum())
247 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['linunits'],
248 amp.getLinearityUnits())
249 self.assertEqual(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['lintype'],
250 amp.getLinearityType())
251 for c1, c2 in zip(cw.ampDataDict[det.getName()]['linInfo'][amp.getName()]['lincoeffs'],
252 amp.getLinearityCoeffs()):
253 if np.isfinite(c1) and np.isfinite(c2):
254 self.assertEqual(c1, c2)
256 def testAssembly(self):
257 ccdNames = ('R:0,0 S:1,0', 'R:0,0 S:0,1')
258 compMap = {True: afwImage.ImageU(os.path.join(testPath, 'test_comp_trimmed.fits.gz'),
259 allowUnsafe=True),
260 False: afwImage.ImageU(os.path.join(testPath, 'test_comp.fits.gz'), allowUnsafe=True)}
261 for cw in self.cameraList:
262 camera = cw.camera
263 imList = self.assemblyList[camera.getName()]
264 for ccdName in ccdNames:
265 for trim, assemble in ((False, assembleAmplifierRawImage), (True, assembleAmplifierImage)):
266 det = camera[ccdName]
267 if not trim:
268 outBbox = cameraGeomUtils.calcRawCcdBBox(det)
269 else:
270 outBbox = det.getBBox()
271 outImage = afwImage.ImageU(outBbox)
272 if len(imList) == 1:
273 for amp in det:
274 assemble(outImage, imList[0], amp)
275 else:
276 for amp, im in zip(det, imList):
277 assemble(outImage, im, amp)
278 self.assertListEqual(outImage.getArray().flatten().tolist(),
279 compMap[trim].getArray().flatten().tolist())
281 @unittest.skipIf(not display, "display variable not set; skipping cameraGeomUtils test")
282 def testCameraGeomUtils(self):
283 for cw in self.cameraList:
284 camera = cw.camera
285 disp = afwDisplay.Display()
286 cameraGeomUtils.showCamera(camera, display=disp)
287 disp.incrDefaultFrame()
288 for det in (camera[10], camera[20]):
289 cameraGeomUtils.showCcd(det, inCameraCoords=False)
290 disp.incrDefaultFrame()
291 cameraGeomUtils.showCcd(det, inCameraCoords=True)
292 disp.incrDefaultFrame()
293 cameraGeomUtils.showCcd(det, inCameraCoords=False)
294 disp.incrDefaultFrame()
295 cameraGeomUtils.showCcd(det, inCameraCoords=True)
296 disp.incrDefaultFrame()
297 for amp in det:
298 cameraGeomUtils.showAmp(amp, display=disp, imageFactory=afwImage.ImageF)
299 disp.incrDefaultFrame()
301 def testCameraRaises(self):
302 for cw in self.cameraList:
303 camera = cw.camera
304 point = lsst.geom.Point2D(0, 0)
305 # non-existant source camera system
306 with self.assertRaises(pexExcept.InvalidParameterError):
307 camera.getTransform(CameraSys("badSystem"), FOCAL_PLANE)
308 with self.assertRaises(pexExcept.InvalidParameterError):
309 camera.transform(point, CameraSys("badSystem"), FOCAL_PLANE)
310 # non-existant destination camera system
311 with self.assertRaises(pexExcept.InvalidParameterError):
312 camera.getTransform(FOCAL_PLANE, CameraSys("badSystem"))
313 with self.assertRaises(pexExcept.InvalidParameterError):
314 camera.transform(point, FOCAL_PLANE, CameraSys("badSystem"))
315 # non-existent source detector
316 with self.assertRaises(pexExcept.InvalidParameterError):
317 camera.getTransform(CameraSys("pixels", "invalid"), FOCAL_PLANE)
318 with self.assertRaises(pexExcept.InvalidParameterError):
319 camera.transform(point, CameraSys("pixels", "invalid"), FOCAL_PLANE)
320 # non-existent destination detector
321 with self.assertRaises(pexExcept.InvalidParameterError):
322 camera.getTransform(FOCAL_PLANE, CameraSys("pixels", "invalid"))
323 with self.assertRaises(pexExcept.InvalidParameterError):
324 camera.transform(point, FOCAL_PLANE, CameraSys("pixels", "invalid"))
326 def testDetectorCollectionPersistence(self):
327 """Test that we can round-trip a DetectorCollection through FITS I/O.
328 """
329 for wrapper in self.cameraList:
330 camera = wrapper.camera
331 detectors = list(camera)
332 collectionIn = DetectorCollection(detectors)
333 with lsst.utils.tests.getTempFilePath(".fits") as filename:
334 collectionIn.writeFits(filename)
335 collectionOut = DetectorCollection.readFits(filename)
336 self.assertDetectorCollectionsEqual(collectionIn, collectionOut)
338 def testCameraPersistence(self):
339 """Test that we can round-trip a Camera through FITS I/O.
340 """
341 for wrapper in self.cameraList:
342 cameraIn = wrapper.camera
343 with lsst.utils.tests.getTempFilePath(".fits") as filename:
344 cameraIn.writeFits(filename)
345 cameraOut = Camera.readFits(filename)
346 self.assertCamerasEqual(cameraIn, cameraOut)
349class TestMemory(lsst.utils.tests.MemoryTestCase):
350 pass
353def setup_module(module):
354 lsst.utils.tests.init()
357if __name__ == "__main__": 357 ↛ 358line 357 didn't jump to line 358, because the condition on line 357 was never true
358 lsst.utils.tests.init()
359 unittest.main()