Skip to content

torchsky.catalogs

Stability: core. Names on this page follow the deprecation policy in contributing.

Catalog streaming, indexing, and crossmatching helpers.

Guides: crossmatch


This reference is generated from the package source. Names are grouped by the module that defines them; import every name from the public namespace torchsky.catalogs unless the group text says otherwise.

Top-level namespace

Catalog streaming, indexing, and crossmatching helpers.

crossmatch

crossmatch(
    left: Any,
    right: Any,
    radius_arcsec_or_right_lon: Any = None,
    right_lat: Any = None,
    radius_arcsec: float | Tensor | None = None,
    *,
    find: str = "best",
    streamed: bool = False,
    **kwargs: Any,
) -> CrossmatchResult

Unified sky crossmatch entry point.

Supports: - crossmatch(cat_left, cat_right, radius_arcsec, ...) for :class:Catalog objects - crossmatch(left_lon, left_lat, right_lon, right_lat, radius_arcsec=..., ...) for coordinate tensors - crossmatch((left_lon, left_lat), (right_lon, right_lat), radius_arcsec, ...) - crossmatch(left_arrow, right_arrow, radius_arcsec, streamed=False, ...) for Arrow tables/paths

Catalog

Catalog streaming, indexing, and crossmatching helpers.

Catalog dataclass

Catalog(
    lon: Tensor,
    lat: Tensor,
    frame: str = "ICRS",
    epoch: Tensor | float = 2000.0,
    pm_lon_coslat_mas_yr: Tensor | None = None,
    pm_lat_mas_yr: Tensor | None = None,
    parallax_mas: Tensor | None = None,
    radial_velocity_km_s: Tensor | None = None,
    valid: Tensor | None = None,
    columns: dict[str, Tensor] = dict(),
)

Batched table of sky rows on one device.

Required sky columns are lon / lat in degrees. Missing kinematics stay None. Extra payload lives in columns (same length, same device).

Parameters:

Name Type Description Default
lon Tensor

Longitude tensor in degrees.

required
lat Tensor

Latitude tensor in degrees, with the same row count as lon.

required
frame str

Celestial or observed coordinate-frame name.

'ICRS'
epoch Tensor | float

Scalar or per-row Julian epoch.

2000.0
pm_lon_coslat_mas_yr Tensor | None

Cosine-weighted longitude proper motion in mas/yr.

None
pm_lat_mas_yr Tensor | None

Latitude proper motion in mas/yr.

None
parallax_mas Tensor | None

Parallax in milliarcseconds.

None
radial_velocity_km_s Tensor | None

Radial velocity in km/s.

None
valid Tensor | None

Optional per-row validity mask.

None
columns dict[str, Tensor]

Additional tensors whose leading dimension matches the catalog.

dict()

Examples:

>>> import torch
>>> from torchsky.catalogs import Catalog
>>> catalog = Catalog(torch.tensor([10.0]), torch.tensor([-2.0]))
>>> len(catalog)
1

_index class-attribute instance-attribute

_index: HealpixIndex | None = field(
    default=None, init=False, repr=False, compare=False
)

columns class-attribute instance-attribute

columns: dict[str, Tensor] = field(default_factory=dict)

device property

device: device

epoch class-attribute instance-attribute

epoch: Tensor | float = 2000.0

frame class-attribute instance-attribute

frame: str = 'ICRS'

lat instance-attribute

lat: Tensor

lon instance-attribute

lon: Tensor

parallax_mas class-attribute instance-attribute

parallax_mas: Tensor | None = None

pm_lat_mas_yr class-attribute instance-attribute

pm_lat_mas_yr: Tensor | None = None

pm_lon_coslat_mas_yr class-attribute instance-attribute

pm_lon_coslat_mas_yr: Tensor | None = None

radial_velocity_km_s class-attribute instance-attribute

radial_velocity_km_s: Tensor | None = None

state property

state: CatalogState

Return this catalog's celestial kinematic state as a :class:CatalogState.

valid class-attribute instance-attribute

valid: Tensor | None = None

__len__

__len__() -> int

__post_init__

__post_init__() -> None

_optional_rows staticmethod

_optional_rows(
    value: Tensor | None, n: int, device: device
) -> Tensor | None

_with_state

_with_state(state: CatalogState) -> Catalog

cone

cone(
    lon: Tensor | float,
    lat: Tensor | float,
    radius_deg: float,
    *,
    nside: int | None = None,
) -> Tensor

crossmatch

crossmatch(
    other: Catalog,
    radius_arcsec: float | Tensor | None = None,
    *,
    find: str = "best",
    right_index: HealpixIndex | None = None,
    target_epoch: float | Tensor | None = None,
    motion: str = "angular",
    left_err_arcsec: float | Tensor | str | None = None,
    right_err_arcsec: float | Tensor | str | None = None,
) -> CrossmatchResult

Match stored positions, or explicitly propagate to a common Julian year.

motion="angular" requires both PM components for rows whose epoch changes; "physical" additionally requires parallax and radial velocity. Missing motion is never filled with zero. A supplied index must be rebuilt by the caller after frame/epoch changes.

find and the error options follow :func:crossmatch_sky; left_err_arcsec / right_err_arcsec may also name a column of self / other (values in arcsec). Epoch/frame alignment reorders nothing, so returned indices always refer to the original rows.

from_arrow classmethod

from_arrow(
    source: Any,
    *,
    lon: str = "ra",
    lat: str = "dec",
    frame: str = "ICRS",
    epoch: float = 2000.0,
    columns: tuple[str, ...] = (),
) -> Catalog

from_state classmethod

from_state(
    state: CatalogState,
    *,
    columns: dict[str, Tensor] | None = None,
) -> Catalog

index

index(nside: int | None = None) -> HealpixIndex

inside

inside(
    coverage: Any, *, time_mjd: Tensor | float | None = None
) -> Tensor

observe

observe(
    to_frame: str = "ALTAZ",
    *,
    obstime_mjd_utc: Any,
    site: Site,
    eop: EarthOrientation,
    pn: PNModel | None = None,
    aberration: bool = False,
    refraction: bool = False,
    refraction_model: Literal[
        "refco", "saemundsson"
    ] = "refco",
) -> Catalog

propagate_angular

propagate_angular(epoch: Tensor | float) -> Catalog

propagate_physical

propagate_physical(epoch: Tensor | float) -> Catalog

take

take(mask_or_index: Tensor) -> Catalog

to

to(device: device | str) -> Catalog

Return a copy on device; __post_init__ re-normalises dtypes.

to_frame

to_frame(
    name: str, *, tt_mjd: Tensor | float | None = None
) -> Catalog

to_state

to_state() -> CatalogState

Core

Catalog streaming, indexing, and crossmatching helpers.

CatalogStreamDataset

CatalogStreamDataset(
    partitions: Iterable[object]
    | Callable[[], Iterable[object]],
    loader: Callable[
        [object], object
    ] = _identity_partition,
)

Bases: IterableDataset

Wrap a partition iterator into a PyTorch IterableDataset.

_loader instance-attribute

_loader = loader

_partitions instance-attribute

_partitions = partitions

__iter__

__iter__() -> Iterator[object]

from_arrow classmethod

from_arrow(
    source: Any,
    *,
    batch_size: int = 65536,
    columns: tuple[str, str] = ("ra", "dec"),
) -> CatalogStreamDataset

Build a CatalogStreamDataset that yields (ra, dec) Tensor pairs from Arrow.

Parameters

source A pyarrow.Table, pyarrow.ipc.RecordBatchReader, or a path string / pathlib.Path to an Arrow IPC Feather file. batch_size Row count per yielded batch. columns Column names for RA (degrees) and Dec (degrees).

CrossmatchResult dataclass

CrossmatchResult(
    left_index: Tensor,
    right_index: Tensor,
    separation_arcsec: Tensor,
)

left_index instance-attribute

left_index: Tensor

right_index instance-attribute

right_index: Tensor

separation_arcsec instance-attribute

separation_arcsec: Tensor

crossmatch_sky

crossmatch_sky(
    left_ra_deg: Tensor,
    left_dec_deg: Tensor,
    right_ra_deg: Tensor,
    right_dec_deg: Tensor,
    *,
    radius_arcsec: float | Tensor | None = None,
    left_chunk_size: int = 65536,
    right_chunk_size: int = 65536,
    find: str = "best",
    right_index: HealpixIndex | None = None,
    device: device | str | None = None,
    left_err_arcsec: float | Tensor | None = None,
    right_err_arcsec: float | Tensor | None = None,
    left_distance: Tensor | None = None,
    right_distance: Tensor | None = None,
    distance_tol: float | None = None,
) -> CrossmatchResult

Chunked exact angular crossmatch on one in-memory tile.

Matchers (choose one radius rule):

  • sky: radius_arcsec (scalar or per-left-row), pair kept when separation <= radius.
  • skyerr: left_err_arcsec / right_err_arcsec (scalar or per row, either side optional, missing side = 0), pair kept when separation <= err_left[i] + err_right[j]. Candidate windows use max(err_left) + max(err_right); the exact test uses each pair's sum.
  • sky + distance (STILTS sky with a 1-d cut): also pass left_distance, right_distance and distance_tol; a pair must additionally satisfy |d_left - d_right| <= distance_tol (NaN never matches).

