Hide keyboard shortcuts

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

1import unittest 

2import lsst.utils.tests 

3from lsst.utils import getPackageDir 

4from lsst.sims.utils import findHtmid, trixelFromHtmid 

5from lsst.sims.utils import HalfSpace, basic_trixels 

6from lsst.sims.utils import halfSpaceFromRaDec, levelFromHtmid 

7from lsst.sims.utils import halfSpaceFromPoints 

8from lsst.sims.utils import intersectHalfSpaces 

9from lsst.sims.utils import getAllTrixels 

10from lsst.sims.utils import arcsecFromRadians 

11from lsst.sims.utils.htmModule import _findHtmid_fast 

12from lsst.sims.utils.htmModule import _findHtmid_slow 

13 

14import numpy as np 

15import os 

16import numbers 

17 

18from lsst.sims.utils import sphericalFromCartesian, cartesianFromSpherical 

19from lsst.sims.utils import rotAboutY, rotAboutX, rotAboutZ 

20from lsst.sims.utils import angularSeparation, _angularSeparation 

21 

22 

23def setup_module(module): 

24 lsst.utils.tests.init() 

25 

26 

27def trixel_intersects_half_space(trix, hspace): 

28 """ 

29 This is a brute force method to determine whether a trixel 

30 is inside, or at least intersects, a halfspace. 

31 """ 

32 if hspace.phi > 0.25*np.pi: 

33 raise RuntimeError("trixel_intersects_half_space is not safe for " 

34 "large HalfSpaces") 

35 

36 # if any of the trixel's corners are within the 

37 # HalfSpace, return True 

38 raRad, decRad = sphericalFromCartesian(hspace.vector) 

39 for corner in trix.corners: 

40 raRad1, decRad1 = sphericalFromCartesian(corner) 

41 if _angularSeparation(raRad, decRad, raRad1, decRad1) < hspace.phi: 

42 return True 

43 

44 # if the trixel contains the HalfSpace's center, 

45 # return True 

46 if trix.contains_pt(hspace.vector): 

47 return True 

48 

49 sinphi = np.abs(np.sin(hspace.phi)) 

50 

51 # Iterate over each pair of corners (c1, c2). For each pair, 

52 # construct a coordinate basis in which +z is in the 

53 # direction of c3, and +x is along the 

54 # unit vector defining c_i such that the angle 

55 # phi of c_j in the x,y plane is positive. This coordinate 

56 # system is such that the trixel edge defined by c1, c2 is 

57 # now along the equator of the unit sphere. Find the point 

58 # of closest approach of the HalfSpace's center to the equator. 

59 # If that point is between c1 and c2, return True. 

60 for i_c_1 in range(3): 

61 c1 = trix.corners[i_c_1] 

62 for i_c_2 in range(3): 

63 if i_c_2 <= i_c_1: 

64 continue 

65 c2 = trix.corners[i_c_2] 

66 i_c_3 = 3 - (i_c_1+i_c_2) 

67 c3 = trix.corners[i_c_3] 

68 assert i_c_3 != i_c_2 

69 assert i_c_3 != i_c_1 

70 assert i_c_1 != i_c_2 

71 

72 z_axis = np.array([c1[1]*c2[2]-c1[2]*c2[1], 

73 c2[0]*c1[2]-c1[0]*c2[2], 

74 c1[0]*c2[1]-c2[0]*c1[1]]) 

75 z_axis = z_axis/np.sqrt((z_axis**2).sum()) 

76 

77 if np.dot(z_axis, c3) < 0.0: 

78 z_axis *= -1.0 

79 

80 assert np.abs(1.0-np.dot(z_axis, z_axis)) < 1.0e-10 

81 assert np.abs(1.0-np.dot(c1, c1)) < 1.0e-10 

82 assert np.abs(1.0-np.dot(c2, c2)) < 1.0e-10 

83 assert np.abs(np.dot(z_axis, c1)) < 1.0e-10 

84 assert np.abs(np.dot(z_axis, c2)) < 1.0e-10 

85 

86 # if the dot product of the center of the HalfSpace 

87 # with the z axis of the new coordinate system is 

88 # greater than the sine of the radius of the 

89 # halfspace, then there is no way that the halfspace 

90 # intersects the equator of the unit sphere in this 

91 # coordinate system 

92 if np.abs(np.dot(z_axis, hspace.vector)) > sinphi: 

93 continue 

94 

95 x_axis = c1 

96 y_axis = -1.0*np.array([x_axis[1]*z_axis[2]-x_axis[2]*z_axis[1], 

97 z_axis[0]*x_axis[2]-x_axis[0]*z_axis[2], 

98 x_axis[0]*z_axis[1]-z_axis[0]*x_axis[1]]) 

99 

100 cos_a = np.dot(x_axis, c2) 

