Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
878e075
Port AccuSphGeom EFT algorithms and add spherical geometry user guide
rajeeja May 22, 2026
1bbf349
Fix pre-commit: remove unused imports and variable
rajeeja May 22, 2026
1034a18
Fix pre-commit: split semicolons in notebook cells
rajeeja May 22, 2026
fb74e75
Address Hongyu's review: port AccuSphGeom compensated arithmetic, rew…
rajeeja May 28, 2026
256b347
Fix RTD: remove RST-invalid numbered list from gca_const_lat_intersec…
rajeeja May 29, 2026
e8999e6
Fix RTD notebook kernel metadata
rajeeja Jun 1, 2026
0220276
Address AccuSphGeom review feedback
rajeeja Jun 4, 2026
0fe327c
Separate intersection kernels into three layers: numerical core, stat…
rajeeja Jun 4, 2026
94c06fe
Fix NaN/Inf propagation in intersection kernels for denom=0 and plana…
rajeeja Jun 4, 2026
2f4147b
Add geometry kernel benchmarks and to_raster auto-extent test
rajeeja Jun 10, 2026
3b3f30c
Address AccuSphGeom review: precision wording, dead code removal, cor…
rajeeja Jun 10, 2026
e33a26c
Pin tornado<6.5.7 to fix Windows CI; remove dead shim; fix notebook A…
rajeeja Jun 10, 2026
f3c8618
Trim computing.py docstring; drop inline=always from L1 kernels
rajeeja Jun 10, 2026
59a8020
Revert tornado pin; root cause is openssl 3.6.3 on Windows
rajeeja Jun 10, 2026
35c85a8
Port AccuSphGeom EFT algorithms and add spherical geometry user guide
rajeeja May 22, 2026
6c1497e
Fix pre-commit: remove unused imports and variable
rajeeja May 22, 2026
57373d0
Fix pre-commit: split semicolons in notebook cells
rajeeja May 22, 2026
3a55e5d
Address Hongyu's review: port AccuSphGeom compensated arithmetic, rew…
rajeeja May 28, 2026
683bc9a
Fix RTD: remove RST-invalid numbered list from gca_const_lat_intersec…
rajeeja May 29, 2026
3ae95f3
Fix RTD notebook kernel metadata
rajeeja Jun 1, 2026
7e35cee
Address AccuSphGeom review feedback
rajeeja Jun 4, 2026
1f844e1
Separate intersection kernels into three layers: numerical core, stat…
rajeeja Jun 4, 2026
ec81e16
Fix NaN/Inf propagation in intersection kernels for denom=0 and plana…
rajeeja Jun 4, 2026
58de273
Merge origin/rajeeja/accusphere: resolve geometry kernel conflicts
rajeeja Jun 18, 2026
896d968
Merge branch 'main' into rajeeja/accusphere
rajeeja Jun 18, 2026
c0f281d
computing: use FMA two_prod where available (portable Veltkamp fallback)
rajeeja Jun 19, 2026
3438b90
intersections: add allocation-free scalar const-lat L1 kernel
rajeeja Jun 19, 2026
aab2bbd
Merge branch 'main' into rajeeja/accusphere
rajeeja Jun 23, 2026
16cb171
Merge branch 'main' into rajeeja/accusphere
rajeeja Jun 24, 2026
69c6d48
style: apply ruff formatting to computing.py
rajeeja Jun 24, 2026
4c4fa94
Merge branch 'main' into rajeeja/accusphere
rajeeja Jun 24, 2026
7968008
Merge branch 'main' into rajeeja/accusphere
rajeeja Jun 30, 2026
e323380
Merge branch 'main' into rajeeja/accusphere
hongyuchen1030 Jul 3, 2026
5b6082b
Merge remote-tracking branch 'origin/main' into update-accusphere-1513
rajeeja Jul 7, 2026
456c587
Merge remote-tracking branch 'origin/main' into update-accusphere-1513
rajeeja Jul 7, 2026
07ac029
Clean up spherical geometry review items
rajeeja Jul 7, 2026
273f968
Merge branch 'main' into rajeeja/accusphere
rajeeja Jul 9, 2026
dba0206
Scalarize constant-latitude intersection dispatcher to remove per-cal…
rajeeja Jul 11, 2026
4366e02
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja Jul 11, 2026
ffcc91e
Merge remote-tracking branch 'origin/rajeeja/accusphere' into rajeeja…
rajeeja Jul 11, 2026
64885f6
Merge branch 'main' into rajeeja/accusphere
rajeeja Jul 13, 2026
a463913
Import benchmark kernels inside setup to avoid asv collection failures
rajeeja Jul 13, 2026
465fd4c
Cmd/accusphere (#1579)
cmdupuis3 Jul 16, 2026
75f06c0
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja Jul 16, 2026
0d131d7
Add thread-scaling benchmark for FP64 vs AccuX constlat dispatcher
rajeeja Jul 16, 2026
b9f5bb3
Cap thread-scaling sweep at performance cores to avoid E-core artifact
rajeeja Jul 16, 2026
f61555b
Fix thread-scaling plot ticks to show only 1,2,4,8
rajeeja Jul 16, 2026
146672c
o Fix pre-commit
rajeeja Jul 16, 2026
e9b394f
o doc fixes
rajeeja Jul 16, 2026
a7d8e88
o Remove benchmark image from repo
rajeeja Jul 17, 2026
a5fd603
Split out point-in-face and lat-lon bounds to a separate PR
rajeeja Jul 17, 2026
d2550bc
Merge remote-tracking branch 'origin/main' into rajeeja/accusphere
rajeeja Jul 18, 2026
e87873f
o computing: match AccuSphGeom acc_sqrt_re exactly (residual order, b…
rajeeja Jul 18, 2026
4af6ed1
o intersections: compensated norm in AccuXGCA kernel, branch-free mas…
rajeeja Jul 18, 2026
ff2d6e0
o arcs: branch-free on_minor_arc int mask at tol 1e-8 matching AccuSp…
rajeeja Jul 18, 2026
3e3a972
o benchmarks: drop point-in-polygon kernel bench, moved to point-in-f…
rajeeja Jul 18, 2026
5c80ff7
o benchmarks: drop thread-scaling constlat, moved to separate scaling…
rajeeja Jul 18, 2026
66b907b
o docs: move spherical-geometry notebook out, depends on split-out po…
rajeeja Jul 18, 2026
8e6c88a
o docs: drop spherical-geometry notebook from user-guide toctree
rajeeja Jul 18, 2026
64ecad4
o test_plot: drop to_raster_auto_extent, unrelated to AccuXGCA/AccuXC…
rajeeja Jul 18, 2026
f6d6142
o point_in_face: AccuSphGeom spherical point-in-polygon via ray-casti…
rajeeja Jul 18, 2026
4d18bae
o bounds: GCA face-bounds path with compensated interior arc z-extrema
rajeeja Jul 18, 2026
8fad6e9
o test: restore point-in-polygon baseline cases for point-in-face path
rajeeja Jul 18, 2026
d80330b
o benchmarks: point-in-polygon sphere kernel benchmark
rajeeja Jul 18, 2026
3a246b4
o docs: spherical geometry accuracy user guide notebook
rajeeja Jul 18, 2026
8cc2168
o docs: add spherical geometry notebook to user-guide toctree
rajeeja Jul 18, 2026
71d97fb
Merge remote-tracking branch 'origin/main' into rajeeja/point_in_face…
rajeeja Jul 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions benchmarks/geometry_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ def _unit(v):
_X2 = _unit(np.array([0.0, 1.0, 0.3]))
_CONST_Z = 0.3

# Polygon for point-in-polygon (spherical triangle)
_POLY = np.array(
[
_unit(np.array([1.0, 0.0, 0.1])),
_unit(np.array([0.0, 1.0, 0.1])),
_unit(np.array([-1.0, 0.0, 0.5])),
],
dtype=np.float64,
)
_Q_INSIDE = _unit(np.array([0.1, 0.3, 0.9]))
_Q_OUTSIDE = _unit(np.array([-0.5, -0.5, -0.7]))


class EFTPrimitives:
"""Benchmark the low-level EFT building blocks: two_sum, two_prod,
Expand Down Expand Up @@ -205,3 +217,21 @@ def time_try_gca_const_lat_intersection(self):
def time_gca_const_lat_intersection(self):
"""Layer 3: dispatcher (full public API)."""
self.gca_const_lat_intersection(self.gca_cart, _CONST_Z)


class PointInPolygonSphere:
"""Benchmark the spherical point-in-polygon kernel."""

def setup(self):
from uxarray.grid.point_in_face import _point_in_polygon_sphere

self._point_in_polygon_sphere = _point_in_polygon_sphere

_point_in_polygon_sphere(_Q_INSIDE, _POLY)
_point_in_polygon_sphere(_Q_OUTSIDE, _POLY)

def time_point_inside(self):
self._point_in_polygon_sphere(_Q_INSIDE, _POLY)

def time_point_outside(self):
self._point_in_polygon_sphere(_Q_OUTSIDE, _POLY)
601 changes: 601 additions & 0 deletions docs/user-guide/spherical-geometry-accuracy.ipynb

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ Supplementary Guides

These user guides provide additional details about specific features in UXarray.

`Accurate Spherical Geometry <user-guide/spherical-geometry-accuracy.ipynb>`_
How UXarray uses compensated arithmetic to avoid catastrophic cancellation in cross-product and point-in-polygon operations

`Working with HEALPix Grids <user-guide/healpix.ipynb>`_
Use UXarray with HEALPix

Expand Down Expand Up @@ -131,6 +134,7 @@ These user guides provide additional details about specific features in UXarray.
user-guide/dual-mesh.ipynb
user-guide/structured.ipynb
user-guide/from-points.ipynb
user-guide/spherical-geometry-accuracy.ipynb
user-guide/healpix.ipynb
user-guide/holoviz.ipynb
user-guide/from_file.ipynb
73 changes: 73 additions & 0 deletions test/grid/geometry/test_accusphgeom_baseline.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
Specific C++ tests mirrored here:
tests/test_gca_gca_intersection_baseline.cpp — 31 near-tangent GCA pairs
tests/test_gca_constlat_intersection_baseline.cpp — 200 arc/latitude cases
tests/test_pip_robust.cpp — simple spherical triangle
tests/test_pip_complicated.cpp — 12-vertex concave polygon

The C++ library uses ultra-tight tolerances (3–100 ULP) backed by Shewchuk
adaptive precision and a geogram fallback. This Python port implements only
Expand All @@ -14,6 +16,7 @@

GCA-GCA intersection: 3e-8 (C++ reference: 1e-8)
GCA-const-lat intersection: 1e-13 (C++ reference: 3–100 ULP ≈ 7e-16–2e-14)
Point-in-polygon: exact location codes (same as C++)
"""

import math
Expand All @@ -23,6 +26,13 @@
import pytest

from uxarray.grid.intersections import gca_const_lat_intersection, gca_gca_intersection
from uxarray.grid.point_in_face import (
_LOC_INSIDE,
_LOC_ON_EDGE,
_LOC_ON_VERTEX,
_LOC_OUTSIDE,
_point_in_polygon_sphere,
)

_DATA_DIR = os.path.join(os.path.dirname(__file__), "data", "accusphgeom")
_GCA_GCA_CSV = os.path.join(
Expand Down Expand Up @@ -133,3 +143,66 @@ def test_gca_constlat_intersection_baseline(gca_constlat_rows, idx):
dy = result[0, 1] - by
err = math.sqrt(dx * dx + dy * dy)
assert err < 5e-15, f"case_id={case_id}: err_xy={err:.3e} ≥ 5e-15"


# ── Point-in-polygon: simple spherical triangle ───────────────────────────────
# From test_pip_robust.cpp: triangle A=(1,0,0) B=(0,1,0) C=(0,0,1)

_SIMPLE_POLY = np.array(
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], dtype=np.float64
)


def test_pip_simple_on_vertex():
q = np.array([1.0, 0.0, 0.0])
assert _point_in_polygon_sphere(q, _SIMPLE_POLY) == _LOC_ON_VERTEX


def test_pip_simple_on_edge():
# Normalize([1,1,0]) — midpoint of edge AB
q = np.array([0.70710678118654752, 0.70710678118654752, 0.0])
assert _point_in_polygon_sphere(q, _SIMPLE_POLY) == _LOC_ON_EDGE


def test_pip_simple_inside():
q = np.array([1.0, 1.0, 1.0])
q = q / np.linalg.norm(q)
assert _point_in_polygon_sphere(q, _SIMPLE_POLY) == _LOC_INSIDE


# ── Point-in-polygon: complicated 12-vertex polygon ──────────────────────────
# From test_pip_complicated.cpp (Tier 4 / no-global-id overload)

_COMPLICATED_POLY = np.array(
[
[0.77114888623389370, -0.15726142646764130, 0.61692644537707060],
[0.45249789144681710, -0.75061357063415830, 0.48148200985709080],
[0.68946150885186746, -0.59933974587969335, 0.40673664307580021],
[0.53398361424012150, -0.82144802877974800, 0.20021147753544170],
[0.72547341102583852, -0.63064441484306173, 0.27563735581699919],
[0.90662646752004000, -0.37288916572560260, 0.19743889808393390],
[0.74736479846796566, -0.64967430761889954, 0.13917310096006544],
[0.75468084319451650, -0.65603404827296060, -0.00872653549837396],
[0.49138625363591330, -0.85368085756667700, -0.17253562867386300],
[0.86555356123625300, -0.23932615843504300, -0.43993183849315200],
[0.73819995144420940, -0.26096774566031860, -0.62205841157622660],
[0.60166139617200880, -0.05234812405382043, -0.79703402578835670],
],
dtype=np.float64,
)

_PIP_CASES = [
([0.75367527697268680, -0.65515992289232780, -0.05233595624294383], _LOC_INSIDE, "Q1 inside"),
([0.92054211727315200, -0.38498585550407840, 0.06624274592780397], _LOC_INSIDE, "Q2 inside"),
([0.53882393432914170, -0.82565565483991800, 0.16721694718218960], _LOC_OUTSIDE, "Q3 outside"),
([0.63494819288856630, -0.65761549896072850, 0.40544130015845230], _LOC_OUTSIDE, "Q4 outside"),
# Q5 is exactly vertex P8 (0-indexed)
([0.49138625363591330, -0.85368085756667700, -0.17253562867386300], _LOC_ON_VERTEX, "Q5 on vertex"),
]


@pytest.mark.parametrize("q_xyz,expected,name", _PIP_CASES)
def test_pip_complicated(q_xyz, expected, name):
q = np.array(q_xyz, dtype=np.float64)
result = _point_in_polygon_sphere(q, _COMPLICATED_POLY)
assert result == expected, f"{name}: expected {expected}, got {result}"
Loading