find selects which pairs are returned:

  • "best" / "best1": nearest right row per left row (right rows may repeat); ties go to the lowest right index.
  • "best2": nearest left row per right row (left rows may repeat); ties go to the lowest left index.
  • "mutual": STILTS find=best. Exactly the mutual-best pairs (best1 intersected with best2, same tie rules); a row whose best partner prefers another row is left unmatched.
  • "symmetric": greedy one-to-one (not STILTS). Ascending separation, ties by lowest left then lowest right index; every row is used at most once and the assignment is maximal.
  • "all": every pair inside the threshold.

"all" is ordered by left index, separation, then right index; the other modes by left index, then right index. Use :func:~torchsky.catalogs.join_rows for join semantics with unmatched rows.

radius_arcsec may be a positive scalar or a per-left-row tensor. Pass right_index to reuse a nested HealpixIndex (find best1/all and sky matcher only). device moves inputs onto that device when given. On MPS the work dtype is float32, so thresholds below the float32 position resolution are not meaningful there.

Parameters:

Name Type Description Default
left_ra_deg Tensor

Left-catalog right ascensions in degrees.

required
left_dec_deg Tensor

Left-catalog declinations in degrees.

required
right_ra_deg Tensor

Right-catalog right ascensions in degrees.

required
right_dec_deg Tensor

Right-catalog declinations in degrees.

required
radius_arcsec float | Tensor | None

Scalar or per-left-row match radius in arcseconds.

None
left_chunk_size int

Maximum left rows processed per outer chunk.

65536
right_chunk_size int

Maximum candidate right rows processed per chunk.

65536
find str

"best", "best1", "best2", "mutual", "symmetric" or "all".

'best'
right_index HealpixIndex | None

Reusable nested HEALPix index for the right catalog.

None
device device | str | None

Optional compute device.

None
left_err_arcsec float | Tensor | None

Per-left-row error radius (skyerr matcher).

None
right_err_arcsec float | Tensor | None

Per-right-row error radius (skyerr matcher).

None
left_distance Tensor | None

Per-left-row distance for the distance cut.

None
right_distance Tensor | None

Per-right-row distance for the distance cut.

None
distance_tol float | None

Largest allowed distance difference.

None

Returns:

Type Description
CrossmatchResult

Matching row indices and great-circle separations in arcseconds.

Examples:

>>> import torch
>>> from torchsky.catalogs import crossmatch_sky
>>> result = crossmatch_sky(torch.tensor([0.0]), torch.tensor([0.0]), torch.tensor([0.0]), torch.tensor([0.0]), radius_arcsec=1.0)
>>> result.right_index.tolist()
[0]

crossmatch_sky_from_arrow

crossmatch_sky_from_arrow(
    left_source: Any,
    right_source: Any,
    *,
    radius_arcsec: float,
    left_columns: tuple[str, str] = ("ra", "dec"),
    right_columns: tuple[str, str] = ("ra", "dec"),
    batch_size: int = 131072,
    find: str = "best",
) -> CrossmatchResult

Crossmatch Arrow inputs through temporary HEALPix tiles.

Both catalogs are streamed once (partitioned by coarse HEALPix pixel into temporary Parquet files with a coarse_pixel column). Per-pixel tiles are then loaded one at a time for fine-grained distance computation. Coordinate memory scales with the largest tile and its neighboring right tiles. Pixel metadata and returned pairs remain in RAM. Indices always refer to the original input row order, including across batches. Use them to select native ID columns without converting IDs to float.

For uniformly distributed sky positions and a well-chosen coarse nside, per-pixel tile size is typically a few thousand rows regardless of whether the total catalog has 10^6 or 10^9 rows.

Parameters

left_source, right_source Each can be a pyarrow.Table, pyarrow.ipc.RecordBatchReader, or a path string / pathlib.Path to an Arrow IPC file (Feather format v2). File sources use memory-mapped I/O. radius_arcsec Match radius in arcseconds. left_columns, right_columns (ra_column_name, dec_column_name) for each catalog. batch_size Row count per streaming batch during the tiling phase. find "best" selects one nearest right row per left row, breaking ties by original right row index. "all" returns every pair in radius.

crossmatch_sky_streamed

crossmatch_sky_streamed(
    left_batches: Iterable[tuple[Tensor, Tensor]],
    right_ra_deg: Tensor,
    right_dec_deg: Tensor,
    *,
    radius_arcsec: float,
    right_chunk_size: int = 65536,
) -> CrossmatchResult

Streamed crossmatch where the left catalog arrives in batches.

The right catalog must fit in memory (required for the coarse HEALPix index). The left catalog is consumed incrementally from left_batches, which yields (ra_deg, dec_deg) tensor pairs.

Parameters

left_batches Iterator yielding (ra_tensor, dec_tensor) pairs (each 1-D float64). Batches are concatenated in order and their indices are mapped back into the final left_index output. right_ra_deg, right_dec_deg Full right-catalog coordinates (1-D float64). radius_arcsec Match radius in arcseconds. right_chunk_size Right-side chunk size for the inner distance loop.

Deblending

torchsky.catalogs.deblending.FactorizationBlendDiagnostics dataclass

FactorizationBlendDiagnostics(
    group_index: int,
    source_indices: Tensor,
    component_ids: Tensor,
    converged: bool,
    iterations: int,
    initial_objective: Tensor,
    final_objective: Tensor,
    accepted_steps: int,
    final_step_size: float,
    morphology_sums: Tensor,
    zero_morphology_indices: Tensor,
    spectrum_gauge_parameter_indices: Tensor,
    failure_reason: str | None,
)

accepted_steps instance-attribute

accepted_steps: int

component_ids instance-attribute

component_ids: Tensor

converged instance-attribute

converged: bool

failure_reason instance-attribute

failure_reason: str | None

final_objective instance-attribute

final_objective: Tensor

final_step_size instance-attribute

final_step_size: float

group_index instance-attribute

group_index: int

initial_objective instance-attribute

initial_objective: Tensor

iterations instance-attribute

iterations: int

morphology_sums instance-attribute

morphology_sums: Tensor

source_indices instance-attribute

source_indices: Tensor

spectrum_gauge_parameter_indices instance-attribute

spectrum_gauge_parameter_indices: Tensor

zero_morphology_indices instance-attribute

zero_morphology_indices: Tensor

torchsky.catalogs.deblending.FitAdequacyDiagnostics dataclass

FitAdequacyDiagnostics(
    evaluated_pixel_count: int,
    fitted_parameter_count: int,
    degrees_of_freedom: int,
    chi_square: Tensor,
    reduced_chi_square: Tensor,
    upper_reduced_chi_square: Tensor,
    heldout_log_likelihood: Tensor | None,
    evaluation: Literal["heldout", "in_sample"],
    inadequate: bool,
    reason: str | None,
)

chi_square instance-attribute

chi_square: Tensor

degrees_of_freedom instance-attribute

degrees_of_freedom: int

evaluated_pixel_count instance-attribute

evaluated_pixel_count: int

evaluation instance-attribute

evaluation: Literal['heldout', 'in_sample']

fitted_parameter_count instance-attribute

fitted_parameter_count: int

heldout_log_likelihood instance-attribute

heldout_log_likelihood: Tensor | None

inadequate instance-attribute

inadequate: bool

reason instance-attribute

reason: str | None

reduced_chi_square instance-attribute

reduced_chi_square: Tensor

upper_reduced_chi_square instance-attribute

upper_reduced_chi_square: Tensor

torchsky.catalogs.deblending.KnownComponentFitError

KnownComponentFitError(message: str, result: object)

Bases: RuntimeError

result instance-attribute

result = result

torchsky.catalogs.deblending.MorphologySpectrumDeblendResult dataclass

MorphologySpectrumDeblendResult(
    catalog: Catalog,
    scene: SourceCatalogScene,
    initial_scene: SourceCatalogScene,
    spectra: Tensor,
    initial_spectra: Tensor,
    morphologies: tuple[Tensor, ...],
    initial_morphologies: tuple[Tensor, ...],
    covariance: BlockSparseCovariance,
    predictions: tuple[Tensor, ...],
    residuals: tuple[Tensor, ...],
    grouping: SourceGrouping,
    diagnostics: tuple[FactorizationBlendDiagnostics, ...],
    linear_diagnostics: tuple[object, ...],
    adequacy: FitAdequacyDiagnostics,
    background_coefficients: tuple[Tensor, ...],
    provenance: dict[str, object],
)

adequacy instance-attribute

background_coefficients instance-attribute

background_coefficients: tuple[Tensor, ...]

catalog instance-attribute

catalog: Catalog

covariance instance-attribute

diagnostics instance-attribute

diagnostics: tuple[FactorizationBlendDiagnostics, ...]

flux property

flux: Tensor

fluxes property

fluxes: Tensor

grouping instance-attribute

grouping: SourceGrouping

initial_morphologies instance-attribute

initial_morphologies: tuple[Tensor, ...]

initial_scene instance-attribute

initial_scene: SourceCatalogScene

initial_spectra instance-attribute

initial_spectra: Tensor

linear_diagnostics instance-attribute

linear_diagnostics: tuple[object, ...]

morphologies instance-attribute

morphologies: tuple[Tensor, ...]

predictions instance-attribute

predictions: tuple[Tensor, ...]

provenance instance-attribute

provenance: dict[str, object]

residuals instance-attribute

residuals: tuple[Tensor, ...]

scene instance-attribute

spectra instance-attribute

spectra: Tensor

torchsky.catalogs.deblending.ParametricBlendDiagnostics dataclass

