From 0242bde9e85f2c2bcc309494f04e42d8bf641691 Mon Sep 17 00:00:00 2001 From: Vidushi Sharma <103223246+Vidushi-GitHub@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:32:50 -0500 Subject: [PATCH] Healpix doc (#2498) Resolves: #2035 --- app/routes/docs.client.samples.md | 178 ++++++++++++++++++++++++++++++ references.bib | 36 ++++++ 2 files changed, 214 insertions(+) diff --git a/app/routes/docs.client.samples.md b/app/routes/docs.client.samples.md index 1cf3f3f89..0d543dcdf 100644 --- a/app/routes/docs.client.samples.md +++ b/app/routes/docs.client.samples.md @@ -12,6 +12,9 @@ and some samples from the FAQs section of the [gcn-kafka-python](https://github. To contribute your own ideas, make a GitHub pull request to add it to [the Markdown source for this document](https://github.com/nasa-gcn/gcn.nasa.gov/blob/CodeSamples/app/routes/docs.client.samples.md), or [contact us](/contact). +- [Working with Kafka messages](#parsing) +- [HEALPix Sky Maps](#healpix-sky-maps) + ## Parsing Within your consumer loop, use the following functions to convert the @@ -162,3 +165,178 @@ for message in consumer.consume(end[0].offset - start[0].offset, timeout=1): continue print(message.value()) ``` + +## HEALPix Sky Maps + +[HEALPix](https://healpix.sourceforge.io) (Hierarchical, Equal Area, and iso-Latitude Pixelisation) is a scheme for indexing positions on the unit sphere. +For localization of events, the multi-messenger community uses the standard HEALPix [@2005ApJ...622..759G] with the file extension `.fits.gz`, as well as multi-resolution HEALPix [@2015A&A...578A.114F] with the file extension `.multiorder.fits`. The preferred format is the multi-resolution HEALPix format. + +### Multi-Order Sky Maps + +GCN strongly encourages the use of multi-order sky maps. They utilize a variable resolution, with higher probability regions having higher resolution and lower probability regions being encoded with a lower resolution. Variable resolution allows multi-order sky maps to be significantly more efficient than single-resolution HEALPix sky maps with respect to both storage footprint and read speed. + +#### Reading Sky Maps + +Sky maps can be parsed using Python and a small number of packages. While this documentation covers the use of `astropy-healpix`, there are several packages that can be used for this purpose; a number of [alternatives](#other-documentation-and-healpix-packages) are listed at the bottom of this page. + +```python +import astropy_healpix as ah +import numpy as np + +from astropy import units as u +from astropy.table import QTable +``` + +A given sky map can then be read in as: + +```python +skymap = QTable.read('skymap.multiorder.fits') +``` + +#### Most Probable Sky Location + +Let's calculate the index of the sky point with the highest probability density, as follows: + +```python +hp_index = np.argmax(skymap['PROBDENSITY']) +uniq = skymap[hp_index]['UNIQ'] + +level, ipix = ah.uniq_to_level_ipix(uniq) +nside = ah.level_to_nside(level) + +ra, dec = ah.healpix_to_lonlat(ipix, nside, order='nested') +``` + +#### Probability Density at a Known Position + +Similarly, one can calculate the probability density at a known position: + +```python +ra, dec = 197.450341598 * u.deg, -23.3814675445 * u.deg + +level, ipix = ah.uniq_to_level_ipix(skymap['UNIQ']) +nside = ah.level_to_nside(level) + +match_ipix = ah.lonlat_to_healpix(ra, dec, nside, order='nested') + +match_index = np.flatnonzero(ipix == match_ipix)[0] + +prob_density = skymap[match_index]['PROBDENSITY'].to_value(u.deg**-2) +``` + +#### 90% Probability Region + +The estimation of a 90% probability region involves sorting the pixels, calculating the area of each pixel, and then summing the probability of each pixel until 90% is reached. + +```python +#Sort the pixels by decending probability density +skymap.sort('PROBDENSITY', reverse=True) + +#Area of each pixel +level, ipix = ah.uniq_to_level_ipix(skymap['UNIQ']) +pixel_area = ah.nside_to_pixel_area(ah.level_to_nside(level)) + +#Pixel area times the probability +prob = pixel_area * skymap['PROBDENSITY'] + +#Cummulative sum of probability +cumprob = np.cumsum(prob) + +#Pixels for which cummulative is 0.9 +i = cumprob.searchsorted(0.9) + +#Sum of the areas of the pixels up to that one +area_90 = pixel_area[:i].sum() +area_90.to_value(u.deg**2) +``` + +### Flat Resolution HEALPix Sky maps + +A sky map `.fits.gz` file can be read in using `healpy`: + +```python +import healpy as hp +import numpy as np +from matplotlib import pyplot as plt + +# Read both the HEALPix image data and the FITS header +hpx, header = hp.read_map('skymap.fits.gz', h=True) + +# Plot a Mollweide-projection all-sky image +np.mollview(hpx) +plt.show() +``` + +#### Most Probable Sky Location + +The point on the sky with the highest probability can be found by finding the maximum value in the HEALPix sky map: + +```python +# Reading Sky Maps with Healpy +healpix_image = hp.read_map('bayestar.fits.gz,0') +npix = len(hpx) + +# Lateral resolution of the HEALPix map +nside = hp.npix2nside(npix) + +# Find the highest probability pixel +ipix_max = np.argmax(hpx) + +# Probability density per square degree at that position +hpx[ipix_max] / hp.nside2pixarea(nside, degrees=True) + +# Highest probability pixel on the sky +ra, dec = hp.pix2ang(nside, ipix_max, lonlat=True) +ra, dec +``` + +#### Integrated probability in a Circle + +We can calculate the integrated probability within an arbitrary circle on the sky: + +```python +# First define the Cartesian coordinates of the center of the circle +ra = 180.0 +dec = -45.0 +radius = 2.5 + +# Calculate Cartesian coordinates of the center of the Circle +xyz = hp.ang2vec(ra, dec, lonlat=True) + +# Obtain an array of indices for the pixels inside the circle +ipix_disc = hp.query_disc(nside, xyz, np.deg2rad(radius)) + +# Sum the probability in all of the matching pixels: +hpx[ipix_disc].sum() +``` + +#### Integrated probability in a Polygon + +Similar to the integrated probability within a circle, it is possible to calculate the integrated probability of the source lying within an arbitrary polygon: + +```python +# Indices of the pixels within a polygon (defined by the Cartesian coordinates of its vertices) + +xyz = [[0, 0, 0], + [1, 0, 0], + [1, 1, 0], + [0, 1, 0]] +ipix_poly = hp.query_polygon(nside, xyz) +hpx[ipix_poly].sum() +``` + +#### Other Documentation and HEALPix Packages + +For more information and resources on the analysis of pixelated data on a sphere, explore the following links: + +- Additional information can be found on the [LIGO website](https://emfollow.docs.ligo.org/userguide/tutorial/multiorder_skymaps.html) + +- [healpy](https://healpy.readthedocs.io/en/latest/): Official Python library for handling the pixelated data on sphere + +- [astropy-healpix](https://pypi.org/project/astropy-healpix/): Integrates HEALPix with Astropy for data manipulation and analysis + +- [mhealpy](https://mhealpy.readthedocs.io/en/latest/): Object-oriented wrapper of healpy for handling the multi-resolution maps + +- [MOCpy](https://cds-astro.github.io/mocpy/): Python library allowing easy creation, parsing and manipulation of Multi-Order Coverage maps. + +## Bibilography diff --git a/references.bib b/references.bib index 872ba1ef6..ca79daebd 100644 --- a/references.bib +++ b/references.bib @@ -12,3 +12,39 @@ @ARTICLE{1995Ap&SS.231..255A adsurl = {https://ui.adsabs.harvard.edu/abs/1995Ap&SS.231..255A}, adsnote = {Provided by the SAO/NASA Astrophysics Data System} } + +@ARTICLE{2005ApJ...622..759G, + author = {{G{\'o}rski}, K.~M. and {Hivon}, E. and {Banday}, A.~J. and {Wandelt}, B.~D. and {Hansen}, F.~K. and {Reinecke}, M. and {Bartelmann}, M.}, + title = "{HEALPix: A Framework for High-Resolution Discretization and Fast Analysis of Data Distributed on the Sphere}", + journal = {Astrophysical Journal}, + keywords = {Cosmology: Cosmic Microwave Background, Cosmology: Observations, Methods: Statistical, Astrophysics}, + year = 2005, + month = apr, + volume = {622}, + number = {2}, + pages = {759-771}, + doi = {10.1086/427976}, +archivePrefix = {arXiv}, + eprint = {astro-ph/0409513}, + primaryClass = {astro-ph}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2005ApJ...622..759G}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +} + +@ARTICLE{2015A&A...578A.114F, + author = {{Fernique}, P. and {Allen}, M.~G. and {Boch}, T. and {Oberto}, A. and {Pineau}, F. -X. and {Durand}, D. and {Bot}, C. and {Cambr{\'e}sy}, L. and {Derriere}, S. and {Genova}, F. and {Bonnarel}, F.}, + title = "{Hierarchical progressive surveys. Multi-resolution HEALPix data structures for astronomical images, catalogues, and 3-dimensional data cubes}", + journal = {Astronomy and Astrophysics}, + keywords = {surveys, atlases, astronomical databases: miscellaneous, catalogs, virtual observatory tools, methods: statistical, Astrophysics - Instrumentation and Methods for Astrophysics}, + year = 2015, + month = jun, + volume = {578}, + eid = {A114}, + pages = {A114}, + doi = {10.1051/0004-6361/201526075}, +archivePrefix = {arXiv}, + eprint = {1505.02291}, + primaryClass = {astro-ph.IM}, + adsurl = {https://ui.adsabs.harvard.edu/abs/2015A&A...578A.114F}, + adsnote = {Provided by the SAO/NASA Astrophysics Data System} +}