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 numpy as np 

2import healpy as hp 

3from scipy.optimize import minimize 

4from .utils import hp_kd_tree, set_default_nside, read_fields 

5from lsst.sims.utils import _hpid2RaDec, _xyz_angular_radius, _xyz_from_ra_dec 

6 

7default_nside = set_default_nside() 

8 

9 

10def wrapRADec(ra, dec): 

11 # XXX--from MAF, should put in general utils 

12 """ 

13 Wrap RA into 0-2pi and Dec into +/0 pi/2. 

14 

15 Parameters 

16 ---------- 

17 ra : numpy.ndarray 

18 RA in radians 

19 dec : numpy.ndarray 

20 Dec in radians 

21 

22 Returns 

23 ------- 

24 numpy.ndarray, numpy.ndarray 

25 Wrapped RA/Dec values, in radians. 

26 """ 

27 # Wrap dec. 

28 low = np.where(dec < -np.pi / 2.0)[0] 

29 dec[low] = -1 * (np.pi + dec[low]) 

30 ra[low] = ra[low] - np.pi 

31 high = np.where(dec > np.pi / 2.0)[0] 

32 dec[high] = np.pi - dec[high] 

33 ra[high] = ra[high] - np.pi 

34 # Wrap RA. 

35 ra = ra % (2.0 * np.pi) 

36 return ra, dec 

37 

38 

39def rotate_ra_dec(ra, dec, ra_target, dec_target, init_rotate=0.): 

40 """ 

41 Rotate ra and dec coordinates to be centered on a new dec. 

42 

43 Rotates around the x-axis 1st, then to the dec, then ra. 

44 

45 Inputs 

46 ------ 

47 ra : float or np.array 

48 RA coordinate(s) to be rotated in radians 

49 dec : float or np.array 

50 Dec coordinate(s) to be rotated in radians 

51 ra_rotation : float 

52 RA distance to rotate in radians 

53 dec_target : float 

54 Dec distance to rotate in radians 

55 init_rotate : float (0.) 

56 The amount to rotate the points around the x-axis first (radians). 

57 """ 

58 # point (ra,dec) = (0,0) is at x,y,z = 1,0,0 

59 

60 x, y, z = _xyz_from_ra_dec(ra, dec) 

61 

62 # Rotate around the x axis to start 

63 xp = x 

64 if init_rotate != 0.: 

65 c_i = np.cos(init_rotate) 

66 s_i = np.sin(init_rotate) 

67 yp = c_i*y - s_i*z 

68 zp = s_i*y + c_i*z 

69 else: 

70 yp = y 

71 zp = z 

72 

73 theta_y = dec_target 

74 c_ty = np.cos(theta_y) 

75 s_ty = np.sin(theta_y) 

76 

77 # Rotate about y 

78 xp2 = c_ty*xp + s_ty*zp 

79 zp2 = -s_ty*xp + c_ty*zp 

80 

81 # Convert back to RA, Dec 

82 ra_p = np.arctan2(yp, xp2) 

83 dec_p = -np.arcsin(zp2) 

84 

85 # Rotate to the correct RA 

86 ra_p += ra_target 

87 

88 ra_p, dec_p = wrapRADec(ra_p, dec_p) 

89 

90 return ra_p, dec_p 

91 

92 

93class pointings2hp(object): 

94 """ 

95 Convert a list of telescope pointings and convert them to a pointing map 

96 """ 

97 def __init__(self, nside, radius=1.75): 

98 """ 

99 

100 """ 

101 # hmm, not sure what the leafsize should be? Kernel can crash if too low. 

102 self.tree = hp_kd_tree(nside=nside, leafsize=300) 

103 self.nside = nside 

104 self.rad = _xyz_angular_radius(radius) 

105 self.bins = np.arange(hp.nside2npix(nside)+1)-.5 

106 

107 def __call__(self, ra, dec, stack=True): 

108 """ 

109 similar to utils.hp_in_lsst_fov, but can take a arrays of ra,dec. 

110 

111 Parameters 

112 ---------- 

113 ra : array_like 

114 RA in radians 

115 dec : array_like 

116 Dec in radians 

117 

118 Returns 

119 ------- 

120 result : healpy map 

121 The number of times each healpxel is observed by the given pointings 

122 """ 

123 xs, ys, zs = _xyz_from_ra_dec(ra, dec) 

124 coords = np.array((xs, ys, zs)).T 

125 indx = self.tree.query_ball_point(coords, self.rad) 

126 # Convert array of lists to single array 

127 if stack: 

128 indx = np.hstack(indx) 

129 result, bins = np.histogram(indx, bins=self.bins) 

130 else: 

131 result = indx 

132 

133 return result 

134 

135 

136class hpmap_cross(object): 

137 """ 

138 Find the cross-correlation of a healpix map and a bunch of rotated pointings 

139 """ 

140 # XXX--just a very random radius search 

