Coverage for python/lsst/sims/featureScheduler/utils/dithering.py : 13%

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
7default_nside = set_default_nside()
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.
15 Parameters
16 ----------
17 ra : numpy.ndarray
18 RA in radians
19 dec : numpy.ndarray
20 Dec in radians
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
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.
43 Rotates around the x-axis 1st, then to the dec, then ra.
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
60 x, y, z = _xyz_from_ra_dec(ra, dec)
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
73 theta_y = dec_target
74 c_ty = np.cos(theta_y)
75 s_ty = np.sin(theta_y)
77 # Rotate about y
78 xp2 = c_ty*xp + s_ty*zp
79 zp2 = -s_ty*xp + c_ty*zp
81 # Convert back to RA, Dec
82 ra_p = np.arctan2(yp, xp2)
83 dec_p = -np.arcsin(zp2)
85 # Rotate to the correct RA
86 ra_p += ra_target
88 ra_p, dec_p = wrapRADec(ra_p, dec_p)
90 return ra_p, dec_p
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 """
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
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.
111 Parameters
112 ----------
113 ra : array_like
114 RA in radians
115 dec : array_like
116 Dec in radians
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
133 return result
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 """
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
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']
161 # Healpixel ra and dec
162 self.hp_ra, self.hp_dec = _hpid2RaDec(nside, np.arange(hp.nside2npix(nside)))
164 def set_map(self, inmap):
165 """
166 Set the map that will be cross correlated
167 """
168 self.inmap = inmap
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
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
191 # Unpack the x variable
192 ra_rot = x[0]
193 dec_rot = x[1]
194 im_rot = x[2]
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]
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
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 """
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])
228 x0 = np.array([ra_guess, dec_guess, 0.])
230 ra_delta = np.radians(ra_delta)
231 dec_delta = np.radians(dec_delta)
232 rot_delta = np.radians(rot_delta)
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