101 sin_a = np.dot(y_axis, c2) 

102 

103 if sin_a < 0.0: 

104 x_axis = c2 

105 y_axis = -1.0*np.array([x_axis[1]*z_axis[2]-x_axis[2]*z_axis[1], 

106 z_axis[0]*x_axis[2]-x_axis[0]*z_axis[2], 

107 x_axis[0]*z_axis[1]-z_axis[0]*x_axis[1]]) 

108 

109 cos_a = np.dot(x_axis, c1) 

110 sin_a = np.dot(y_axis, c1) 

111 

112 assert cos_a >= 0.0 

113 assert sin_a >= 0.0 

114 assert np.abs(1.0-cos_a**2-sin_a**2) < 1.0e-10 

115 assert np.abs(np.dot(x_axis, z_axis)) < 1.0e-10 

116 assert np.abs(np.dot(x_axis, y_axis)) < 1.0e-10 

117 assert np.abs(np.dot(y_axis, z_axis)) < 1.0e-10 

118 

119 x_center = np.dot(x_axis, hspace.vector) 

120 

121 # if the x-coordinate of the HalfSpace's center is 

122 # negative, the HalfSpace is on the opposite side 

123 # of the unit sphere; ignore this pair c1, c2 

124 if x_center < 0.0: 

125 continue 

126 

127 y_center = np.dot(y_axis, hspace.vector) 

128 

129 # tan_a is the tangent of the angle between 

130 # the x_axis and the other trixel corner in 

131 # the x, y plane 

132 tan_a = sin_a/cos_a 

133 

134 # tan_extreme is the tangent of the angle in 

135 # the x, y plane defining the point of closest 

136 # approach of the HalfSpace's center to the 

137 # equator. If this point is between c1, c2, 

138 # return True. 

139 tan_extreme = y_center/x_center 

140 if tan_extreme > 0.0 and tan_extreme < tan_a: 

141 return True 

142 

143 return False 

144 

145 

146class HalfSpaceTest(unittest.TestCase): 

147 

148 longMessage = True 

149 

150 def test_half_space_contains_pt(self): 

151 hs = HalfSpace(np.array([0.0, 0.0, 1.0]), 0.1) 

152 nhs = HalfSpace(np.array([0.0, 0.0, -1.0]), -0.1) 

153 theta = np.arcsin(0.1) 

154 rng = np.random.RandomState(88) 

155 n_tests = 200 

156 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

157 dec_list = rng.random_sample(n_tests)*(0.5*np.pi-theta)+theta 

158 for ra, dec, in zip(ra_list, dec_list): 

159 xyz = cartesianFromSpherical(ra, dec) 

160 self.assertTrue(hs.contains_pt(xyz)) 

161 self.assertFalse(nhs.contains_pt(xyz)) 

162 

163 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

164 dec_list = theta - rng.random_sample(n_tests)*(0.5*np.pi+theta) 

165 for ra, dec, in zip(ra_list, dec_list): 

166 xyz = cartesianFromSpherical(ra, dec) 

167 self.assertFalse(hs.contains_pt(xyz)) 

168 self.assertTrue(nhs.contains_pt(xyz)) 

169 

170 hs = HalfSpace(np.array([1.0, 0.0, 0.0]), 0.2) 

171 nhs = HalfSpace(np.array([-1.0, 0.0, 0.0]), -0.2) 

172 theta = np.arcsin(0.2) 

173 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

174 dec_list = rng.random_sample(n_tests)*(0.5*np.pi-theta)+theta 

175 for ra, dec in zip(ra_list, dec_list): 

176 xyz_rot = cartesianFromSpherical(ra, dec) 

177 xyz = rotAboutY(xyz_rot, 0.5*np.pi) 

178 self.assertTrue(hs.contains_pt(xyz)) 

179 self.assertFalse(nhs.contains_pt(xyz)) 

180 

181 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

182 dec_list = theta - rng.random_sample(n_tests)*(0.5*np.pi+theta) 

183 for ra, dec, in zip(ra_list, dec_list): 

184 xyz_rot = cartesianFromSpherical(ra, dec) 

185 xyz = rotAboutY(xyz_rot, 0.5*np.pi) 

186 self.assertFalse(hs.contains_pt(xyz)) 

187 self.assertTrue(nhs.contains_pt(xyz)) 

188 

189 vv = np.array([0.5*np.sqrt(2), -0.5*np.sqrt(2), 0.0]) 

190 hs = HalfSpace(vv, 0.3) 

191 nhs = HalfSpace(-1.0*vv, -0.3) 

192 theta = np.arcsin(0.3) 

193 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

194 dec_list = rng.random_sample(n_tests)*(0.5*np.pi-theta)+theta 

195 

196 for ra, dec in zip(ra_list, dec_list): 

197 xyz_rot = cartesianFromSpherical(ra, dec) 

