Coverage for tests/test_distortion.py : 25%

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#
4# Copyright 2008-2016 AURA/LSST.
5#
6# This product includes software developed by the
7# LSST Project (http://www.lsst.org/).
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 LSST License Statement and
20# the GNU General Public License along with this program. If not,
21# see <https://www.lsstcorp.org/LegalNotices/>.
22#
23import math
24import os.path
25import unittest
26import pickle
28import lsst.utils.tests
29from lsst.obs.hsc import HscMapper
30from lsst.pipe.base import Struct
31from lsst.afw.geom import transformRegistry
32from lsst.afw.cameraGeom import FOCAL_PLANE, FIELD_ANGLE, PIXELS
34# Set SAVE_DATA True to save new distortion data; this will make the test fail,
35# to remind you to set it False before committing the code.
36SAVE_DATA = False
38DataFileName = "data/distortionData.pickle" # path relative to the tests directory
41class HscDistortionTestCase(lsst.utils.tests.TestCase):
42 """Testing HscDistortion implementation
44 HscDistortion is based on the HSC package "distEst". We
45 test that it produces the same results.
46 """
47 def testDistortion(self):
48 """Test that the distortion data matches the saved data or create new data
50 If SAVE_DATA is true then save newly created data and then fail the test
51 in order to prevent anyone from committing the test with SAVE_DATA true!
53 Otherwise create new data and compare to the saved data
54 """
55 newData = self.makeDistortionData()
56 dataPath = os.path.join(os.path.dirname(__file__), DataFileName)
58 if SAVE_DATA:
59 with open(dataPath, "wb") as dataFile:
60 pickle.dump(newData, dataFile, protocol=2)
61 self.fail("Saved new data to %r; please set SAVE_DATA back to False" % dataPath)
63 if not os.path.exists(dataPath):
64 self.fail("Cannot find saved data %r; set SAVE_DATA = True and run again to save new data" %
65 dataPath)
67 fieldAngleToFocalPlaneTolerance = transformRegistry["hsc"].ConfigClass().tolerance
69 with open(dataPath, "rb") as dataFile:
70 savedData = pickle.load(dataFile)
71 maxRoundTripFocalPlaneError = 0
72 maxRoundTripPixPosError = 0
73 for detectorName, ccdData in newData.items():
74 savedCcdData = savedData[detectorName]
75 self.assertEqual(ccdData.serial, savedCcdData.serial)
76 for pixPosKey, cornerData in ccdData.cornerDict.items():
77 savedCornerData = savedCcdData.cornerDict[pixPosKey]
78 self.assertEqual(cornerData.pixPos, savedCornerData.pixPos)
79 self.assertPairsAlmostEqual(cornerData.focalPlane, savedCornerData.focalPlane)
80 self.assertPairsAlmostEqual(cornerData.fieldAngle, savedCornerData.fieldAngle)
81 maxRoundTripFocalPlaneError = max(
82 maxRoundTripFocalPlaneError,
83 math.hypot(*(cornerData.focalPlaneRoundTrip - cornerData.focalPlane))
84 )
85 self.assertPairsAlmostEqual(cornerData.focalPlaneRoundTrip, cornerData.focalPlane,
86 maxDiff=fieldAngleToFocalPlaneTolerance)
87 maxRoundTripPixPosError = max(maxRoundTripPixPosError,
88 math.hypot(*(cornerData.pixPosRoundTrip - cornerData.pixPos)))
89 self.assertPairsAlmostEqual(cornerData.pixPosRoundTrip, cornerData.pixPos)
90 print("maxRoundTripFocalPlaneError =", maxRoundTripFocalPlaneError)
91 print("maxRoundTripPixPosError =", maxRoundTripPixPosError)
93 def makeDistortionData(self):
94 """Make distortion data
96 The data format is a dict of detector name: ccdData, where
97 ccdData is a Struct containing these fields:
98 - serial: detector.getSerial
99 - cornerDict: a dict of pixPosKey, cornerData, where:
100 - pixPosKey: self.asKey(pixPos) where pixPos is pixel position
101 - cornerData is Struct contains these fields (all of type lsst.geom.Point2D):
102 - pixPos: pixel position
103 - focalPlane: focal plane position computed from pixPos
104 - fieldAngle: fieldAngle position computed from focalPlane
105 - focalPlaneRoundTrip: focal plane position computed from fieldAngle
106 - pixPosRoundTrip: pixel position computed from focalPlane
107 """
108 camera = HscMapper(root=".", calibRoot=".").camera
109 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE)
110 data = {} # dict of detector name: CcdData
111 for detector in camera:
112 # for each corner of each CCD:
113 # - get pixel position
114 # - convert to focal plane coordinates using the detector and record it
115 # - convert to field angle (this is the conversion that uses HscDistortion) and record it
116 # - convert back to focal plane (testing inverse direction of HscDistortion) and record it
117 # - convert back to pixel position and record it; pixel <-> focal plane is affine
118 # so there is no reason to doubt the inverse transform, but there is no harm
119 pixelsToFocalPlane = detector.getTransform(PIXELS, FOCAL_PLANE)
120 cornerDict = {}
121 for pixPos in detector.getCorners(PIXELS):
122 pixPos = pixPos
123 focalPlane = pixelsToFocalPlane.applyForward(pixPos)
124 fieldAngle = focalPlaneToFieldAngle.applyForward(focalPlane)
125 focalPlaneRoundTrip = focalPlaneToFieldAngle.applyInverse(fieldAngle)
126 pixPosRoundTrip = pixelsToFocalPlane.applyInverse(focalPlane)
127 cornerDict[self.toKey(pixPos)] = Struct(
128 pixPos = pixPos,
129 focalPlane = focalPlane,
130 fieldAngle = fieldAngle,
131 focalPlaneRoundTrip = focalPlaneRoundTrip,
132 pixPosRoundTrip = pixPosRoundTrip,
133 )
135 data[detector.getName()] = Struct(serial=detector.getSerial(), cornerDict=cornerDict)
137 return data
139 def toKey(self, pixPos):
140 return "(%0.1f, %0.1f)" % tuple(pixPos)
143class TestMemory(lsst.utils.tests.MemoryTestCase):
144 def setUp(self):
145 HscMapper.clearCache()
146 lsst.utils.tests.MemoryTestCase.setUp(self)
149def setup_module(module):
150 lsst.utils.tests.init()
153if __name__ == "__main__": 153 ↛ 154line 153 didn't jump to line 154, because the condition on line 153 was never true
154 lsst.utils.tests.init()
155 unittest.main()