ParametricBlendDiagnostics(
    group_index: int,
    source_indices: Tensor,
    component_ids: Tensor,
    parameter_indices: Tensor,
    parameter_labels: tuple[str, ...],
    converged: bool,
    iterations: int,
    initial_objective: Tensor,
    final_objective: Tensor,
    damping: float,
    rank: int,
    condition_number: float,
    gauge_parameter_indices: Tensor,
    covariance: Tensor,
    catastrophic_swap: bool,
    failure_reason: str | None,
)

catastrophic_swap instance-attribute

catastrophic_swap: bool

component_ids instance-attribute

component_ids: Tensor

condition_number instance-attribute

condition_number: float

converged instance-attribute

converged: bool

covariance instance-attribute

covariance: Tensor

damping instance-attribute

damping: float

failure_reason instance-attribute

failure_reason: str | None

final_objective instance-attribute

final_objective: Tensor

gauge_parameter_indices instance-attribute

gauge_parameter_indices: Tensor

group_index instance-attribute

group_index: int

initial_objective instance-attribute

initial_objective: Tensor

iterations instance-attribute

iterations: int

parameter_indices instance-attribute

parameter_indices: Tensor

parameter_labels instance-attribute

parameter_labels: tuple[str, ...]

rank instance-attribute

rank: int

source_indices instance-attribute

source_indices: Tensor

torchsky.catalogs.deblending.ParametricBounds dataclass

ParametricBounds(
    position_arcsec: float = 2.0,
    gaussian_sigma_arcsec: tuple[float, float] = (
        0.05,
        30.0,
    ),
    sersic_radius_arcsec: tuple[float, float] = (
        0.05,
        60.0,
    ),
    sersic_axis_ratio: tuple[float, float] = (0.05, 1.0),
    sersic_index: tuple[float, float] = (0.3, 8.0),
    angle_delta_deg: float = 180.0,
)

angle_delta_deg class-attribute instance-attribute

angle_delta_deg: float = 180.0

gaussian_sigma_arcsec class-attribute instance-attribute

gaussian_sigma_arcsec: tuple[float, float] = (0.05, 30.0)

position_arcsec class-attribute instance-attribute

position_arcsec: float = 2.0

sersic_axis_ratio class-attribute instance-attribute

sersic_axis_ratio: tuple[float, float] = (0.05, 1.0)

sersic_index class-attribute instance-attribute

sersic_index: tuple[float, float] = (0.3, 8.0)

sersic_radius_arcsec class-attribute instance-attribute

sersic_radius_arcsec: tuple[float, float] = (0.05, 60.0)

__post_init__

__post_init__() -> None

torchsky.catalogs.deblending.ParametricDeblendResult dataclass

ParametricDeblendResult(
    catalog: Catalog,
    scene: SourceCatalogScene,
    initial_scene: SourceCatalogScene,
    coefficients: Tensor,
    covariance: BlockSparseCovariance,
    nonlinear_covariance: tuple[Tensor, ...],
    predictions: tuple[Tensor, ...],
    residuals: tuple[Tensor, ...],
    grouping: SourceGrouping,
    diagnostics: tuple[ParametricBlendDiagnostics, ...],
    linear_diagnostics: tuple[object, ...],
    adequacy: FitAdequacyDiagnostics,
    background_coefficients: tuple[Tensor, ...],
    provenance: dict[str, object],
)

adequacy instance-attribute

background_coefficients instance-attribute

background_coefficients: tuple[Tensor, ...]

catalog instance-attribute

catalog: Catalog

coefficients instance-attribute

coefficients: Tensor

covariance instance-attribute

diagnostics instance-attribute

diagnostics: tuple[ParametricBlendDiagnostics, ...]

flux property

flux: Tensor

fluxes property

fluxes: Tensor

grouping instance-attribute

grouping: SourceGrouping

initial_scene instance-attribute

initial_scene: SourceCatalogScene

linear_diagnostics instance-attribute

linear_diagnostics: tuple[object, ...]

nonlinear_covariance instance-attribute

nonlinear_covariance: tuple[Tensor, ...]

predictions instance-attribute

predictions: tuple[Tensor, ...]

provenance instance-attribute

provenance: dict[str, object]

residuals instance-attribute

residuals: tuple[Tensor, ...]

scene instance-attribute

torchsky.catalogs.deblending.fit_morphology_spectrum_deblending

fit_morphology_spectrum_deblending(
    observations: Sequence[FieldObservation],
    scene: SourceCatalogScene,
    *,
    monotonic: bool | Tensor = False,
    symmetric: bool | Tensor = False,
    background: BackgroundModel = "constant",
    max_iterations: int = 50,
    step_size: float = 0.1,
    backtracking: float = 0.5,
    step_tolerance: float = 1e-05,
    objective_tolerance: float = 1e-08,
    max_group_size: int = 64,
    heldout_masks: Sequence[Tensor] | None = None,
    adequacy_sigma: float = 5.0,
    failure_policy: FailurePolicy = "raise",
    index_nside: int | None = None,
    footprint_order: int | None = None,
    chunk_size: int | None = None,
    rank_rtol: float | None = None,
    rank_atol: float = 0.0,
) -> MorphologySpectrumDeblendResult

Fit fixed-count nonnegative component morphologies and spectra by blocks.

torchsky.catalogs.deblending.fit_parametric_deblending

fit_parametric_deblending(
    observations: Sequence[FieldObservation],
    scene: SourceCatalogScene,
    *,
    refine_positions: bool | Tensor = True,
    refine_shapes: bool | Tensor = True,
    bounds: ParametricBounds | None = None,
    linear_solver: Literal["signed", "nnls"] = "nnls",
    background: BackgroundModel = "constant",
    max_iterations: int = 20,
    initial_damping: float = 0.01,
    finite_difference_step: float = 0.001,
    step_tolerance: float = 0.0001,
    objective_tolerance: float = 1e-08,
    max_group_size: int = 64,
    heldout_masks: Sequence[Tensor] | None = None,
    adequacy_sigma: float = 5.0,
    failure_policy: FailurePolicy = "raise",
    index_nside: int | None = None,
    footprint_order: int | None = None,
    chunk_size: int | None = None,
    rank_rtol: float | None = None,
    rank_atol: float = 0.0,
) -> ParametricDeblendResult

Refine supplied point, Gaussian, and bounded-Sersic components by LM.

Index

Catalog streaming, indexing, and crossmatching helpers.

HealpixIndex dataclass

HealpixIndex(
    nside: int,
    nest: bool,
    lon: Tensor,
    lat: Tensor,
    order: Tensor,
    unique_pixels: Tensor,
    bucket_starts: Tensor,
    bucket_stops: Tensor,
)

Bucketed nested-HEALPix index over catalog rows.

Build once, then query_disc / lookup. Crossmatch uses the same buckets; a later fused gather can replace lookup without API churn.

bucket_starts instance-attribute

bucket_starts: Tensor

bucket_stops instance-attribute

bucket_stops: Tensor

lat instance-attribute

lat: Tensor

lon instance-attribute

lon: Tensor

nest instance-attribute

nest: bool

nside instance-attribute

nside: int

order instance-attribute

order: Tensor

unique_pixels instance-attribute

unique_pixels: Tensor

build classmethod

build(
    lon: Tensor,
    lat: Tensor,
    *,
    nside: int | None = None,
    nest: bool = True,
) -> HealpixIndex

expand

expand(pixels: Tensor) -> tuple[Tensor, Tensor]

Map per-query pixels [N] to (query_index, row_index) pairs.

lookup

lookup(pixels: Tensor) -> Tensor

Row indices whose HEALPix pixel is in pixels (vectorized gather).

lookup_moc

lookup_moc(footprint: MOC) -> Tensor

Rows inside a MOC using occupied buckets for candidate pruning.

occupied_moc

occupied_moc() -> MOC

Coverage of occupied catalog buckets as canonical NUNIQ cells.

query_disc

query_disc(
    lon: Tensor | float,
    lat: Tensor | float,
    radius_deg: float,
) -> Tensor

Row indices whose positions lie within radius_deg of the center.

to

to(device: device | str) -> HealpixIndex

cap_covering_pixels

cap_covering_pixels(
    nside: int,
    lon_deg: Tensor | float,
    lat_deg: Tensor | float,
    radius_rad: float,
    *,
    nest: bool = True,
) -> Tensor

Pixels whose centers lie in the cap of radius radius_rad.

Small caps expand nested neighbours (O(hops^2)). Wide caps use query_disc. Callers that need a sound match covering pass r + 2ρ.

nside_for_cap

nside_for_cap(
    radius_rad: float, *, max_nside: int = _MAX_NSIDE
) -> int

Coarsest power-of-two NSIDE whose max pixel radius is <= radius_rad.

If the cap is larger than an nside=1 pixel, return 1. If even max_nside pixels are larger than the cap, return max_nside (covering stays sound via the r + 2ρ pad; tiles are just coarser).

Multi

Catalog streaming, indexing, and crossmatching helpers.

ManyMatchResult dataclass

ManyMatchResult(
    index: Tensor,
    n_members: Tensor,
    group_size: Tensor,
    separation_arcsec: Tensor,
)

One output row per match group across C catalogues.

index[g, c] is the row of catalogue c in output row g (-1 if absent). n_members counts present entries; group_size counts every member of the underlying group (equal to n_members in pairs mode). separation_arcsec[g, c] is the separation to the reference (pairs) or the nearest link in the group (group); NaN when absent or unlinked.

group_size instance-attribute

group_size: Tensor

index instance-attribute

index: Tensor

n_members instance-attribute

n_members: Tensor

separation_arcsec instance-attribute