141 def __init__(self, nside=default_nside, radius=1.75, radius_search=1.75): 

142 """ 

143 

144 """ 

145 self.nside = nside 

146 # XXX -- should I shrink the radius slightly to get rid of overlap? That would be clever! 

147 self.p2hp_search = pointings2hp(nside=nside, radius=radius_search) 

148 self.p2hp = pointings2hp(nside=nside, radius=radius) 

149 # Load up a list of pointings, chop them down to a small block 

150 

151 # XXX--Should write code to generate a new tellelation so we know where it came from, 

152 # not just a random .dat file that's been floating around! 

153 fields = read_fields() 

154 good = np.where((fields['RA'] > np.radians(360.-15.)) | (fields['RA'] < np.radians(15.))) 

155 fields = fields[good] 

156 good = np.where(np.abs(fields['dec']) < np.radians(15.)) 

157 fields = fields[good] 

158 self.ra = fields['RA'] 

159 self.dec = fields['dec'] 

160 

161 # Healpixel ra and dec 

162 self.hp_ra, self.hp_dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside))) 

163 

164 def set_map(self, inmap): 

165 """ 

166 Set the map that will be cross correlated 

167 """ 

168 self.inmap = inmap 

169 

170 def __call__(self, x, return_pointings_map=False): 

171 """ 

172 Parameters 

173 ---------- 

174 x[0], ra_rot : float 

175 Amount to rotate the fields in RA (radians) 

176 x[1], dec_rot : float 

177 Amount to rotate the fields in Dec (radians) 

178 x[2], im_rot : float 

179 Initial rotation to apply to fields (radians) 

180 return_pointings_map : bool (False) 

181 If set, return the overlapping fields and the resulting observing helpix map 

182 

183 Returns 

184 ------- 

185 cross_corr : float 

186 If return_pointings_map is False, return the sum of the pointing map multipled 

187 with the 

188 """ 

189 # XXX-check the nside 

190 

191 # Unpack the x variable 

192 ra_rot = x[0] 

193 dec_rot = x[1] 

194 im_rot = x[2] 

195 

196 # Rotate pointings to desired position 

197 final_ra, final_dec = rotate_ra_dec(self.ra, self.dec, ra_rot, dec_rot, init_rotate=im_rot) 

198 # Find the number of observations at each healpixel 

199 obs_map = self.p2hp_search(final_ra, final_dec) 

200 good = np.where(self.inmap != hp.UNSEEN)[0] 

201 

202 if return_pointings_map: 

203 obs_indx = self.p2hp_search(final_ra, final_dec, stack=False) 

204 good_pointings = np.array([True if np.intersect1d(indxes, good).size > 0 

205 else False for indxes in obs_indx]) 

206 if True not in good_pointings: 

207 raise ValueError('No pointings overlap requested pixels') 

208 obs_map = self.p2hp(final_ra[good_pointings], final_dec[good_pointings]) 

209 return final_ra[good_pointings], final_dec[good_pointings], obs_map 

210 else: 

211 # If some requested pixels are not observed 

212 if np.min(obs_map[good]) == 0: 

213 return np.inf 

214 else: 

215 result = np.sum(self.inmap[good] * 

216 obs_map[good])/float(np.sum(self.inmap[good] + obs_map[good])) 

217 return result 

218 

219 def minimize(self, ra_delta=1., dec_delta=1., rot_delta=30.): 

220 """ 

221 Let's find the minimum of the cross correlation. 

222 """ 

223 

224 reward_max = np.where(self.inmap == self.inmap.max())[0] 

225 ra_guess = np.median(self.hp_ra[reward_max]) 

226 dec_guess = np.median(self.hp_dec[reward_max]) 

227 

228 x0 = np.array([ra_guess, dec_guess, 0.]) 

229 

230 ra_delta = np.radians(ra_delta) 

231 dec_delta = np.radians(dec_delta) 

232 rot_delta = np.radians(rot_delta) 

233 

234 # rots = np.arange(-np.pi/2., np.pi/2.+rot_delta, rot_delta) 

235 rots = [np.radians(0.)] 

236 # Make sure the initial simplex is large enough 

237 # XXX--might need to update scipy latest version to actually use this. 

238 deltas = np.array([[ra_delta, 0, 0], 

239 [0, dec_delta, rot_delta], 

240 [-ra_delta, 0, -rot_delta], 

241 [ra_delta, -dec_delta, 2.*rot_delta]]) 

242 init_simplex = deltas + x0 

243 minimum = None 

244 for rot in rots: 

245 x0[-1] = rot 

246 min_result = minimize(self, x0, method='Nelder-Mead', 

247 options={'initial_simplex': init_simplex}) 

248 if minimum is None: 

249 minimum = min_result.fun 

250 result = min_result 

251 if min_result.fun < minimum: 

252 minimum = min_result.fun 

253 result = min_result 

254 return result.x 

255 

256 

257 

258