198 xyz_rot = rotAboutX(xyz_rot, 0.5*np.pi) 

199 xyz = rotAboutZ(xyz_rot, 0.25*np.pi) 

200 self.assertTrue(hs.contains_pt(xyz)) 

201 self.assertFalse(nhs.contains_pt(xyz)) 

202 

203 ra_list = rng.random_sample(n_tests)*2.0*np.pi 

204 dec_list = theta - rng.random_sample(n_tests)*(0.5*np.pi+theta) 

205 for ra, dec, in zip(ra_list, dec_list): 

206 xyz_rot = cartesianFromSpherical(ra, dec) 

207 xyz_rot = rotAboutX(xyz_rot, 0.5*np.pi) 

208 xyz = rotAboutZ(xyz_rot, 0.25*np.pi) 

209 self.assertFalse(hs.contains_pt(xyz)) 

210 self.assertTrue(nhs.contains_pt(xyz)) 

211 

212 def test_halfspace_contains_pt_scaled(self): 

213 """ 

214 Test that HalfSpace.contains_pt returns the same answer 

215 for points on and off the unit sphere 

216 """ 

217 vv = np.array([0.5*np.sqrt(2), -0.5*np.sqrt(2), 0.0]) 

218 hs = HalfSpace(vv, 0.3) 

219 

220 ct_inside = 0 

221 ct_outside = 0 

222 rng = np.random.RandomState(8812) 

223 random_pts = rng.random_sample((100, 3))*5.0 

224 for pt in random_pts: 

225 norm = np.sqrt(np.power(pt, 2).sum()) 

226 self.assertGreater(np.abs(1.0-norm), 0.01) 

227 unnormed_ans = hs.contains_pt(pt) 

228 normed_pt = pt/norm 

229 normed_ans = hs.contains_pt(normed_pt) 

230 self.assertEqual(unnormed_ans, normed_ans) 

231 

232 if normed_ans: 

233 ct_inside += 1 

234 else: 

235 ct_outside += 1 

236 

237 self.assertGreater(ct_inside, 0) 

238 self.assertGreater(ct_outside, 0) 

239 

240 def test_halfspace_contains_trixel(self): 

241 

242 # test half space that is on the equator wher N3 and S0 meet 

243 hs = HalfSpace(np.array([1.0, 1.0, 0.0]), 0.8) 

244 for tx in basic_trixels: 

245 status = hs.contains_trixel(basic_trixels[tx]) 

246 msg = 'Failed on %s' % tx 

247 if tx not in ('S0', 'N3'): 

248 self.assertEqual(status, 'outside', msg=msg) 

249 else: 

250 self.assertEqual(status, 'partial', msg=msg) 

251 

252 # test halfspace that is centered on vertex where S0, S3, N0, N3 meet 

253 hs = HalfSpace(np.array([1.0, 0.0, 0.0]), 0.8) 

254 for tx in basic_trixels: 

255 status = hs.contains_trixel(basic_trixels[tx]) 

256 msg = 'Failed on %s' % tx 

257 if tx not in ('S0', 'S3', 'N0', 'N3'): 

258 self.assertEqual(status, 'outside', msg=msg) 

259 else: 

260 self.assertEqual(status, 'partial', msg=msg) 

261 

262 # test halfspace fully contained in N3 

263 hs = HalfSpace(np.array([1.0, 1.0, 1.0]), 0.9) 

264 for tx in basic_trixels: 

265 status = hs.contains_trixel(basic_trixels[tx]) 

266 msg = 'Failed on %s' % tx 

267 if tx != 'N3': 

268 self.assertEqual(status, 'outside', msg=msg) 

269 else: 

270 self.assertEqual(status, 'partial', msg=msg) 

271 

272 # test halfspace that totally contains N3 

273 ra, dec = basic_trixels['N3'].get_center() 

274 hs = HalfSpace(np.array([1.0, 1.0, 1.0]), np.cos(0.31*np.pi)) 

275 for tx in basic_trixels: 

276 status = hs.contains_trixel(basic_trixels[tx]) 

277 msg = 'Failed on %s' % tx 

278 if tx == 'N3': 

279 self.assertEqual(status, 'full', msg=msg) 

280 elif tx in ('N1', 'N2', 'N0', 'S0', 'S1', 'S3'): 

281 self.assertEqual(status, 'partial', msg=msg) 

282 else: 

283 self.assertEqual(status, 'outside', msg=msg) 

284 

285 def test_half_space_eq(self): 

286 """ 

287 Test that __eq__() works for HalfSpace 

288 """ 

289 vv = np.array([1.0, 0.9, 2.4]) 

290 hs1 = HalfSpace(vv, 0.1) 

291 hs2 = HalfSpace(2.0*vv, 0.1) 

292 self.assertEqual(hs1, hs2) 

