Skip to content

Dask-native SCRIP corner dedup when opened with chunks= - #1775

Open
rajeeja wants to merge 3 commits into
mainfrom
rajeeja/scrip-dask-lazy-dedup-2
Open

rajeeja wants to merge 3 commits into
mainfrom
rajeeja/scrip-dask-lazy-dedup-2

Conversation

@rajeeja

@rajeeja rajeeja commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Closes #1774

Overview

Dedups with a dask shuffle and builds the inverse index lazily via map_blocks when corners are dask-backed; eager path unchanged. A 24 GB grid that killed eager opens in ~60 s, and the dedup stage runs on the 63 GB np4 grid in 135 s at 38 GiB peak. Unblocks the MCP server path with no server change.

Expected Usage

grid = ux.open_grid("huge_scrip.nc", chunks="auto")

General

  • An issue is created and linked
  • Added appropriate labels (if your uxarray repo permissions allow it)
  • Filled out Overview and Expected Usage (if applicable) sections

Testing & Benchmarking

  • There is adequate test coverage of changes from this PR (add new tests if needed)
  • [N/A] If this PR could affect performance, ran ASV benchmarks and confirmed they show expected behavior (add a new benchmark if necessary)

Documentation and Examples

  • Docstrings updated with any function changes, and included in all new functions
  • [N/A] User (public) functions added to docs/api.rst; internal (private) function names start with an underscore (_)
  • [N/A] If touched any notebook files, cleared the output of all cells before committing
  • [N/A] If added new notebook files, put into appropriate directories and referenced in appropriate files

AI Disclosure

AI Usage:

  • I have tested and take responsibility for all AI-generated content in my PR.

A SCRIP file stores one (lon, lat) pair per face-corner, so the corner
table is n_face * n_corners rows even though adjacent faces share almost
every vertex. The reader deduped that table with Polars, which needs the
whole thing resident: 53.6 GiB of float64 for a 300M-element np4 grid,
before the join copy. That is why large np4 SCRIP grids fail to open.

When the grid is opened with chunks=, dedup with a dask shuffle instead,
which spills partition-by-partition, and build the inverse index lazily
with map_blocks against the (small) unique-node table. The eager path is
unchanged.
Without a distributed client dask picks an in-memory shuffle. On the
226M-face ne2048np4 grid that peaked at 17.9 GiB against 12.1 GiB for the
disk shuffle, for the same answer. Pin it, but only when the caller has
not chosen a method and no client is running, since p2p is the better
choice on a real cluster.

Also drop the intermediate DataFrame once its two columns are out. The
unique-node table is 3.4 GiB at this size, so holding it past its last
use is not free. Together these take the full open from ~31.0 to
~29.3 GiB peak (3 runs each).
The block lookup relied on a sort-after-join to keep each block aligned
with its inputs. Polars' own docs say not to rely on an observed join
order without requesting one, so request it with maintain_order="left"
and check the row count survived. Misalignment here would not raise --
it would build every face from the wrong corners.

Lift the closure to module level so that contract is testable, and widen
the tests: parametrize over chunks= spellings including "auto" (what
callers actually pass), compare corners in winding order rather than
sorted, cover the radians path, and assert the connectivity is still
lazy after open so a future .compute() in the reader fails here rather
than only on a multi-GB file.
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.

open_grid on large SCRIP grids OOMs: corner dedup ignores chunks=

1 participant