Coverage for tests/test_angle.py: 14%

175 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-06-11 02:31 -0700

1# 

2# Developed for the LSST Data Management System. 

3# This product includes software developed by the LSST Project 

4# (https://www.lsst.org). 

5# See the COPYRIGHT file at the top-level directory of this distribution 

6# for details of code ownership. 

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 GNU General Public License 

19# along with this program. If not, see <https://www.gnu.org/licenses/>. 

20# 

21 

22""" 

23Tests for Angle 

24 

25Run with: 

26 angle.py 

27or 

28 python 

29 >>> import angle; angle.run() 

30""" 

31import itertools 

32import math 

33import unittest 

34 

35import numpy as np 

36 

37import lsst.utils.tests 

38import lsst.geom 

39 

40 

41class AngleTestCase(lsst.utils.tests.TestCase): 

42 """A test case for Angle""" 

43 

44 def setUp(self): 

45 self.pi = lsst.geom.Angle(math.pi, lsst.geom.radians) 

46 self.d = 180*lsst.geom.degrees 

47 

48 def testCtor(self): 

49 self.assertEqual(self.pi, math.pi) 

50 self.assertEqual(self.pi, lsst.geom.Angle(math.pi)) 

51 self.assertEqual(self.pi, self.d) 

52 

53 dd = lsst.geom.Angle(180, lsst.geom.degrees) 

54 self.assertEqual(self.d, dd) 

55 dd = lsst.geom.Angle(60*180, lsst.geom.arcminutes) 

56 self.assertEqual(self.d, dd) 

57 dd = lsst.geom.Angle(60*60*180, lsst.geom.arcseconds) 

58 self.assertEqual(self.d, dd) 

59 dd = lsst.geom.Angle(60*60*180*1000, lsst.geom.milliarcseconds) 

60 self.assertEqual(self.d, dd) 

61 

62 def testArithmetic(self): 

63 self.assertTrue(lsst.geom.isAngle(self.pi)) 

64 self.assertFalse(lsst.geom.isAngle(self.pi.asRadians())) 

65 self.assertFalse(lsst.geom.isAngle(math.pi)) 

66 

67 with self.assertRaises(TypeError): 

68 self.pi - math.pi # subtracting a float from an Angle 

69 self.assertEqual(self.pi - math.pi*lsst.geom.radians, 0) 

70 self.assertEqual(self.pi - self.d, 0) # can subtract Angles 

71 

72 with self.assertRaises(TypeError): 

73 self.pi + math.pi # adding a float to an Angle 

74 

75 with self.assertRaises(NotImplementedError): 

76 self.pi*lsst.geom.degrees # self.pi is already an Angle 

77 

78 self.assertEqual((self.pi + self.d).asAngularUnits(lsst.geom.degrees), 

79 360) 

80 self.assertEqual((self.pi).asRadians(), math.pi) 

81 self.assertEqual((self.pi/2).asDegrees(), 90) 

82 self.assertEqual((self.pi*2).asArcminutes(), 360*60) 

83 self.assertEqual((self.pi*2).asArcseconds(), 360*60*60) 

84 self.assertEqual((self.pi*2).asMilliarcseconds(), 360*60*60*1000) 

85 self.assertEqual((-self.pi).asRadians(), -math.pi) 

86 

87 with self.assertRaises(TypeError): 

88 2.0 / self.pi # dividing a float by an Angle 

89 with self.assertRaises(TypeError): 

90 self.pi / self.pi 

91 

92 # automatic conversion to double 

93 self.assertEqual(math.sin(self.pi/2), 1.0) 

94 

95 def testAbs(self): 

96 self.assertEqual(abs(0.0*lsst.geom.degrees - self.pi), self.pi) 

97 

98 def testPi(self): 

99 self.assertEqual(lsst.geom.PI, math.pi) 

100 

101 def testComparison(self): 

102 a2 = 2.0 * lsst.geom.arcseconds 

103 a1 = 0.5 * lsst.geom.arcseconds 

104 a3 = 0.5 * lsst.geom.arcseconds 

105 self.assertEqual(a1, a3) 

106 self.assertNotEqual(a1, a2) 

107 self.assertLessEqual(a1, a2) 

108 self.assertLess(a1, a2) 

109 self.assertGreater(a2, a1) 

110 self.assertGreaterEqual(a2, a1) 

111 

112 self.assertFalse(a1 != a3) 

113 self.assertFalse(a1 == a2) 

114 self.assertFalse(a1 >= a2) 

115 self.assertFalse(a1 > a2) 

116 self.assertFalse(a2 < a1) 

117 self.assertFalse(a2 <= a1) 

118 

119 self.assertTrue(a1 == float(a1)) 

120 self.assertTrue(float(a1) == a1) 

121 

122 def testTrig(self): 

123 self.assertEqual(math.cos(self.d), -1.0) 

124 self.assertAlmostEqual(math.sin(self.d), 0.0, places=15) 

125 thirty = 30.*lsst.geom.degrees 

126 self.assertAlmostEqual(math.sin(thirty), 0.5, places=15) 

127 

128 def testSeparation(self): 

129 """Tests whether angle differences are computed as expected. 

130 

131 Wrapping accuracy is assumed tested by testWrap. 

132 """ 

133 angleBase = 0.0*lsst.geom.degrees 

134 angleWrap = 360.0*lsst.geom.degrees 

135 angleHalf = -180.0*lsst.geom.degrees 

136 angleOdd = 32.0*lsst.geom.degrees 

137 

138 self.checkWrappedAngle(angleBase.separation(angleWrap), 

139 0.0*lsst.geom.degrees) 

140 self.checkWrappedAngle(angleWrap.separation(angleBase), 

141 0.0*lsst.geom.degrees) 

142 

143 self.checkWrappedAngle(angleBase.separation(angleHalf), angleHalf) 

144 self.checkWrappedAngle(angleHalf.separation(angleBase), angleHalf) 

145 

146 self.checkWrappedAngle(angleWrap.separation(angleHalf), angleHalf) 

147 self.checkWrappedAngle(angleHalf.separation(angleWrap), angleHalf) 

148 

149 self.checkWrappedAngle(angleOdd.separation(angleBase), angleOdd) 

150 self.checkWrappedAngle(angleBase.separation(angleOdd), -angleOdd) 

151 

152 self.checkWrappedAngle(angleOdd.separation(angleWrap), angleOdd) 

153 self.checkWrappedAngle(angleWrap.separation(angleOdd), -angleOdd) 

154 

155 def checkWrappedAngle(self, observed, expected): 

156 """Tests whether an angle wrapped to [-pi, pi) both matches its expected 

157 value and strictly satisfies its range restriction. 

158 """ 

159 obs = observed.asRadians() 

160 exp = expected.asRadians() 

161 self.assertAlmostEqual(obs, exp, delta=np.finfo(float).eps) 

162 self.assertGreaterEqual(obs, -math.pi) 

163 self.assertLess(obs, math.pi) 

164 

165 def testWrap(self): 

166 eps = np.finfo(float).eps 

167 self.assertNotEqual(1 + eps, eps) 

168 for wrap, offset, epsMult in itertools.product( 

169 (-1000, -10, -1, 0, 1, 10, 1000), 

170 (-2*math.pi, -math.pi, -math.pi*0.5, 0.0, math.pi*0.5, math.pi*0.75, math.pi, math.pi*2.0), 

171 (-3, -2, -1, 0, 1, 2, 3), 

172 ): 

173 angRad = (offset + (wrap*math.pi)) * (1 + (eps*epsMult)) 

174 ang = angRad * lsst.geom.radians 

175 

176 posAng = (angRad * lsst.geom.radians).wrap() 

177 self.assertAnglesAlmostEqual(ang, posAng) 

178 posAngRad = posAng.asRadians() 

179 posAngDeg = posAng.asDegrees() 

180 posAngArcmin = posAng.asArcminutes() 

181 posAngArcsec = posAng.asArcseconds() 

182 posAngMilliarcsec = posAng.asMilliarcseconds() 

183 # the code promises 0 <= posAng for all units 

184 self.assertGreaterEqual(posAngRad, 0) 

185 self.assertGreaterEqual(posAngDeg, 0) 

186 self.assertGreaterEqual(posAngArcmin, 0) 

187 self.assertGreaterEqual(posAngArcsec, 0) 

188 self.assertGreaterEqual(posAngMilliarcsec, 0) 

189 # wrap promises posAng < 2*pi only for radians, 

190 # but it seems to work for all units 

191 self.assertLess(posAngRad, 2*math.pi) 

192 self.assertLess(posAngDeg, 360) 

193 self.assertLess(posAngArcmin, 360 * 60) 

194 self.assertLess(posAngArcsec, 360 * 3600) 

195 self.assertLess(posAngMilliarcsec, 360 * 3.6e6) 

196 

197 ctrAng = (angRad * lsst.geom.radians).wrapCtr() 

198 self.assertAnglesAlmostEqual(ang, ctrAng) 

199 ctrAngRad = ctrAng.asRadians() 

200 ctrAngDeg = ctrAng.asDegrees() 

