Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vectorize HierarchicalHealpixMap evluate #33

Merged
merged 3 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
vectorize HierarchicalHealpixMap evluate
  • Loading branch information
henrysky committed Jul 5, 2023
commit b53139a21197dda5982ab7fb494970777c56a0b8
13 changes: 9 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,15 @@ and they can be plotted as a function of distance at a given (l,b)

combined.plot(55.,0.5) # inputs are (l,b)

(plot not shown). Maps that are derived from the
``HierarchicalHealpixMap.py`` class (currently all Green-type maps and
the combined maps) can also be plotted on the sky using a Mollweide
projection at a given distance using
(plot not shown). Maps that are derived from the ``HierarchicalHealpixMap.py`` class (currently all Green-type maps and
the combined maps) can be vectorized to evaluate on array inputs of *l*, *b*, *D*

.. code-block:: python

combined(numpy.array([30.,40.,50.,60.]),numpy.array([3.,4.,3.,6.]),numpy.array([1.,2.,3.,10.]))
array([0.22304147, 0.3780736 , 0.42528571, 0.22258065])

They can also be plotted on the sky using a Mollweide projection at a given distance using

.. code-block:: python

Expand Down
100 changes: 83 additions & 17 deletions mwdust/HierarchicalHealpixMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,75 @@ def _evaluate(self,l,b,d):
PURPOSE:
evaluate the dust-map
INPUT:
l- Galactic longitude (deg)
b- Galactic latitude (deg)
l- Galactic longitude (deg) can be array
b- Galactic latitude (deg) can be array
d- distance (kpc) can be array
OUTPUT:
extinction E(B-V)
HISTORY:
2015-03-02 - Started - Bovy (IAS)
"""
distmod= 5.*numpy.log10(d)+10.
if isinstance(l,numpy.ndarray) or isinstance(b,numpy.ndarray):
raise NotImplementedError("array input for l and b for combined dust map not implemented")
lbIndx= self._lbIndx(l,b)
if self._intps[lbIndx] != 0:
out= self._intps[lbIndx][0](distmod)
return self._evaluate_vectorized(l, b, d)
else:
interpData=\
interpolate.InterpolatedUnivariateSpline(self._distmods,
self._best_fit[lbIndx],
k=self._interpk)
out= interpData(distmod)
self._intps[lbIndx]= interpData
if self._filter is None:
return out
else:
return out*aebv(self._filter,sf10=self._sf10)
distmod= 5.*numpy.log10(d)+10.
lbIndx= self._lbIndx(l,b)
if self._intps[lbIndx] != 0:
out= self._intps[lbIndx][0](distmod)
else:
interpData=\
interpolate.InterpolatedUnivariateSpline(self._distmods,
self._best_fit[lbIndx],
k=self._interpk)
out= interpData(distmod)
self._intps[lbIndx]= interpData
if self._filter is None:
return out
else:
return out*aebv(self._filter,sf10=self._sf10)


def _evaluate_vectorized(self, ls, bs, ds):
"""
NAME:
_evaluate_vectorized
PURPOSE:
evaluate the dust-map for array input
INPUT:
l- Galactic longitude (deg) can be array
b- Galactic latitude (deg) can be array
d- distance (kpc) can be array
OUTPUT:
extinction E(B-V)
HISTORY:
2023-07-05 - Written - Henry Leung (UofT)
"""
ls = numpy.atleast_1d(ls)
bs = numpy.atleast_1d(bs)
ds = numpy.atleast_1d(ds)

distmod= 5.*numpy.log10(ds)+10.
lbIndx= self._lbIndx_vectorized(ls, bs)

result = numpy.zeros_like(ls)
for counter, i, d in zip(numpy.arange(len(result)), lbIndx, distmod):
if self._intps[i] != 0:
out= self._intps[i](d)
else:
interpData=\
interpolate.InterpolatedUnivariateSpline(self._distmods,
self._best_fit[i],
k=self._interpk)
out= interpData(d)
self._intps[i]= interpData
if self._filter is None:
result[counter] = out
else:
result[counter] = out*aebv(self._filter,sf10=self._sf10)
# set nan for invalid indices
result[lbIndx==-1] = numpy.nan
return result


def dust_vals_disk(self,lcen,bcen,dist,radius):
"""
Expand Down Expand Up @@ -136,6 +180,28 @@ def _lbIndx(self,l,b):
raise IndexError("Given (l,b) pair has multiple matches!")
raise IndexError("Given (l,b) pair not within the region covered by the extinction map")

def _lbIndx_vectorized(self, l, b):
"""Return the indices in the _combineddata array corresponding to arrays of (l, b)"""
stop_mask = numpy.zeros(len(l), dtype=bool) # mask to be accumulated when looping through nside
indx_result = numpy.ones(len(l), dtype=int) * -1 # -1 for bad star, int array has no nan
for nside in self._nsides:
tpix = ang2pix(nside, (90.-b)*_DEGTORAD, l*_DEGTORAD, nest=True)
nside_idx = numpy.where(self._pix_info['nside'] == nside)[0]
healpix_index_nside = self._pix_info['healpix_index'][nside_idx]
sorted_order = numpy.argsort(healpix_index_nside)
# use searchsorted to find the index of tpix in healpix_index_nside efficiently
result = numpy.searchsorted(healpix_index_nside, tpix, sorter=sorted_order)
# need to deal with indices where they are larger than the largest healpix_index_nside
known_bad_idx = (result == len(nside_idx))
result[known_bad_idx] = 0 # wrap around
result = sorted_order[result] # reverse the sorting before indexing
result = nside_idx[result]
good_result_idx = ((self._pix_info['healpix_index'][result] == tpix) & (self._pix_info['nside'][result] == nside) & (~known_bad_idx))
indx_result = numpy.where(~stop_mask & good_result_idx, result, indx_result)
indx_result = numpy.where(known_bad_idx & ~stop_mask, -1, indx_result) # set bad star to -1
stop_mask = stop_mask | good_result_idx # update mask for the next nside
return indx_result

def plot_mollweide(self,d,**kwargs):
"""
NAME:
Expand Down
2 changes: 1 addition & 1 deletion mwdust/util/healpix.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def check_nside(nside, nest=False):
# nside can only be a power of 2 for nest, but generally less than 2**29
nside_arr = np.array(nside).astype(np.int64)
is_ok = True
if nside == nside_arr and 0 < nside and nside <= 2**29:
if np.all(np.logical_and(np.logical_and(nside == nside_arr, np.all(np.less(0, nside))), nside_arr <= 2**29)):
if nest:
is_ok = (nside_arr & (nside_arr - 1)) == 0
else:
Expand Down