Coverage for tests/test_detector.py: 97%
149 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-14 20:11 -0700
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-14 20:11 -0700
1#
2# LSST Data Management System
3# Copyright 2014 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.cameraGeom.Detector
24"""
25import unittest
27import numpy as np
29import lsst.utils.tests
30import lsst.pex.exceptions
31import lsst.geom
32import lsst.afw.geom
33import lsst.afw.cameraGeom as cameraGeom
34from lsst.afw.cameraGeom.testUtils import DetectorWrapper
37class DetectorTestCase(lsst.utils.tests.TestCase):
39 def testBasics(self):
40 """Test getters and other basics
41 """
42 dw = DetectorWrapper()
43 detector = dw.detector
44 for methodName in ("begin", "end", "size"):
45 if hasattr(detector, methodName): 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 self.assertFalse(hasattr(detector, methodName))
47 self.assertEqual(dw.name, detector.getName())
48 self.assertEqual(dw.id, detector.getId())
49 self.assertEqual(dw.type, detector.getType())
50 self.assertEqual(dw.serial, detector.getSerial())
51 bbox = detector.getBBox()
52 for i in range(2):
53 self.assertEqual(bbox.getMin()[i], dw.bbox.getMin()[i])
54 self.assertEqual(bbox.getMax()[i], dw.bbox.getMax()[i])
55 self.assertAlmostEqual(dw.pixelSize, detector.getPixelSize())
56 self.assertEqual(len(detector), len(dw.ampList))
58 orientation = detector.getOrientation()
60 transformMap = detector.getTransformMap()
61 # add 1 for null transform
62 self.assertEqual(len(transformMap), len(dw.transMap) + 1)
63 for cameraSys in dw.transMap:
64 self.assertTrue(cameraSys in transformMap)
66 self.assertEqual(detector.getPhysicalType(), "CCD") # the default in DetectorWrapper, not Detector
68 # make sure some complex objects stick around after detector is deleted
70 detectorName = detector.getName()
71 nativeCoordSys = detector.getNativeCoordSys()
72 offset = dw.orientation.getFpPosition()
73 del detector
74 del dw
75 self.assertEqual(orientation.getFpPosition(), offset)
76 self.assertEqual(nativeCoordSys,
77 cameraGeom.CameraSys(cameraGeom.PIXELS.getSysName(), detectorName))
79 def testConstructorErrors(self):
80 """Test constructor errors
81 """
82 def duplicateAmpName(dw):
83 """Set two amplifiers to the same name"""
84 builder = dw.ampList[1]
85 builder.setName(dw.ampList[0].getName())
86 dw.ampList[1] = builder
87 with self.assertRaises(lsst.pex.exceptions.Exception):
88 DetectorWrapper(modFunc=duplicateAmpName)
90 # These break in the pybind layer
91 for crosstalk in ([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], # 1D and not numpy
92 np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]), # 1D, wrong numpy type
93 np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=np.float32), # 1D
94 ):
95 self.assertRaises(TypeError, DetectorWrapper, crosstalk=crosstalk)
96 # These break in the Detector ctor: wrong shape
97 self.assertRaises(lsst.pex.exceptions.InvalidParameterError,
98 DetectorWrapper, crosstalk=np.array([[1.0, 2.0], [3.0, 4.0]]))
99 self.assertRaises(lsst.pex.exceptions.InvalidParameterError,
100 DetectorWrapper, crosstalk=np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]))
102 def testTransform(self):
103 """Test the transform method
104 """
105 dw = DetectorWrapper()
106 pixOffset = dw.orientation.getReferencePoint()
107 for xyMM in ((25.6, -31.07), (0, 0), (-1.234e5, 3.123e4)):
108 fpPoint = lsst.geom.Point2D(*xyMM)
109 pixPoint = dw.detector.transform(fpPoint, cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS)
110 for i in range(2):
111 self.assertAlmostEqual(
112 fpPoint[i]/dw.pixelSize[i] + pixOffset[i], pixPoint[i])
113 fpPoint2 = dw.detector.transform(pixPoint, cameraGeom.PIXELS, cameraGeom.FOCAL_PLANE)
114 self.assertPairsAlmostEqual(fpPoint, fpPoint2)
116 # test pix to pix
117 pixPoint2 = dw.detector.transform(pixPoint, cameraGeom.PIXELS, cameraGeom.PIXELS)
118 self.assertPairsAlmostEqual(pixPoint, pixPoint2)
120 def testIteration(self):
121 """Test iteration over amplifiers and __getitem__
122 """
123 dw = DetectorWrapper()
124 ampList = [amp for amp in dw.detector]
125 self.assertEqual(len(ampList), len(dw.ampList))
126 for i, amp in enumerate(ampList):
127 self.assertEqual(amp.getName(), dw.detector[i].getName())
128 self.assertEqual(amp.getName(), dw.ampList[i].getName())
129 self.assertEqual(
130 amp.getName(), dw.detector[amp.getName()].getName())
132 def testTransformAccess(self):
133 """Test hasTransform and getTransform
134 """
135 detector = DetectorWrapper().detector
136 for fromSys in (cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS, cameraGeom.TAN_PIXELS):
137 fullFromSys = detector.makeCameraSys(fromSys)
138 for toSys in (cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS, cameraGeom.TAN_PIXELS):
139 fullToSys = detector.makeCameraSys(toSys)
140 self.assertTrue(detector.hasTransform(fromSys))
141 self.assertTrue(detector.hasTransform(fullFromSys))
142 self.assertTrue(detector.hasTransform(toSys))
143 self.assertTrue(detector.hasTransform(fullToSys))
144 detector.getTransform(fromSys, toSys)
145 detector.getTransform(fromSys, fullToSys)
146 detector.getTransform(fullFromSys, toSys)
147 detector.getTransform(fullFromSys, fullToSys)
149 for badCamSys in (
150 cameraGeom.CameraSys("badName"),
151 cameraGeom.CameraSys("pixels", "badDetectorName")
152 ):
153 self.assertFalse(detector.hasTransform(badCamSys))
154 self.assertTrue(detector.hasTransform(cameraGeom.PIXELS))
155 with self.assertRaises(lsst.pex.exceptions.Exception):
156 detector.getTransform(cameraGeom.PIXELS, badCamSys)
158 def testMakeCameraSys(self):
159 """Test the makeCameraSys method
160 """
161 dw = DetectorWrapper()
162 for sysName in ("csys1", "csys2"):
163 for detectorName in ("", dw.name, "a different detector"):
164 inCamSys = cameraGeom.CameraSys(sysName, detectorName)
165 outCamSys = dw.detector.makeCameraSys(inCamSys)
166 self.assertEqual(inCamSys, outCamSys)
168 inCamSysPrefix = cameraGeom.CameraSysPrefix(sysName)
169 outCamSys2 = dw.detector.makeCameraSys(inCamSysPrefix)
170 self.assertEqual(
171 outCamSys2, cameraGeom.CameraSys(sysName, dw.name))
173 def testGetCorners(self):
174 """Test the getCorners method
175 """
176 dw = DetectorWrapper()
177 for cameraSys in (cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS):
178 # positions of corners in specified camera system
179 cornerList = dw.detector.getCorners(cameraSys)
180 for posPixels, posCameraSys in zip(lsst.geom.Box2D(dw.bbox).getCorners(), cornerList):
181 pixelsToCameraSys = dw.detector.getTransform(cameraGeom.PIXELS, cameraSys)
182 predPosCameraSys = pixelsToCameraSys.applyForward(posPixels)
183 self.assertPairsAlmostEqual(predPosCameraSys, posCameraSys)
184 if cameraSys == cameraGeom.PIXELS:
185 self.assertPairsAlmostEqual(posPixels, predPosCameraSys)
187 def testGetCenter(self):
188 """Test the getCenter method
189 """
190 dw = DetectorWrapper()
191 ctrPixPoint = lsst.geom.Box2D(dw.detector.getBBox()).getCenter()
192 for cameraSys in (cameraGeom.FOCAL_PLANE, cameraGeom.PIXELS):
193 ctrPoint = dw.detector.getCenter(cameraSys)
194 transform = dw.detector.getTransform(cameraGeom.PIXELS, cameraSys)
195 predCtrPoint = transform.applyForward(ctrPixPoint)
196 self.assertPairsAlmostEqual(predCtrPoint, ctrPoint)
198 def testDetectorRebuild(self):
199 """Test Detector.rebuild() method
200 """
201 #
202 # Make a copy without any modifications
203 #
204 detector = DetectorWrapper().detector
205 ndetector = detector.rebuild().finish()
207 self.assertEqual(detector.getName(), ndetector.getName())
208 self.assertEqual(detector.getBBox(), ndetector.getBBox())
209 self.assertEqual(detector.getPhysicalType(), ndetector.getPhysicalType())
210 for amp, namp in zip(detector, ndetector):
211 self.assertEqual(amp.getBBox(), namp.getBBox())
212 self.assertEqual(amp.getRawXYOffset(), namp.getRawXYOffset())
213 #
214 # Now make a copy with a hacked-up set of amps
215 #
216 builder = detector.rebuild()
217 for i, amp in enumerate(builder, 1):
218 amp.setRawXYOffset(i*lsst.geom.ExtentI(1, 1))
220 ndetector = builder.finish()
222 self.assertEqual(detector.getName(), ndetector.getName())
223 self.assertEqual(detector.getBBox(), ndetector.getBBox())
224 for i, (amp, namp) in enumerate(zip(detector, ndetector), 1):
225 self.assertEqual(amp.getBBox(), namp.getBBox())
226 self.assertNotEqual(amp.getRawXYOffset(), namp.getRawXYOffset())
227 self.assertEqual(namp.getRawXYOffset()[0], i)
229 def testPersistence(self):
230 """Test round-tripping a Detector through FITS I/O.
231 """
232 dw = DetectorWrapper()
233 detectorIn = dw.detector
234 with lsst.utils.tests.getTempFilePath("*.fits") as filename:
235 detectorIn.writeFits(filename)
236 detectorOut = cameraGeom.Detector.readFits(filename)
237 self.assertDetectorsEqual(detectorIn, detectorOut)
240class MemoryTester(lsst.utils.tests.MemoryTestCase):
241 pass
244def setup_module(module):
245 lsst.utils.tests.init()
248if __name__ == "__main__": 248 ↛ 249line 248 didn't jump to line 249 because the condition on line 248 was never true
249 lsst.utils.tests.init()
250 unittest.main()