Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
be994ff
Drive section neighbor-finding from xgcm topology metadata
hdrake Jun 23, 2026
d97ecca
Unify seam handling via zero-length-face dropping; add bipolar fold s…
hdrake Jun 23, 2026
41ce711
Re-execute all example notebooks against the bipolar-fold grid
hdrake Jun 23, 2026
4949fe6
Add gridutils.symmetrize: support 'left'-staggered (MITgcm/ECCO) grids
hdrake Jun 25, 2026
362ab56
Fix self-connection fold check to read grid._face_connections
hdrake Jun 26, 2026
e81bd4e
Trim reciprocity-check comments to the essentials
hdrake Jun 26, 2026
64cc479
Support 'left' (MITgcm/ECCO) C-grids natively via per-position corner…
hdrake Jun 26, 2026
47ab5af
Sync example-notebook prose with three-position grid support
hdrake Jun 26, 2026
3d852e6
Add CLAUDE.md documenting three-position grid support (supersedes #46)
hdrake Jun 26, 2026
ec71fe4
CLAUDE.md: keep PR description in sync when pushing to an open PR
hdrake Jun 26, 2026
7952e75
Make the section walk deterministic and add a curve option
hdrake Jun 26, 2026
f606683
Test that oblique multi-segment sections are reversible
hdrake Jun 26, 2026
b981c15
Rewrite example 5 as depth-space MOC from ECCOv4r4 (LLC90)
hdrake Jun 27, 2026
88132c3
Correct cross-tile seam neighbors on staggered (left/right) grids
hdrake Jun 27, 2026
2fc76dc
CLAUDE.md: use docs_env_sectionate (fold-aware xgcm) for local dev
hdrake Jun 27, 2026
59571e1
Load example 5 ECCO data from Zenodo instead of earthaccess
hdrake Jun 30, 2026
0a66164
Demo latitude-circle sections in the tripolar example
hdrake Jun 30, 2026
f666a7b
Ignore local data cache, build artifacts, and .DS_Store
hdrake Jun 30, 2026
e19551e
Address review: drop model-convention labels from source; rename chec…
hdrake Jun 30, 2026
7a0dd36
Fix docs build pointer and sync CLAUDE.md with renamed API
hdrake Jun 30, 2026
1debde8
Address review: pin pandoc for docs, harden Zenodo loader, doc f_c
hdrake Jun 30, 2026
a6f5286
Add native-grid section visualization to the ECCO MOC example
hdrake Jun 30, 2026
4df149c
Fix is_mask_inside sign inversion on multi-tile grids
hdrake Jun 30, 2026
870d9a8
Rebuild staggered multi-tile corner topology on the outer lattice
hdrake Jul 4, 2026
9b79c02
Route all multi-tile topology through the outer corner graph; add exa…
hdrake Jul 4, 2026
b1baabc
tests: geometry-first 'left' cubed-sphere fixture with streamfunction…
hdrake Jul 4, 2026
b45d75a
ci: install combined xgcm (fold #711/#712 + vector-pad #749); re-exec…
hdrake Jul 4, 2026
8e6e0f2
tests: cover right-staggering, open-wall zeroing, and reverse-gluing …
hdrake Jul 4, 2026
67248ca
reverse=True: precise error for non-symmetric grids; document outer+r…
hdrake Jul 4, 2026
2ed310e
Support outer (symmetric) grids with reverse=True gluings
hdrake Jul 5, 2026
9af0c92
Add AI Usage Policy to CLAUDE.md (follow xgcm policy)
hdrake Jul 13, 2026
223cb76
Migrate to xgcm dev-v1.0.0 (boundary/periodic -> padding API)
hdrake Jul 13, 2026
5f2576f
tests: pin geometric per-edge sign stability at the geographic pole
hdrake Jul 13, 2026
af6e878
Allow zero-length (360->0) latitude-circle loop closures
hdrake Jul 13, 2026
2cbeb27
Fix conservation leak at multi-tile corners stored on no face (#49)
hdrake Jul 13, 2026
fbeaf00
Terminology cleanup: replace metaphorical jargon with conventional ph…
hdrake Jul 14, 2026
b1264d9
Guard spherical_angle against degenerate zero-length arcs
hdrake Jul 14, 2026
188685d
Resolve multi-tile corner identity topologically; snap only for the p…
hdrake Jul 14, 2026
d2854a9
Address PR review: accurate cut comment, rung-2 note, snap-failure guard
hdrake Jul 14, 2026
0062cd0
Merge pull request #6 from hdrake/topological-corner-resolution
hdrake Jul 14, 2026
9326473
Depend on released xgcm >= 0.10.1 instead of the dev-v1.0.0 git ref
hdrake Jul 21, 2026
113b03a
Merge upstream master into topology-driven-neighbors
hdrake Aug 1, 2026
a15638e
Per-branch conda envs in CLAUDE.md; fix version section after merge
hdrake Aug 1, 2026
0c2708d
Match layer/interface through the grid, not string substitution
hdrake Aug 2, 2026
096e132
Pad only over the axes the array actually spans
hdrake Aug 2, 2026
4d7306a
Take layer/interface from the grid's Z axis; default both to None
hdrake Aug 2, 2026
140de68
Re-execute the example notebooks on this branch
hdrake Aug 2, 2026
4a78d98
Re-execute the example notebooks on this branch
hdrake Aug 2, 2026
9023252
Merge pull request #8 from hdrake/fix-horizontal-only-padding
hdrake Aug 4, 2026
5b92ea2
Merge base branch (with #8) into fix-layer-interface-matching
hdrake Aug 4, 2026
0eeb6e1
Take the vertical coordinates from the data and the grid, not the caller
hdrake Aug 4, 2026
9993903
Re-execute the example notebooks on the merge result
hdrake Aug 4, 2026
055010c
Merge pull request #7 from hdrake/fix-layer-interface-matching
hdrake Aug 4, 2026
dfbe0b2
Address PR #47 review: docs wording, ECCO record, topology check
hdrake Aug 10, 2026
7b93104
Always take the shortest path between section waypoints
hdrake Aug 10, 2026
568ac7b
Correct two overclaiming comments surfaced by the review questions
hdrake Aug 10, 2026
efe9101
Add an animated docs page explaining the section-tracing algorithm
hdrake Aug 10, 2026
fe35ddc
Merge pull request #12 from hdrake/algorithm-walkthrough-docs
hdrake Aug 10, 2026
d0ef93b
Decide the curve per segment, and make "latitude circle" mean it
hdrake Aug 11, 2026
c566d7d
Correct false claims in the docs added by the review follow-up
hdrake Aug 11, 2026
124fa06
Update section.py
hdrake Aug 11, 2026
2269bb0
Drop the face-existence and mutuality checks; trust xgcm
hdrake Aug 11, 2026
007a534
Update section.py
hdrake Aug 11, 2026
eabe1f5
Merge pull request #10 from hdrake/pr47-lat-circle-shortest-path
hdrake Aug 11, 2026
9fc40cd
Refactor comments in section tracing logic
hdrake Aug 11, 2026
7313dd8
Merge remote-tracking branch 'origin/topology-driven-neighbors' into …
hdrake Aug 11, 2026
68d2c2e
Merge pull request #9 from hdrake/pr47-review-docs
hdrake Aug 11, 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
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,10 @@ venv.bak/

# mypy
.mypy_cache/

# Local example data cache (fetched from Zenodo / PO.DAAC at runtime)
data/*.nc

# Build artifacts and OS cruft
*.tar.gz
.DS_Store
85 changes: 85 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Sectionate is a Python package for sampling grid-consistent hydrographic sections from structured ocean model outputs (e.g. MOM6). It traces paths along C-grid velocity faces between geographic waypoints and computes transports/tracer values along those sections. Only structured grids are supported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "(e.g. MOM6)"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change "Only structured grids are supported" to supports any structured model whose grid can be defined by a xgcm.Grid object.

@hdrake hdrake Aug 10, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

In hdrake#9.

Written by Claude Code.

@hdrake hdrake Aug 10, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — it now reads that sectionate supports any structured model whose grid can be described by an xgcm.Grid object.

In hdrake#9.

Written by Claude Code.


## Development Setup

```bash
conda env create -f ci/environment.yml
conda activate test_env_sectionate
pip install -e .
```

**Local development environment:** use the `docs_env_sectionate` conda env
(`/opt/anaconda3/envs/docs_env_sectionate`) for developing this package — running the
test suite, executing the example notebooks, and any package work in this workspace.
Keep it updated as needed; in particular it must carry the unreleased **fold-aware
xgcm** (`hdrake/xgcm@tripolar-north-fold`, which includes the bipolar fold #711 and the
face-connection padding fix #713). The ECCO example (notebook 5) pulls its data subset
from Zenodo (concept DOI `10.5281/zenodo.21051424`) via stdlib `urllib` with MD5 checks —
no `earthaccess`/Earthdata login needed. Install xgcm with
`pip install "git+https://github.com/hdrake/xgcm.git@tripolar-north-fold"`.
Caveat: that dev build reports version `0.1.devNNN`, which is `< 0.9.0`, so re-running
`pip install -e .` (sectionate requires `xgcm>=0.9.0`) silently reinstalls a released xgcm
over it; reinstall the fold-aware xgcm afterwards with `--no-deps`. A correctly set up env
runs the full suite with **0 skips** (fold tests require the fold-aware xgcm; they `skip`
otherwise).

## Commands

- **Run all tests:** `pytest`
- **Run a single test:** `pytest sectionate/tests/test_section.py::test_find_closest_grid_point`
- **Build docs:** `cd docs && make html` (requires `docs/environment.yml` environment)

## Definition of Done (always, before committing)

Before committing any change to this repository, always do all of the following — use
parallel agents where it is faster:

1. **Run the full test suite** (`pytest`) and confirm it passes.
2. **Re-execute the example notebooks** in `examples/` and confirm they run cleanly.
3. **Scan the repo for inconsistencies / outdated information** introduced by the change —
including docstrings, `README.md`, files under `docs/`, the example notebooks, and this
`CLAUDE.md` — and update them so docs and code stay in sync.

## Keeping pull requests in sync

When pushing a new commit to a branch that already has an open PR, also update that PR's
top-comment description so it stays consistent with the most recent commit:

- Revise the summary / "Changes" prose to reflect what the latest commit actually did.
- Update any task/status checklist — check off completed items (`- [ ]` → `- [x]`) and add
new ones as needed.
- Edit it in place with `gh pr edit <number> --repo <owner>/<repo> --body-file <file>` (find
the PR for the current branch with `gh pr view --json number,url`).

## Architecture

The package is organized around a pipeline: define sections → map to grid → compute transports/tracers.

### Core Modules

- **`section.py`** — Section definition and grid path algorithms. `Section` holds named waypoint coordinates; `GriddedSection` extends it with grid index information. `grid_section()` is the main entry point that maps geographic waypoints to grid vorticity-point indices `(i_c, j_c)` by walking the grid between waypoints along the `curve` requested (`"great circle"`, the default geodesic, or `"latitude circle"`); each segment must span less than 180°. The walk is deterministic and direction-independent: it admits neighbors strictly closer to the endpoint plus any seam twin of the current cell (so periodic/fold-seam crossings don't hinge on floating-point rounding), and breaks ties by index. Grid topology is inferred entirely from `xgcm.Grid` metadata — each axis' `boundary` (periodic wrap, fill/extend wall, or a single-tile bipolar north fold `{"Y": {"fold": ...}}`) and `face_connections` for multi-tile grids — so there is no `topology` keyword. The pathfinder consumes topology-aware neighbor maps built by `gridutils.build_neighbor_maps`.

- **`transports.py`** — Transport computation along sections. `uvindices_from_qindices()` converts vorticity-point indices to U/V velocity-point indices using a per-position corner offset (`gridutils.corner_offset`) covering all three C-grid staggerings: 'outer' (symmetric), 'right' (non-symmetric), and 'left' (MITgcm/ECCO). `convergent_transport()` is the main function: it lazily computes signed normal transports with configurable orientation (positive inward to the polygon defined by the section).

- **`tracers.py`** — `extract_tracer()` interpolates tracer data to U/V points along a section path for cross-section plotting.

- **`gridutils.py`** — Grid introspection utilities: `corner_position()` returns the shared vorticity corner position (`"outer"`/symmetric, `"right"`/non-symmetric, or `"left"`/MITgcm-ECCO) and `corner_offset()` the corresponding velocity index shift used throughout transports/tracers; `check_symmetric()` is a thin wrapper (True iff `"outer"`). `coord_dict()` and `get_geo_corners()` extract coordinate/dimension names from `xgcm.Grid` metadata; `build_neighbor_maps()` builds the topology-aware neighbor maps the pathfinder consumes.

- **`utils.py`** — Section catalog I/O. Sections can be loaded by name from JSON catalog files in `sectionate/catalog/`. Also provides `save_gridded_section()`/`load_gridded_section()` for persisting gridded sections.

### Key Concepts

- **Vorticity points (q-points):** Sections are defined as paths through vorticity-point indices `(i_c, j_c)`. Consecutive q-points define velocity faces (either U or V).
- **Corner staggering (three positions):** Vorticity sits at one of three xgcm positions: `"outer"` (MOM6 symmetric, M+1×N+1), `"right"` (MOM6 non-symmetric, M×N), or `"left"` (MITgcm/ECCO, incl. the lat-lon-cap, M×N). All three are native; they differ only by a per-position velocity index offset (`gridutils.corner_offset`: outer→0, right→+1, left→0). `"left"` indexes like `"outer"`; it differs only in array length and in that the *high* corner row/column is absent (so a section exactly on the north/east domain wall clips one row inside).
- **Sign conventions:** For a *closed* section, `convergent_transport()` determines orientation (clockwise/counterclockwise) using stereographic projection and signed polygon area, then applies sign corrections so positive transport means "inward" (toward the enclosed polygon). For an *open* section there is no enclosing polygon, so `positive_in` is undefined; it instead uses the **left-of-transect** convention — `positive_in=True` makes positive transport point to the left of the section as traversed from the first to the last waypoint — and emits a `UserWarning`. `is_section_counterclockwise()` is only consulted in the closed case.
- **xgcm.Grid dependency:** The package relies heavily on `xgcm.Grid` for grid metadata (axis boundaries, coordinate positions, dataset access via `grid._ds`).

## Version

Version is in `sectionate/version.py` and read by hatchling at build time.
Loading
Loading