Coverage for tests/test_astrometryTransform.py: 29%

121 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-09-09 10:41 +0000

1# This file is part of jointcal. 

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 

22"""Tests of the AstrometryTransform objects and its helpers.""" 

23import numpy as np 

24 

25import unittest 

26import lsst.utils.tests 

27 

28import lsst.geom 

29import lsst.log 

30import lsst.jointcal 

31from lsst.jointcal.astrometryTransform import (AstrometryTransformLinear, 

32 AstrometryTransformPolynomial, inversePolyTransform) 

33 

34 

35class AstrometryTransformPolynomialBase: 

36 def setUp(self): 

37 self.longMessage = True 

38 np.random.seed(100) 

39 

40 # TRACE level so we can see all the inverse iteration steps. 

41 lsst.log.setLevel('', lsst.log.TRACE) 

42 

43 # default initialized gtansfoPoly should be the identity 

44 self.polyIdentity = AstrometryTransformPolynomial(9) 

45 

46 self.poly2 = AstrometryTransformPolynomial(2) 

47 # A "reasonable" polynomial from fitting some data. 

48 poly2Str = "1\norder 2\n-0.167892516757 -5.19165870491e-05 2.0273225968e-07 2.75150063324e-11 -1.41377300894e-11 7.74131250823e-12 -0.355492046755 3.64496426108e-07 5.18028912201e-05 -1.38429955144e-11 -2.23437887691e-11 2.23415522405e-11 \n" # noqa: E501 

49 self.poly2.read(poly2Str) 

50 

51 # NOTE: taken from the "visit" component of a model, so needs to be 

52 # evaluated on a different bbox. 

53 self.poly3 = AstrometryTransformPolynomial(3) 

54 poly3Str = "1\norder 3\n-0.110057982995 -0.00384939195727 2.22344576374e-05 1.01023479109e-09 -5.2469672592e-09 1.50668618207e-09 1.46346365118e-09 -1.63797654003e-10 1.27721649626e-09 -2.05428888876e-12 -0.115317494814 2.21413899258e-05 0.00385039699775 -5.72570678996e-09 -9.59649028554e-09 1.41401351683e-08 -1.97886824954e-10 -1.30841763583e-09 -1.95789465504e-11 -1.26379172569e-09 " # noqa: E501 

55 self.poly3.read(poly3Str) 

56 

57 self.poly9 = AstrometryTransformPolynomial(9) 

58 # A "reasonable" polynomial from fitting some data. 

59 poly9Str = "1\norder 9\n-0.16793632503 -5.19113500529e-05 4.6484637807e-07 1.41209689574e-09 -8.13063885535e-10 -5.52862561337e-10 -7.20917258572e-12 1.44268609502e-12 1.01158414497e-12 5.81014911789e-13 1.72417265293e-14 -1.39482997394e-15 -1.34837756422e-15 -5.5315543147e-16 -3.26263945732e-16 -2.27835189163e-17 1.54838068036e-19 1.36520025882e-18 4.39218993176e-19 1.09711054987e-19 1.07909937222e-19 1.75422369904e-20 9.46438635874e-22 -7.47653768123e-22 -4.21000295132e-22 -1.63261283932e-23 2.18164463104e-23 -2.29125892915e-23 -7.79605618485e-24 -8.96899627417e-25 1.8328850322e-25 2.429872115e-25 1.57383988209e-26 -1.25289729437e-26 -1.60798488421e-26 3.50802217715e-27 1.84635563103e-27 3.27562810981e-28 4.22037315014e-30 -5.54664361872e-29 -2.8639429519e-29 1.1383384424e-29 1.04059046024e-30 3.18765755999e-30 -3.9564850064e-31 -1.8028544084e-31 -3.82041218173e-32 -1.70229355716e-32 1.32310477371e-32 9.28363194124e-35 1.5990001923e-33 -1.31379494936e-33 6.88708291597e-35 -2.23831688762e-34 2.33466866348e-35 -0.355489287839 1.33735110546e-06 5.17833395824e-05 -6.95722643803e-09 -1.04122329759e-09 6.40573984039e-12 2.20559605231e-11 5.41569680032e-12 4.5540873349e-13 1.26533773481e-13 -3.78843184326e-14 -1.40950401053e-14 -1.79770054411e-15 -8.32768571948e-18 -2.33287234831e-16 3.78385302886e-17 2.09977398841e-17 2.56668227438e-18 6.94279232614e-19 -1.62606627953e-19 2.04795207418e-19 -2.24588539576e-20 -1.79824251677e-20 -2.83870287858e-21 -3.26896753863e-22 -2.59328106552e-22 1.11975449899e-22 -9.70654488549e-23 7.70881954106e-24 8.70008772668e-24 2.08267357514e-24 -3.206329176e-26 1.25458989726e-25 5.34275512133e-26 -3.49011241433e-26 2.55423596751e-26 -1.37582742645e-27 -2.21887269529e-27 -7.87749245556e-28 4.74161700401e-29 -2.40765221773e-29 -1.32783315748e-29 -7.20736097924e-30 5.46646062406e-30 -3.51762994693e-30 9.24376571391e-32 2.36641447777e-31 1.05246663621e-31 7.65324655394e-33 -1.07479816221e-32 6.75168688604e-33 -7.73208895042e-34 6.25567958067e-34 -3.54400370636e-34 1.97908370676e-34 \n" # noqa: E501 

