Skip to content

numpy-level point-kind geo2mort: geo2mort(lat, lon, order=29, points=True) #96

Description

@espg

What's needed

A vectorized, numpy-in/numpy-out point-kind encoder on the geo2mort surface:

geo2mort(lats, lons, order=29, points=True)  # -> np.ndarray[uint64], Kind::Point words
  • points=False (default): exactly today's behavior — area-kind words, byte-identical, fully backward compatible.
  • points=True: route through the kernel's existing point encoding (rust_ang2pix at order 29 → rust_mi_from_nested_point, i.e. Kind::Point via from_nested_point in src_rust/src/decimal_morton.rs) and return a plain contiguous uint64 ndarray — no pandas ExtensionArray wrapper.
  • Point encoding is order-29-only, so points=True with an explicit order != 29 should raise ValueError, matching MortonIndexArray.from_latlon's existing contract (mortie/morton_index.py).
  • Null/sentinel semantics: same as the area path — inputs are dense float64 lat/lon with no null mask; non-finite lat/lon should fail loudly (or produce the documented sentinel 0, whichever the area path does today — the two paths must agree). 0 remains the only reserved word (empty/invalid), never a valid encode result.
  • Tests to include: (a) points=True words differ from area words at order 29 but clip2order coarsening is bit-identical at every order (this is the property zagg's grouping relies on); (b) a lone points=True word survives common_ancestor unchanged (kind preserved); (c) points=True, order != 29 raises; (d) dtype is uint64, C-contiguous, same shape as input.

Why (the hot-path numbers)

zagg's HealpixGrid.assign runs on every observation of every granule read (per-photon for ATL03 — millions of points per shard). It now needs point-kind words end-to-end (englacial/zagg#87: located t-digest channel via common_ancestor). The only public point encode today is the pandas ExtensionArray classmethod MortonIndexArray.from_latlon(..., points=True), and the public ways out of the wrapper are element-wise. Measured on 1M random points (mortie 0.8.4):

path ms / 1M pts
geo2mort (area) 42.6
raw kernel rust_ang2pix + rust_mi_from_nested_point 46.0
from_latlon(points=True) construction alone 45.2
+ public unwrap (np.asarray / .to_numpy / .astype) ~490–560

The kernel is at parity with the area encode (~8%); the ~12x penalty is entirely the ExtensionArray→ndarray unwrap. zagg currently works around it by reading the array's internal uint64 storage through its own boundary adapter (zagg.grids.morton.morton_words), which is fast (~70 ms total) but leans on a private attribute. A numpy-level geo2mort(..., points=True) gives the hot path a public vectorized surface and drops the wrapper entirely.

Consumer

englacial/zagg#87 / englacial/zagg#165HealpixGrid.assign swaps one call (from_latlon(points=True) + unwrap → geo2mort(..., points=True)) the moment this lands in a release; that swap is the PR's tracked phase 6.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions