Celestial sphere: overview¶
This area works on the whole sphere rather than on one flat image. Its core is
HEALPix (Hierarchical Equal Area isoLatitude Pixelization): a division of the
sphere into \(12\,N_\mathrm{side}^2\) pixels of identical area, where
\(N_\mathrm{side}\) is a power of two. Each pixel splits into four children at
the next resolution, so the same index scheme serves full-sky maps, coverage
masks, catalog partitions, and harmonic analysis. torchsky.sphere provides
the pixelization, spherical regions, partial-sky maps, and spherical harmonic
transforms, all as torch code on CPU, CUDA, and MPS.
How the Pieces Fit¶
flowchart LR
P["positions<br/>(lon, lat)"] -->|"ang2pix"| I["pixel indices<br/>at nside"]
R["regions<br/>disc, polygon"] -->|"query_*"| I
I --> D["dense map<br/>12 nside² values"]
I --> M["MOC<br/>coverage only"]
I --> S["sparse map<br/>values on covered pixels"]
D -->|"map2alm"| A["harmonic<br/>coefficients a_lm"]
A -->|"alm2map"| D
D -->|"sample_healpix_map"| V["values at<br/>positions"]
HEALPix numbers its pixels in two orders. RING runs along iso-latitude
rings; harmonic transforms use it. NESTED follows the quadtree, so children
of a pixel are contiguous; coverage maps, sparse maps, and spatial indexing use
it. RING is the default (nest=False), as in healpy.
Choose a Page¶
| Task | Page | Entry point |
|---|---|---|
| Positions to pixels, neighbours, discs and polygons, sampling a map | HEALPix | ang2pix, query_disc, sample_healpix_map |
| Describe a footprint, intersect surveys, filter a catalog by coverage | MOC | MOC |
| Store values only where you have data | Sparse maps | SparseHealpixMap |
| Power spectra, smoothing, beams, spin-2 polarization | SHT | map2alm, anafast |
| Exact polygon areas, containment, unions | This page | SphericalPolygon |
| Coadd images onto HEALPix | HEALPix mapmaking | coadd_healpix |
A MOC (Multi-Order Coverage map, an IVOA standard) records which cells of the sphere are covered, mixing resolutions so a footprint costs memory in proportion to its boundary, not its area. An SHT (spherical harmonic transform) expands a map into coefficients \(a_{\ell m}\), the spherical analogue of a Fourier transform.
Positions, Pixels, and Discs¶
The positions below are the targets of three examples:
Sgr A* and the pointing of Chandra ObsID 5950, the open cluster M67, and the
globular cluster NGC 288. At nside = 2048 a pixel is 1.7 arcmin across, and a
10 arcmin disc around the Chandra pointing contains Sgr A* but neither cluster.
import math
import torch
import torchsky.sphere as sphere
# Sgr A* Chandra 5950 M67 NGC 288
ra = torch.tensor([266.4168371, 266.401, 132.825, 13.1885], dtype=torch.float64)
dec = torch.tensor([-29.0078106, -28.987, 11.800, -26.5826], dtype=torch.float64)
nside = 2048
pix = sphere.ang2pix(nside, ra, dec, lonlat=True, nest=True)
counts = torch.bincount(pix, minlength=sphere.nside2npix(nside))
assert counts.sum() == 4 and counts.numel() == 12 * nside**2
pointing = sphere.lonlat_to_unit_xyz(ra[1], dec[1])
disc = sphere.query_disc(nside, pointing, math.radians(10 / 60), nest=True)
assert torch.isin(pix, disc).tolist() == [True, True, False, False]
Queries take a unit 3-vector and a radius in radians, as in healpy.
Spherical Regions¶
SphericalPolygon, SphericalCap, and SphericalMultiPolygon are exact region
objects with containment, area, and pixel queries. Boolean algebra works on the
polygons themselves and, at a fixed nside, on PixelizedRegion sets. The
union, intersection, or difference of polygons has an exact area(): the
result's boundary is clipped from the operands' great-circle edges and
integrated exactly. With a cap, box, or ellipse operand area() raises; use
area_estimate(), which converges over a ladder of nside. The two
polygons below are the footprints of two CFHT MegaPipe cutouts from the
mosaic example,
from tiles with different tangent points. The corners are the centres of the
corner pixels, as WCS.footprint returns them.
import math
import torch
from torchsky.sphere import SphericalPolygon
tile_712_165 = SphericalPolygon( # 512 x 512 px at 0.18577 arcsec
lon_deg=torch.tensor([358.83839378, 358.83840792, 358.81182784, 358.81181209], dtype=torch.float64),
lat_deg=torch.tensor([-7.26829861, -7.24193051, -7.24191619, -7.26828424], dtype=torch.float64),
)
tile_712_166 = SphericalPolygon(
lon_deg=torch.tensor([358.83319463, 358.83318560, 358.80655375, 358.80656127], dtype=torch.float64),
lat_deg=torch.tensor([-7.25793050, -7.23156228, -7.23157078, -7.25793902], dtype=torch.float64),
)
area = 3600 * tile_712_165.area(degrees=True) # arcmin^2
assert abs(area - (511 * 0.18577 / 60) ** 2) < 0.005 # corner pixel centres are 511 px apart
assert tile_712_165.intersects(tile_712_166)
both = tile_712_165.intersection(tile_712_166)
assert bool(both.contains(lon_deg=358.81359, lat_deg=-7.252031)) # a star seen by both
overlap_arcmin2 = 3600 * both.area(degrees=True) # exact
assert 1.1 < overlap_arcmin2 < 1.3
pixels = tile_712_165.pixelize(nside=2**16).intersection(tile_712_166.pixelize(nside=2**16))
pixel_arcmin2 = pixels.area() * (180 * 60 / math.pi) ** 2 # pixel-centre count, steradians to arcmin^2
assert abs(pixel_arcmin2 - overlap_arcmin2) < 0.05
area and PixelizedRegion.area are in steradians unless degrees=True.
Polygon edges are great circles.
Conventions and Pitfalls¶
- Angles.
lonlat=Truetakes longitude and latitude in degrees; otherwise inputs are colatitudethetaand longitudephiin radians. Query radii are radians. - Ordering. Mixing RING and NESTED indices is the most common HEALPix bug.
Pass
nest=explicitly and convert withring2nest/nest2ring. - Resolution.
nsidemust be a power of two. Pixel size halves each timensidedoubles; a full-sky dense map atnside=4096holds 201 million values. Use a MOC or sparse map when coverage is partial. - Boundaries.
query_discandquery_polygonreturn pixels whose centres fall inside by default; passinclusive=Truefor every pixel that touches the region. - Precision. On MPS, angles are float32: fine for pixel indexing at
moderate
nside, too coarse for arcsecond geometry.
Connections¶
Catalogs index rows with NESTED HEALPix and filter them
by MOC. HEALPix mapmaking coadds images onto
the sphere, and models can represent the sky as a HEALPix
or harmonic field. The healpy-shaped
torchsky.sphere.compat namespace helps port
existing scripts; new code uses torchsky.sphere directly.
Indexing, queries, and transforms are validated against healpy, hpgeom, astropy-healpix, mocpy, and spherical-geometry in torchsky-harness; see the parity page.
Examples on real data: the Planck power spectrum (masks, apodisation, and \(C_\ell\) of a HEALPix map) and the Chandra events (a sparse counts map and a MOC).