Catalogs: overview¶
This area handles tables of sources on the sky: positions, frames, epochs,
motion, and the operations that relate one table to another. A catalog in
torchsky is a batch of tensors, lon and lat in degrees plus optional proper
motion, parallax, radial velocity, a validity mask, and your own columns. There
is no per-source object. torchsky.catalogs indexes those tensors on the
sphere, selects rows by cone or footprint, and crossmatches tables of any size.
The Core Idea: Prune, Then Test Exactly¶
A crossmatch pairs each row of one catalog with rows of another that lie within
a radius. Comparing every pair is \(O(NM)\). torchsky first sorts the right
catalog by declination strip and right ascension, uses binary search to produce
a small set of candidate pairs per left row, and then applies the exact
great-circle distance test to each candidate. The strips are at least one radius
high, so pruning never drops a true match inside the radius. When you pass a
prebuilt HealpixIndex as right_index=, candidates come from nested HEALPix
cells (padded by the maximum pixel radius) instead.
flowchart LR
L["left catalog"] --> C["candidate ranges<br/>binary search"]
R["right catalog"] -->|"sort by<br/>(dec strip, RA)"| C
C -->|"exact great-circle<br/>test"| M["CrossmatchResult<br/>left_index, right_index,<br/>separation_arcsec"]
The result holds indices into the original row order, never a sorted copy, so you can gather any column of either table with them.
Choose a Page¶
| Task | Page | Entry point |
|---|---|---|
| Nearest match within a radius | Crossmatch | crossmatch_sky, Catalog.crossmatch |
| All pairs, one-to-one, groups, per-row error radii (STILTS modes) | Crossmatch | find= on crossmatch_sky |
| Left catalog larger than memory | Crossmatch | crossmatch_sky_streamed |
| Both catalogs are Arrow tables or IPC files | Crossmatch | crossmatch_sky_from_arrow |
| Hold columns with frame and epoch; cone, footprint, and frame operations | Catalog containers | Catalog, HealpixIndex |
| Propagate proper motion to another epoch before matching | Frames and time | CatalogState |
crossmatch(...) dispatches on its arguments: two Catalog objects, four
coordinate arrays, two (lon, lat) tuples, or Arrow inputs. Prefer the explicit
function when you want the call site to state which path runs.
A Crossmatch¶
The rows below are every Gaia DR3 star with a proper motion, and every 2MASS source, within 43 arcsec of the centre of the open cluster M67. Gaia positions are at epoch J2016.0; 2MASS observed this field in November 1997 (J1997.875). Cluster members move by about 11 mas a year, so in 18 years they shift by 0.2 arcsec. Propagating Gaia to the 2MASS epoch before matching halves the separations.
import torch
from torchsky.catalogs import Catalog
f64 = dict(dtype=torch.float64)
gaia = Catalog( # Gaia DR3 gaia_source, cone r = 0.012 deg at (132.825, 11.8)
lon=torch.tensor([132.8210755, 132.8290199, 132.8210048, 132.8298726, 132.8300139,
132.8211976, 132.8291247, 132.8154407, 132.8287306], **f64),
lat=torch.tensor([11.8085863, 11.8060330, 11.8074335, 11.7985114, 11.8074898,
11.8044791, 11.7892283, 11.8043686, 11.8089885], **f64),
frame="ICRS", epoch=2016.0,
pm_lon_coslat_mas_yr=torch.tensor([-10.51, -11.11, -10.82, -10.99, -11.00, -11.38, -11.22,
7.09, 0.25], **f64),
pm_lat_mas_yr=torch.tensor([-2.60, -2.59, -2.94, -3.16, -2.99, -3.06, -2.67, 0.94, -2.78], **f64),
)
twomass = Catalog( # 2MASS Point Source Catalog (IRSA fp_psc), same cone
lon=torch.tensor([132.829189, 132.829927, 132.830069, 132.823341, 132.815406, 132.821257], **f64),
lat=torch.tensor([11.789187, 11.798506, 11.807475, 11.80454, 11.804343, 11.80446], **f64),
frame="ICRS", epoch=1997.875,
)
as_stored = gaia.crossmatch(twomass, radius_arcsec=1.0)
same_epoch = gaia.crossmatch(twomass, radius_arcsec=1.0, target_epoch=1997.875)
assert same_epoch.left_index.tolist() == [3, 4, 5, 6, 7]
assert same_epoch.right_index.tolist() == [1, 2, 5, 0, 4]
assert as_stored.separation_arcsec.median() > 0.19 > 0.11 > same_epoch.separation_arcsec.median()
Five of the six 2MASS sources have a Gaia counterpart. The sixth, 7.6 arcsec from the G = 9.9 star, has no Gaia source within 1 arcsec. Indices refer to the rows as you passed them, before any propagation.
Conventions and Pitfalls¶
- Units. Positions are degrees. Match radii and separations are
arcseconds (
radius_arcsec), while cone queries takeradius_deg. - Precision. Keep positions in float64. On MPS they are float32, whose resolution near RA 360° is about 0.1 arcsecond; radii close to that are not meaningful there.
- Best is not one-to-one. The default
find="best"returns the nearest right row for each left row; two left rows may pick the same right row. Usefind="symmetric"for a one-to-one match. - Frames and epochs. Two catalogs are compared in one frame at one epoch.
Catalogcarries both; convert or propagate proper motion before matching high-proper-motion sources. - Index reuse. Build the right index once and pass
right_index=when you match many left batches against the same table. - I/O. Read FITS tables with
torchfits.tableand pass the columns as tensors or Arrow batches; see the file boundary.
Connections¶
Catalog indexing uses HEALPix, and footprint selection uses MOC coverage. Frames, epochs, and proper motion come from Coordinates. A catalog combined with a WCS places sources on an image, for example as inputs to a scene model.
Separations are validated against Astropy SkyCoord in
torchsky-harness; see the
parity page.
The Gaia × 2MASS × AllWISE example runs every matching mode on the full 0.25° cone around M67.