293 hs2 = HalfSpace(vv, 0.09) 

294 self.assertNotEqual(hs1, hs2) 

295 hs2 = HalfSpace(vv-1.0e-4*np.array([1.0, 0.0, 0.0]), 0.1) 

296 self.assertNotEqual(hs1, hs2) 

297 

298 def test_findAllTrixels_radius(self): 

299 """ 

300 Test the method that attempts to find all of the trixels 

301 inside a given half space by approximating the angular 

302 scale of the trixels and verifying that all returned 

303 trixels are within radius+angular scale of the center 

304 of the half space. 

305 """ 

306 level = 5 

307 

308 # approximate the linear angular scale (in degrees) 

309 # of a trixel grid using the fact that there are 

310 # 8*4**(level-1) trixels in the grid as per equation 2.5 of 

311 # 

312 # https://www.microsoft.com/en-us/research/wp-content/uploads/2005/09/tr-2005-123.pdf 

313 angular_scale = np.sqrt(4.0*np.pi*(180.0/np.pi)**2/(8.0*4.0**(level-1))) 

314 

315 ra = 43.0 

316 dec = 22.0 

317 radius = 20.0 

318 half_space = halfSpaceFromRaDec(ra, dec, radius) 

319 trixel_list = half_space.findAllTrixels(level) 

320 self.assertGreater(len(trixel_list), 2) 

321 

322 # first, check that all of the returned trixels are 

323 # inside the HalfSpace 

324 good_htmid_list = [] 

325 for i_limit, limits in enumerate(trixel_list): 

326 

327 # verify that the tuples have been sorted by 

328 # htmid_min 

329 if i_limit > 0: 

330 self.assertGreater(limits[0], trixel_list[i_limit-1][1]) 

331 

332 for htmid in range(limits[0], limits[1]+1): 

333 test_trixel = trixelFromHtmid(htmid) 

334 ra_trix, dec_trix = test_trixel.get_center() 

335 good_htmid_list.append(htmid) 

336 self.assertNotEqual(half_space.contains_trixel(test_trixel), 

337 'outside') 

338 

339 # check that the returned trixels are within 

340 # radius+angular_scale of the center of the HalfSpace 

341 self.assertLess(angularSeparation(ra, dec, ra_trix, dec_trix), 

342 radius+angular_scale) 

343 

344 # next, verify that all of the possible trixels that 

345 # were not returned are outside the HalfSpace 

346 for base_htmid in range(8, 16): 

347 htmid_0 = base_htmid << 2*(level-1) 

348 self.assertEqual(levelFromHtmid(htmid_0), level) 

349 for ii in range(2**(2*level-2)): 

350 htmid = htmid_0 + ii 

351 self.assertEqual(levelFromHtmid(htmid), level) 

352 if htmid not in good_htmid_list: 

353 test_trixel = trixelFromHtmid(htmid) 

354 self.assertEqual(half_space.contains_trixel(test_trixel), 'outside') 

355 ra_trix, dec_trix = test_trixel.get_center() 

356 self.assertGreater(angularSeparation(ra, dec, ra_trix, dec_trix), 

357 radius) 

358 

359 def test_findAllTrixels_brute(self): 

360 """ 

361 Use the method trixel_intersects_half_space defined at the 

362 top of this script to verify that HalfSpace.findAllTrixels works 

363 """ 

364 level = 7 

365 trixel_dict = getAllTrixels(level) 

366 all_htmid = [] 

367 for htmid in trixel_dict.keys(): 

368 if levelFromHtmid(htmid) == level: 

369 all_htmid.append(htmid) 

370 

371 hspace = halfSpaceFromRaDec(36.0, 22.1, 2.0) 

372 

373 # make sure that the two methods of determining if 

374 # a HalfSpace contains a trixel (HalfSpace.contains_trixel 

375 # and trixel_interects_half_space) agree 

376 for htmid in all_htmid: 

377 trix = trixel_dict[htmid] 

378 msg = 'offending htmid %d' % htmid 

379 if trixel_intersects_half_space(trix, hspace): 

380 self.assertNotEqual(hspace.contains_trixel(trix), 'outside', 

381 msg=msg) 

382 else: 

383 self.assertEqual(hspace.contains_trixel(trix), 'outside', 

384 msg=msg) 

385 

386 trixel_limits = hspace.findAllTrixels(level) 

387 intersecting_htmid = set() 

388 

389 # check that all of the trixels included in the limits 

390 # do, in fact, intersect or exist in the HalfSpace 

391 for lim in trixel_limits: 

392 for htmid in range(lim[0], lim[1]+1): 

393 trix = trixel_dict[htmid] 

394 self.assertTrue(trixel_intersects_half_space(trix, hspace)) 

395 intersecting_htmid.add(htmid) 

396 

397 # check that all of the trixels not included in the limits 