separation_arcsec: Tensor

SelfMatchResult dataclass

SelfMatchResult(
    left_index: Tensor,
    right_index: Tensor,
    separation_arcsec: Tensor,
    group_id: Tensor | None = None,
    group_size: Tensor | None = None,
    n_groups: int = 0,
)

Pairs i < j within one catalogue, optionally with FoF groups.

group_id / group_size are per row (None unless group=True). Rows with no partner get group_id = -1 and group_size = 1; groups of two or more rows are numbered 0..n_groups-1 by smallest member row.

group_id class-attribute instance-attribute

group_id: Tensor | None = None

group_size class-attribute instance-attribute

group_size: Tensor | None = None

left_index instance-attribute

left_index: Tensor

n_groups class-attribute instance-attribute

n_groups: int = 0

right_index instance-attribute

right_index: Tensor

separation_arcsec instance-attribute

separation_arcsec: Tensor

crossmatch_exact

crossmatch_exact(
    left_key: Tensor,
    right_key: Tensor,
    *,
    find: str = "all",
) -> CrossmatchResult

Equal-key join (STILTS exact): O((N + M) log M + K).

Keys are 1-d tensors (integers or floats; NaN never matches). Separations are returned as zeros. find follows :func:crossmatch_sky; every pair scores equally, so ties resolve by lowest row index ("symmetric" pairs the lowest rows of each key first).

crossmatch_many

crossmatch_many(
    catalogs: Sequence[Any],
    radius_arcsec: float | None = None,
    *,
    mode: str = "pairs",
    reference: int = 0,
    err_arcsec: Sequence[float | Tensor | None]
    | None = None,
) -> ManyMatchResult

Match C >= 2 catalogues (STILTS tmatchn).

Each catalogue is a Catalog or a (lon_deg, lat_deg) pair. A pair of rows matches when separation <= radius_arcsec or, with err_arcsec (one scalar/tensor/None per catalogue), <= err_a + err_b.

mode="pairs" (multimode=pairs): every other catalogue is matched to catalogs[reference] with find="best1": each reference row takes its nearest partner (ties by lowest row) in every other catalogue, so one entry may serve several reference rows. Output rows are all reference rows in order (-1 where no partner), then, for each other catalogue in order, its rows that are no reference row's partner (alone in their row). Two non-reference rows are never linked directly.

mode="group" (multimode=group): friends-of-friends over all cross-catalogue pairs; one output row per group, ordered by the group's smallest stacked row (catalogue order, then row). A group may hold several rows of one catalogue (chains); the representative of catalogue c is the member with the smallest separation to any linked member of another catalogue, ties by lowest row index. Same-catalogue pairs are not links.

crossmatch_self

crossmatch_self(
    lon_deg: Tensor,
    lat_deg: Tensor,
    radius_arcsec: float | Tensor | None = None,
    *,
    err_arcsec: float | Tensor | None = None,
    group: bool = False,
    left_chunk_size: int = 65536,
    right_chunk_size: int = 65536,
) -> SelfMatchResult

Pairs within one catalogue (STILTS tmatch1).

Returns every pair i < j (no self pairs, each pair once) with separation <= radius_arcsec or, with err_arcsec (scalar or per row), separation <= err[i] + err[j]. With group=True also returns friends-of-friends groups (connected components of the pair graph) as per-row group_id and group_size (STILTS action=identify). Candidate generation is the declination-strip index, so cost is O(N log N + K); components use scatter-min union-find.

join_rows

join_rows(
    result: CrossmatchResult,
    n_left: int,
    n_right: int,
    join: str = "1and2",
) -> tuple[Tensor, Tensor]

Row-index pairs for a STILTS join type, -1 where a side is absent.

join is one of 1and2 (matched pairs), 1or2 (matched pairs plus every unmatched row of both), all1 / all2 (matched pairs plus the unmatched rows of table 1 / 2), 1not2 / 2not1 (unmatched rows only). Output order: matched pairs as given, then unmatched left rows ascending, then unmatched right rows ascending. Costs O(K + N + M).

Returns:

Type Description
tuple[Tensor, Tensor]

(left_rows, right_rows) int64 tensors of equal length.

Photometry

torchsky.catalogs.photometry.BlendFitDiagnostics dataclass

BlendFitDiagnostics(
    group_index: int,
    source_indices: Tensor,
    exposure_indices: Tensor,
    parameter_indices: Tensor,
    active_parameter_indices: Tensor,
    data_rank: int,
    active_rank: int,
    parameter_count: int,
    active_parameter_count: int,
    condition_number: float,
    rank_cutoff: Tensor,
    information_eigenvalues: Tensor,
    gauge_parameter_indices: Tensor,
    solver: PhotometrySolver,
    converged: bool,
    iterations: int,
)

active_parameter_count instance-attribute

