Coverage for python/lsst/sims/utils/healpyUtils.py : 12%

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
1from __future__ import division
2import numpy as np
3import healpy as hp
4import warnings
6__all__ = ['hpid2RaDec', 'raDec2Hpid', 'healbin', '_hpid2RaDec', '_raDec2Hpid',
7 '_healbin', 'moc2array', 'hp_grow_argsort']
10def _hpid2RaDec(nside, hpids, **kwargs):
11 """
12 Correct for healpy being silly and running dec from 0-180.
14 Parameters
15 ----------
16 nside : int
17 Must be a value of 2^N.
18 hpids : np.array
19 Array (or single value) of healpixel IDs.
21 Returns
22 -------
23 raRet : float (or np.array)
24 RA positions of the input healpixel IDs. In radians.
25 decRet : float (or np.array)
26 Dec positions of the input healpixel IDs. In radians.
27 """
29 lat, lon = hp.pix2ang(nside, hpids, **kwargs)
30 decRet = np.pi / 2.0 - lat
31 raRet = lon
33 return raRet, decRet
36def hpid2RaDec(nside, hpids, **kwargs):
37 """
38 Correct for healpy being silly and running dec from 0-180.
40 Parameters
41 ----------
42 nside : int
43 Must be a value of 2^N.
44 hpids : np.array
45 Array (or single value) of healpixel IDs.
47 Returns
48 -------
49 raRet : float (or np.array)
50 RA positions of the input healpixel IDs. In degrees.
51 decRet : float (or np.array)
52 Dec positions of the input healpixel IDs. In degrees.
53 """
54 ra, dec = _hpid2RaDec(nside, hpids, **kwargs)
55 return np.degrees(ra), np.degrees(dec)
58def _raDec2Hpid(nside, ra, dec, **kwargs):
59 """
60 Assign ra,dec points to the correct healpixel.
62 Parameters
63 ----------
64 nside : int
65 Must be a value of 2^N.
66 ra : np.array
67 RA values to assign to healpixels. Radians.
68 dec : np.array
69 Dec values to assign to healpixels. Radians.
71 Returns
72 -------
73 hpids : np.array
74 Healpixel IDs for the input positions.
75 """
76 lat = np.pi / 2.0 - dec
77 hpids = hp.ang2pix(nside, lat, ra, **kwargs)
78 return hpids
81def raDec2Hpid(nside, ra, dec, **kwargs):
82 """
83 Assign ra,dec points to the correct healpixel.
85 Parameters
86 ----------
87 nside : int
88 Must be a value of 2^N.
89 ra : np.array
90 RA values to assign to healpixels. Degrees.
91 dec : np.array
92 Dec values to assign to healpixels. Degrees.
94 Returns
95 -------
96 hpids : np.array
97 Healpixel IDs for the input positions.
98 """
99 return _raDec2Hpid(nside, np.radians(ra), np.radians(dec), **kwargs)
102def _healbin(ra, dec, values, nside=128, reduceFunc=np.mean, dtype=float, fillVal=hp.UNSEEN):
103 """
104 Take arrays of ra's, dec's, and value and bin into healpixels. Like numpy.hexbin but for
105 bins on a sphere.
107 Parameters
108 ----------
109 ra : np.array
110 RA positions of the data points. Radians.
111 dec : np.array
112 Dec positions of the data points. Radians
113 values : np.array
114 The values at each ra,dec position.
115 nside : int
116 Healpixel nside resolution. Must be a value of 2^N.
117 reduceFunc : function (numpy.mean)
118 A function that will return a single value given a subset of `values`.
119 dtype : dtype ('float')
120 Data type of the resulting mask
122 Returns
123 -------
124 mapVals : np.array
125 A numpy array that is a valid Healpixel map.
126 """
128 hpids = _raDec2Hpid(nside, ra, dec)
130 order = np.argsort(hpids)
131 hpids = hpids[order]
132 values = values[order]
133 pixids = np.unique(hpids)
135 left = np.searchsorted(hpids, pixids)
136 right = np.searchsorted(hpids, pixids, side='right')
138 mapVals = np.zeros(hp.nside2npix(nside), dtype=dtype) + fillVal
140 # Wow, I thought histogram would be faster than the loop, but this has been faster!
141 for i, idx in enumerate(pixids):
142 mapVals[idx] = reduceFunc(values[left[i]:right[i]])
144 # Change any NaNs to fill value
145 mapVals[np.isnan(mapVals)] = fillVal
147 return mapVals
150def healbin(ra, dec, values, nside=128, reduceFunc=np.mean, dtype=float):
151 """
152 Take arrays of ra's, dec's, and value and bin into healpixels. Like numpy.hexbin but for
153 bins on a sphere.
155 Parameters
156 ----------
157 ra : np.array
158 RA positions of the data points. Degrees.
159 dec : np.array
160 Dec positions of the data points. Degrees.
161 values : np.array
162 The values at each ra,dec position.
163 nside : int
164 Healpixel nside resolution. Must be a value of 2^N.
165 reduceFunc : function (numpy.mean)
166 A function that will return a single value given a subset of `values`.
167 dtype : dtype ('float')
168 Data type of the resulting mask
170 Returns
171 -------
172 mapVals : np.array
173 A numpy array that is a valid Healpixel map.
174 """
175 return _healbin(np.radians(ra), np.radians(dec), values, nside=nside,
176 reduceFunc=reduceFunc, dtype=dtype)
179def moc2array(data, uniq, nside=128, reduceFunc=np.sum, density=True, fillVal=0.):
180 """Convert a Multi-Order Coverage Map to a single nside HEALPix array. Useful
181 for converting maps output by LIGO alerts. Expect that future versions of
182 healpy or astropy will be able to replace this functionality. Note that this is
183 a convienence function that will probably degrade portions of the MOC that are
184 sampled at high resolution.
186 Details of HEALPix Mulit-Order Coverage map: http://ivoa.net/documents/MOC/20190404/PR-MOC-1.1-20190404.pdf
188 Parameters
189 ----------
190 data : np.array
191 Data values for the MOC map
192 uniq : np.array
193 The UNIQ values for the MOC map
194 nside : int (128)
195 The output map nside
196 reduceFunc : function (np.sum)
197 The function to use to combine data into single healpixels.
198 density : bool (True)
199 If True, multiplies data values by pixel area before applying reduceFunc, and divides
200 the final array by the output pixel area. Should be True if working on a probability density MOC.
201 fillVal : float (0.)
202 Value to fill empty HEALPixels with. Good choices include 0 (default), hp.UNSEEN, and np.nan.
204 Returns
205 -------
206 np.array : HEALpy array of nside. Units should be the same as the input map as processed by reduceFunc.
207 """
209 # NUNIQ packing, from page 12 of http://ivoa.net/documents/MOC/20190404/PR-MOC-1.1-20190404.pdf
210 orders = np.floor(np.log2(uniq / 4) / 2).astype(int)
211 npixs = (uniq - 4 * 4**orders).astype(int)
213 nsides = 2**orders
214 names = ['ra', 'dec', 'area']
215 types = [float]*len(names)
216 data_points = np.zeros(data.size, dtype=list(zip(names, types)))
217 for order in np.unique(orders):
218 good = np.where(orders == order)
219 ra, dec = _hpid2RaDec(nsides[good][0], npixs[good], nest=True)
220 data_points['ra'][good] = ra
221 data_points['dec'][good] = dec
222 data_points['area'][good] = hp.nside2pixarea(nsides[good][0])
224 if density:
225 tobin_data = data*data_points['area']
226 else:
227 tobin_data = data
229 result = _healbin(data_points['ra'], data_points['dec'], tobin_data, nside=nside,
230 reduceFunc=reduceFunc, fillVal=fillVal)
232 if density:
233 good = np.where(result != fillVal)
234 result[good] = result[good] / hp.nside2pixarea(nside)
236 return result
239def hp_grow_argsort(in_map, ignore_nan=True):
240 """Find the maximum of a healpix map, then orders healpixels by selecting the maximum bordering the selected area.
242 Parameters
243 ----------
244 in_map : np.array
245 A valid HEALpix array
246 ignore_nan : bool (True)
247 If true, ignores values that are NaN
249 Returns
250 -------
251 ordered_hp : int array
252 The indices that put in_map in the correct order
253 """
254 nside = hp.npix2nside(np.size(in_map))
255 npix = np.size(in_map)
256 pix_indx = np.arange(npix)
258 if ignore_nan:
259 not_nan_pix = ~np.isnan(in_map)
260 npix = np.size(in_map[not_nan_pix])
262 # Check if we have already run with this nside
263 if hasattr(hp_grow_argsort, 'nside'):
264 nside_match = nside == hp_grow_argsort.nside
265 else:
266 nside_match = False
268 # If we already have neighbors chached, just use it
269 if nside_match:
270 neighbors = hp_grow_argsort.neighbors_cache
271 else:
272 # Running a new nside, or for the first time, compute neighbors and set attributes
273 # Make a boolean area to keep track of which pixels still need to be sorted
274 neighbors = hp.get_all_neighbours(nside, pix_indx).T
275 hp_grow_argsort.neighbors_cache = neighbors
276 hp_grow_argsort.nside = nside
278 valid_neighbors_mask = np.ones(neighbors.shape, dtype=bool)
280 # Sometimes there can be no neighbors in some directions
281 valid_neighbors_mask[np.where(neighbors == -1)] = False
283 ordered_hp = np.zeros(npix, dtype=int)
284 current_max = np.where(in_map == np.nanmax(in_map))[0].min()
286 ordered_hp[0] = current_max
288 # Remove max from valid_neighbors. Can be clever with indexing
289 # so we don't have to do a brute force search of the entire
290 # neghbors array to mask it.
291 # valid_neighbors_mask[np.where(neighbors == current_max)] = False
292 current_neighbors = neighbors[current_max][valid_neighbors_mask[current_max]]
293 sub_indx = np.where(neighbors[current_neighbors] == current_max)
294 valid_neighbors_mask[(current_neighbors[sub_indx[0]], sub_indx[1])] = False
296 for i in np.arange(1, npix):
297 current_neighbors = neighbors[ordered_hp[0:i]][valid_neighbors_mask[ordered_hp[0:i]]]
298 indx = np.where(in_map[current_neighbors] == np.nanmax(in_map[current_neighbors]))[0]
299 if np.size(indx) == 0:
300 # We can't connect to any more pixels
301 warnings.warn('Can not connect to any more pixels.')
302 return ordered_hp[0:i]
303 else:
304 indx = np.min(indx)
305 current_max = current_neighbors[indx]
306 ordered_hp[i] = current_max
307 # current_max is no longer a valid neighbor to consider
308 neighbors_of_current = neighbors[current_max]
309 sub_indx = np.where(neighbors[neighbors_of_current] == current_max)
310 valid_neighbors_mask[(neighbors_of_current[sub_indx[0]], sub_indx[1])] = False
312 return ordered_hp