398 # are, in fact, outside of the HalfSpace 

399 self.assertLess(len(intersecting_htmid), len(all_htmid)) 

400 self.assertGreater(len(intersecting_htmid), 0) 

401 for htmid in all_htmid: 

402 if htmid in intersecting_htmid: 

403 continue 

404 trix = trixel_dict[htmid] 

405 self.assertFalse(trixel_intersects_half_space(trix, hspace)) 

406 

407 def test_halfSpaceFromPoints(self): 

408 rng = np.random.RandomState(88) 

409 for ii in range(10): 

410 pt1 = (rng.random_sample()*360.0, rng.random_sample()*180.0-90.0) 

411 pt2 = (rng.random_sample()*360.0, rng.random_sample()*180.0-90.0) 

412 pt3 = (rng.random_sample()*360.0, rng.random_sample()*180.0-90.0) 

413 hs = halfSpaceFromPoints(pt1, pt2, pt3) 

414 

415 # check that the HalfSpace contains pt3 

416 vv3 = cartesianFromSpherical(np.radians(pt3[0]), np.radians(pt3[1])) 

417 self.assertTrue(hs.contains_pt(vv3)) 

418 

419 # check that the HalfSpace encompasses 1/2 of the unit sphere 

420 self.assertAlmostEqual(hs.phi, 0.5*np.pi, 10) 

421 self.assertAlmostEqual(hs.dd, 0.0, 10) 

422 

423 # check that pt1 and pt2 are 90 degrees away from the center 

424 # of the HalfSpace 

425 vv1 = cartesianFromSpherical(np.radians(pt1[0]), np.radians(pt1[1])) 

426 vv2 = cartesianFromSpherical(np.radians(pt2[0]), np.radians(pt2[1])) 

427 self.assertAlmostEqual(np.dot(vv1,hs.vector), 0.0, 10) 

428 self.assertAlmostEqual(np.dot(vv2,hs.vector), 0.0, 10) 

429 

430 def test_HalfSpaceIntersection(self): 

431 

432 # Test that the two roots of an intersection are the 

433 # correct angular distance from the centers of the 

434 # half spaces 

435 ra1 = 22.0 

436 dec1 = 45.0 

437 rad1 = 10.0 

438 ra2 = 23.5 

439 dec2 = 37.9 

440 rad2 = 9.2 

441 hs1 = halfSpaceFromRaDec(ra1, dec1, rad1) 

442 hs2 = halfSpaceFromRaDec(ra2, dec2, rad2) 

443 roots = intersectHalfSpaces(hs1, hs2) 

444 self.assertEqual(len(roots), 2) 

445 self.assertAlmostEqual(np.sqrt(np.sum(roots[0]**2)), 1.0, 10) 

446 self.assertAlmostEqual(np.sqrt(np.sum(roots[1]**2)), 1.0, 10) 

447 ra_r1, dec_r1 = np.degrees(sphericalFromCartesian(roots[0])) 

448 ra_r2, dec_r2 = np.degrees(sphericalFromCartesian(roots[1])) 

449 dd = angularSeparation(ra1, dec1, ra_r1, dec_r1) 

450 self.assertAlmostEqual(dd, rad1, 10) 

451 dd = angularSeparation(ra1, dec1, ra_r2, dec_r2) 

452 self.assertAlmostEqual(dd, rad1, 10) 

453 dd = angularSeparation(ra2, dec2, ra_r1, dec_r1) 

454 self.assertAlmostEqual(dd, rad2, 10) 

455 dd = angularSeparation(ra2, dec2, ra_r2, dec_r2) 

456 self.assertAlmostEqual(dd, rad2, 10) 

457 

458 # test that two non-intersecting HalfSpaces return no roots 

459 hs1 = halfSpaceFromRaDec(0.0, 90.0, 1.0) 

460 hs2 = halfSpaceFromRaDec(20.0, -75.0, 5.0) 

461 roots = intersectHalfSpaces(hs1, hs2) 

462 self.assertEqual(len(roots), 0) 

463 

464 # test that two half spaces that are inside each other 

465 # return no roots 

466 hs1 = halfSpaceFromRaDec(77.0, 10.0, 20.0) 

467 hs2 = halfSpaceFromRaDec(75.0, 8.0, 0.2) 

468 roots = intersectHalfSpaces(hs1, hs2) 

469 self.assertEqual(len(roots), 0) 

470 

471 # test that two half spaces with identical centers 

472 # return no roots 

473 hs1 = halfSpaceFromRaDec(11.0, -23.0, 1.0) 

474 hs2 = halfSpaceFromRaDec(11.0, -23.0, 0.2) 

475 roots = intersectHalfSpaces(hs1, hs2) 

476 self.assertEqual(len(roots), 0) 

477 

478 roots = intersectHalfSpaces(hs1, hs1) 