201 ctrAngArcmin = ctrAng.asArcminutes() 

202 ctrAngArcsec = ctrAng.asArcseconds() 

203 ctrAngMilliarcsec = ctrAng.asMilliarcseconds() 

204 # wrapCtr promises -pi <= ctrAngRad < pi only for radians, 

205 # but it seems to work for all units 

206 self.assertGreaterEqual(ctrAngRad, -math.pi) 

207 self.assertGreaterEqual(ctrAngDeg, -180) 

208 self.assertGreaterEqual(ctrAngArcmin, -180 * 60) 

209 self.assertGreaterEqual(ctrAngArcsec, -180 * 3600) 

210 self.assertGreaterEqual(ctrAngMilliarcsec, -180 * 3.6e6) 

211 self.assertLess(ctrAngRad, math.pi) 

212 self.assertLess(ctrAngDeg, 180) 

213 self.assertLess(ctrAngArcmin, 180 * 60) 

214 self.assertLess(ctrAngArcsec, 180 * 3600) 

215 

216 for refAngBase, refAngWrap, refEpsMult in itertools.product( 

217 (-math.pi, 0.0, math.pi, math.pi*2.0), 

218 range(-10, 11, 2), 

219 (-3, -2, -1, 0, 1, 2, 3), 

220 ): 

221 refAngRad = refAngBase * (1 + (eps * refEpsMult)) + refAngWrap * math.pi * 2.0 

222 refAng = refAngRad * lsst.geom.radians 

223 refAngDeg = refAng.asDegrees() 

224 refAngArcmin = refAng.asArcminutes() 

225 refAngArcsec = refAng.asArcseconds() 

226 refAngMilliarcsec = refAng.asMilliarcseconds() 

227 nearAng = (angRad * lsst.geom.radians).wrapNear(refAng) 

228 self.assertAnglesAlmostEqual(ang, nearAng) 

229 nearAngRad = nearAng.asRadians() 

230 nearAngDeg = nearAng.asDegrees() 

231 nearAngArcmin = nearAng.asArcminutes() 

232 nearAngArcsec = nearAng.asArcseconds() 

233 nearAngMilliarcsec = nearAng.asMilliarcseconds() 

234 

235 fudgeFactor = 5 # 1 and 2 are too small for one case, 3 works; 5 has margin 

236 relEps = max(1, nearAngRad / math.pi, refAngRad / math.pi) * eps * fudgeFactor 

237 piWithSlop = math.pi * (1 + relEps) 

238 oneEightyWithSlop = 180 * (1 + relEps) 

239 

240 # wrapNear promises nearAngRad - refAngRad >= -pi 

241 # for radians but has known failures due to 

242 # roundoff error for other units and for < pi 

243 self.assertGreaterEqual(nearAngRad - refAngRad, -math.pi) 

244 self.assertGreaterEqual(nearAngDeg - refAngDeg, -oneEightyWithSlop) 

245 self.assertGreaterEqual(nearAngArcmin - refAngArcmin, -oneEightyWithSlop * 60) 

246 self.assertGreaterEqual(nearAngArcsec - refAngArcsec, -oneEightyWithSlop * 3600) 

247 self.assertGreaterEqual(nearAngMilliarcsec - refAngMilliarcsec, -oneEightyWithSlop * 3.6e6) 

248 self.assertLessEqual(nearAngRad - refAngRad, piWithSlop) 

249 self.assertLessEqual(nearAngDeg - refAngDeg, oneEightyWithSlop) 

250 self.assertLessEqual(nearAngArcmin - refAngArcmin, oneEightyWithSlop * 60) 

251 self.assertLessEqual(nearAngArcsec - refAngArcsec, oneEightyWithSlop * 3600) 

252 self.assertLessEqual(nearAngMilliarcsec - refAngMilliarcsec, oneEightyWithSlop * 3.6e6) 

253 

254 def test_repr(self): 

255 """Test that eval(repr(Angle)) round-trips for finite and non-finite 

256 values. 

257 """ 

258 from lsst.geom import Angle, degrees # need symbols in scope for eval 

259 d = 4.0 / 7.0 

260 self.assertEqual(eval(repr(Angle(d, degrees))), Angle(d, degrees)) 

261 self.assertEqual(eval(repr(Angle(float("inf"), degrees))), Angle(float("inf"), degrees)) 

262 self.assertTrue(np.isnan(eval(repr(Angle(float("NaN"), degrees))).asDegrees())) 

263 

264 

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

266 pass 

267 

268 

269def setup_module(module): 

270 lsst.utils.tests.init() 

271 

272 

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

274 lsst.utils.tests.init() 

275 unittest.main()