Repository navigation
Add the tripolar bipolar-north-fold example (05_tripolar_fold) - #26
Merged
Merged
Conversation
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
hdrake
force-pushed
the
examples-06-tripolar-fold
branch
2 times, most recently
from
June 27, 2026 14:46
e759800 to
c1c4858
Compare
Adds 05_tripolar_fold.ipynb, demonstrating that xgcm's diff/interp work across the bipolar north fold of a tripolar grid on three real models (MOM6 GFDL-CM4, NEMO IPSL-CM6A-LR via the CMIP6 GCS zarr store, and an Oceananigans/ClimaOcean snapshot fetched from Zenodo via pooch). - environment.yml: add pooch and note the fold example needs the north-fold feature (install xgcm from the tripolar-north-fold branch) until it is released. - Re-execute 01-04 against the same xgcm#711 build so all committed notebook outputs are consistent (0 cell errors across all five). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
hdrake
force-pushed
the
examples-06-tripolar-fold
branch
from
June 30, 2026 14:16
c1c4858 to
d57c790
Compare
Independent review pass — behavior-preserving cleanups (verified the operator outputs are unchanged except the noted X-wrap fix): - Remove the unused `speed_corner` helper and the never-taken `fold=False` branches / `fold` parameter from `speed_centre`/`divergence` (the notebook only ever calls them with the fold active). - `divergence`: drop `boundary="fill"` on the periodic-X `du/dx` diff so the zonal seam wraps instead of being filled (it was only invisible because the plotted window avoids the x=0 wrap). - Drop the inert `other_component=` from the fold interp/diff calls (the 180° fold sign-flips the lone dict-wrapped component and ignores a partner) and tidy the dependency note (deps are already in environment.yml). Re-executed end-to-end: 0 cell errors, 4 figures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
hdrake
added a commit
to hdrake/xgcm
that referenced
this pull request
Jun 30, 2026
Independent review pass on the fold boundary:
- padding.py: validate the position strings in an explicit {axis: position}
fold pivot. Previously a typo (e.g. "centre") was silently coerced to an
edge pivot, giving a wrong halo with no error; it now raises with the
allowed positions. Adds a test.
- grid_topology.md: clarify that a vector is folded by passing it as a
single-key dict (that is what flags the sign flip); the 180° fold needs no
partner, so `other_component` is optional on the fold path.
- Bump the xgcm-examples submodule to the reviewed example notebook
(xgcm/xgcm-examples#26): drops dead helper code, restores X-periodicity in
the divergence demo, re-executed clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
…branch
- 02_mitgcm: the MITgcm domain is bounded in Y, not zonally periodic in the
meridional direction. Build the grid X-periodic / Y-extend
(`Grid(ds, periodic=['X'], boundary={'Y': 'extend'})`) instead of
periodic in both, so diff/interp at the northern and southern Y edges
extend the edge value rather than wrapping across the domain.
- Re-executed 02-05 against the merged tripolar-north-fold xgcm build
(the live working tree), refreshing outputs. 0 cell errors across all five.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
hdrake
added a commit
to hdrake/xgcm
that referenced
this pull request
Jun 30, 2026
…e-exec) Points at xgcm/xgcm-examples#26 = 534d73a: 02_mitgcm now uses an X-periodic / Y-extend grid (the meridional direction is bounded, not periodic), and all example notebooks were re-executed against this branch's xgcm build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
The `diff`/`interp` code spans in the H1 made mkdocs-jupyter mis-render the page title in the docs nav sidebar. Drop the ": do `diff` and `interp` work across the seam?" subtitle, leaving a plain "The bipolar north fold (on tripolar grids)". Markdown-only; cell outputs unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
hdrake
added a commit
to hdrake/xgcm
that referenced
this pull request
Jun 30, 2026
Points at xgcm/xgcm-examples#26 = e2858a5: drops the backtick'd subtitle from the notebook's H1 so the docs nav sidebar renders the page title correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4
…aster - Rename `boundary`->`padding` (Grid, interp, diff) and `boundary_width`->`padding_width` (pad) to match xgcm's v1.0 API. - Re-execute 05 against the tripolar-north-fold branch (xgcm/xgcm#711) and commit the regenerated figure outputs. - Reset notebooks 01–04 to upstream master so this branch carries only the new tripolar example on top of the current examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lotting, add cheap first plots Acts on an independent review of the five examples as of xgcm#26. Every notebook changed here was re-executed end-to-end against the xgcm#711 (north-fold) build, 0 cell errors. Correctness - 02_mitgcm: the boundary->padding migration mistranslated a deliberate non-periodic Y into `padding={'Y': 'periodic'}`, wrapping the Antarctic edge (YG=-80) onto the Arctic row (YC=+78). The domain is global in longitude but spans only 78S-78N, so Y must be 'extend'. Measured: exactly one row (39, the northernmost) changes, max|div| 2.55e-6 -> 3.29e-6 against an interior scale of 6.23e-6 -- the same order as the interior, which is why it survived review. psi is unaffected (it passes an explicit padding='fill' to cumsum). Plotting moved into modules (plotting only -- every xgcm call stays inline) - llc_plots.py: LLCMapper out of 01 (56 lines of pyresample/cartopy vs ~4 lines of xgcm). Drops a dead `else` branch that built a stray unused axes, and documents the antimeridian split and the resampling constants. - tripolar_plots.py: the ~250-line matplotlib layer out of 05. The notebook now computes every halo with xgcm up front and the module only draws. A cheap first plot in each notebook - 02: `ds.THETA[0, 0].plot()` before any xgcm -- the grid is a plain lon/lat rectangle, so no projection is needed. - 03: plots in nominal index coordinates first, then explains why they mislead. The top row's nominal y reads as a single 89.95, while the true 2-D lat along that row runs 64.11-89.94 -- it is the fold seam, flattened into a straight line. Motivates the 2-D lon/lat map that follows. - 05: a raw index-space overview of each model before any fold machinery. - 01 already had one (per-tile in index space); reframed so it reads as the honest layout view rather than a failed map. Clarity - 02: prose said xgcm found "five axes ... 1RHO"; it reports four (X/Y/Z/T) and the dataset has no 1RHO dim. Rewritten, along with the pre-rename "periodic" framing. Four empty trailing cells removed. - 01: the 13-tile face_connections dict now says how to read a (tile, axis, reverse) triple and links to the Face Connections Spec. - 05: dropped three dead `padding=` kwargs. On a fold axis the north edge always folds (topology) and only the south edge honours a per-call padding; these operators pad only the north edge, so the kwargs were exact no-ops. Verified behaviour-preserving: fold vs extend gives max|diff| = 0 on real data. They misled a reviewer into a false bug report, which is reason enough to go. - 05: explained autoparse_metadata=False, and framed the xgcm.padding.pad use explicitly as an under-the-hood aside. - 04: dropped the cell printing a dev version hash into the rendered docs. environment.yml advertised the pre-rename `boundary={"Y": {"fold": ...}}`. Readme documents the module convention and the cheap-plot-first guidance. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2MVD2DtZLieu1zXgEoXbv
Renumbering (simplest -> most complex)
- 02_mitgcm -> 01_mitgcm simplest grid, plain .plot()
- 04_nemo_idealized -> 02_nemo_idealized closed basin; adds metrics/derivative/
integrate/cumint/transform
- 03_MOM6 -> 03_MOM6 first real curvilinear grid (unchanged)
- 01_eccov4 -> 04_eccov4 hardest topology (13-tile LLC), 1.1 GB
- 05_tripolar_fold -> 05_tripolar_fold most specialized (unchanged)
Filenames now match the nav rather than the order the examples happened to be
written in. Cross-refs in Readme.md and llc_plots.py updated; the mkdocs nav and
the docs/ links live in the xgcm repo and land with #711.
05 restructured to start where 03 leaves off
03 computes GFDL-CM4's relative vorticity and then admits it "just looks away
from the Arctic seam". 05 now opens by finishing that sentence: same dataset,
same grid, same diff, same Robinson/RdBu_r figure -- the only change is
padding={"Y": {"fold": "corner"}} instead of {"Y": "extend"}. Load, grid,
compute, plot; the machinery comes after.
Vorticity rather than surface speed, because speed cannot show this: MOM6 puts vo
on the north face, so interpolating it to centres pads the SOUTH edge and the
fold never enters -- fold vs extend is bit-identical (max|diff| = 0). diff(uo,'Y')
goes centre -> north face and does pad north, so vorticity changes at the seam
(max|diff| = 9.3e-02, and 95th-pct seam |zeta| roughly doubles, 2.0e-2 -> 4.0e-2).
It is also the diagnostic 03 explicitly punts on.
The opening figure is 03's map redrawn, and deliberately shows no visible change:
the correction is one row of 1080, a hairline near the pole an order of magnitude
below the coastal vorticity that sets the colour scale. That is what motivates
the index-space zoom-ins that follow.
Then: why "corner" is not a magic word (the four pivots, linked to the topology
docs), the same fold on three models with two pivots, and only then the
under-the-hood pad() asides and the seam figures.
03_MOM6: dropped the per-call padding="fill" from the vorticity, which overrode
the grid's periodic X and put a spurious seam at the dateline. X now wraps and Y
uses the grid's 'extend' -- so 05 recomputes exactly 03's field and the two agree.
Prose reframed to hand off to 05.
All notebooks re-executed against the xgcm#711 build, 0 cell errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2MVD2DtZLieu1zXgEoXbv
The prose added in this PR was overwritten and dramatic. Rewritten across all five
notebooks, the two plotting modules and the Readme: fewer rhetorical set-pieces
("this notebook stops looking away", "that is the honest situation", "the mirror
symmetry is the tell", "that 'extend' is a lie"), less bolding, fewer em-dashes,
and plain declarative sentences instead.
05 specifically
- Opens with what a tripolar grid is ("Many ocean models, like MOM6, NEMO and
Oceananigans, have tripolar grids: ...") rather than a build-up to a reveal.
- Refers to "the MOM6 example earlier in these docs", not "03_MOM6" -- readers see
page titles in the rendered docs, not notebook filenames. Same for the summary
and the module docstrings.
- The "picking up where 03_MOM6 left off" section is cut to the two things a
reader needs: same velocities and grid as before, and why each component is
passed as a {"X": vo} dict.
Also drops an unverified claim from the pivot table: the "center"/"t" row said
"some MOM5 configurations", which is not something the topology docs state and I
have not confirmed. The table now lists only what each pivot means, and the prose
names the two pivots this notebook actually exercises ("corner" for MOM6/NEMO,
"u" for Oceananigans).
Prose-only, except the pivot table and two figure titles. 05 re-executed (its
suptitle lives in the module); all five notebooks show 0 cell errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2MVD2DtZLieu1zXgEoXbv
The text said "the red line marks the top edge of the array" but there was no red
line. index_space_overview drew ax.axhline(ny - 0.5), and imshow's default extent
makes the y range exactly [-0.5, ny - 0.5], so the line landed on the top of the
axes and was hidden under the frame.
The folded edge is the top of the array, so there is nothing to draw a line at:
mark the frame instead. The top spine is now red and 3pt, which is visible in all
three panels. Text and suptitle updated to match ("the red edge along the top of
each panel").
05 re-executed; 0 cell errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2MVD2DtZLieu1zXgEoXbv
Address the review of the example notebooks (into xgcm#26)
hdrake
added a commit
to hdrake/xgcm
that referenced
this pull request
Jul 17, 2026
…ebooks) Advance the docs/xgcm-examples pointer from xgcm-examples master (24aa947) to the head of xgcm/xgcm-examples#26 (20bb1a1), which renames and improves the example notebooks (adds 05_tripolar_fold, renumbers 01-04, and updates to the xgcm v1.0 padding API). This matches the pointer already recorded on the base branch (tripolar-north-fold), so PR #1's remaining diff is just the nav reorder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ebooks master advanced with the v0.10.0 modernization (xgcm#33) under the old notebook numbering (01_eccov4, 02_mitgcm, 04_nemo_idealized), which conflicts with this branch's renumber-to-reading-order + xgcm v1.0 API rewrite. Resolve every notebook conflict in favor of this branch: the renumbered notebooks (01_mitgcm, 02_nemo_idealized, 03_MOM6, 04_eccov4, 05_tripolar_fold) already carry the modernization and go further to the v1.0 padding API, so they supersede master's versions. 02_mitgcm.ipynb stays deleted (renamed to 01_mitgcm). environment.yml auto-merged (master's xgcm>=0.10.0 pin + this branch's tripolar-fold install note). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The docs render notebook cells through mkdocs-jupyter's mistune parser
(MathJax on $...$), not arithmatex. The divergence equation broke because
its inline $...$ spanned a source line break whose continuation started
with "+ ", which mistune parsed as a bullet-list item, splitting the math
across </p> and <li>; \nabla\!\cdot also lost its backslash to a literal
"!". Rewrite both the vorticity and divergence equations in the repo's
\frac{}{} convention, each on one line, no leading "+", no \!.
Also:
- drop the stale "day-365 v2 bump" comment on the Zenodo constants;
- link the previous MOM6 example from the intro and vorticity sections;
- remove the notebook name from the mom6_vorticity docstring/comment;
- make the "single keyword" note explicit (extend -> {"fold": "corner"});
- point the summary at the MOM6 Analysis Cookbook and COSIMA cosima-recipes
(matching 03_MOM6) instead of the dead "more MOM6 recipes" self-link.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hdrake
added a commit
to xgcm/xgcm
that referenced
this pull request
Jul 21, 2026
* Add bipolar/tripolar north-fold boundary condition (closes #194) Tripolar ocean grids (MOM6/OM4, NEMO/ORCA, MOM5, Oceananigans) have a bipolar Arctic seam where the northern edge of the logical grid folds onto itself: the seam (zonal) axis is mirrored about the pole and vector components reverse sign. xgcm previously ignored this, so a Y diff/interp/ derivative across the top row used an ordinary boundary and was wrong. Model this the way all three reference codes do -- as a north-edge boundary condition on the single logical tile (FMS FOLD_NORTH_EDGE, NEMO lbcnfd, Oceananigans Zipper) -- rather than as a face connection. It is requested as a per-axis boundary value on the fold axis: Grid(ds, boundary={"X": "periodic", "Y": {"fold": "corner"}}) The value names the pivot type (center/T, corner/F, U, V, or an explicit {axis: position} pair) -- the center-vs-edge sublattice the fold's fixed line sits on. The seam axis is inferred (the periodic partner); the north edge folds, the south uses a normal mode (default fill). Per field, the seam reflection offset and the redundant-row skip are derived from the pivot pair and the field's own stagger, reproducing Oceananigans' four per-location kernels: seam roll = (pivot_seam=="center") + (field_seam=="edge") skip = 1 if field_fold_kind == pivot_fold else 0 Implemented in the padding layer (_pad_fold + _fold_north_halo, dispatched from pad()); validated at construction in Grid._validate_folds. Pure indexed gather + concat, so it stays lazy and works with multi-chunk dask. No face_connections changes. Verified against Oceananigans' UPivot kernels and MOM6's vmo = -vmo[::-1] seam transport. New tests in xgcm/test/test_fold.py cover per-position halos for multiple pivots, an independent geometric check that center and edge fields mirror about the same pole, vector sign, left/right pivot equivalence, multi-row halos, simultaneous seam padding, end-to-end diff across the seam, and dask parity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add tripolar fold example to nav; bump xgcm-examples Adds the 06_tripolar_fold notebook (relative vorticity across the fold for MOM6, NEMO eORCA1, and Oceananigans, on a polar projection) to the docs nav and bumps the xgcm-examples submodule (also rewrites 03_MOM6's fold section to the new boundary API). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: seamless tripolar-fold maps + cross-fold transect; bump xgcm-examples Bump docs/xgcm-examples to the re-plotted 06_tripolar_fold (seamless polar-cap maps with the fold seam drawn, per-panel difference, and a cross-fold transect showing fold-continuation vs naive flatline). Restrict mkdocs-jupyter to *.ipynb so the example builder scripts under scripts/ aren't rendered as pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add bipolar north-fold section + schematics; clarify terminology Add a "The Bipolar North Fold (Tripolar Grids)" section to grid_topology.md with two publication-quality schematics -- the four pivot conventions, and how the northern halo is filled by mirroring about the pole (with a sign flip for vector components) -- generated by scripts/make_fold_schematics.py. The worked code blocks print the mirror and the sign flip on index-encoded data. Clarify throughout (here, padding.py, test_fold.py) that the global grids are tripolar -- the South Pole plus two poles displaced over Arctic land -- while the fold itself is bipolar: its seam is the line joining the two northern poles. Bump the xgcm-examples submodule pointer (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: add changelog entry for the bipolar north-fold boundary Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — seam-space tripolar-fold example Re-do 06_tripolar_fold in logical grid-index space (seam strip, vector sign-flip strip, cross-seam transect, Rossby seam-row difference) instead of polar-stereographic maps, so the fold's action at the seam is transparent and free of projection distortion / land grey-line / noise ambiguity (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — crisp per-cell seam-space figures The 06_tripolar_fold heatmaps now use nearest-neighbour shading and zoom to a window of open-water columns, so individual grid cells are resolvable instead of looking upscaled/blurry (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — shared seam window + NEMO gap explanation All three 06_tripolar_fold seam figures now share one open-water window per model (directly comparable), and a note explains the two-row gap in NEMO's speed strip as a masked top-v-row dataset quirk spread by centre interpolation, not a fold error (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — reconstruct NEMO's masked seam v-row The 06_tripolar_fold example now rebuilds the redundant (masked) top meridional-velocity row that CMIP6 NEMO/MOM6 ship on the fold seam, so centre-interpolated diagnostics are gap-free and continuous across the seam (xgcm/xgcm-examples#24). The fold method is unchanged; this completes the input. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — fix Rossby seam figure layout Redraw the Rossby ζ/f seam figure as naive / fold / difference (matching the speed strip) instead of a difference-only panel that looked empty/broken (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — uniform seam-figure formatting + grey land The Rossby figure now straddles the seam with the same axis limits and solid seam line as the speed/v strips, and masked land renders grey (distinct from the diverging colormap) across all three figures (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples — fix vorticity sign across the fold ζ/f is a pseudoscalar (flips sign across the bipolar fold), so its halo is the sign-reversed mirror of the interior; the Rossby figure previously padded it as a plain scalar and showed a spurious sign jump at the seam (xgcm/xgcm-examples#24). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: add known-answer interp/diff-across-the-seam test; bump xgcm-examples Add test_interp_diff_across_seam_known_answer to test_fold.py: it builds the north-fold halo by hand in pure numpy (mirror in x, skip the redundant seam row, sign-flip only for vectors) and checks that grid.interp and grid.diff across the seam match that reference exactly, for both a scalar and a vector. This verifies the operators (not just pad) are correct across the fold with an exact, non-statistical check. Bump the docs/xgcm-examples submodule: the example notebook now demonstrates diff-across-the-fold with a horizontal-divergence strip (a true scalar, so no ad-hoc sign handling) instead of the Rossby/ζ figure, and adds u-velocity panels to the component strip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: broaden the seam known-answer test; bump xgcm-examples Extend test_interp_diff_across_seam_known_answer to span the distinct fold branches through the operators, not just the degenerate one: add a seam=edge field (corner pivot, q-position) whose mirror is a non-trivial roll rather than a plain reverse, and a skip=0 case (U pivot). Previously only corner+v (seam=center, reverse, skip 1) was covered, which could not catch a regression in the seam offset/roll branch. Bump docs/xgcm-examples: correct the divergence-strip prose (the difference panel is nonzero across the halo, not only on the seam row). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fold: guard unsupported combos and let per-call boundary override the south edge Address review nits on the north-fold boundary: - Reject `fold` + `face_connections` at construction with a clear NotImplementedError, instead of a cryptic `TypeError: unhashable type: 'dict'` raised later inside `_pad_basic`. - Raise a clear NotImplementedError for an `inner` seam position under a center-type pivot (center/V) instead of an opaque IndexError: reflecting the inner sublattice about a cell-center pole has no mirror partner. - The south edge of a fold axis now honours a per-call `boundary` override when one is given (north always folds; only a plain string is a real override, distinguished from the default fold-spec dict), matching xgcm's usual per-call-overrides-default semantics. Add tests for all three, and update the docs (whats-new, grid_topology) — also correcting a stale reference to the removed Rossby-number diagnostic (now the horizontal-divergence diagnostic). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * scripts: silence ruff T201 for the schematic generator's status prints `make_fold_schematics.py` prints "wrote <file>" after saving each figure (fold_pivots.png / fold_halo.png) — intentional CLI feedback in a dev script. Add `# noqa: T201` to the two print calls so the repo-wide T201 ban (which targets stray prints in library code) passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Fix silent north-halo under-fill in _fold_north_halo A north-fold halo can only mirror as many rows as there are interior rows below the fold (grid length minus any redundant pole row). When a wider halo was requested, the row slice silently clamped and returned a too-short array with no error. Raise a clear ValueError naming the axis, the requested width, and the max available instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Infer fold seam only from explicitly-periodic axes The north-fold seam axis was inferred as "the unique other axis whose boundary is periodic". Because unspecified axes default to periodic, a 3D grid (X periodic, Y fold, Z left unspecified) could not be built: the defaulted-periodic vertical made the seam ambiguous, and a partial dict boundary even KeyError'd before validation. Capture the set of axes the user *explicitly* marked periodic (via boundary={ax: "periodic"} or an explicit periodic list/dict; the legacy periodic=True default is not treated as explicit) and restrict seam candidates to that set. Also make a dict `boundary` tolerate omitted axes (they take the periodic/fill default) instead of raising KeyError, so the vertical can simply be left unspecified. Also document why a 180-degree north fold needs no `other_component` (both vector components sign-flip but do not swap axes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Make north-fold pivot schematic subtitles model-agnostic The per-panel subtitles in the 4-pivot north-fold schematic attributed specific pivot conventions to specific ocean models (e.g. MOM5 -> T-point; NEMO/ORCA and Oceananigans pinned to single panels). These attributions were misleading: several of these models support more than one pivot convention, so tying each to a single panel overstates the relationship. Replace the model-name subtitles with general, geometric descriptions of where the pole sits on the staggered C-grid sublattice (tracer T point, cell corner F point, zonal U face, meridional V face) and regenerate docs/images/fold_pivots.png. The geometry and the halo figure are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Make north-fold pivot table model-agnostic in grid_topology.md Match the doc prose to the now-model-agnostic schematic: the pivot conventions table previously bound specific models to specific pivots (MOM5 -> T, MOM6/OM4 + NEMO/ORCA -> F, Oceananigans -> U), which overstates the relationship since several of these models support more than one convention. Replace the "Used by" column with a "Pole sits on" column describing the C-grid sublattice point (tracer T point, cell corner F point, zonal-velocity U face, meridional-velocity V face), mirroring the schematic subtitles. The general tripolar-grid model list elsewhere in the section is left intact: it lists these models as examples of tripolar grids in general, making no pivot-specific claim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Revert "Potential fix for pull request finding" This reverts commit 42962e6. * Address Copilot findings correctly (replace broken autofix) The Copilot autofix (reverted in the previous commit) tried to make the fold-halo iteration deterministic but deleted the `fold_axes` definition while still referencing it, leaving `for fax in sorted(fold_axes)` with `fold_axes` undefined (NameError at pad time). Re-apply the three findings properly: 1. padding.py: keep the `fold_axes` set definition and iterate it in sorted order, so multi-fold-axis concatenation is deterministic. 2. grid_topology.md / make_fold_schematics.py: the redundant (skipped) top row is per-field, not a property of the pivot alone. A field's top row lies on the fold line exactly when the field's own Y sublattice (center/edge) matches the pivot's Y role -- so Y-centered pivots (center/u) also skip a row for their center-positioned fields, not only the Y-edge pivots (corner/v). Reword the prose and the schematic captions to state the field-relative rule, and regenerate fold_pivots.png. All 28 tests in test_fold.py pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2rfygHbejfx6fHqXcWLXb * Apply review feedback to the north-fold feature Independent review pass on the fold boundary: - padding.py: validate the position strings in an explicit {axis: position} fold pivot. Previously a typo (e.g. "centre") was silently coerced to an edge pivot, giving a wrong halo with no error; it now raises with the allowed positions. Adds a test. - grid_topology.md: clarify that a vector is folded by passing it as a single-key dict (that is what flags the sign flip); the 180° fold needs no partner, so `other_component` is optional on the fold path. - Bump the xgcm-examples submodule to the reviewed example notebook (xgcm/xgcm-examples#26): drops dead helper code, restores X-periodicity in the divergence demo, re-executed clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4 * docs: bump xgcm-examples submodule (02_mitgcm Y-extend + live-build re-exec) Points at xgcm/xgcm-examples#26 = 534d73a: 02_mitgcm now uses an X-periodic / Y-extend grid (the meridional direction is bounded, not periodic), and all example notebooks were re-executed against this branch's xgcm build. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4 * docs: bump xgcm-examples submodule (simplify 05_tripolar_fold title) Points at xgcm/xgcm-examples#26 = e2858a5: drops the backtick'd subtitle from the notebook's H1 so the docs nav sidebar renders the page title correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DKgY8TmjWfwdr8xLTkCME4 * docs: drop duplicated v0.9.0 changelog entries from the unreleased section A prior merge of master's #732 (which split the stale "v0.9.0 (unreleased)" changelog into a released v0.9.0 + a fresh empty v0.10.0) left the SGRID autoparse (#559) and multi-dim transform (#642) entries duplicated: once correctly under the released ## v0.9.0 (2025/08/20) heading, and again under ## v0.10.0 (unreleased) -> New Features. The stray copies made them show up in this PR's diff. Remove them from the unreleased section, leaving only the bipolar north-fold (#711) entry there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fold: document Mapping boundary + fail loudly on multiple fold axes Address review feedback on #711: - Axis.__init__ docstring now documents the fold-spec dict boundary option, matching the widened `Union[str, Mapping]` type hint. - _pad_fold raises NotImplementedError for more than one fold axis instead of defensively sorting the fold-axis set; a tripolar grid has a single north fold and multiple folds are unsupported. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add idealized tripolar-grid globe figure to grid topology Add a "hero" visualization to the Bipolar North Fold section of docs/grid_topology.md: a hand-shaded orthographic globe showing the bipolar Arctic cap with its two displaced northern poles and the bipolar seam, joining an ordinary spherical lat-lon grid at the join latitude. The cap is a faithful port of the Murray (1996) / MOM6 bipolar projection, so every cap grid line meets the regular grid continuously at the join (verified to ~1e-14 deg). New generator scripts/make_tripolar_grid.py (pure numpy + matplotlib, mirroring scripts/make_fold_schematics.py) writes docs/images/tripolar_grid.png. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Xu6fKVLT3efRxXP3htvNTF * docs+code: flag the north fold as experimental Mark the tripolar north-fold boundary as experimental so users know its API and numerical behavior may still change: - grid.py: emit a UserWarning at Grid construction when a fold boundary is declared, with an inline NOTE explaining why. - padding.py: EXPERIMENTAL banner atop the fold kernel/comment block. - grid_topology.md: an "Experimental" warning admonition under the north-fold section header. - whats-new.md: label the changelog entry experimental and note the warning. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CbLtjojoSQCrQY7s3Xt56f * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs: bump xgcm-examples submodule to add the tripolar north-fold example Point docs/xgcm-examples at examples-06-tripolar-fold (xgcm/xgcm-examples @20bb1a1), which adds 05_tripolar_fold.ipynb — the worked tripolar example that the docs nav and grid_topology.md already reference. The previous pin lacked that notebook, so `mkdocs build --strict` aborted on the dangling nav entry and cross-reference. The notebook is executed against this branch's `padding`/`padding_width` API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: reorder the examples nav to reading order and follow the renumbering The examples nav went MITgcm, ECCOv4, MOM6, NEMO -- which was neither the filename order nor a difficulty order. xgcm-examples renumbers the notebooks to match how they are meant to be read, and this follows: 01_mitgcm simplest grid, plain .plot(), diff/interp/cumsum 02_nemo_idealized closed basin; adds metrics/derivative/integrate/cumint/transform 03_MOM6 first real curvilinear (tripolar) grid; sets up the fold 04_eccov4 hardest topology (13-tile LLC face_connections), 1.1 GB 05_tripolar_fold most specialized; picks up 03's Arctic seam Two changes of substance over the old order: NEMO moves up from last (it is the natural second step, and burying it meant metrics/transform appeared only at the very end) and ECCOv4 moves down from second (13-tile face_connections is the hardest concept in the suite and it carries the largest download). Also updates the two links the renumbering would have broken: - docs/grid_topology.md -> xgcm-examples/04_eccov4.ipynb - docs/grid_metrics.ipynb -> .../xgcm-examples/01_mitgcm/ (two anchors) Needs the xgcm-examples submodule bumped to the renumbered commit; the notebooks are renamed there, so this nav is only correct alongside that bump. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2MVD2DtZLieu1zXgEoXbv * docs: bump xgcm-examples submodule to PR #26 head (renumbered notebooks) Advance the docs/xgcm-examples pointer from xgcm-examples master (24aa947) to the head of xgcm/xgcm-examples#26 (20bb1a1), which renames and improves the example notebooks (adds 05_tripolar_fold, renumbers 01-04, and updates to the xgcm v1.0 padding API). This matches the pointer already recorded on the base branch (tripolar-north-fold), so PR #1's remaining diff is just the nav reorder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: bump xgcm-examples submodule to a327003 (renamed notebooks) The nav in mkdocs.yml references the renumbered example notebooks (01_mitgcm, 02_nemo_idealized, 04_eccov4), but the submodule pointer was still at 20bb1a1, whose notebooks use the old numbering (01_eccov4, 02_mitgcm, 04_nemo_idealized). Under `mkdocs build --strict` this mismatch produced 4 warnings and aborted the Read the Docs build. Advance the pointer to a327003 (head of xgcm-examples#26), which merges the review fixes from #34 and renames the notebooks to match the nav. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: add docstrings to north-fold test functions Document each test and helper in test_fold.py so the fold cases are easier to review and maintain, without changing any behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: cut v0.10.1 release and open v1.0.0 unreleased section Move the experimental north-fold entry into a released v0.10.1 (2026/07/20) section and reset v1.0.0 as the next unreleased target. Minor wording tweak in grid_topology.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Henri Drake <hfdrake@Henris-MacBook-Pro-2.local>
hdrake
commented
Jul 21, 2026
hdrake
left a comment
Collaborator
Author
There was a problem hiding this comment.
This was already reviewed via xgcm/xgcm@2565dbc
hdrake
marked this pull request as ready for review
July 21, 2026 02:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ready to merge. xgcm#711 (the north-fold feature) has merged into xgcm
master(cbb6082), so this is no longer gated on a draft. Rebased onto currentmaster(the modernized 01–04 examples), so the diff is the new fold example plus re-executed outputs.Adds
05_tripolar_fold.ipynb, which demonstrates that xgcm'sdiff/interpwork correctly across the bipolar north fold of a tripolar grid, on three real models.Changes
05_tripolar_fold.ipynb— a self-contained, hand-editable notebook (like the other examples). Data:zarr+gcsfs).pooch(md5-pinned). The URL and md5 are constants in the notebook.environment.yml— addspoochand pinsxgcm>=0.10.1(the release that carries the north-fold feature); until v0.10.1 is published to conda-forge the file notes installing xgcm from source.The full fixture-regeneration bundle (ClimaOcean sim script, netCDF converter, pinned Julia
Manifest.toml, README) lives in the Zenodo deposit.Verification
All five notebooks re-executed end-to-end against the north-fold build, 0 cell errors:
01_eccov4·02_mitgcm(7 figs) ·03_MOM6(2 figs) ·04_nemo_idealized(9 figs) ·05_tripolar_fold(8 code cells, 4 figs).Dependencies / merge order
grid_topology.mdnorth-fold section,mkdocs.ymlnav, changelog) are in xgcmmaster. This PR is ready to merge alongside it.environment.yml's pin is satisfied by installing xgcm from source, as noted in the file.xgcm-examplessubmodule needs a bump to this commit to pick up05(the previous bump, xgcm#763, covered 01–04).🤖 Generated with Claude Code
Polish passes (post-review)
An independent review pass applied behavior-preserving cleanups to
05_tripolar_fold.ipynb(operator outputs unchanged except the noted fix):speed_cornerhelper and the never-takenfold=Falsebranches/parameter;∂u/∂xno longer overrides the periodic-X boundary withfill);other_component=from the foldinterp/diffcalls (the 180° fold sign-flips the lone dict-wrapped component and ignores a partner);A follow-up doc pass fixed rendering/cross-references (markdown/comment/docstring only — no outputs re-run):
\frac{}{}form on single lines so mkdocs-jupyter's markdown parser no longer splits the inline$…$math (a wrapped continuation line starting with+was being parsed as a bullet list) or drops the\nabla\!backslash;03_MOM6) instead of a dead self-link;