479 self.assertEqual(len(roots), 0) 

480 

481 def test_merge_trixel_bounds(self): 

482 """ 

483 Test that the merge_trixel_bounds method works 

484 """ 

485 input_bound = [(1,7), (2,4), (21,35), (8,11), (36, 42), (43, 43)] 

486 result = HalfSpace.merge_trixel_bounds(input_bound) 

487 shld_be = [(1, 11), (21, 43)] 

488 self.assertEqual(result, shld_be) 

489 

490 def test_join_trixel_bound_sets(self): 

491 """ 

492 Test that HalfSpace.join_trixel_bound_sets works 

493 """ 

494 b1 = [(32,47), (6,8), (11,19), (12,14), (66,73)] 

495 b2 = [(35,41), (7,15), (41, 44)] 

496 result = HalfSpace.join_trixel_bound_sets(b1, b2) 

497 shld_be = [(7,8), (11, 15), (35,44)] 

498 self.assertEqual(result, shld_be) 

499 

500 def test_contains_many_pts(self): 

501 """ 

502 Test that HalfSpace.contains_many_pts works 

503 """ 

504 rng = np.random.RandomState(5142) 

505 n_pts = 100 

506 vv_list = np.zeros((n_pts,3), dtype=float) 

507 for ii in range(n_pts): 

508 vv = rng.random_sample(3)-0.5 

509 vv /= np.sqrt(np.sum(vv**2)) 

510 vv_list[ii] = vv 

511 

512 vv = rng.random_sample(3)-0.5 

513 vv /= np.sqrt(np.sum(vv**2)) 

514 hs = HalfSpace(vv, 0.3) 

515 results = hs.contains_many_pts(vv_list) 

516 is_true = np.where(results)[0] 