60 self.poly9.read(poly9Str) 

61 

62 # make a grid of points to evaluate on 

63 self._makePoints(0, 1000, 0, 1000, 200) 

64 

65 def _makePoints(self, minX, maxX, minY, maxY, num): 

66 """Sets self.points to a 2d grid of num points from min->max.""" 

67 self.frame = lsst.jointcal.frame.Frame(lsst.jointcal.star.Point(minX, minY), 

68 lsst.jointcal.star.Point(maxX, maxY)) 

69 num = 200 

70 xx = np.linspace(minX, maxX, num) 

71 yy = np.linspace(minY, maxY, num) 

72 self.points = [] 

73 for x in xx: 

74 for y in yy: 

75 self.points.append(lsst.geom.Point2D(x, y)) 

76 

77 

78class LinearTransformTestCase(AstrometryTransformPolynomialBase, lsst.utils.tests.TestCase): 

79 def test_str(self): 

80 """Check that the string representations of a linear transform is reasonable. 

81 """ 

82 # an identity polynomial 

83 linear = AstrometryTransformLinear() 

84 expect = "1 0 + 0\n0 1 + 0" 

85 self.assertIn(expect, str(linear)) 

86 

87 # make the zeroth-order newy term non-zero 

88 linear.setCoefficient(0, 0, 1, 2.0) 

89 # make the first-order x term of newx zero 

90 linear.setCoefficient(1, 0, 0, 0) 

91 expect = "0 0 + 0\n0 1 + 2" 

92 self.assertIn(expect, str(linear)) 

93 

94 

95class InversePolyTransformTestCase(AstrometryTransformPolynomialBase, lsst.utils.tests.TestCase): 

96 def checkInverse(self, poly, inverse, maxDiff): 

97 """Test that ``astrometryTransformPolynomial(inverse(point))==point`` to within maxDiff.""" 

98 results = [] 

99 for point in self.points: 

100 # TODO: Fix these "Point"s once DM-4044 is done. 

101 tempPoint = lsst.jointcal.star.Point(point[0], point[1]) 

102 result = inverse.apply(poly.apply(tempPoint)) 

103 results.append(lsst.geom.Point2D(result.x, result.y)) 

104 

105 self.assertPairListsAlmostEqual(results, self.points, maxDiff=maxDiff) 

106 

107 def testInversePolyIdentity(self): 

108 precision = 1e-8 

109 inverse = inversePolyTransform(self.polyIdentity, self.frame, precision) 

110 self.checkInverse(self.polyIdentity, inverse, precision) 

111 

112 def testInversePoly2(self): 

113 precision = 1e-6 

114 inverse = inversePolyTransform(self.poly2, self.frame, precision) 

115 self.checkInverse(self.poly2, inverse, 1e-7) 

116 

117 def testInversePoly3(self): 

118 # Different bbox for this one, because it is a focal plane to tangent plane transform. 

119 minX = 14.6927 

120 maxX = 42.38 

121 minY = -62.5486 

122 maxY = -0.323848 

123 self._makePoints(minX, maxX, minY, maxY, 200) 

124 

