Coverage for tests/test_hpxUtils.py: 18%

68 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-08 14:31 -0800

1# 

2# LSST Data Management System 

3# Copyright 2021 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# 

22import unittest 

23import healpy as hp 

24import numpy as np 

25from astropy.wcs import WCS 

26 

27import lsst.utils.tests 

28import lsst.afw.geom as afwGeom 

29 

30 

31class HpxUtilsTestCase(lsst.utils.tests.TestCase): 

32 def test_hpx_wcs(self): 

33 """Test various computations of HPX wcs. 

34 """ 

35 # The default is 2**9=512 pixels on a side. We also 

36 # test smaller and larger tiles. 

37 shift_order_tests = [8, 9, 10] 

38 # Choose arbitrary positions that are north, south, 

39 # equatorial, east, and west. 41.8 degrees is special. 

40 pos_tests = [(0.0, 0.0), 

41 (43.0, 3.0), 

42 (127.0, -23.0), 

43 (194.0, 36.0), 

44 (207.0, -41.5), 

45 (256.0, 42.0), 

46 (302.0, -67.0), 

47 (332.0, 75.0), 

48 (348.0, -83.0), 

49 (358.0, 89.0)] 

50 

51 # Default order of 11 (nside=2048) gives 0.2" pixels for 512 

52 # subpixels 

53 hips_order_tests = [12, 11, 10] 

54 

55 for shift_order, hips_order in zip(shift_order_tests, hips_order_tests): 

56 nside = 2**hips_order 

57 nsubpix = 2**shift_order 

58 

59 for pos in pos_tests: 

60 pixel = hp.ang2pix(nside, pos[0], pos[1], lonlat=True, nest=True) 

61 

62 wcs = afwGeom.makeHpxWcs(hips_order, pixel, shift_order=shift_order) 

63 

64 y, x = np.meshgrid(np.arange(nsubpix), np.arange(nsubpix)) 

65 x = x.ravel().astype(np.float64) 

66 y = y.ravel().astype(np.float64) 

67 ra, dec = wcs.pixelToSkyArray(x, y, degrees=True) 

68 ra[ra == 360.0] = 0.0 

69 

70 # Check that all these positions are truly inside the pixel 

71 radec_pixels = hp.ang2pix(nside, ra, dec, lonlat=True, nest=True) 

72 np.testing.assert_array_equal(radec_pixels, pixel) 

73 

74 # Check that the orientation is correct. 

75 # pixel (0, 0) should be E 

76 # pixel (nsubpix - 1, 0) should be N 

77 # pixel (nsubpix - 1, nsubpix - 1) should be W 

78 # pixel (0, nsubpix - 1) should be S 

79 xx = np.array([0, nsubpix - 1, nsubpix - 1, 0], dtype=np.float64) 

80 yy = np.array([0, 0, nsubpix - 1, nsubpix - 1], dtype=np.float64) 

81 ra_cornerpix, dec_cornerpix = wcs.pixelToSkyArray(xx, yy, degrees=True) 

82 

83 # Generate all the sub-pixels 

84 bit_shift = 2*int(np.round(np.log2(nsubpix))) 

85 sub_pixels = np.left_shift(pixel, bit_shift) + np.arange(nsubpix*nsubpix) 

86 ra_sub, dec_sub = hp.pix2ang(nside*nsubpix, sub_pixels, lonlat=True, nest=True) 

87 # Deal with RA = 0 for testing... 

88 if ra_sub.max() > 350.0 and ra_sub.min() < 10.0: 

89 hi, = np.where(ra_sub > 180.0) 

90 ra_sub[hi] -= 360.0 

91 hi_corner, = np.where(ra_cornerpix > 180.0) 

92 ra_cornerpix[hi_corner] -= 360.0 

93 

94 cos_dec = np.cos(np.deg2rad(np.median(dec))) 

95 

96 easternmost = np.argmax(ra_sub) 

97 self.assertFloatsAlmostEqual(ra_cornerpix[0], ra_sub[easternmost], atol=1e-13/cos_dec) 

98 self.assertFloatsAlmostEqual(dec_cornerpix[0], dec_sub[easternmost], atol=5e-13) 

99 northernmost = np.argmax(dec_sub) 

100 self.assertFloatsAlmostEqual(ra_cornerpix[1], ra_sub[northernmost], atol=1e-13/cos_dec) 

101 self.assertFloatsAlmostEqual(dec_cornerpix[1], dec_sub[northernmost], atol=5e-13) 

102 westernmost = np.argmin(ra_sub) 

103 self.assertFloatsAlmostEqual(ra_cornerpix[2], ra_sub[westernmost], atol=1e-13/cos_dec) 

104 self.assertFloatsAlmostEqual(dec_cornerpix[2], dec_sub[westernmost], atol=5e-13) 

105 southernmost = np.argmin(dec_sub) 

106 self.assertFloatsAlmostEqual(ra_cornerpix[3], ra_sub[southernmost], atol=1e-13/cos_dec) 

107 self.assertFloatsAlmostEqual(dec_cornerpix[3], dec_sub[southernmost], atol=5e-13) 

108 

109 # Confirm that the transformation also works with astropy WCS 

110 astropy_wcs = WCS(header=wcs.getFitsMetadata().toDict()) 

111 astropy_coords = astropy_wcs.pixel_to_world(x, y) 

112 

113 astropy_ra = astropy_coords.ra.degree 

114 astropy_dec = astropy_coords.dec.degree 

115 

116 # The astropy warping is only consistent at the 5e-11 level. 

117 self.assertFloatsAlmostEqual(astropy_ra, ra, atol=5e-11/cos_dec) 

118 self.assertFloatsAlmostEqual(astropy_dec, dec, atol=5e-11) 

119 

120 def test_hpx_wcs_bad_inputs(self): 

121 """Test assertions for bad inputs to makeHpxWcs() 

122 """ 

123 # order must be positive. 

124 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 0, 100) 

125 self.assertRaises(ValueError, afwGeom.makeHpxWcs, -10, 100) 

126 

127 # pixel number must be in range. 

128 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, -1) 

129 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 12*2**10 + 1) 

130 

131 # tilepix must be a positive power of 2. 

132 # shift_order must be positive 

133 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 0, shift_order=0) 

134 self.assertRaises(ValueError, afwGeom.makeHpxWcs, 5, 0, shift_order=-10) 

135 

136 

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

138 pass 

139 

140 

141def setup_module(module): 

142 lsst.utils.tests.init() 

143 

144 

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

146 lsst.utils.tests.init() 

147 unittest.main()