517 self.assertGreater(len(is_true), n_pts//4) 

518 self.assertLess(len(is_true), 3*n_pts//4) 

519 for i_vv, vv in enumerate(vv_list): 

520 self.assertEqual(hs.contains_pt(vv), results[i_vv]) 

521 

522 

523class TrixelFinderTest(unittest.TestCase): 

524 

525 longMessage = True 

526 

527 def check_pt(self, pt, answer): 

528 """ 

529 Take a Cartesian point (pt) and a known 

530 htmid for that point (answer). Find the htmid 

531 for the point using findHtmid and verify that 

532 we get the expected answer. 

533 """ 

534 ra, dec = sphericalFromCartesian(pt) 

535 ii = findHtmid(np.degrees(ra), np.degrees(dec), 3) 

536 binary = '{0:b}'.format(ii) 

537 self.assertEqual(binary, answer) 

538 

539 def test_against_fatboy(self): 

540 """ 

541 Test findHtmid against a random selection of stars from fatboy 

542 """ 

543 dtype = np.dtype([('htmid', int), ('ra', float), ('dec', float)]) 

544 data = np.genfromtxt(os.path.join(getPackageDir('sims_utils'), 'tests', 

545 'testData', 'htmid_test_data.txt'), 

546 dtype=dtype) 

547 self.assertGreater(len(data), 20) 

548 for i_pt in range(len(data)): 

549 htmid_test = findHtmid(data['ra'][i_pt], data['dec'][i_pt], 21) 

550 self.assertEqual(htmid_test, data['htmid'][i_pt]) 

551 level_test = levelFromHtmid(htmid_test) 

552 self.assertEqual(level_test, 21) 

553 

554 def test_findHtmid_vectorized(self): 

555 """ 

556 Test that findHtmid works correctly on vectors 

557 """ 

558 rng = np.random.RandomState(81723122) 

559 n_samples = 1000 

560 ra = rng.random_sample(n_samples)*360.0 

561 dec = rng.random_sample(n_samples)*180.0-90.0 

562 level = 7 

563 htmid_vec = findHtmid(ra, dec, level) 

564 self.assertIsInstance(htmid_vec, np.ndarray) 

565 htmid_fast = _findHtmid_fast(ra, dec, level) 

566 self.assertIsInstance(htmid_fast, np.ndarray) 

567 np.testing.assert_array_equal(htmid_vec, htmid_fast) 

568 for ii in range(n_samples): 

569 htmid_slow = _findHtmid_slow(ra[ii], dec[ii], level) 

570 self.assertIsInstance(htmid_slow, numbers.Number) 

571 self.assertEqual(htmid_slow, htmid_vec[ii]) 

572 htmid_single = findHtmid(ra[ii], dec[ii], level) 

573 self.assertIsInstance(htmid_single, numbers.Number) 

574 self.assertEqual(htmid_single, htmid_vec[ii]) 

575 

576 def test_levelFromHtmid(self): 

577 """ 

578 Test that levelFromHtmid behaves as expected 

579 """ 

580 for ii in range(8, 16): 

581 self.assertEqual(levelFromHtmid(ii), 1) 

582 

583 self.assertEqual(levelFromHtmid(2**9+5), 4) 

584 self.assertEqual(levelFromHtmid(2**15+88), 7) 

585 

586 with self.assertRaises(RuntimeError) as context: 

587 levelFromHtmid(2**10) 

588 self.assertIn("4+2n", context.exception.args[0]) 

589 

590 for ii in range(8): 

591 with self.assertRaises(RuntimeError) as context: 

592 levelFromHtmid(2**10) 

593 self.assertIn("4+2n", context.exception.args[0]) 

594 

595 def test_trixel_finding(self): 

596 """ 

597 Check that findHtmid works by passing in some 

598 points whose htmid are known because of their 

599 proximity to the corners of low-level Trixels. 

600 Use check_pt to verify that findHtmid gives 

601 the right answer. 

602 """ 

603 epsilon = 1.0e-6 

604 dx = np.array([epsilon, 0.0, 0.0]) 

605 dy = np.array([0.0, epsilon, 0.0]) 

606 dz = np.array([0.0, 0.0, epsilon]) 

607 

608 xx = np.array([1.0, 0.0, 0.0]) 

609 yy = np.array([0.0, 1.0, 0.0]) 

610 zz = np.array([0.0, 0.0, 1.0]) 

611 

612 pt = xx + dy + dz 

613 # N320 

614 self.check_pt(pt, '11111000') 

615 

616 pt = xx - dy + dz 

617 # N000 

618 self.check_pt(pt, '11000000') 

619 

620 pt = xx - dy - dz 

621 # S320 

622 self.check_pt(pt, '10111000') 

623 

624 pt = yy + dx + dz 

625 # N300 

626 self.check_pt(pt, '11110000') 

627 

628 pt = yy - dx + dz 

629 # N220 

630 self.check_pt(pt, '11101000') 

631 

632 pt = yy - dx - dz 

633 # S100 

634 self.check_pt(pt, '10010000') 

635 

636 pt = zz + dy + dx 

637 # N310 

638 self.check_pt(pt, '11110100') 

639 

640 pt = zz - dy + dx 

641 # N010 

642 self.check_pt(pt, '11000100') 

643 

644 pt = zz - dy - dx 

645 # N110 

646 self.check_pt(pt, '11010100') 

647 

648 pt = -xx + dz + dy 

649 # N200 

650 self.check_pt(pt, '11100000') 

651 

652 pt = -xx - dz + dy 

653 # S120 

654 self.check_pt(pt, '10011000') 

655 

656 pt = -xx - dz - dy 

657 # S200 

658 self.check_pt(pt, '10100000') 

659 

660 pt = -yy + dx + dz 

661 # N020 

662 self.check_pt(pt, '11001000') 

663 

664 pt = -yy - dx + dz 

665 # N100 

666 self.check_pt(pt, '11010000') 

667 

668 pt = -yy - dx - dz 

669 # S220 

670 self.check_pt(pt, '10101000') 

671 

672 pt = -zz + dx + dy 

673 # S010 

674 self.check_pt(pt, '10000100') 

675 

676 pt = -zz - dx + dy 

677 # S110 

678 self.check_pt(pt, '10010100') 

679 

680 pt = -zz - dx - dy 

681 # S210 

682 self.check_pt(pt, '10100100') 

683 

684 pt = xx + yy + zz 

685 # N333 

686 self.check_pt(pt, '11111111') 

687 

688 def test_trixel_from_htmid(self): 

689 """ 

690 Check that trixelFromHtmid works by 

691 finding the htmid from an RA, Dec pair, 

692 instantiating the Trixel corresponding 

693 to that htmid, and verifying that that 

694 Trixel (and not its neighbors) contains 

695 the RA, Dec pair. 

696 """ 

697 rng = np.random.RandomState(88) 

698 n_tests = 100 

699 for i_test in range(n_tests): 

700 pt = rng.normal(0.0, 1.0, 3) 

701 ra, dec = sphericalFromCartesian(pt) 

702 ra = np.degrees(ra) 

703 dec = np.degrees(dec) 

704 ii = findHtmid(ra, dec, 5) 

705 tt = trixelFromHtmid(ii) 

706 self.assertTrue(tt.contains(ra, dec)) 

707 tt1 = trixelFromHtmid(ii-1) 

708 self.assertFalse(tt1.contains(ra, dec)) 

709 tt2 = trixelFromHtmid(ii+1) 

710 self.assertFalse(tt2.contains(ra, dec)) 

711 

712 def test_trixel_eq_ne(self): 

713 """ 

714 Test that the __eq__ and __ne__ operators on the Trixel class work 

715 """ 

716 t1 = trixelFromHtmid(8*16+1) 

717 t2 = trixelFromHtmid(8*16+1) 

718 self.assertEqual(t1, t2) 

719 t3 = trixelFromHtmid(8*16+3) 

720 self.assertNotEqual(t1, t3) 

721 self.assertTrue(t1 == t2) 

722 self.assertFalse(t1 == t3) 

723 self.assertTrue(t1 != t3) 

724 self.assertFalse(t2 == t3) 

725 self.assertTrue(t2 != t3) 

726 

727 def test_get_all_trixels(self): 

728 """ 

729 Test method to get all trixels up to a certain level 

730 """ 

731 max_level = 5 

732 n_trixel_per_level = {} 

733 n_trixel_per_level[0] = 0 

734 for level in range(1, max_level+1): 

735 n_trixel_per_level[level] = 8*(4**(level-1)) 

736 

737 trixel_dict = getAllTrixels(max_level) 

738 n_found = {} 

739 for level in range(max_level+1): 

740 n_found[level] = 0 

741 

742 for htmid in trixel_dict: 

743 level = levelFromHtmid(htmid) 

744 n_found[level] += 1 

745 

746 # verify that the correct number of trixels were 

747 # found per level 

748 for level in n_found: 

749 msg = 'failed on level %d' % level 

750 self.assertEqual(n_found[level], n_trixel_per_level[level], 

751 msg=msg) 

752 

753 # make sure no trixels were duplicated 

754 self.assertEqual(len(np.unique(list(trixel_dict.keys()))), 

755 len(trixel_dict)) 

756 

757 for htmid in trixel_dict.keys(): 

758 level = levelFromHtmid(htmid) 

759 self.assertLessEqual(level, max_level) 

760 self.assertGreaterEqual(level, 1) 

761 t0 = trixelFromHtmid(htmid) 

762 self.assertEqual(t0, trixel_dict[htmid]) 

763 

764 def test_trixel_bounding_circle(self): 

765 """ 

766 Verify that the trixel's bounding_circle method returns 

767 a circle that contains all of the corners of the 

768 trixel 

769 """ 

770 rng = np.random.RandomState(142) 

771 n_test_cases = 5 

772 for i_test in range(n_test_cases): 

773 htmid = (13 << 6)+rng.randint(1, 2**6-1) 

774 trixel = trixelFromHtmid(htmid) 

775 bounding_circle = trixel.bounding_circle 

776 ra_0, dec_0 = sphericalFromCartesian(bounding_circle[0]) 

777 ra_list = [] 

778 dec_list = [] 

779 for cc in trixel.corners: 

780 ra, dec = sphericalFromCartesian(cc) 

781 ra_list.append(ra) 

782 dec_list.append(dec) 

783 ra_list = np.array(ra_list) 

784 dec_list = np.array(dec_list) 

785 distance = _angularSeparation(ra_0, dec_0, 

786 ra_list, dec_list) 

787 distance = arcsecFromRadians(distance) 

788 radius = arcsecFromRadians(bounding_circle[2]) 

789 self.assertLessEqual(distance.max()-radius, 1.0e-8) 

790 self.assertLess(np.abs(distance.max()-radius), 1.0e-8) 

791 

792 def test_trixel_contains_many(self): 

793 """ 

794 Test that trixel.contains_pt and trixel.contains can 

795 work with numpy arrays of input 

796 """ 

797 htmid = (15 << 6) + 45 

798 trixel = trixelFromHtmid(htmid) 

799 ra_0, dec_0 = trixel.get_center() 

800 radius = trixel.get_radius() 

801 rng = np.random.RandomState(44) 

802 n_pts = 100 

803 rr = radius*rng.random_sample(n_pts)*1.1 

804 theta = rng.random_sample(n_pts)*2.0*np.pi 

805 ra_list = ra_0 + rr*np.cos(theta) 

806 dec_list = dec_0 + rr*np.sin(theta) 

807 contains_arr = trixel.contains(ra_list, dec_list) 

808 n_in = 0 

809 n_out = 0 

810 for i_pt in range(n_pts): 

811 single_contains = trixel.contains(ra_list[i_pt], dec_list[i_pt]) 

812 self.assertEqual(single_contains, contains_arr[i_pt]) 

813 if single_contains: 

814 n_in += 1 

815 else: 

816 n_out += 1 

817 

818 self.assertGreater(n_in, 0) 

819 self.assertGreater(n_out, 0) 

820 

821 xyz_list = cartesianFromSpherical(np.radians(ra_list), 

822 np.radians(dec_list)) 

823 

824 contains_xyz_arr = trixel.contains_pt(xyz_list) 

825 np.testing.assert_array_equal(contains_xyz_arr, contains_arr) 

826 

827 

828class MemoryTestClass(lsst.utils.tests.MemoryTestCase): 

829 pass 

830 

831 

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

833 lsst.utils.tests.init() 

834 unittest.main()