125 precision = 1e-7 

126 inverse = inversePolyTransform(self.poly3, self.frame, precision, maxOrder=5) 

127 self.checkInverse(self.poly3, inverse, 3e-8) 

128 

129 def testInversePoly9(self): 

130 precision = 1e-6 

131 inverse = inversePolyTransform(self.poly9, self.frame, precision, 

132 maxOrder=11, nSteps=100) 

133 self.checkInverse(self.poly9, inverse, 4e-5) 

134 

135 def testNotEnoughPoints(self): 

136 with self.assertRaises(RuntimeError): 

137 inversePolyTransform(self.poly2, self.frame, 1e-4, nSteps=2) 

138 

139 

140class AstrometryTransformPolynomialTestCase(AstrometryTransformPolynomialBase, lsst.utils.tests.TestCase): 

141 def checkToAstMap(self, poly, inverseMaxDiff=1e-6): 

142 """Test that AstrometryTransformPolynomial.toAstMap() gives accurate results. 

143 

144 Parameters 

145 ---------- 

146 poly : `lsst.jointcal.astrometryTransform.AstrometryTransformPolynomial` 

147 The polynomial to be tested. 

148 inverseMaxDiff : `float` 

149 Required accuracy on inverse polynomial. 

150 See `lsst.afw.geom.utils.assertPairsAlmostEqual`. 

151 """ 

152 # maxDiff should be small, because this should be a near-exact conversion, 

153 # modulo implementation details. 

154 maxDiff = 1e-10 

155 

156 astMap = poly.toAstMap(self.frame) 

157 expects = [] 

158 forwards = [] 

159 inverses = [] 

160 for point in self.points: 

161 # TODO: Fix these "Point"s once DM-4044 is done. 

162 tempPoint = lsst.jointcal.star.Point(point[0], point[1]) 

163 expect = poly.apply(tempPoint) 

164 expects.append(lsst.geom.Point2D(expect.x, expect.y)) 

165 result = astMap.applyForward(point) 

166 forwards.append(result) 

167 inverses.append(astMap.applyInverse(result)) 

168 

169 self.assertPairListsAlmostEqual(forwards, expects, maxDiff=maxDiff) 

170 self.assertPairListsAlmostEqual(inverses, self.points, maxDiff=inverseMaxDiff) 

171 

172 def testToAstMapIdentity(self): 

173 self.checkToAstMap(self.polyIdentity) 

174 

175 def testToAstMapOrder2(self): 

176 self.checkToAstMap(self.poly2) 

177 

178 def testToAstMapOrder3(self): 

179 # Different bbox for this one, because it was fit on a focal plane. 

180 minX = 14.6927 

181 maxX = 42.38 

182 minY = -62.5486 

183 maxY = -0.323848 

184 self._makePoints(minX, maxX, minY, maxY, 200) 

185 

186 self.checkToAstMap(self.poly3, inverseMaxDiff=5e-6) 

187 

188 def testToAstMapOrder9(self): 

189 # looser tolerance: 9th order polynomials are harder to get a good inverse for. 

190 self.checkToAstMap(self.poly9, inverseMaxDiff=4e-5) 

191 

192 def test_str(self): 

193 """Check that the string representations of polynomials are reasonable. 

194 """ 

195 # an identity polynomial 

196 poly2 = AstrometryTransformPolynomial(2) 

197 expect = "newx = 1*x\nnewy = 1*y" 

198 self.assertIn(expect, str(poly2)) 

199 

200 # make the zeroth-order newy term non-zero 

201 poly2.setCoefficient(0, 0, 1, 2.0) 

202 # make the first-order x term of newx zero 

203 poly2.setCoefficient(1, 0, 0, 0) 

204 expect = "newx = 0\nnewy = 2 + 1*y" 

205 self.assertIn(expect, str(poly2)) 

206 

207 # make the second-order y term of newx non-zero 

208 poly2.setCoefficient(0, 2, 0, 2.0) 

209 expect = "newx = 2*y^2\nnewy = 2 + 1*y" 

210 self.assertIn(expect, str(poly2)) 

211 

212 

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

214 pass 

215 

216 

217def setup_module(module): 

218 lsst.utils.tests.init() 

219 

220 

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

222 lsst.utils.tests.init() 

223 unittest.main()