Skip to content

GridInterface: the shared grid contract - #6

Open
asinghvi17 wants to merge 31 commits into
mainfrom
gridinterface
Open

asinghvi17 wants to merge 31 commits into
mainfrom
gridinterface

Conversation

@asinghvi17

@asinghvi17 asinghvi17 commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

The first implementation of GridInterface: a shared contract for grids on the plane and the sphere, used by ConservativeRegridding.jl (JuliaGeo/ConservativeRegridding.jl#155) and DiscreteGlobalGrids.jl (JuliaGeo/DiscreteGlobalGrids.jl#125).

  • Core: identity and positions, geometry and extents, neighbours and rings, hierarchy, a keyed dual, chunks and subsets, GridAndTree. Small answers are stack-allocated SmallVectors.
  • Trees: treeify(grid; leaf_capacity = Balanced() | ForSweeps() | ForQueries() | n) builds the grid's own tree. subtree(grid, cells) restricts that tree to a subset (tight root, position_span pruning, leaf filtering). Node cost is STI.node_extent_is_expensive on the tree type.
  • GridInterface.Structured: the face protocol (faces, axes, seams, fans), quadtrees, tiles and masked subsets. It takes CR's grids.
  • lib/GridInterfaceConformance: laws every provider is checked against, with mocks that each fail exactly one family.

The history is unrelated to main's template commits; this PR merges the template in. It keeps the repo's UUID (ae69c2d7), and the downstream PRs use it.

Depends on GeometryOps.jl as/split-unitsphericalgeometrycore (JuliaGeo/GeometryOps.jl#524) through [sources], so CI starts at Julia 1.12.

CI runs the tests on 1.12, 1 and pre on Linux, macOS and Windows, runs GridInterfaceConformance's suite, and builds the docs.

🤖 Generated with Claude Code

asinghvi17 and others added 30 commits October 13, 2026 23:06
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y walk

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hbours, collections, keyed dual, lazy chunks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, alignment, keyed dual, dual tiling, subset algebra, sampled chunks, hierarchy obligations; tests for the revised API

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ALPix vertices, rooted halo/border, storage chunks, CopernicusDEM root index; CR bridge on GridAndTree with split_weight forwarding and raster tiles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r huge analytic grids

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…c halo

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bs on analytic grids

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he shim

GridInterface now depends on the two GeometryOps sub-packages (resolved by
path from the as/split-unitsphericalgeometrycore worktree) instead of
lib/SpatialTreeInterfaceShim. split_weight is STI's function; subtree_indices
is answered lazily by HierarchyCursor and SelectionCursor through a frontier
primitive, forwarded by GridAndTree and the CR bridge. Fixes group nodes under
a RootIndex returning their parent's window, with a regression testset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rtex numbering

treeify(grid; leaf_capacity = Balanced() | ForSweeps() | ForQueries() | n)
is the tree entry; providers map intents to integers through
leaf_capacity(grid, hint) and write one integer builder. AbstractWrappedGrid
forwards every verb to parent(g) from one list; GridAndTree, ChunkedGrid
(mockchunks) and GridAndVertices are small wrappers over it. The extension's
IdDict memos and clear_caches! are gone: fallbacks build a ForQueries tree per
call and GridAndTree is the cache. Traits are instance-only.

Vertex numbering is a provider hook: vertex_numbering_is_analytic(grid) lets a
provider answer the dense vertex verbs in O(1), dual(grid) then holds nothing,
and the derived numbering or geometric table is built only where it must be.
The HEALPix adapter numbers its vertices by face arithmetic.

Conformance gains the intent, wrapper-forwarding, laziness and vertex
numbering laws.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GridInterface.Structured holds the five face hooks (nfaces, facesize,
facevertex, faceposition, facecell), the optional face axes, and the seam
hooks facecross and facefan, from which corners, extents, the per-face
TopDownQuadtreeCursor under a FaceRootTree, TileIndices/TileChunks, axis-floor
or walking cellat, aligned neighbours and analytic vertices derive.
CellBasedGrid, RegularGrid and ExplicitPolygonGrid are copied from CR with
their constructors; CR itself is untouched and its grids reach GridInterface
as Structured twins through the adapter.

Neighbours and vertex fans are stack-allocated SmallInts (now a core type).
Seams are arithmetic through facecross; the shared-vertex border scan is the
conformance suite's oracle, not runtime. Periodic and collapsed axes are
detected with a tolerance or stated through constructor keywords.

Gates: every node extent of the Structured quadtree is bit-equal to CR's
cursor on all of CR's test grids, and regridding through the twins is
bit-identical to CR's own path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GridInterfaceConformance takes DGG's engineering: has_nonfallback_method (a
fallback answering in place of a declared analytic trait is a failure), seeded
sampling with the seed in the report header, test_grid/test_system emitting one
@test per law and @test_skip per unclaimed suite, and a Mocks module with DGG's
CubeSystem plus single-fault wrappers that each break exactly one law. New laws:
tree (leaves emit 1:n once, node covers leaf geometry, O(1) leaf re-read, all
three intents, subtree_indices is the leaf walk), wrapper forwarding, structured
face round-trips, rings concatenate to discs, declared-analytic providers, CCW
vertex fans, inference, and the hierarchy laws (children partition the next
level, descendant_range against a recursion oracle). The lib's own test proves
every mock fails only its family and every prototype grid passes.

Also: SelectionCursor's subtree_indices follows STI's traversal order; the
wrapper laws sample instead of enumerating so the 6.9e10-cell CopernicusDEM L1
grid passes check_grid in about 12 s, which the prototype conformance script
now runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replaces the Int-only SmallInts with the generic SmallVector{N,T} that
DiscreteGlobalGrids and ConservativeRegridding already return: capacity N,
runtime length, no heap, any element type, so a hexagon's five or six corners
and a vertex fan are the same shape across the stack. GridInterface re-exports
SmallVector.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
DiscreteGlobalGrids now answers GridInterface directly, so
prototype/adapters/DGGAdapter.jl goes and the prototype scripts, bench
and revision bench use DGG itself (treeify intent assertions added to
conformance.jl; explicit `using GridInterface:` for cellat, cell_extent
and the DE-9IM predicates). The untyped `level(grid) = nothing` fallback
no longer swallows cell indices.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FaceSeams builds a cubed-sphere/HEALPix seam table from the face corner
vertices (with a verified cross-back) and answers facecross through
seamcross. MaskedCursor is the quadtree of a structured Subset (leaves
filtered by membership), used by treeify(::Subset) and subtree(grid,
cells). facecolumnmajor lets a face declare a non-column-major position
layout (ClimaCore's Gilbert order, ring-ordered full grids); TileChunks
sorts such chunks and the pole fan goes through faceposition.

The CR adapter keeps only the bridge and `structured`; Healpix maps now
come through ConservativeRegridding's HealpixMapGrid. cr_parity.jl
compares CR's tree path with the provider path pair by pair.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
blockextent(grid, f, ir, jr) is the documented, exported seam the
quadtree, tiles and cell extents call, so a provider with an analytic
block extent (GlobalRegridding's raster view) overrides it instead of a
private function. The CR adapter and the one-off parity scripts are
gone: ConservativeRegridding's engine now takes grids directly, and the
prototype bench and conformance scripts call it as such.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GlobalRegridding's raster chunks are lazy tile indices with an O(1) tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `subtree(grid, cells; leaf_capacity)` is now `treeify(grid, n)` restricted
  to `cells` by a generic `MaskedCursor` (tight root, `position_span`
  pruning, leaf filtering), for any provider's tree. `treeify(::Subset)` is
  the same restriction emitting subset positions. `hierarchy_subtree` goes.
- `extent_is_expensive` is removed: node cost is `STI.node_extent_is_expensive`
  on the tree type; bucketed hierarchy leaves build their entries once per
  visit, one-cell leaves stay lazy.
- Type-stable `HierarchyCursor` child iteration.
- Conformance: laws for `subtree` restricting `treeify`'s tree and for
  declared face numbering; property tests for the restriction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hes; stop tracking manifests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Join main's PkgTemplates history, keeping this branch's code. Take the
  repo's UUID (ae69c2d7) and author list.
- CI tests Julia 1.11 (oldest with `[sources]`), 1 and pre on three OSes,
  runs GridInterfaceConformance's suite, and builds the docs.
- Docs: API pages for GridInterface and GridInterface.Structured; the docs
  environment gets the split GeometryOps sources.
- Add Random to the test environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.11 cannot meet the zero-allocation laws and ignores dependencies' [sources].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI stays on 1.12+, where the zero-allocation laws hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Julia 1.11 the compiler leaves 16-304 bytes behind the same code that
is allocation-free on 1.12, so every zero-allocation check failed there and
nothing else did.

- Conformance: `allocation_verdict(bytes)` turns a byte count into
  `:pass`, `:fail` (1.12+) or `:broken` (older Julia); the two "allocates
  nothing" laws use it. `:broken` is printed, counts against no family in
  `family_status`, and is a `@test_broken` (named in one @info) in the
  test forms, so a downstream suite passes on 1.11.
- Tests: `@test_noalloc f(...)` records a non-zero count as broken below
  1.12 and still passes a zero count; `expected_fails` drops the
  allocation laws from an expected failure list there. The lib's own suite
  counts skips apart from broken laws and expects the Stateful mock's
  sweep to be broken, not failed, on 1.11.
- CI stays at 1.12, 1 and pre, with the reason in the workflow; README
  states the 1.11 requirement and the 1.12+ allocation guarantee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A "Trees and restriction" page describes the contracts: `treeify` as the
  provider's seam (`n::Integer`), `subtree` as the generic `MaskedCursor`
  restriction of the grid's own tree pruned by `position_span`,
  `SubtreeCapacity` as the reason the generic `treeify` can answer
  `nothing`, node extent cost on tree node types through
  `SpatialTreeInterface.node_extent_is_expensive`, and leaf entries built
  once per visit.
- The API reference is one page per family under docs/src/api/, each
  with a short intro and an explicit `@docs` block covering every
  docstring in GridInterface and GridInterface.Structured.
- `split_weight` and `subtree_indices` get GridInterface docstrings (they
  are SpatialTreeInterface's verbs, which Documenter will not show from an
  unlisted module), and `seamcross` gets its own docstring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urning

The dual law "vertex fans wind CCW about the vertex" wound every fan's cell
centroids as a closed ring about the vertex. That is the right test when the
vertex is interior; an open fan (vertex_isopen) has the vertex on the grid's
boundary, so its centroids form an arc beside it that turns by nothing about
it in any order, and a correctly ordered fan failed.

Open fans now pass when their centroid azimuths about the vertex step CCW
with a total sweep short of a full turn (is_ccw_open_sequence); closed fans
keep ring_is_ccw. Azimuths come from _azimuth on the grid's manifold, as the
declared-winding law already does.

Tests: a RegularGrid(Spherical(), -40:10:40, 50:10:90) reaching the north
pole passes every law, its BrokenDual fails only the dual family and names
this law, and the helper is checked across the azimuth branch cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Separate rectangular storage from shared-vertex geometry while retaining optimized native maps. Stream planar polygon bounds and inline corner construction; document minimal provider recipes and strengthen neighbour conformance checks.

Validated exact source: GridInterface 2902 passed, 1 existing skip; GridInterfaceConformance 116 passed; generic multiface defaults 60 passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant