22 (1/2): auto-catchment, the fine catchment - #110
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Ola's correction) 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>
The red suite for increment 22 PR 1 (docs/increments/22-auto-catchment.md, "The red suites", PR 1): the flood, the outline, the window loop and the `rasputin catchment` command. No production code. - tests/cpp/unit/test_hydrology_upstream.cpp: upstream<R> equals lowest-neighbour descent node for node on 300 random no-pit, no-tie DEMs (an oracle in the file); depressions, a flat lake with shelves, a V-valley (exact); NoData never in; seed on the edge; wrong seed size; 1-node rasters; invariants on 400 random DEMs with pits, flats and NoData (seeds in, 8-path to a seed, bounds, determinism, catchments of a union are the union). Registered behind if(EXISTS hydrology/upstream.hpp), increment 12's scaffold: REMOVE AT GREEN. - test_core_upstream.py: the binding (mask uint8 (rows, cols), bounds, flags, ValueError on a seed of another shape). - test_outline.py: diamond, octagon, L, saddle, pinch, holes clockwise, and 40 random masks against the guarantee and a square-by-square area oracle. - test_catchment.py (in-memory repository): lake seeds, refusals for no lake and two lakes, WGS 84 and EPSG:3035 inputs, the grown window equal to one flood over the whole raster, the edge (north, east), NoData and memory refusals, holes filled and pieces dropped with counts, to_thread. - test_cli_catchment.py: round trip through read_domain and mesh --domain, stderr lines, --seed-crs default, GeoJSON and GeoPackage lakes, refusals, and Bygdin on Ola's data (skipped without it) within 2 % of 305.54 km^2. Red because include/terrain/hydrology/upstream.hpp, _core.upstream, tin_engine.outline, tin_engine.catchment and the catchment command do not exist. Every fixture's expected value was checked in the scratchpad with the design's flood written out, not committed. The main build exits 0, ctest 803/803, and the rest of pytest, ruff, format and mypy are green. Names the design leaves open are chosen in each file's docstring. Design gap: edge and NoData-adjacent nodes are outlets and labelled in only when seeds, so touches_edge/touches_nodata as worded fire only for seeds; the C++ suite pins only what any repair keeps, and the Python suite pins the refusals by behaviour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sputin catchment) Makes the PR 1 red suite (1e1b3bb) pass, per docs/increments/22-auto-catchment.md as amended in 06776de. No test file changed except removing the red-step if(EXISTS hydrology/upstream.hpp) guard in tests/cpp/CMakeLists.txt. - include/terrain/hydrology/upstream.hpp: Priority-Flood labelling of one catchment, keys (level, push counter); flags as "The flags" words them. - bindings/core.cpp, _core.pyi: _core.upstream and UpstreamOutcome (read-only (rows, cols) uint8 mask anchored to the outcome), GIL released. - outline.py: marching squares with the (8, 4) saddle rule, boundary squares only. - catchment.py: CatchmentRequest, delineate, the window loop, the fine outline. - feature_input.py: read_source split out of _Tally.source; read_lakes. - dem_input.py: repository_for, shared by open_dem and catchment. - cli.py: rasputin catchment, writing the fine outline as GeoJSON. Design deviation, for review: step c of "The window" doubles the margin and tests growth against the doubled margin, so every step grows and the loop only stops at the data's edge (Bygdin: 8 min, then refused by 15a's coverage check). Here growth is tested with the base 2000 m margin and the step takes the doubled one. Bygdin: 3 windows, 304.9096 km2 against NVE's 305.54 (-0.21 %), 6.3 s end to end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two pins the amended design asks for (0e1c29a, "What a test should pin"): - test_the_loop_stops_early_far_from_the_data_edge: the bowl, with more than four base margins of data beyond the catchment on every side and a catchment larger than the first window, is accepted in 2 or 3 windows, the last reaching the data's edge on no side, equal to one flood. - test_a_catchment_inside_the_first_window_takes_one_window: a lake on a peak, 49 nodes, accepted in one window. Both pass at 58f6904. Against the old rule (the check on the doubled margin, the step on the undoubled one: a scratchpad copy of catchment.py with those two lines changed, src_python untouched) the first fails: 6 windows, 49x49 up to the whole 400x200 raster. The second passes there too; it is the cheaper pin the design describes, guarding the other direction. 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>
# Conflicts: # project_structure.md
…N analogy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
upstream.hpp: cast kIn to std::uint8_t so both arms of the conditional have the same type (GCC -Wextra: enumerated and non-enumerated type). core.cpp: hold the raster geometry by value rather than as a reference bound through std::visit (GCC -Wdangling-reference false positive). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
skavhaug
added a commit
that referenced
this pull request
Sep 29, 2026
22 (2/2): auto-catchment, the outline reduction (stacked on #110)
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.
Increment 22, PR 1 of 2: auto-catchment, the fine catchment. Design:
docs/increments/22-auto-catchment.md. PR 2 (the outline reduction) is stacked on this one.What it does
rasputin catchment --dem … --seed X Y [--seed-crs] [--lakes …] --out catchment.geojsoncomputes the catchment draining into a seed and writes it in the DEM's CRS, ready forrasputin mesh --domain.--lakes), else the nearest DEM node.--lakes.Numbers
Bygdin (Jotunheimen) over DTM10: 304.91 km² against NVE's 305.54 km² (−0.21 %), 6.3 s over 3 windows across two tiles; the fine outline has 17 812 vertices (PR 2 reduces it).
Size
Net +619 production lines (CLAUDE.md §2). Together with PR 2 the increment is 1 025 lines, hence the split.
🤖 Generated with Claude Code