active_parameter_count: int

active_parameter_indices instance-attribute

active_parameter_indices: Tensor

active_rank instance-attribute

active_rank: int

condition_number instance-attribute

condition_number: float

converged instance-attribute

converged: bool

data_rank instance-attribute

data_rank: int

exposure_indices instance-attribute

exposure_indices: Tensor

gauge_detected property

gauge_detected: bool

gauge_parameter_indices instance-attribute

gauge_parameter_indices: Tensor

group_index instance-attribute

group_index: int

information_eigenvalues instance-attribute

information_eigenvalues: Tensor

iterations instance-attribute

iterations: int

parameter_count instance-attribute

parameter_count: int

parameter_indices instance-attribute

parameter_indices: Tensor

rank_cutoff instance-attribute

rank_cutoff: Tensor

rank_deficient property

rank_deficient: bool

solver instance-attribute

solver: PhotometrySolver

source_indices instance-attribute

source_indices: Tensor

torchsky.catalogs.photometry.BlockSparseCovariance dataclass

BlockSparseCovariance(
    parameter_count: int,
    parameter_indices: tuple[Tensor, ...],
    blocks: tuple[Tensor, ...],
    constrained: bool,
)

blocks instance-attribute

blocks: tuple[Tensor, ...]

constrained instance-attribute

constrained: bool

device property

device: device

dtype property

dtype: dtype

parameter_count instance-attribute

parameter_count: int

parameter_indices instance-attribute

parameter_indices: tuple[Tensor, ...]

__post_init__

__post_init__() -> None

apply_covariance

apply_covariance(value: Tensor) -> Tensor

diagonal

diagonal() -> Tensor

query_variance

query_variance(query: Tensor) -> Tensor

to_dense

to_dense() -> Tensor

torchsky.catalogs.photometry.ForwardPhotometryResult dataclass

ForwardPhotometryResult(
    catalog: Catalog,
    scene: SourceCatalogScene,
    coefficients: Tensor,
    covariance: BlockSparseCovariance,
    predictions: tuple[Tensor, ...],
    residuals: tuple[Tensor, ...],
    grouping: SourceGrouping,
    diagnostics: tuple[BlendFitDiagnostics, ...],
    background_coefficients: tuple[Tensor, ...],
    provenance: dict[str, object],
)

background_coefficients instance-attribute

background_coefficients: tuple[Tensor, ...]

catalog instance-attribute

catalog: Catalog

coefficients instance-attribute

coefficients: Tensor

covariance instance-attribute

diagnostics instance-attribute

diagnostics: tuple[BlendFitDiagnostics, ...]

flux property

flux: Tensor

flux_variance property

flux_variance: Tensor

fluxes property

fluxes: Tensor

grouping instance-attribute

grouping: SourceGrouping

predictions instance-attribute

predictions: tuple[Tensor, ...]

provenance instance-attribute

provenance: dict[str, object]

residuals instance-attribute

residuals: tuple[Tensor, ...]

scene instance-attribute

torchsky.catalogs.photometry.SourceGrouping dataclass

SourceGrouping(
    source_exposures: tuple[Tensor, ...],
    exposure_sources: tuple[Tensor, ...],
    blend_groups: tuple[Tensor, ...],
    group_exposures: tuple[Tensor, ...],
    observation_footprints: tuple[MOC, ...],
)

blend_groups instance-attribute

blend_groups: tuple[Tensor, ...]

exposure_sources instance-attribute

exposure_sources: tuple[Tensor, ...]

group_exposures instance-attribute

group_exposures: tuple[Tensor, ...]

observation_footprints instance-attribute

observation_footprints: tuple[MOC, ...]

source_exposures instance-attribute

source_exposures: tuple[Tensor, ...]

torchsky.catalogs.photometry.build_source_groups

build_source_groups(
    scene: SourceCatalogScene,
    observations: Sequence[FieldObservation],
    *,
    index_nside: int | None = None,
    footprint_order: int | None = None,
) -> SourceGrouping

Build exact source-exposure and source-overlap groups with HEALPix/MOCs.

torchsky.catalogs.photometry.fit_forced_photometry

fit_forced_photometry(
    observations: Sequence[FieldObservation],
    scene: SourceCatalogScene,
    *,
    solver: PhotometrySolver = "signed",
    background: BackgroundModel = "constant",
    index_nside: int | None = None,
    footprint_order: int | None = None,
    chunk_size: int | None = None,
    rank_rtol: float | None = None,
    rank_atol: float = 0.0,
    nnls_max_iterations: int = 256,
) -> ForwardPhotometryResult

Fit known fixed source templates on native exposures without detection.

Typing

BackgroundModel

typing.Literal['none', 'constant', 'planar']

FailurePolicy

typing.Literal['raise', 'return']

PhotometrySolver

typing.Literal['signed', 'nnls']