From 039cf3c15274606456ffa09ecd14e5acf59b07c5 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:05:13 +0200 Subject: [PATCH 01/16] 22 PR2 red: the outline reduction The red suite for increment 22 PR 2 (docs/increments/22-auto-catchment.md, "The reduction", "The command" and "The red suites", PR 2). No production code. - tests/cpp/unit/test_area_collapse.cpp: statuses (negative, NaN, +-inf tolerance; clockwise; fewer than 4 vertices); tolerance 0 drops exactly the collinear vertices; traced rectangles reduce to at most 8 vertices at one cell; a keep-point in a tab stays strictly inside; a zigzag corridor 1.5 cells wide at 5 cells stays simple; equal bits twice; 300 random star polygons. Every case checks the design's guarantees from input and output alone: area to 1e-9, simple by brute-force classify, keep strictly inside by exact winding, tolerance both ways, and input - collinear - collapses = output. Registered behind if(EXISTS vector_simplify/area_collapse.hpp): REMOVE AT GREEN. - test_core_reduce.py: the binding, on 12 random traced blobs at five tolerances and on the PR 1 bowl and plus catchments: the five guarantees (Hausdorff within tolerance plus a cell), determinism, statuses, ValueError on bad shapes, and a median reduction below 0.8 at two cells. - test_cli_catchment.py: --outline-tolerance 0 writes the fine ring less its collinear vertices; the default (twice the cell) reduces, keeps the area and stays within the tolerance; the properties and the stderr line; -1, nan and inf refused; Bygdin (skipped without Ola's data): reduced area equal to fine to 1e-9, at most 17,812 / 5 vertices, and mesh --domain at --tolerance 10 succeeds. Test amendment: PR 1's two area checks compare at 1e-9, the design's area guarantee, since the default output is now reduced. The refusal helper also rejects Typer's "No such option", so refusals are red until the option exists. The C++ harness was run in the scratchpad against an identity stub: every fixture premise holds and the 16 failures are the behaviour an identity lacks. Red because area_collapse.hpp, _core.reduce_ring and --outline-tolerance do not exist. Main build exits 0, ctest 814/814; the rest of pytest, ruff, format and mypy are green. Co-Authored-By: Claude Opus 5.5 --- tests/cpp/CMakeLists.txt | 22 ++ tests/cpp/unit/test_area_collapse.cpp | 390 ++++++++++++++++++++++++++ tests/python/test_cli_catchment.py | 151 +++++++++- tests/python/test_core_reduce.py | 179 ++++++++++++ 4 files changed, 737 insertions(+), 5 deletions(-) create mode 100644 tests/cpp/unit/test_area_collapse.cpp create mode 100644 tests/python/test_core_reduce.py diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index 57e27d1a..e4f799aa 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -317,3 +317,25 @@ add_terrain_backend_test(prop_noding_verifier_sweep property/prop_noding_verifie # "The red suites"). Not named invariant-critical; the descent oracle and the # invariants on random DEMs are the defence. It starts no threads. add_terrain_test(test_hydrology_upstream unit/test_hydrology_upstream.cpp) + +# The outline reduction, increment 22 PR 2 (docs/increments/22-auto-catchment.md, +# "The reduction" and "The red suites"): area-preserving segment collapse with +# the exact kernel. A backend test because it names DefaultKernel. Not named +# invariant-critical; it starts no threads. +# +# RED UNTIL vector_simplify/area_collapse.hpp EXISTS (increment 12's scaffold, +# 39dba00): while the header is absent the target is EXCLUDE_FROM_ALL and not +# registered with ctest, so `cmake --build build` and `ctest` stay green; see +# the red with `cmake --build build --target test_area_collapse`. Once the +# header lands, re-running `cmake -S . -B build` registers the suite. +# REMOVE AT GREEN: replace the if/else with the plain add_terrain_backend_test line. +if(EXISTS "${PROJECT_SOURCE_DIR}/include/terrain/vector_simplify/area_collapse.hpp") + add_terrain_backend_test(test_area_collapse unit/test_area_collapse.cpp) +else() + message(STATUS "test_area_collapse: include/terrain/vector_simplify/area_collapse.hpp " + "absent; suite built only on request (increment 22 PR 2 red step)") + add_executable(test_area_collapse EXCLUDE_FROM_ALL unit/test_area_collapse.cpp) + target_link_libraries(test_area_collapse PRIVATE + terrain_headers terrain_test_support Catch2::Catch2WithMain terrain_predicates) + target_compile_options(test_area_collapse PRIVATE -Wall -Wextra -Wpedantic -Werror) +endif() diff --git a/tests/cpp/unit/test_area_collapse.cpp b/tests/cpp/unit/test_area_collapse.cpp new file mode 100644 index 00000000..948d2949 --- /dev/null +++ b/tests/cpp/unit/test_area_collapse.cpp @@ -0,0 +1,390 @@ +// Increment 22, PR 2 (docs/increments/22-auto-catchment.md, "The reduction" +// and "The red suites", PR 2 C++): the fine outline reduced by +// area-preserving segment collapse (Kronenfeld et al. 2020) to a horizontal +// tolerance, simple and with the seed kept inside. +// +// The design names this file tests/cpp/unit/area_collapse.cpp; it carries the +// test_ prefix every other suite in tests/cpp/unit/ has. +// +// Interface assumed (the design fixes the header, the function, the statuses +// and which counts exist; the spelling is chosen here and stated in the +// handback): +// +// #include +// namespace terrain::vector_simplify { +// enum class ReduceStatus : std::uint8_t { +// Ok, InvalidTolerance, NotCounterClockwise, TooFewVertices }; +// struct ReduceCounts { +// std::size_t collinear; // vertices dropped by the collinear pass +// std::size_t collapses; // B, C -> E replacements made +// std::size_t rejected_crossing, rejected_seed, rejected_tolerance; +// }; +// struct ReduceOutcome { +// std::vector ring; // open: the first vertex not repeated +// ReduceStatus status; +// ReduceCounts counts; +// }; +// template +// ReduceOutcome reduce_ring(std::span ring, double tolerance, +// std::span keep); +// } +// +// The input ring is open too. On a status other than Ok the ring's content is +// not pinned. +// +// THE CHECKS ARE THE DESIGN'S GUARANTEES, computed here from the output and +// the input alone: area to 1e-9 relative; simple by a brute-force pairwise +// classify (non-adjacent edges Disjoint, adjacent ones +// Touching); keep-points strictly inside by an exact winding number; every +// input vertex within the tolerance of the output ring and every output +// vertex within it of the input ring; the same input twice gives equal bits. +// Plus the bookkeeping identity: input size - collinear - collapses = output +// size (each collapse replaces two vertices by one). + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using terrain::Point2; +using terrain::Segment2; +using terrain::noding::classify; +using terrain::noding::SegmentRelation; +using terrain::pred::DefaultKernel; +using terrain::pred::Orientation; +using terrain::vector_simplify::reduce_ring; +using terrain::vector_simplify::ReduceOutcome; +using terrain::vector_simplify::ReduceStatus; + +namespace { + +using Ring = std::vector; + +ReduceOutcome reduce(const Ring& ring, double tolerance, const Ring& keep = {}) { + return reduce_ring(std::span{ring}, tolerance, + std::span{keep}); +} + +double area(const Ring& r) { + double twice = 0.0; + for (std::size_t i = 0; i < r.size(); ++i) { + const Point2& a = r[i]; + const Point2& b = r[(i + 1) % r.size()]; + twice += a.x * b.y - b.x * a.y; + } + return 0.5 * twice; +} + +Segment2 edge(const Ring& r, std::size_t i) { return Segment2{r[i], r[(i + 1) % r.size()]}; } + +// Brute force, exact: adjacent edges share only their vertex, others nothing. +bool simple(const Ring& r) { + const std::size_t n = r.size(); + if (n < 3) + return false; + for (std::size_t i = 0; i < n; ++i) + for (std::size_t j = i + 1; j < n; ++j) { + const bool adjacent = j == i + 1 || (i == 0 && j == n - 1); + const SegmentRelation rel = classify(edge(r, i), edge(r, j)); + if (adjacent ? rel != SegmentRelation::Touching : rel != SegmentRelation::Disjoint) + return false; + } + return true; +} + +// Strictly inside: on no edge, and an exact winding number of 1. +bool strictly_inside(const Point2& p, const Ring& r) { + int winding = 0; + for (std::size_t i = 0; i < r.size(); ++i) { + const Point2& a = r[i]; + const Point2& b = r[(i + 1) % r.size()]; + if (classify(Segment2{p, p}, Segment2{a, b}) != SegmentRelation::Disjoint) + return false; + const Orientation o = DefaultKernel::orient2d(a, b, p); + if (a.y <= p.y && b.y > p.y && o == Orientation::CounterClockwise) + ++winding; + if (a.y > p.y && b.y <= p.y && o == Orientation::Clockwise) + --winding; + } + return winding == 1; +} + +double distance(const Point2& p, const Point2& a, const Point2& b) { + const double dx = b.x - a.x, dy = b.y - a.y; + const double len2 = dx * dx + dy * dy; + double t = len2 > 0.0 ? ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2 : 0.0; + t = std::clamp(t, 0.0, 1.0); + return std::hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy)); +} + +double distance(const Point2& p, const Ring& r) { + double best = std::numeric_limits::infinity(); + for (std::size_t i = 0; i < r.size(); ++i) + best = std::min(best, distance(p, r[i], r[(i + 1) % r.size()])); + return best; +} + +// Every guarantee of the design, on one call. +void check_guarantees(const Ring& fine, double tolerance, const Ring& keep, const ReduceOutcome& out) { + REQUIRE(out.status == ReduceStatus::Ok); + const Ring& red = out.ring; + REQUIRE(red.size() >= 3); + CHECK(fine.size() - out.counts.collinear - out.counts.collapses == red.size()); + const double a0 = area(fine); + CHECK(std::abs(area(red) - a0) <= 1e-9 * a0); + CHECK(area(red) > 0.0); // still counter-clockwise + CHECK(simple(red)); + for (const Point2& k : keep) + CHECK(strictly_inside(k, red)); + const double slack = 1e-9 * std::max(1.0, tolerance); + for (const Point2& p : fine) + CHECK(distance(p, red) <= tolerance + slack); + for (const Point2& p : red) + CHECK(distance(p, fine) <= tolerance + slack); + for (const Point2& p : red) + CHECK((std::isfinite(p.x) && std::isfinite(p.y))); +} + +bool same_bits(const Ring& a, const Ring& b) { + return a.size() == b.size() + && std::equal(a.begin(), a.end(), b.begin(), [](const Point2& p, const Point2& q) { + return std::bit_cast(p.x) == std::bit_cast(q.x) + && std::bit_cast(p.y) == std::bit_cast(q.y); + }); +} + +// The fine outline of a w x h block of nodes (cell `cell`, first node at +// `origin`), as the tracer draws it: a vertex at the midpoint of every lattice +// edge leaving the block, counter-clockwise. 2 (w + h) vertices, of which all +// but the 8 corner-cutting ones are collinear; area (w h - 1/2) cells. +Ring block(std::size_t w, std::size_t h, double cell = 10.0, Point2 origin = {1000.0, 2000.0}) { + Ring r; + const auto at = [&](double i, double j) { return Point2{origin.x + i * cell, origin.y + j * cell}; }; + const double W = static_cast(w), H = static_cast(h); + for (std::size_t i = 0; i < w; ++i) + r.push_back(at(static_cast(i), -0.5)); + for (std::size_t j = 0; j < h; ++j) + r.push_back(at(W - 0.5, static_cast(j))); + for (std::size_t i = w; i-- > 0;) + r.push_back(at(static_cast(i), H - 0.5)); + for (std::size_t j = h; j-- > 0;) + r.push_back(at(-0.5, static_cast(j))); + return r; +} + +// The same up to where the cycle starts. +bool same_cycle(const Ring& a, const Ring& b) { + if (a.size() != b.size()) + return false; + for (std::size_t s = 0; s < a.size(); ++s) { + bool all = true; + for (std::size_t i = 0; i < a.size() && all; ++i) + all = a[(s + i) % a.size()] == b[i]; + if (all) + return true; + } + return a.empty(); +} + +Ring corners_of(const Ring& r) { + Ring out; + for (std::size_t i = 0; i < r.size(); ++i) { + const Point2& a = r[(i + r.size() - 1) % r.size()]; + const Point2& b = r[(i + 1) % r.size()]; + if (DefaultKernel::orient2d(a, r[i], b) != Orientation::Collinear) + out.push_back(r[i]); + } + return out; +} + +// A random star polygon round `centre`: sorted distinct angles, radii on a +// half-metre grid, so it is simple and the centre is strictly inside. +Ring star(std::mt19937& rng, std::size_t n, Point2 centre = {5000.0, 7000.0}) { + std::uniform_int_distribution radius(100, 300); + std::vector ticks(3600); + std::iota(ticks.begin(), ticks.end(), 0); + std::shuffle(ticks.begin(), ticks.end(), rng); + ticks.resize(n); + std::sort(ticks.begin(), ticks.end()); + Ring r; + for (int t : ticks) { + const double angle = 2.0 * std::numbers::pi * t / 3600.0; + const double rho = 0.5 * radius(rng); + r.push_back(Point2{std::round(centre.x + rho * std::cos(angle)), + std::round(centre.y + rho * std::sin(angle))}); + } + // Rounding may make three consecutive vertices collinear or two equal on + // a small star; the fixture keeps only simple, counter-clockwise ones. + return r; +} + +} // namespace + +TEST_CASE("status: an invalid tolerance, a clockwise ring, fewer than 4 vertices", + "[vector_simplify][area_collapse][status]") { + const Ring ring = block(5, 4); + CHECK(reduce(ring, -1.0).status == ReduceStatus::InvalidTolerance); + CHECK(reduce(ring, std::numeric_limits::quiet_NaN()).status + == ReduceStatus::InvalidTolerance); + CHECK(reduce(ring, std::numeric_limits::infinity()).status + == ReduceStatus::InvalidTolerance); + CHECK(reduce(ring, -std::numeric_limits::infinity()).status + == ReduceStatus::InvalidTolerance); + + Ring clockwise(ring.rbegin(), ring.rend()); + CHECK(reduce(clockwise, 10.0).status == ReduceStatus::NotCounterClockwise); + + const Ring triangle{{0.0, 0.0}, {10.0, 0.0}, {0.0, 10.0}}; + CHECK(reduce(triangle, 10.0).status == ReduceStatus::TooFewVertices); + CHECK(reduce(Ring{{0.0, 0.0}, {10.0, 0.0}}, 10.0).status == ReduceStatus::TooFewVertices); + CHECK(reduce(Ring{}, 10.0).status == ReduceStatus::TooFewVertices); + + const Ring square{{0.0, 0.0}, {10.0, 0.0}, {10.0, 10.0}, {0.0, 10.0}}; + CHECK(reduce(square, 10.0).status == ReduceStatus::Ok); +} + +TEST_CASE("tolerance 0 drops only the exactly collinear vertices", "[vector_simplify][area_collapse][zero]") { + const Ring ring = block(20, 10); + REQUIRE(ring.size() == 60); + const ReduceOutcome out = reduce(ring, 0.0); + REQUIRE(out.status == ReduceStatus::Ok); + CHECK(out.counts.collinear == 52); + CHECK(out.counts.collapses == 0); + CHECK(same_cycle(out.ring, corners_of(ring))); + CHECK(out.ring.size() == 8); + CHECK(area(out.ring) == area(ring)); + + // A ring with no collinear vertex comes back as it went in. + const Ring square{{0.0, 0.0}, {10.0, 0.0}, {10.0, 10.0}, {5.0, 12.5}, {0.0, 10.0}}; + const ReduceOutcome same = reduce(square, 0.0); + REQUIRE(same.status == ReduceStatus::Ok); + CHECK(same_cycle(same.ring, square)); + CHECK(same.counts.collinear == 0); +} + +TEST_CASE("a traced rectangle of nodes reduces to at most 8 vertices at one cell, area unchanged", + "[vector_simplify][area_collapse][rectangle]") { + constexpr std::array, 4> shapes{{{20, 10}, {3, 17}, {2, 2}, {40, 40}}}; + for (const auto& [w, h] : shapes) { + const Ring ring = block(w, h); + const ReduceOutcome out = reduce(ring, 10.0); + INFO(w << " x " << h); + check_guarantees(ring, 10.0, {}, out); + CHECK(out.ring.size() <= 8); + CHECK(out.ring.size() >= 4); + } +} + +TEST_CASE("a keep-point half a cell inside a tab a collapse would cut off stays inside", + "[vector_simplify][area_collapse][keep]") { + // A 200 m square with a tab one cell (10 m) wide and 1.5 cells tall on its + // top side, densified at 10 m so there is plenty to collapse. At a + // tolerance of 5 cells the tab is well within reach of a collapse; the + // keep-point is half a cell inside the tab on every side. + Ring ring; + for (int x = 0; x < 200; x += 10) + ring.push_back({static_cast(x), 0.0}); + for (int y = 0; y < 200; y += 10) + ring.push_back({200.0, static_cast(y)}); + for (int x = 200; x > 110; x -= 10) + ring.push_back({static_cast(x), 200.0}); + ring.push_back({110.0, 200.0}); + ring.push_back({110.0, 215.0}); + ring.push_back({100.0, 215.0}); + ring.push_back({100.0, 200.0}); + for (int x = 90; x > 0; x -= 10) + ring.push_back({static_cast(x), 200.0}); + for (int y = 200; y > 0; y -= 10) + ring.push_back({0.0, static_cast(y)}); + REQUIRE(simple(ring)); + REQUIRE(area(ring) > 0.0); + + const Ring keep{{105.0, 210.0}}; + REQUIRE(strictly_inside(keep[0], ring)); + const ReduceOutcome out = reduce(ring, 50.0, keep); + check_guarantees(ring, 50.0, keep, out); +} + +TEST_CASE("a thin corridor at a tolerance of five cells does not cross itself", + "[vector_simplify][area_collapse][corridor]") { + // Two 100 m squares joined by a corridor 300 m long whose sides are 1.5 + // cells (15 m) apart, each side a zigzag of 2.5 m so every vertex is a + // candidate. A collapse on one side that reached the other would cross. + Ring ring{{0.0, 0.0}, {100.0, 0.0}}; + for (int k = 0; k <= 30; ++k) + ring.push_back({100.0 + 10.0 * k, 42.5 + 2.5 * (k % 2)}); + ring.push_back({400.0, 0.0}); + ring.push_back({500.0, 0.0}); + ring.push_back({500.0, 100.0}); + ring.push_back({400.0, 100.0}); + for (int k = 30; k >= 0; --k) + ring.push_back({100.0 + 10.0 * k, 57.5 - 2.5 * (k % 2)}); + ring.push_back({100.0, 100.0}); + ring.push_back({0.0, 100.0}); + REQUIRE(simple(ring)); + REQUIRE(area(ring) > 0.0); + + const Ring keep{{50.0, 50.0}, {450.0, 50.0}}; + const ReduceOutcome out = reduce(ring, 50.0, keep); + check_guarantees(ring, 50.0, keep, out); + CHECK(out.ring.size() < ring.size()); +} + +TEST_CASE("the same input twice gives equal bits", "[vector_simplify][area_collapse][determinism]") { + std::mt19937 rng{22201}; + for (int trial = 0; trial < 20; ++trial) { + const Ring ring = star(rng, 40); + if (!simple(ring) || !(area(ring) > 0.0)) + continue; + const Ring keep{{5000.0, 7000.0}}; + const ReduceOutcome a = reduce(ring, 20.0, keep); + const ReduceOutcome b = reduce(ring, 20.0, keep); + CHECK(a.status == b.status); + CHECK(same_bits(a.ring, b.ring)); + CHECK(a.counts.collapses == b.counts.collapses); + } + const Ring rect = block(30, 7); + CHECK(same_bits(reduce(rect, 10.0).ring, reduce(rect, 10.0).ring)); +} + +TEST_CASE("random star polygons keep every guarantee", "[vector_simplify][area_collapse][property]") { + std::mt19937 rng{22202}; + std::uniform_int_distribution size(8, 80); + const double tolerances[] = {0.0, 2.0, 10.0, 30.0, 100.0}; + int checked = 0, reduced = 0; + for (int trial = 0; trial < 300; ++trial) { + const Ring ring = star(rng, size(rng)); + if (!simple(ring) || !(area(ring) > 0.0)) + continue; + const Ring keep{{5000.0, 7000.0}}; + if (!strictly_inside(keep[0], ring)) + continue; + const double tolerance = tolerances[trial % 5]; + const ReduceOutcome out = reduce(ring, tolerance, keep); + INFO("trial " << trial << ", " << ring.size() << " vertices, tolerance " << tolerance); + check_guarantees(ring, tolerance, keep, out); + if (tolerance == 0.0) + CHECK(out.counts.collapses == 0); + ++checked; + reduced += out.counts.collapses > 0 ? 1 : 0; + } + // The fixture must exercise the loop, not only the collinear pass. + CHECK(checked > 200); + CHECK(reduced > 100); +} diff --git a/tests/python/test_cli_catchment.py b/tests/python/test_cli_catchment.py index 2b31c738..43e2a9ab 100644 --- a/tests/python/test_cli_catchment.py +++ b/tests/python/test_cli_catchment.py @@ -21,6 +21,16 @@ `vertices` and `km`. - Every refusal is a non-zero exit and no file. +PR 2 (the reduction): the default output is the reduced polygon, whose area is +the fine outline's to 1e-9 relative (the design's area guarantee), so the +PR 1 area checks above compare at 1e-9 (amended in the PR 2 red step; at +1e-12 they held only for the fine ring). `--outline-tolerance 0` writes the +fine ring with only its exactly collinear vertices removed. The properties +gain `reduced_vertices`, `reduced_area_m2` and `outline_tolerance_m` (names +chosen here, beside PR 1's `fine_vertices` and `fine_area_m2`). stderr gains +a line with the word `reduced`, its vertices, its area in km and the +difference in m2. + The Bygdin test runs against Ola's data in ../rasputin_data and is skipped when it is absent: the area must be within 2 % of NVE's 305.54 km^2 (delfelt 1187), as the design's acceptance asks. @@ -35,10 +45,23 @@ import numpy as np import pytest -from shapely.geometry import Point +import shapely +from shapely.geometry import Point, Polygon from typer.testing import CliRunner -from catchment_fixtures import EPSG, D, bowl, filled, full_flood, lake_box, lat, seeds_in, tile_of +from catchment_fixtures import ( + EPSG, + X0, + Y0, + D, + bowl, + filled, + full_flood, + lake_box, + lat, + seeds_in, + tile_of, +) from gpkg_fixtures import DTM10, OLA_NORWAY, Layer, Row, write_gpkg from mosaic_fixtures import quadrants from test_catchment import moved, to_4326 @@ -105,7 +128,7 @@ def refused(tmp_path: Path, *args: str, out: str = "c.geojson") -> str: code, output = invoke("catchment", *args, "--out", str(target)) assert code != 0, output # Refused by the command, not by Typer for want of one. - assert "No such command" not in output, output + assert "No such command" not in output and "No such option" not in output, output assert not target.exists() return output @@ -142,7 +165,7 @@ def test_read_domain_reads_it_back_with_the_fine_area( assert parse_crs(domain.crs) == parse_crs(EPSG) assert domain.polygon.is_valid assert len(domain.polygon.interiors) == 0 - assert domain.polygon.area == pytest.approx(expected_area(), rel=1e-12) + assert domain.polygon.area == pytest.approx(expected_area(), rel=1e-9) def test_mesh_accepts_it_as_a_domain(tmp_path: Path, dem_dir: Path, lakes: Path) -> None: @@ -219,7 +242,7 @@ def test_without_lakes_the_seed_is_a_pour_point(tmp_path: Path, dem_dir: Path) - seed = np.zeros((400, 200), dtype=np.uint8) seed[200, 100] = 1 area = square_area(filled(full_flood(tile, seed))) * D * D - assert read_domain(out).polygon.area == pytest.approx(area, rel=1e-12) + assert read_domain(out).polygon.area == pytest.approx(area, rel=1e-9) # --------------------------------------------------------------------------- @@ -281,3 +304,121 @@ def test_bygdin_is_within_two_percent_of_nve(tmp_path: Path) -> None: # The seed is mid-lake, so the outline contains it. ((x, y),) = reprojector("EPSG:4326", EPSG)([[float(v) for v in BYGDIN_SEED]]) assert domain.polygon.contains(Point(x, y)) + + +# --------------------------------------------------------------------------- +# PR 2: the reduction +# --------------------------------------------------------------------------- + + +def feature(out: Path) -> dict[str, Any]: + (f,) = json.loads(out.read_text())["features"] + return dict(f) + + +def vertices(polygon: Any) -> int: + return len(polygon.exterior.coords) - 1 + + +def fine_corners() -> set[tuple[float, float]]: + """The fine outline's vertices less the exactly collinear ones: the + outer ring round the seed of the traced whole-raster catchment. Every + coordinate is a multiple of 50 m, so the collinearity test is exact.""" + from tin_engine.outline import trace + + tile = tile_of(bowl()) + mask = filled(full_flood(tile, seeds_in(tile, lake_box()))) + rings = [np.asarray(r, dtype=np.float64) for r in trace(mask)] + world = [np.column_stack([X0 + r[:, 1] * D, Y0 - r[:, 0] * D]) for r in rings] + (ring,) = [w for w in world if Polygon(w).contains(Point(SEED))] + if np.array_equal(ring[0], ring[-1]): + ring = ring[:-1] + a, b, c = np.roll(ring, 1, axis=0), ring, np.roll(ring, -1, axis=0) + cross = (b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (b[:, 1] - a[:, 1]) * (c[:, 0] - a[:, 0]) + return {(float(x), float(y)) for x, y in ring[cross != 0]} + + +def test_outline_tolerance_0_writes_the_fine_ring_less_its_collinear_vertices( + tmp_path: Path, dem_dir: Path, lakes: Path +) -> None: + out = tmp_path / "fine.geojson" + run(dem_dir, out, *utm_seed(), "--lakes", str(lakes), "--outline-tolerance", "0") + polygon = read_domain(out).polygon + assert {(float(x), float(y)) for x, y in polygon.exterior.coords} == fine_corners() + assert vertices(polygon) == len(fine_corners()) + assert polygon.area == pytest.approx(expected_area(), rel=1e-12) + + +def test_the_default_reduces_to_twice_the_cell_keeping_the_area( + tmp_path: Path, dem_dir: Path, lakes: Path +) -> None: + fine_out, out = tmp_path / "fine.geojson", tmp_path / "c.geojson" + run(dem_dir, fine_out, *utm_seed(), "--lakes", str(lakes), "--outline-tolerance", "0") + run(dem_dir, out, *utm_seed(), "--lakes", str(lakes)) + fine, reduced = read_domain(fine_out).polygon, read_domain(out).polygon + assert vertices(reduced) < vertices(fine) + assert abs(reduced.area - fine.area) <= 1e-9 * fine.area + assert reduced.is_valid + assert reduced.contains(Point(SEED)) + tolerance = 2 * D + assert shapely.hausdorff_distance(fine.exterior, reduced.exterior, densify=0.05) <= ( + tolerance + D + ) + + props = feature(out)["properties"] + assert props["outline_tolerance_m"] == pytest.approx(tolerance) + assert props["reduced_vertices"] == vertices(reduced) + assert props["fine_vertices"] > props["reduced_vertices"] + assert props["reduced_area_m2"] == pytest.approx(reduced.area, rel=1e-12) + assert props["fine_area_m2"] == pytest.approx(fine.area, rel=1e-9) + + +def test_an_explicit_tolerance_is_kept(tmp_path: Path, dem_dir: Path, lakes: Path) -> None: + fine_out, out = tmp_path / "fine.geojson", tmp_path / "c.geojson" + run(dem_dir, fine_out, *utm_seed(), "--lakes", str(lakes), "--outline-tolerance", "0") + run(dem_dir, out, *utm_seed(), "--lakes", str(lakes), "--outline-tolerance", "50") + fine, reduced = read_domain(fine_out).polygon, read_domain(out).polygon + distance = shapely.distance(shapely.points(np.asarray(fine.exterior.coords)), reduced.exterior) + assert distance.max() <= 50.0 + 1e-6 + assert feature(out)["properties"]["outline_tolerance_m"] == pytest.approx(50.0) + + +def test_stderr_reports_the_reduced_outline(tmp_path: Path, dem_dir: Path, lakes: Path) -> None: + output = run(dem_dir, tmp_path / "c.geojson", *utm_seed(), "--lakes", str(lakes)) + lines = [line for line in output.splitlines() if re.search(r"(?i)\breduced\b", line)] + assert lines, output + line = " ".join(lines) + assert re.search(r"\bvertices\b", line), line + assert re.search(r"km", line), line + assert re.search(r"\d\s*m(2|\u00b2)(?![a-z])", line.replace("km", "")), line + + +@pytest.mark.parametrize("value", ["-1", "nan", "inf"]) +def test_a_bad_outline_tolerance_is_refused( + tmp_path: Path, dem_dir: Path, lakes: Path, value: str +) -> None: + refused( + tmp_path, "--dem", str(dem_dir), *utm_seed(), "--lakes", str(lakes), + "--outline-tolerance", value, + ) # fmt: skip + + +@pytest.mark.skipif( + not (DTM10.is_dir() and OLA_NORWAY.is_file()), reason="Ola's DTM10 and CORINE are not here" +) +def test_bygdin_reduced_keeps_the_area_and_meshes_at_10_m(tmp_path: Path) -> None: + lakes_args = ("--lakes", str(OLA_NORWAY), "--lakes-layer", "corine2018") + fine_out, out = tmp_path / "fine.geojson", tmp_path / "bygdin.geojson" + run(DTM10, fine_out, "--seed", *BYGDIN_SEED, *lakes_args, "--outline-tolerance", "0") + run(DTM10, out, "--seed", *BYGDIN_SEED, *lakes_args) + fine, reduced = read_domain(fine_out).polygon, read_domain(out).polygon + assert abs(reduced.area - fine.area) <= 1e-9 * fine.area + # PR 1 measured 17,812 fine vertices; the design's gauge is about 900. + assert vertices(reduced) <= 17_812 // 5, vertices(reduced) + assert feature(out)["properties"]["outline_tolerance_m"] == pytest.approx(20.0) + vtk = tmp_path / "bygdin.vtk" + code, output = invoke( + "mesh", "--dem", str(DTM10), "--domain", str(out), "--tolerance", "10", "--out", str(vtk) + ) + assert code == 0, output + assert vtk.is_file() and vtk.stat().st_size > 0 diff --git a/tests/python/test_core_reduce.py b/tests/python/test_core_reduce.py new file mode 100644 index 00000000..3c18a5d4 --- /dev/null +++ b/tests/python/test_core_reduce.py @@ -0,0 +1,179 @@ +"""`_core.reduce_ring`, the outline reduction through the binding (increment 22, PR 2). + +`docs/increments/22-auto-catchment.md`, "The reduction" and "The red suites" +(PR 2, `test_core_reduce.py`): on rings traced from random blobs and from the +PR 1 synthetic catchments, the five guarantees and determinism. + +Interface assumed (names chosen here, stated in the handback; they mirror the +C++ `ReduceOutcome` in `tests/cpp/unit/test_area_collapse.cpp`): + +- `_core.reduce_ring(ring, tolerance, keep) -> ReduceOutcome`. `ring` is an + `(N, 2)` float64 array-like of `(x, y)`, open (the first vertex not + repeated), counter-clockwise; `keep` an `(K, 2)` array-like of points that + must stay strictly inside. Any other shape is a `ValueError`. Releases the + GIL. +- `ReduceOutcome.ring`: an `(M, 2)` float64 array, open. `.status`: a + `_core.ReduceStatus` (`Ok`, `InvalidTolerance`, `NotCounterClockwise`, + `TooFewVertices`). Counts: `collinear`, `collapses`, `rejected_crossing`, + `rejected_seed`, `rejected_tolerance`. + +The guarantees, as the design words its tests: area `|A_r - A_f| <= 1e-9 +A_f`; simple (shapely `is_valid`, `is_simple`); the seed strictly inside +(shapely `contains`); every fine vertex within the tolerance of the reduced +ring and every reduced vertex within it of the fine ring; the Hausdorff +distance (`densify=0.05`) at most the tolerance plus one cell. +""" + +from __future__ import annotations + +from typing import Any + +import numpy as np +import pytest +import shapely +from shapely.geometry import LinearRing, Point, Polygon + +from catchment_fixtures import EPSG, MemoryRepository, bowl, lake_box, lat, tile_of +from mosaic_fixtures import quadrants +from test_catchment import plus_with_a_hole + +CELL = 10.0 + + +@pytest.fixture(scope="module") +def core() -> Any: + import tin_engine._core as core + + assert hasattr(core, "reduce_ring"), "tin_engine._core has no reduce_ring" + return core + + +def open_ring(coords: Any) -> np.ndarray: + a = np.asarray(coords, dtype=np.float64) + return a[:-1] if len(a) > 1 and np.array_equal(a[0], a[-1]) else a + + +def check( + core: Any, fine: np.ndarray, tolerance: float, keep: np.ndarray, cell: float = CELL +) -> np.ndarray: + """Reduce `fine` and check every guarantee; the reduced ring.""" + out = core.reduce_ring(fine, tolerance, keep) + assert out.status == core.ReduceStatus.Ok + red = np.asarray(out.ring, dtype=np.float64) + assert red.ndim == 2 and red.shape[1] == 2 + assert np.isfinite(red).all() + assert len(fine) - out.collinear - out.collapses == len(red) + f, r = Polygon(fine), Polygon(red) + assert r.exterior.is_ccw + assert abs(r.area - f.area) <= 1e-9 * f.area, (r.area, f.area) + assert LinearRing(red).is_simple and r.is_valid + for k in keep: + assert r.contains(Point(k)) + slack = 1e-9 * max(1.0, tolerance) + assert shapely.distance(shapely.points(fine), r.exterior).max() <= tolerance + slack + assert shapely.distance(shapely.points(red), f.exterior).max() <= tolerance + slack + assert shapely.hausdorff_distance(f.exterior, r.exterior, densify=0.05) <= tolerance + cell + # Deterministic: the same input again gives the same bits. + again = core.reduce_ring(fine, tolerance, keep) + assert np.asarray(again.ring).tobytes() == np.asarray(out.ring).tobytes() + return red + + +def blob_ring(seed: int) -> tuple[np.ndarray, np.ndarray]: + """The outer ring round a random blob of 10 m nodes (a union of disks), + in metres from its window's lower-left corner, and one in-node inside it.""" + from tin_engine.outline import trace + + rng = np.random.default_rng(22_300 + seed) + n = 48 + r, c = np.indices((n, n)) + mask = np.zeros((n, n), dtype=bool) + for _ in range(int(rng.integers(3, 9))): + cr, cc = rng.integers(12, 36, size=2) + mask |= np.hypot(r - cr, c - cc) <= rng.uniform(2.0, 10.0) + mask[24, 24] = True + rings = [open_ring(ring) for ring in trace(mask)] + world = [np.column_stack([ring[:, 1] * CELL, (n - 1 - ring[:, 0]) * CELL]) for ring in rings] + outer = max(world, key=lambda w: Polygon(w).area if LinearRing(w).is_ccw else -1.0) + keep = np.array([[24 * CELL, (n - 1 - 24) * CELL]]) + if not Polygon(outer).contains(Point(keep[0])): + keep = np.empty((0, 2)) + return outer, keep + + +@pytest.mark.parametrize("tolerance", [0.0, 5.0, 10.0, 20.0, 50.0]) +@pytest.mark.parametrize("seed", range(12)) +def test_random_blobs_keep_every_guarantee(core: Any, seed: int, tolerance: float) -> None: + fine, keep = blob_ring(seed) + red = check(core, fine, tolerance, keep) + if tolerance == 0.0: + # Only the collinear pass: every reduced vertex is a fine vertex. + assert {tuple(p) for p in red.tolist()} <= {tuple(p) for p in fine.tolist()} + + +def test_blobs_are_reduced_substantially_at_two_cells(core: Any) -> None: + kept = [] + for seed in range(12): + fine, keep = blob_ring(seed) + zero = core.reduce_ring(fine, 0.0, keep) + two = core.reduce_ring(fine, 2 * CELL, keep) + kept.append(len(two.ring) / len(zero.ring)) + # A reducer that stopped after the collinear pass would keep all of them. + assert np.median(kept) < 0.8 + + +def catchment_ring(fine: Polygon) -> tuple[np.ndarray, np.ndarray]: + """A PR 1 fine outline shifted to its lower-left corner, as the design has + Python do before the reduction.""" + x0, y0 = fine.bounds[0], fine.bounds[1] + return open_ring(np.asarray(fine.exterior.coords)) - (x0, y0), np.array([x0, y0]) + + +@pytest.mark.parametrize("tolerance", [0.0, 100.0, 200.0, 500.0]) +def test_the_pr1_bowl_catchment_keeps_every_guarantee(core: Any, tolerance: float) -> None: + from tin_engine.catchment import CatchmentRequest, delineate + + z = bowl() + result = delineate( + CatchmentRequest(seed=lat(100, 200), seed_crs=EPSG, lakes=(lake_box(),), lakes_crs=EPSG), + MemoryRepository(quadrants(tile_of(z), row_cut=200, col_cut=100, overlap=1)), + ) + fine, origin = catchment_ring(result.fine) + seed = np.array([lat(100, 200)]) - origin + check(core, fine, tolerance, seed, cell=100.0) + + +def test_the_pr1_plus_catchment_keeps_every_guarantee(core: Any) -> None: + from tin_engine.catchment import CatchmentRequest, delineate + + z, lake = plus_with_a_hole() + result = delineate( + CatchmentRequest(seed=lat(35, 34), seed_crs=EPSG, lakes=(lake,), lakes_crs=EPSG), + MemoryRepository({"t.tif": tile_of(z)}), + ) + fine, origin = catchment_ring(result.fine) + check(core, fine, 200.0, np.array([lat(35, 34)]) - origin, cell=100.0) + + +def test_the_statuses_cross_the_binding(core: Any) -> None: + square = np.array([[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]]) + none = np.empty((0, 2)) + status = core.ReduceStatus + assert core.reduce_ring(square, 1.0, none).status == status.Ok + assert core.reduce_ring(square, -1.0, none).status == status.InvalidTolerance + assert core.reduce_ring(square, float("nan"), none).status == status.InvalidTolerance + assert core.reduce_ring(square, float("inf"), none).status == status.InvalidTolerance + assert core.reduce_ring(square[::-1].copy(), 1.0, none).status == status.NotCounterClockwise + assert core.reduce_ring(square[:3], 1.0, none).status == status.TooFewVertices + + +@pytest.mark.parametrize("shape", [(4,), (4, 3), (2, 4, 2)]) +def test_a_ring_of_another_shape_is_a_value_error(core: Any, shape: tuple[int, ...]) -> None: + with pytest.raises(ValueError, match=r"(?i)shape|\(N, ?2\)"): + core.reduce_ring(np.zeros(shape), 1.0, np.empty((0, 2))) + + +def test_a_keep_of_another_shape_is_a_value_error(core: Any) -> None: + square = np.array([[0.0, 0.0], [10.0, 0.0], [10.0, 10.0], [0.0, 10.0]]) + with pytest.raises(ValueError, match=r"(?i)shape|\(K, ?2\)|\(N, ?2\)"): + core.reduce_ring(square, 1.0, np.zeros((3,))) From ac778e434b4b75cdaf398d0eeeb6ee3b0ab9a898 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:13:42 +0200 Subject: [PATCH 02/16] green: 22 PR2 the outline reduction (area-preserving segment collapse) Makes the PR 2 red suite (039cf3c) pass, per docs/increments/22-auto-catchment.md, "The reduction" and "The command". No test file changed except removing the red-step if(EXISTS vector_simplify/area_collapse.hpp) guard. - include/terrain/vector_simplify/area_collapse.hpp: reduce_ring. Collinear pass, then APSC-style collapses (E on line A-B or C-D, else the foot of B-C's midpoint) ordered by (deviation, id). Each edge carries a fine-vertex range; a collapse splits its range between A-E and E-D at the split that minimises the larger distance. Crossing tests use classify over a grid of current edges; keep-points use an exact winding round A-B-C-D-E. Stale heap entries are dropped by a per-node version. - bindings/core.cpp, _core.pyi: reduce_ring, ReduceOutcome, ReduceStatus. - catchment.py: CatchmentRequest.outline_tolerance (None: twice the cell); the fine ring is reduced from its lower-left corner, seed kept inside. - cli.py: --outline-tolerance; writes the reduced polygon, with the properties reduced_vertices, reduced_area_m2 and outline_tolerance_m and a "reduced outline" stderr line. Bygdin at 20 m: 17,812 -> 740 vertices, area 304.909550 km2 both, difference -3e-7 m2. mesh --domain at --tolerance 10: 52,198 triangles in 0.7 s (the fine outline gives 94,374); at --tolerance 1: 1,129,026 triangles in 3.2 s. Co-Authored-By: Claude Opus 5.5 --- bindings/core.cpp | 59 +++ .../terrain/vector_simplify/area_collapse.hpp | 341 ++++++++++++++++++ src_python/tin_engine/_core.pyi | 31 ++ src_python/tin_engine/catchment.py | 35 +- src_python/tin_engine/cli.py | 28 +- tests/cpp/CMakeLists.txt | 18 +- 6 files changed, 491 insertions(+), 21 deletions(-) create mode 100644 include/terrain/vector_simplify/area_collapse.hpp diff --git a/bindings/core.cpp b/bindings/core.cpp index 8e5b51d9..b0661c0c 100644 --- a/bindings/core.cpp +++ b/bindings/core.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -299,6 +300,17 @@ struct BoundUpstream { std::size_t cols; }; +// An (N, 2) float64 array as Point2 pairs, copied; any other shape is a ValueError. +[[nodiscard]] std::vector xy_points(const py::object& a, const char* what) { + const auto xy = py::array_t::ensure(a); + if (!xy || xy.ndim() != 2 || xy.shape(1) != 2) + throw py::value_error(std::format("reduce_ring: {} must have shape (N, 2)", what)); + std::vector out(static_cast(xy.shape(0))); + for (std::size_t i = 0; i < out.size(); ++i) + out[i] = Point2{xy.at(static_cast(i), 0), xy.at(static_cast(i), 1)}; + return out; +} + } // namespace PYBIND11_MODULE(_core, m) { @@ -1004,5 +1016,52 @@ The catchment of a seed mask: every node draining into a seed (Priority-Flood). seed is (rows, cols), non-zero (or True) for a seed; any other shape is a ValueError. NoData is never in. Releases the GIL. +)doc"); + + using terrain::vector_simplify::ReduceOutcome; + using terrain::vector_simplify::ReduceStatus; + py::enum_(m, "ReduceStatus", R"doc( +Why reduce_ring reduced a ring or did not: Ok, or a refusal of the input. +)doc") + .value("Ok", ReduceStatus::Ok) + .value("InvalidTolerance", ReduceStatus::InvalidTolerance) + .value("NotCounterClockwise", ReduceStatus::NotCounterClockwise) + .value("TooFewVertices", ReduceStatus::TooFewVertices); + + py::class_(m, "ReduceOutcome", R"doc( +What reduce_ring returned: the reduced ring, open, a status, and the counts. +)doc") + .def_property_readonly( + "ring", + [](const py::object& self) { + return point_view(self, self.cast().ring); + }, + "Read-only (M, 2) float64, open: the first vertex is not repeated.") + .def_readonly("status", &ReduceOutcome::status) + .def_property_readonly("collinear", [](const ReduceOutcome& o) { return o.counts.collinear; }) + .def_property_readonly("collapses", [](const ReduceOutcome& o) { return o.counts.collapses; }) + .def_property_readonly("rejected_crossing", + [](const ReduceOutcome& o) { return o.counts.rejected_crossing; }) + .def_property_readonly("rejected_seed", + [](const ReduceOutcome& o) { return o.counts.rejected_seed; }) + .def_property_readonly("rejected_tolerance", + [](const ReduceOutcome& o) { return o.counts.rejected_tolerance; }); + + m.def( + "reduce_ring", + [](const py::object& ring, double tolerance, const py::object& keep) { + const std::vector points = xy_points(ring, "ring"); + const std::vector kept = xy_points(keep, "keep"); + const py::gil_scoped_release unlocked; + return terrain::vector_simplify::reduce_ring( + points, tolerance, kept); + }, + py::arg("ring"), py::arg("tolerance"), py::arg("keep"), R"doc( +Reduce an open counter-clockwise ring to a tolerance, keeping its area and the +keep-points inside (area-preserving segment collapse, exact crossing tests). + +ring is (N, 2) and keep (K, 2), float64-convertible; any other shape is a +ValueError. Tolerance 0 only drops exactly collinear vertices. A refused input +comes back as a status. Releases the GIL. )doc"); } diff --git a/include/terrain/vector_simplify/area_collapse.hpp b/include/terrain/vector_simplify/area_collapse.hpp new file mode 100644 index 00000000..f4d51d95 --- /dev/null +++ b/include/terrain/vector_simplify/area_collapse.hpp @@ -0,0 +1,341 @@ +#pragma once + +// Area-preserving segment collapse of a simple ring to a horizontal tolerance +// (docs/increments/22-auto-catchment.md, "The reduction"; after Kronenfeld, +// Stanislawski, Buttenfield and Brockmeyer 2020, APSC). Each collapse replaces +// the two vertices B, C of a chain A-B-C-D by one point E on the line where +// A-E-D encloses the area A-B-C-D did, so the ring's area is kept up to +// rounding. A collapse is made only if every fine vertex it stands for stays +// within the tolerance of the new edges, E within it of the fine ring, the +// ring stays simple (exact kernel) and every keep-point stays inside. +// +// Locality: every collapse reads four vertices, their fine ranges and a grid +// query; the heap is global and the loop serial, ordered by (deviation, id), +// so the output depends on nothing but the input. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace terrain::vector_simplify { + +enum class ReduceStatus : std::uint8_t { Ok, InvalidTolerance, NotCounterClockwise, TooFewVertices }; + +struct ReduceCounts { + std::size_t collinear{}; // vertices dropped by the collinear pass + std::size_t collapses{}; // B, C -> E replacements made + std::size_t rejected_crossing{}, rejected_seed{}, rejected_tolerance{}; +}; + +struct ReduceOutcome { + std::vector ring; // open: the first vertex not repeated + ReduceStatus status{ReduceStatus::Ok}; + ReduceCounts counts{}; +}; + +namespace detail { + +[[nodiscard]] inline double segment_distance(const Point2& p, const Point2& a, const Point2& b) { + const Point2 d = b - a; + const double len2 = dot(d, d); + const double t = len2 > 0.0 ? std::clamp(dot(p - a, d) / len2, 0.0, 1.0) : 0.0; + const Point2 q = a + t * d; + return std::hypot(p.x - q.x, p.y - q.y); +} + +// The current ring: a doubly linked list over nodes. Edge u -> next[u] stands +// for `count[u]` fine vertices starting at fine index `start[u]`. +struct Ring { + std::vector p; + std::vector prev, next, start, count, id; + std::vector version; + std::vector alive; + + std::size_t add(Point2 q, std::size_t ident) { + p.push_back(q); + prev.push_back(0); + next.push_back(0); + start.push_back(0); + count.push_back(0); + id.push_back(ident); + version.push_back(0); + alive.push_back(true); + return p.size() - 1; + } +}; + +// Current edges by grid bucket, keyed by their start node. Entries are never +// removed: a stale one is filtered by the caller against the live ring. +class EdgeGrid { +public: + EdgeGrid(double side, Point2 origin) : side_{side}, origin_{origin} {} + + void insert(std::size_t u, const Point2& a, const Point2& b) { + each_cell(a, b, a, [&](std::uint64_t key) { cells_[key].push_back(u); }); + } + + template + void query(const Point2& a, const Point2& b, const Point2& c, F&& f) const { + each_cell(a, b, c, [&](std::uint64_t key) { + if (const auto it = cells_.find(key); it != cells_.end()) + for (const std::size_t u : it->second) + f(u); + }); + } + +private: + template + void each_cell(const Point2& a, const Point2& b, const Point2& c, F&& f) const { + const auto cell = [&](double v, double o) { + return static_cast(std::floor((v - o) / side_)); + }; + const auto x0 = cell(std::min({a.x, b.x, c.x}), origin_.x); + const auto x1 = cell(std::max({a.x, b.x, c.x}), origin_.x); + const auto y0 = cell(std::min({a.y, b.y, c.y}), origin_.y); + const auto y1 = cell(std::max({a.y, b.y, c.y}), origin_.y); + for (auto ix = x0; ix <= x1; ++ix) + for (auto iy = y0; iy <= y1; ++iy) + f((static_cast(ix) << 32) ^ static_cast(iy & 0xffffffff)); + } + + double side_; + Point2 origin_; + std::unordered_map> cells_; +}; + +struct Candidate { + Point2 e; + std::size_t split; // fine vertices [0, split) of the range go to A-E, the rest to E-D + double deviation; +}; + +// The deviation of placing E for the chain whose range is `fine` from `s`, +// `n` vertices long: the best split of the range between A-E and E-D, and E's +// distance to the fine segments in the range. +[[nodiscard]] inline Candidate deviation_of(std::span fine, std::size_t s, + std::size_t n, const Point2& a, const Point2& e, + const Point2& d) { + const std::size_t size = fine.size(); + const auto at = [&](std::size_t k) -> const Point2& { return fine[(s + k) % size]; }; + std::vector suffix(n + 1, 0.0); + for (std::size_t k = n; k-- > 0;) + suffix[k] = std::max(suffix[k + 1], segment_distance(at(k), e, d)); + Candidate best{e, 0, suffix[0]}; + double prefix = 0.0; + for (std::size_t m = 1; m <= n; ++m) { + prefix = std::max(prefix, segment_distance(at(m - 1), a, e)); + if (const double v = std::max(prefix, suffix[m]); v < best.deviation) + best = Candidate{e, m, v}; + } + double to_fine = std::numeric_limits::infinity(); + for (std::size_t k = 0; k < std::max(n, 1); ++k) + to_fine = std::min(to_fine, segment_distance(e, at(k), at(k + 1))); + best.deviation = std::max(best.deviation, to_fine); + return best; +} + +} // namespace detail + +template +[[nodiscard]] ReduceOutcome reduce_ring(std::span fine, double tolerance, + std::span keep) { + using pred::Orientation; + ReduceOutcome out; + if (!std::isfinite(tolerance) || tolerance < 0.0) { + out.status = ReduceStatus::InvalidTolerance; + return out; + } + const std::size_t n = fine.size(); + if (n < 4) { + out.status = ReduceStatus::TooFewVertices; + return out; + } + double twice = 0.0; + for (std::size_t i = 0; i < n; ++i) + twice += cross(fine[i] - fine[0], fine[(i + 1) % n] - fine[0]); + if (!(twice > 0.0)) { + out.status = ReduceStatus::NotCounterClockwise; + return out; + } + + // 1. The collinear pass: drop every vertex exactly between its neighbours. + std::vector kept; + for (std::size_t i = 0; i < n; ++i) { + const Point2& a = fine[(i + n - 1) % n]; + const Point2& b = fine[(i + 1) % n]; + const bool between = std::min(a.x, b.x) <= fine[i].x && fine[i].x <= std::max(a.x, b.x) + && std::min(a.y, b.y) <= fine[i].y && fine[i].y <= std::max(a.y, b.y); + if (K::orient2d(a, fine[i], b) != Orientation::Collinear || !between) + kept.push_back(i); + } + out.counts.collinear = n - kept.size(); + detail::Ring ring; + for (std::size_t k = 0; k < kept.size(); ++k) { + const std::size_t u = ring.add(fine[kept[k]], kept[k]); + ring.start[u] = kept[k]; + ring.count[u] = (kept[(k + 1) % kept.size()] + n - kept[k]) % n; + } + const std::size_t m0 = kept.size(); + for (std::size_t u = 0; u < m0; ++u) { + ring.next[u] = (u + 1) % m0; + ring.prev[u] = (u + m0 - 1) % m0; + } + std::size_t size = m0, next_id = n; + const auto emit = [&] { + std::size_t u = 0; + while (!ring.alive[u]) + ++u; + for (std::size_t k = 0; k < size; ++k, u = ring.next[u]) + out.ring.push_back(ring.p[u]); + return out; + }; + if (tolerance == 0.0 || size <= 4) + return emit(); + + // The grid of current edges: bucket side the tolerance, at least the mean + // fine edge, so a query meets few buckets. + double perimeter = 0.0; + Point2 low = fine[0]; + for (std::size_t i = 0; i < n; ++i) { + const Point2 d = fine[(i + 1) % n] - fine[i]; + perimeter += std::hypot(d.x, d.y); + low = Point2{std::min(low.x, fine[i].x), std::min(low.y, fine[i].y)}; + } + detail::EdgeGrid grid{std::max(tolerance, perimeter / static_cast(n)), low}; + for (std::size_t u = 0; u < m0; ++u) + grid.insert(u, ring.p[u], ring.p[ring.next[u]]); + + // 2.-4. Candidates: E on line A-B or on line C-D (ties to A-B), or the foot + // of B-C's midpoint when both are parallel to A-D. + const auto candidate = [&](std::size_t b) -> std::optional { + const std::size_t a = ring.prev[b], c = ring.next[b], d = ring.next[c]; + const Point2 A = ring.p[a], vb = ring.p[b] - A, vc = ring.p[c] - A, vd = ring.p[d] - A; + const double area = cross(vb, vc) + cross(vc, vd); // A-E-D must enclose this + const std::size_t s = ring.start[a], len = ring.count[a] + ring.count[b] + ring.count[c]; + std::optional best; + const auto consider = [&](Point2 e) { + if (!std::isfinite(e.x) || !std::isfinite(e.y)) + return; + const detail::Candidate cand = detail::deviation_of(fine, s, len, A, A + e, ring.p[d]); + if (!best || cand.deviation < best->deviation) + best = cand; + }; + if (const double bd = cross(vb, vd); bd != 0.0) + consider(vb * (area / bd)); + if (const double cd = cross(vc, vd); cd != 0.0) + consider(vc + (vd - vc) * (1.0 - area / cd)); + if (!best && dot(vd, vd) > 0.0) { + const Point2 mid = (vb + vc) * 0.5; + consider(mid + Point2{-vd.y, vd.x} * ((cross(mid, vd) - area) / dot(vd, vd))); + } + return best; + }; + + using Entry = std::tuple; // dev, id, node, version + std::priority_queue, std::greater<>> heap; + std::vector> pending(m0); + const auto evaluate = [&](std::size_t b) { + ++ring.version[b]; + pending[b] = candidate(b); + if (pending[b] && pending[b]->deviation <= tolerance) + heap.emplace(pending[b]->deviation, ring.id[b], b, ring.version[b]); + else if (pending[b]) + ++out.counts.rejected_tolerance; + }; + for (std::size_t u = 0; u < m0; ++u) + evaluate(u); + + std::vector stamp(m0, 0); + std::size_t round = 0; + while (!heap.empty() && size > 4) { + const auto [dev, ident, b, version] = heap.top(); + heap.pop(); + if (!ring.alive[b] || ring.version[b] != version) + continue; + const std::size_t a = ring.prev[b], c = ring.next[b], d = ring.next[c]; + const detail::Candidate cand = *pending[b]; + const Point2 A = ring.p[a], E = cand.e, D = ring.p[d]; + + // 5. Simple: A-E and E-D against every current edge near them. + const Segment2 ae{A, E}, ed{E, D}; + bool crosses = noding::classify(ae, ed) != noding::SegmentRelation::Touching; + ++round; + grid.query(A, E, D, [&](std::size_t u) { + if (crosses || !ring.alive[u] || stamp[u] == round || u == a || u == b || u == c) + return; + stamp[u] = round; + const std::size_t v = ring.next[u]; + const Segment2 edge{ring.p[u], ring.p[v]}; + const auto want = [&](bool shared) { + return shared ? noding::SegmentRelation::Touching : noding::SegmentRelation::Disjoint; + }; + crosses = noding::classify(ae, edge) != want(v == a) + || noding::classify(ed, edge) != want(u == d); + }); + if (crosses) { + ++out.counts.rejected_crossing; + continue; + } + // Keep-points: winding zero round A-B-C-D-E-A, and on neither new edge. + const std::array loop{A, ring.p[b], ring.p[c], D, E}; + bool lost = false; + for (const Point2& k : keep) { + int winding = 0; + for (std::size_t i = 0; i < 5; ++i) { + const Point2& p = loop[i]; + const Point2& q = loop[(i + 1) % 5]; + const Orientation o = K::orient2d(p, q, k); + winding += p.y <= k.y && q.y > k.y && o == Orientation::CounterClockwise; + winding -= p.y > k.y && q.y <= k.y && o == Orientation::Clockwise; + } + const Segment2 at{k, k}; + lost = lost || winding != 0 + || noding::classify(at, ae) != noding::SegmentRelation::Disjoint + || noding::classify(at, ed) != noding::SegmentRelation::Disjoint; + } + if (lost) { + ++out.counts.rejected_seed; + continue; + } + + // 6. Apply, then re-evaluate the candidates whose four vertices changed. + const std::size_t len = ring.count[a] + ring.count[b] + ring.count[c]; + const std::size_t e = ring.add(E, next_id++); + pending.emplace_back(); + stamp.push_back(0); + ring.alive[b] = ring.alive[c] = false; + ring.next[a] = e; + ring.prev[e] = a; + ring.next[e] = d; + ring.prev[d] = e; + ring.count[a] = cand.split; + ring.start[e] = (ring.start[a] + cand.split) % n; + ring.count[e] = len - cand.split; + grid.insert(a, A, E); + grid.insert(e, E, D); + --size; + ++out.counts.collapses; + if (size > 4) + for (const std::size_t u : {ring.prev[a], a, e, d}) + evaluate(u); + } + return emit(); +} + +} // namespace terrain::vector_simplify diff --git a/src_python/tin_engine/_core.pyi b/src_python/tin_engine/_core.pyi index d2663579..9635cbb0 100644 --- a/src_python/tin_engine/_core.pyi +++ b/src_python/tin_engine/_core.pyi @@ -471,3 +471,34 @@ class UpstreamOutcome: def upstream(view: RasterView, seed: npt.ArrayLike) -> UpstreamOutcome: """Every node draining into a seed of the ``(rows, cols)`` mask (Priority-Flood); another shape is a ``ValueError``. Releases the GIL.""" + +class ReduceStatus(Enum): + """Why :func:`reduce_ring` refused, or ``Ok``.""" + + Ok = 0 + InvalidTolerance = 1 + NotCounterClockwise = 2 + TooFewVertices = 3 + +@final +class ReduceOutcome: + """What :func:`reduce_ring` returned: the ring, open, a status and counts.""" + + @property + def ring(self) -> npt.NDArray[np.float64]: ... + @property + def status(self) -> ReduceStatus: ... + @property + def collinear(self) -> int: ... + @property + def collapses(self) -> int: ... + @property + def rejected_crossing(self) -> int: ... + @property + def rejected_seed(self) -> int: ... + @property + def rejected_tolerance(self) -> int: ... + +def reduce_ring(ring: npt.ArrayLike, tolerance: float, keep: npt.ArrayLike) -> ReduceOutcome: + """Reduce an open counter-clockwise ``(N, 2)`` ring to ``tolerance``, keeping + its area and the ``(K, 2)`` keep-points inside. Releases the GIL.""" diff --git a/src_python/tin_engine/catchment.py b/src_python/tin_engine/catchment.py index deada36d..e9db57d2 100644 --- a/src_python/tin_engine/catchment.py +++ b/src_python/tin_engine/catchment.py @@ -16,7 +16,7 @@ import math import time -from dataclasses import dataclass +from dataclasses import dataclass, replace from typing import Any, Self import numpy as np @@ -25,7 +25,7 @@ from pydantic import BaseModel, ConfigDict, model_validator from shapely.geometry import Point, Polygon -from tin_engine._core import UpstreamOutcome, upstream +from tin_engine._core import ReduceStatus, UpstreamOutcome, reduce_ring, upstream from tin_engine.crs import crs_label, reprojector from tin_engine.io.models import RasterMeta from tin_engine.io.repository import DemRepository @@ -61,11 +61,17 @@ class CatchmentRequest(BaseModel): seed_crs: str = "EPSG:4326" lakes: tuple[Any, ...] | None = None lakes_crs: str | None = None + #: Metres the reduced outline may stray from the fine one; None is twice + #: the DEM's cell, 0 keeps the fine outline less its collinear vertices. + outline_tolerance: float | None = None @model_validator(mode="after") def _lakes_have_a_crs(self) -> Self: if self.lakes is not None and self.lakes_crs is None: raise ValueError("lakes need their CRS") + t = self.outline_tolerance + if t is not None and not (math.isfinite(t) and t >= 0.0): + raise ValueError(f"the outline tolerance must be finite and at least 0, got {t}") return self @@ -103,6 +109,9 @@ class Catchment: mask: npt.NDArray[np.uint8] meta: RasterMeta trace_seconds: float + reduced: Polygon + tolerance: float + reduce_seconds: float def delineate(request: CatchmentRequest, repository: DemRepository) -> Catchment: @@ -154,7 +163,9 @@ def delineate(request: CatchmentRequest, repository: DemRepository) -> Catchment if out.touches_edge: sides = _edge_sides(m, out) raise CatchmentError(f"the catchment is cut by the data's {' and '.join(sides)} edge") - return _outline(dem_crs, lake, (float(x), float(y)), out, m, seed, tuple(windows)) + fine = _outline(dem_crs, lake, (float(x), float(y)), out, m, seed, tuple(windows)) + tolerance = request.outline_tolerance + return _reduced(fine, 2.0 * max(m.delta_x, m.delta_y) if tolerance is None else tolerance) def _lake(request: CatchmentRequest, dem_crs: str) -> Polygon | None: @@ -307,6 +318,24 @@ def _outline( mask=mask, meta=m, trace_seconds=time.perf_counter() - t0, + reduced=fine, + tolerance=0.0, + reduce_seconds=0.0, + ) + + +def _reduced(fine: Catchment, tolerance: float) -> Catchment: + """The fine outline reduced to `tolerance` by `_core.reduce_ring`, in + metres from its lower-left corner, the seed point kept inside.""" + origin = np.array(fine.fine.bounds[:2]) + ring = np.asarray(fine.fine.exterior.coords)[:-1] - origin + t0 = time.perf_counter() + out = reduce_ring(ring, tolerance, np.array([fine.seed]) - origin) + if out.status != ReduceStatus.Ok: + raise CatchmentError(f"the outline could not be reduced: {out.status.name}") + reduced = Polygon(np.asarray(out.ring) + origin) + return replace( + fine, reduced=reduced, tolerance=tolerance, reduce_seconds=time.perf_counter() - t0 ) diff --git a/src_python/tin_engine/cli.py b/src_python/tin_engine/cli.py index 352f007d..860cbc41 100644 --- a/src_python/tin_engine/cli.py +++ b/src_python/tin_engine/cli.py @@ -1316,6 +1316,15 @@ def catchment( lakes_layer: Annotated[ str | None, typer.Option("--lakes-layer", help="The GeoPackage's features table.") ] = None, + outline_tolerance: Annotated[ + float | None, + typer.Option( + "--outline-tolerance", + metavar="METRES", + help="Reduce the outline, keeping its area, to within this of the fine one; " + "0 writes the fine outline. Default: twice the DEM's cell.", + ), + ] = None, out_parent: Annotated[ Path | None, typer.Option("--out-parent", help="Refuse any output path resolving outside this."), @@ -1327,6 +1336,10 @@ def catchment( data's edge or by NoData is refused, and nothing is written.""" if lakes is None and lakes_layer is not None: raise typer.BadParameter("applies only with --lakes", param_hint="--lakes-layer") + if outline_tolerance is not None and not ( + math.isfinite(outline_tolerance) and outline_tolerance >= 0.0 + ): + raise typer.BadParameter("must be finite and at least 0", param_hint="--outline-tolerance") if out.suffix.lower() not in CATCHMENT_SUFFIXES: raise typer.BadParameter(f"use {' or '.join(CATCHMENT_SUFFIXES)}", param_hint="--out") target = _destination(out, out_parent, out.stem) @@ -1341,6 +1354,7 @@ def catchment( seed_crs=seed_crs, lakes=None if found is None else found[0], lakes_crs=None if found is None else found[1], + outline_tolerance=outline_tolerance, ) result = delineate(request, repository) except OSError as exc: @@ -1378,12 +1392,24 @@ def catchment( f"traced in {result.trace_seconds:.2f} s", err=True, ) + reduced = result.reduced + kept = len(reduced.exterior.coords) - 1 + change = reduced.area - result.fine_area + typer.echo( + f"reduced outline: {kept} vertices, {reduced.area / 1e6:.6f} km2, difference " + f"{change:.3g} m2 ({change / result.fine_area:.2g} relative), tolerance " + f"{result.tolerance:g} m, {result.reduce_seconds:.2f} s", + err=True, + ) properties = { "seed": list(seed), "seed_crs": seed_crs, "nodes": result.nodes, "fine_vertices": vertices, "fine_area_m2": result.fine_area, + "reduced_vertices": kept, + "reduced_area_m2": reduced.area, + "outline_tolerance_m": result.tolerance, "windows": [[w.rows, w.cols] for w in result.windows], } doc = { @@ -1395,7 +1421,7 @@ def catchment( "properties": properties, "geometry": { "type": "Polygon", - "coordinates": [[list(xy) for xy in result.fine.exterior.coords]], + "coordinates": [[list(xy) for xy in reduced.exterior.coords]], }, } ], diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index e4f799aa..8b7415d3 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -322,20 +322,4 @@ add_terrain_test(test_hydrology_upstream unit/test_hydrology_upstream.cpp) # "The reduction" and "The red suites"): area-preserving segment collapse with # the exact kernel. A backend test because it names DefaultKernel. Not named # invariant-critical; it starts no threads. -# -# RED UNTIL vector_simplify/area_collapse.hpp EXISTS (increment 12's scaffold, -# 39dba00): while the header is absent the target is EXCLUDE_FROM_ALL and not -# registered with ctest, so `cmake --build build` and `ctest` stay green; see -# the red with `cmake --build build --target test_area_collapse`. Once the -# header lands, re-running `cmake -S . -B build` registers the suite. -# REMOVE AT GREEN: replace the if/else with the plain add_terrain_backend_test line. -if(EXISTS "${PROJECT_SOURCE_DIR}/include/terrain/vector_simplify/area_collapse.hpp") - add_terrain_backend_test(test_area_collapse unit/test_area_collapse.cpp) -else() - message(STATUS "test_area_collapse: include/terrain/vector_simplify/area_collapse.hpp " - "absent; suite built only on request (increment 22 PR 2 red step)") - add_executable(test_area_collapse EXCLUDE_FROM_ALL unit/test_area_collapse.cpp) - target_link_libraries(test_area_collapse PRIVATE - terrain_headers terrain_test_support Catch2::Catch2WithMain terrain_predicates) - target_compile_options(test_area_collapse PRIVATE -Wall -Wextra -Wpedantic -Werror) -endif() +add_terrain_backend_test(test_area_collapse unit/test_area_collapse.cpp) From c877bbb646f8e9e4232cd4cb8a2a7ee9cd8b4cb7 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:24:28 +0200 Subject: [PATCH 03/16] Retrospective agenda: a green step that changed a design rule Co-Authored-By: Claude Opus 5.5 --- docs/retrospectives/next.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/retrospectives/next.md b/docs/retrospectives/next.md index e59d0c00..f9ce6834 100644 --- a/docs/retrospectives/next.md +++ b/docs/retrospectives/next.md @@ -62,3 +62,8 @@ with `/`) waits for a `@tester` red step. 2026-09-29, night: `@architect` built an uncommitted Python prototype of the 22 design to check its area against NVE (304.91 against 305.54 km²). Useful evidence, but it is implementation work in the design step. + +2026-09-29, night: `@developer` changed the window rule during the 22 PR 1 +green step (the design's rule never stopped early) without stopping to hand +back; the design amendment and the pinning test came after the code. The +fix was right and disclosed, but the order was code first. From 698b19faf983be0772fece34f1538b656cbef9e0 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:26:12 +0200 Subject: [PATCH 04/16] 22 review fixes (docs): Hausdorff measured not guaranteed; as built; project structure Co-Authored-By: Claude Opus 5.5 --- docs/increments/22-auto-catchment.md | 97 ++++++++++++++++++---------- project_structure.md | 43 +++++++++--- 2 files changed, 95 insertions(+), 45 deletions(-) diff --git a/docs/increments/22-auto-catchment.md b/docs/increments/22-auto-catchment.md index 299bcc33..7fd8fa6b 100644 --- a/docs/increments/22-auto-catchment.md +++ b/docs/increments/22-auto-catchment.md @@ -1,7 +1,9 @@ # Increment 22 — auto-catchment: the catchment of a lake, from the DEM (Bygdin first) -Status: **designed** (`@architect`, 2026-09-29), on branch -`increment22-autocatchment` off master `b4847d7`. Nothing is built. Ola was +Status: **built, under review** (2026-09-29). Designed by `@architect` on +branch `increment22-autocatchment` off master `b4847d7`; PR 1 built at +`58f6904`, PR 2 at `ac778e4`; `@reviewer`'s first pass asked for changes, and +the documentation half of them is in this file ("As built" below). Ola was asleep while this was written; every choice he would normally make is marked "Default (main session / @architect, 2026-09-29), for Ola to confirm", with the alternative, so the loop can run tonight. @@ -132,9 +134,9 @@ design leans on a detail beyond the abstract, it says so. flooded it. - **O'Callaghan and Mark 1984**, "The extraction of drainage networks from digital elevation data", *Computer Vision, Graphics, and Image Processing*, - doi:10.1016/S0734-189X(84)80047-X. Crossref lists it as volume 27(2), page - 247; it is commonly cited as 28(3):323-344. Cite it by DOI until someone - resolves which. D8: each node drains to its steepest neighbour, slope + 28(3):323-344, doi:10.1016/S0734-189X(84)80011-0 (checked on Crossref, + 2026-09-29, at review; the DOI first cited here, 80047-X, is a one-page + item in 27(2):247). D8: each node drains to its steepest neighbour, slope measured with the diagonal's length. **Departure, and why:** the flood drains each node to its *lowest* filled neighbour, not its steepest, so the diagonal distance plays no part. The two differ only where a diagonal @@ -581,7 +583,8 @@ seed, for tolerance). - *Simple*: A-E and E-D do not meet any current edge except at A (with the edge ending at A) and at D (with the edge starting at D), and do not overlap those two. A uniform grid of current edges (bucket side the - tolerance, at least one cell) finds the candidates; it is updated on + larger of the tolerance and the mean fine edge, as built) finds the + candidates; it is updated on every collapse. - *Seed inside*: each keep-point has winding number zero around the closed loop A-B-C-D-E-A and lies on neither new edge. (The difference between @@ -591,7 +594,9 @@ seed, for tolerance). four vertices include A, E or D, and repeat until none is admissible or 4 vertices remain. Stale heap entries are skipped by a per-vertex version. -With tolerance 0 only the collinear pass runs. +With tolerance 0 only the collinear pass runs, so the result is the fine +ring less its collinear vertices: 5,513 vertices on Bygdin, not the traced +17,812 (`@reviewer`, at `ac778e4`). Same shape, same area. **Guarantees**, each with the check the tests make: @@ -602,12 +607,24 @@ With tolerance 0 only the collinear pass runs. vertex. Tested by shapely `is_valid` and, on small cases, a brute-force pairwise test with exact orientation. - *Seed inside*: the seed point is strictly inside (shapely `contains`). -- *Tolerance*: every fine vertex is within the tolerance of the reduced ring, - and every reduced vertex within the tolerance of the fine ring. So the - symmetric Hausdorff distance between the two is at most the tolerance plus - one cell: tested with shapely `hausdorff_distance(..., densify=0.05)`. +- *Tolerance, at vertices*: every fine vertex is within the tolerance of the + reduced ring, and every reduced vertex within the tolerance of the fine + ring. That is what the deviation bounds, and all it bounds. - *Deterministic*: serial, ordered by (deviation, id); the same input gives the same bits. +- **Measured, not guaranteed: the Hausdorff distance.** (Revised 2026-09-29 + at review; the first version claimed a symmetric Hausdorff distance of at + most the tolerance plus one cell. That does not follow: the deviation never + bounds the interior of a reduced edge, so a long new edge can pass further + from the fine ring than either of its ends.) `@reviewer`'s probe at + `ac778e4`: on 2,000 traced lattice rings the worst was 0.93 of (tolerance + plus one cell), none over; on 8,994 random star rings, not from a lattice, + 34 exceeded the tolerance, the worst by 1.34 times. Bygdin: 19.92 m at a + 20 m tolerance. So on traced outlines it has held, and it is not proved. + **Open, for Ola to decide**: enforce it, by also checking the interior of + A-E and E-D against the fine range (the distance from each fine segment in + the range to the new chain, and from the new edges' points to the fine + segments), at some cost per candidate; or keep it measured. - Not guaranteed: the fewest vertices (it is greedy), or that every catchment node is inside (a node within the tolerance of the boundary may fall either side). @@ -647,8 +664,9 @@ rasputin catchment --dem PATH [--dem PATH ...] --seed X Y [--seed-crs CRS] point and CRS, tolerance, node count, fine and reduced vertex counts, fine and reduced areas in m², the windows' sizes. Coordinates written with `repr` precision, so the area survives the round trip. -- `--outline-tolerance 0` writes the fine outline (collinear vertices only - removed), for comparison. +- `--outline-tolerance 0` writes the fine ring less its collinear vertices + (5,513 vertices on Bygdin, against 17,812 traced), same shape and area, + for comparison. - stderr, one line each: every window (box, nodes, flood seconds, and "grown: touches north" or "contained"); the seed (lake polygon with its area and seed-node count, or the pour node); the catchment (nodes, node @@ -664,21 +682,26 @@ request and the result are frozen; the CLI is the only place with paths. ### New and changed files -| File | What | Production lines (estimate) | -|---|---|---| -| `include/terrain/hydrology/upstream.hpp` | the flood, `UpstreamOutcome` | 110 | -| `include/terrain/vector_simplify/area_collapse.hpp` | the reduction, `ReduceOutcome`, edge grid | 260 | -| `bindings/core.cpp` | `upstream`, `reduce_ring`, two outcome classes | 80 | -| `src_python/tin_engine/_core.pyi` | their stubs | 30 | -| `src_python/tin_engine/outline.py` | the tracer | 70 | -| `src_python/tin_engine/catchment.py` | request, seed, window loop, result | 170 | -| `src_python/tin_engine/dem_input.py` | repository helper split out | 10 | -| `src_python/tin_engine/feature_input.py` | `read_source` split out of `_Tally.source`, `read_lakes` | 30 | -| `src_python/tin_engine/cli.py` | `catchment` command, report, writer | 100 | -| `project_structure.md` | the two C++ modules and two Python modules | docs | - -About 860 lines, over the 700 ceiling (CLAUDE.md §2), so two PRs on this -branch. +| File | What | Estimate | As built, PR 1 | As built, PR 2 | +|---|---|---|---|---| +| `include/terrain/hydrology/upstream.hpp` | the flood, `UpstreamOutcome` | 110 | 106 | | +| `include/terrain/vector_simplify/area_collapse.hpp` | the reduction, `ReduceOutcome`, edge grid | 260 | | 286 | +| `bindings/core.cpp` | `upstream`, `reduce_ring`, two outcome classes | 80 | 48 | 46 | +| `src_python/tin_engine/_core.pyi` | their stubs | 30 | 19 | 22 | +| `src_python/tin_engine/outline.py` | the tracer | 70 | 50 | | +| `src_python/tin_engine/catchment.py` | request, seed, window loop, result | 170 | 244, plus later edits | 23 | +| `src_python/tin_engine/dem_input.py` | repository helper split out | 10 | 1 | | +| `src_python/tin_engine/feature_input.py` | `read_source` split out of `_Tally.source`, `read_lakes` | 30 | 38 net | | +| `src_python/tin_engine/cli.py` | `catchment` command, report, writer | 100 | 113 | 26 | +| **Total** | | about 860 | **619 net** (674 added, 55 removed) | **403 net** | +| `project_structure.md` | the two C++ modules and two Python modules | docs | | | + +"As built" is `@reviewer`'s count at review (2026-09-29), by CLAUDE.md §2's +rule (blank lines, comments and docstrings not counted), over PR 1 = +`master..608e366` and PR 2 = `039cf3c..ac778e4`. The totals are +`@reviewer`'s; the per-file numbers are theirs too and were not recounted +here, and `catchment.py`'s PR 1 figure predates the window-loop edits, so the +PR 1 column need not sum to 619. Both PRs are under the 700 ceiling. ### The PR split @@ -690,7 +713,9 @@ branch. binding, and the command reducing by default with `--outline-tolerance`. Red, green, review, then the Bygdin acceptance run. -Both go on `increment22-autocatchment`, PR 2 stacked on PR 1. Neither touches +Both go on `increment22-autocatchment`, PR 2 stacked on PR 1. As built: PR 1 +is `master..608e366` (red `1e1b3bb`, green `58f6904`, the window test +`608e366`), PR 2 is `039cf3c..ac778e4` (red `039cf3c`, green `ac778e4`). Neither touches refine or mesh code, so the 1 m benchmark and scaling sweep (README, rule 2) do not apply; the Bygdin run below is this increment's acceptance. @@ -701,7 +726,7 @@ Lean: no throwaway implementations, no mutation round. Default (main session invariants on random inputs are the defence. None is named invariant-critical for mutation testing tonight. -**PR 1, C++ (Catch2), `tests/cpp/unit/hydrology_upstream.cpp`:** +**PR 1, C++ (Catch2), `tests/cpp/unit/test_hydrology_upstream.cpp`:** - *Brute-force oracle*. For random DEMs with no interior pit and no ties, built as z = 10 x (steps to the nearest edge) + a distinct fraction per @@ -747,7 +772,7 @@ for mutation testing tonight. **PR 2, C++ and Python:** -- `tests/cpp/unit/area_collapse.cpp`: status for a negative, NaN or infinite +- `tests/cpp/unit/test_area_collapse.cpp`: status for a negative, NaN or infinite tolerance, a clockwise ring, fewer than 4 vertices; tolerance 0 removes only collinear vertices; a traced rectangle of nodes reduces to at most 8 vertices at a tolerance of one cell, with its area unchanged; a keep-point @@ -756,11 +781,12 @@ for mutation testing tonight. must not cross itself; the same input twice gives equal bits. - `test_core_reduce.py`, on rings traced from random blobs and from the PR 1 synthetic catchments: the five guarantees above (area, simple, seed - inside, tolerance at vertices, Hausdorff within tolerance plus a cell), and - determinism. + inside, tolerance at vertices, and the Hausdorff distance, which on traced + lattice rings is measured within tolerance plus a cell but, per the + revision above, not guaranteed), and determinism. - `test_cli_catchment.py` gains: the default reduces; the reduced line reports vertices, area and the difference; `--outline-tolerance 0` gives - the fine ring. + the fine ring less its collinear vertices. ## Acceptance: Bygdin, end to end @@ -777,7 +803,8 @@ Run by `@perf` after PR 2 is green, recorded under NVE's polygon (fetched by the URL, not committed), both ways. 3. `rasputin mesh --dem ... --domain bygdin.geojson --tolerance 1` and `--tolerance 10`, each with `--stats`: triangles, vertices, time. And once - at `--tolerance 10` with the fine outline (`--outline-tolerance 0`), to + at `--tolerance 10` with the unreduced outline (`--outline-tolerance 0`, + the fine ring less its collinear vertices), to show what the reduction saves, which is Ola's first requirement. 4. A picture is optional; the `.vtk` opens in ParaView. diff --git a/project_structure.md b/project_structure.md index 7f5e37ff..7d45609c 100644 --- a/project_structure.md +++ b/project_structure.md @@ -60,13 +60,21 @@ include/terrain/ # public C++ headers, header-only where possible refine.hpp # RefineOptions, RefineOutcome, the round loop (14), # Delaunay insertion (14b), the quality-start call # (20), constraint feet (20b) + hydrology/ + upstream.hpp # upstream(z, seed) -> UpstreamOutcome: one + # Priority-Flood labelling the nodes that drain + # into the seed set, plus edge/NoData flags (22) + vector_simplify/ + area_collapse.hpp # reduce_ring: area-preserving segment collapse + # (Kronenfeld et al. 2020) to a horizontal + # tolerance, exact crossing tests, keep-points (22) src/ # C++ implementation, one directory per module # (only predicates/ and cdt/ exist; rest planned) predicates/ # exact orient2d/incircle; namespace terrain::pred parallel_util/ # (none: header-only, include/terrain/parallel_util/) - vector_simplify/ # Visvalingam-Whyatt, Douglas-Peucker, topology checks - hydrology/ # pit fill, flow direction, accumulation, catchments, streams + # (no vector_simplify/ or hydrology/ here: both + # are header-only, include/terrain/...) # (no noding/ here, and none planned: the noder is # header-only, its driver a template on the kernel) cdt/ # thin wrapper over vendored Detria @@ -102,7 +110,18 @@ src_python/tin_engine/ # public Python API (distribution name: rasputin) # read, mapped to masks by a ClassMap, pre-clipped, # moved to the DEM's CRS and clipped to the domain # as linework -> FeatureSet (16b); opens GeoJSON - # and .gml itself; never imports _core + # and .gml itself; never imports _core. + # read_source: one file's raw rows (16b's reader, + # shared); read_lakes: the polygons of --lakes + # near the seed point, in the file's CRS (22) + outline.py # trace(mask): marching-squares rings between in- + # and out-nodes, (8, 4) saddle rule; numpy only, + # never imports _core (22) + catchment.py # CatchmentRequest -> delineate(request, repo) -> + # Catchment: seed, the window loop over 15a's + # plan, _core.upstream, the fine ring, and + # _core.reduce_ring; takes a DemRepository and + # no path (22) chains.py # start_chains: the domain's rings, then every # feature line, as (indices, role, mask) (16b); # never imports _core @@ -117,7 +136,9 @@ src_python/tin_engine/ # public Python API (distribution name: rasputin) # carries positions rather than feature names svg.py # (Scene, SvgStyle) -> str; the stylesheet lives here fixtures.py # the synthetic gallery, declarative; `rasputin draw - io/ # all file decoding AND encoding lives here + io/ # all file decoding AND encoding lives here, with + # one exception: the catchment GeoJSON writer is + # in cli.py (22); see "Python API surface" __init__.py ply.py # arrays -> PLY bytes; takes no path and opens nothing vtk_legacy.py # arrays + EdgeVocabulary -> legacy .vtk bytes, for @@ -159,9 +180,9 @@ docs/increments/ # per-increment design records; see its README ``` raster ────────────────┐ ▼ -predicates ───────────┬─→ vector_simplify hydrology (depends on raster, - │ │ parallel_util) - └─→ noding ←──────────┘ (catchment outline, river polylines) +predicates ───────────┬─→ noding ──→ vector_simplify hydrology (raster only) + │ (area_collapse uses noding's classify + │ and the predicates' kernel) │ ▼ cdt (thin wrapper over vendored Detria, @@ -320,11 +341,11 @@ Header-only. Two helpers in `chunks.hpp`, both over `std::jthread` created per c ### `vector_simplify` -Visvalingam-Whyatt for area-preserving polygon simplification; Douglas-Peucker for polyline length-preserving; multi-feature topology checks (no introduced crossings). Pre-noding step. +Header-only. `area_collapse.hpp` (increment 22): `reduce_ring`, Kronenfeld, Stanislawski, Buttenfield and Brockmeyer's area-preserving segment collapse (APSC, 2020): each collapse replaces two vertices by one on the line that keeps the area, so the area is kept up to rounding; collapses are taken least deviation first while the fine ring's vertices stay within a horizontal tolerance; each new edge is tested for crossings with `noding::classify` over a uniform grid, and keep-points (the seed) must stay inside. Serial and deterministic. Visvalingam-Whyatt and Douglas-Peucker are not used: neither keeps area. See `docs/increments/22-auto-catchment.md`. ### `hydrology` -Implements `auto_catchments.md`: pit filling (priority-flood with epsilon, plus optional Lindsay hybrid), D8 flow direction, parallel flow accumulation (Barnes-Lehman-Mulla), catchment BFS, stream extraction with Strahler ordering, mask polygonization. May depend on RichDEM (MIT) as either reference or external library — decision deferred to first implementation pass. +Header-only; depends on `raster` only. `upstream.hpp` (increment 22): `upstream(z, seed)`, a template on the `RasterSource` concept. One Priority-Flood (Barnes, Lehman and Mulla 2014) from the window's edge and from the nodes beside NoData, ties first in, first out, labels a node *in* when it is a seed or was flooded from an *in* node. So each node drains to the node that flooded it, its lowest filled neighbour, and there is no separate fill, no flat resolution and no D8 or accumulation pass. Returns the mask, the in-nodes' count and bounds, and `touches_edge` / `touches_nodata` (an in-node that is, or neighbours, an outlet of that kind). Serial; the binding releases the GIL. What `auto_catchments.md` also sketches (epsilon filling, D8, accumulation, streams and Strahler order, RichDEM) is not built; the outline tracer is Python (`outline.py`). See `docs/increments/22-auto-catchment.md`. ### `noding` @@ -387,7 +408,7 @@ CMake-based, building the header-only core plus one Python extension. C++20 requ - **Detria** (header-only) is vendored at a pinned SHA in `lib/detria/`. CMake does **not** search for a system copy: version skew in a geometry kernel across machines is a reproducibility hazard and vendoring a header costs nothing. -- **External deps under consideration:** RichDEM (optional, MIT), Eigen (if linear algebra needs grow beyond what we want to hand-roll). +- **External deps under consideration:** Eigen (if linear algebra needs grow beyond what we want to hand-roll). - **No CGAL and no GDAL** in the new core: prohibited by `CLAUDE.md` §2. **No Boost.Geometry** either, which is a scope choice and not a prohibition — reading this line as one is what put an unauthored ban in §2 for six @@ -402,6 +423,8 @@ There is no `setup.py`. It was removed with the foundation reset, along with the The public API is the `tin_engine` package, calling into `tin_engine._core`. `tin_engine.viz` is the renderer that turns a triangulation into an SVG a person can look at; it consumes the `typing.Protocol`s in `viz/protocols.py` and **never imports `_core`**, so it is testable with no compiled extension in the process. `cli.py` is the single composition root that joins the two -- the same shape as the rule below that exactly one module adapts decoded raster data into `_core`. The `rasputin draw` command drives it: it looks a fixture up in `viz.fixtures.GALLERY`, maps that fixture's **string** chain roles onto `_core.ChainRole` -- `viz/` may not name the enum, so the mapping is the composition root's -- runs `build_pslg` and `triangulate`, and hands the fixture itself to `build_scene` as the `PslgLike`, which is what lets a fixture the validator rejects still be drawn. It is also the only place a path exists, and it resolves and refuses one before writing. +**The catchment GeoJSON writer is in `cli.py`** (increment 22, `rasputin catchment`): it builds a one-feature `FeatureCollection` with a `crs` member and writes it, an exception to "all file encoding lives in `io/`". Recommended (@architect, 2026-09-29): move it, as a function from a polygon, its CRS text and its properties to bytes that takes no path and opens nothing, into `io/geojson.py`, the shape `io/ply.py` and `io/vtk_legacy.py` already have; `cli.py` keeps only the write. It is small, and a GUI or API worker writing a catchment would otherwise have to go through the CLI. Until it moves, this paragraph is the record of the exception. + The pre-migration `rasputin.*` modules (`mesh.py`, `geometry.py`, `reader.py`, `tin_repository.py` and friends) are archived under `legacy/rasputin/` rather than kept in place, so this is a re-implementation against the new backend rather than a rewiring of stable modules. Porting proceeds one entry point at a time; shapes worth preserving should be read out of `legacy/` before being reintroduced. ## What gets deleted, eventually From fba6a0d3141da3e0706022e937ae946d3144686c Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:27:27 +0200 Subject: [PATCH 05/16] 22 review fixes (tests): Hausdorff measured, not guaranteed test_core_reduce.py's docstring now names four guarantees (area, simple, seed inside, tolerance at vertices) and the Hausdorff bound as measured, not guaranteed, matching the design at 698b19f. check() says it takes traced lattice rings only; every caller passes one (random blobs traced by outline.trace, the PR 1 bowl and plus catchments), so no assertion moved. The C++ random star rings (non-lattice) carry no Hausdorff assertion. test_cli_catchment.py's one Hausdorff check gets the same note. No test matches 'grown: touches' (the stderr tests look for 'window'), so the coming 'grown on north' wording breaks nothing. Co-Authored-By: Claude Opus 5.5 --- tests/python/test_cli_catchment.py | 2 ++ tests/python/test_core_reduce.py | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/python/test_cli_catchment.py b/tests/python/test_cli_catchment.py index 43e2a9ab..a73d312c 100644 --- a/tests/python/test_cli_catchment.py +++ b/tests/python/test_cli_catchment.py @@ -361,6 +361,8 @@ def test_the_default_reduces_to_twice_the_cell_keeping_the_area( assert reduced.is_valid assert reduced.contains(Point(SEED)) tolerance = 2 * D + # Measured, not guaranteed (698b19f); expected here because the fine ring + # is a traced lattice outline. assert shapely.hausdorff_distance(fine.exterior, reduced.exterior, densify=0.05) <= ( tolerance + D ) diff --git a/tests/python/test_core_reduce.py b/tests/python/test_core_reduce.py index 3c18a5d4..deeac74e 100644 --- a/tests/python/test_core_reduce.py +++ b/tests/python/test_core_reduce.py @@ -2,7 +2,8 @@ `docs/increments/22-auto-catchment.md`, "The reduction" and "The red suites" (PR 2, `test_core_reduce.py`): on rings traced from random blobs and from the -PR 1 synthetic catchments, the five guarantees and determinism. +PR 1 synthetic catchments, the four guarantees, the Hausdorff distance +measured, and determinism. Interface assumed (names chosen here, stated in the handback; they mirror the C++ `ReduceOutcome` in `tests/cpp/unit/test_area_collapse.cpp`): @@ -17,11 +18,19 @@ `TooFewVertices`). Counts: `collinear`, `collapses`, `rejected_crossing`, `rejected_seed`, `rejected_tolerance`. -The guarantees, as the design words its tests: area `|A_r - A_f| <= 1e-9 -A_f`; simple (shapely `is_valid`, `is_simple`); the seed strictly inside +The four guarantees, as the design words its tests: area `|A_r - A_f| <= +1e-9 A_f`; simple (shapely `is_valid`, `is_simple`); the seed strictly inside (shapely `contains`); every fine vertex within the tolerance of the reduced -ring and every reduced vertex within it of the fine ring; the Hausdorff -distance (`densify=0.05`) at most the tolerance plus one cell. +ring and every reduced vertex within it of the fine ring. + +MEASURED, NOT GUARANTEED: the Hausdorff distance (`densify=0.05`) at most the +tolerance plus one cell. The deviation bounds vertices only, so a long new +edge may pass further from the fine ring than its ends (the design, revised +at review in 698b19f: on random non-lattice star rings it exceeded the +tolerance by up to 1.34 times). On traced lattice outlines it has held, and +every ring this file draws is one: outlines traced from random blobs and the +PR 1 catchments. `check` asserts it for that reason only; a non-lattice ring +must not be passed to it. """ from __future__ import annotations @@ -56,7 +65,8 @@ def open_ring(coords: Any) -> np.ndarray: def check( core: Any, fine: np.ndarray, tolerance: float, keep: np.ndarray, cell: float = CELL ) -> np.ndarray: - """Reduce `fine` and check every guarantee; the reduced ring.""" + """Reduce `fine`, a TRACED LATTICE RING, and check the four guarantees and + the measured Hausdorff bound; the reduced ring.""" out = core.reduce_ring(fine, tolerance, keep) assert out.status == core.ReduceStatus.Ok red = np.asarray(out.ring, dtype=np.float64) @@ -72,6 +82,8 @@ def check( slack = 1e-9 * max(1.0, tolerance) assert shapely.distance(shapely.points(fine), r.exterior).max() <= tolerance + slack assert shapely.distance(shapely.points(red), f.exterior).max() <= tolerance + slack + # Measured, not guaranteed (see the module docstring): holds on traced + # lattice rings, which is all this file passes here. assert shapely.hausdorff_distance(f.exterior, r.exterior, densify=0.05) <= tolerance + cell # Deterministic: the same input again gives the same bits. again = core.reduce_ring(fine, tolerance, keep) From 675792531373ec1f4297d2fd59281a4745376f00 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:29:45 +0200 Subject: [PATCH 06/16] 22 review fixes (code): help and docstrings as built; 'grown on' - catchment.py: module and Catchment docstrings describe the reduced outline and its tolerance as built; LakeError (a CatchmentError) for the lake refusals. - cli.py: catchment --help and --outline-tolerance help as built; a lake refusal is reported under --lakes, not --dem; stderr 'grown on '. Co-Authored-By: Claude Opus 5.5 --- src_python/tin_engine/catchment.py | 22 ++++++++++++++++------ src_python/tin_engine/cli.py | 17 ++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src_python/tin_engine/catchment.py b/src_python/tin_engine/catchment.py index e9db57d2..c33fffcf 100644 --- a/src_python/tin_engine/catchment.py +++ b/src_python/tin_engine/catchment.py @@ -1,11 +1,12 @@ -"""The catchment of a lake, from the DEM (increment 22, PR 1: the fine outline). +"""The catchment of a lake, from the DEM, as a reduced outline (increment 22). `docs/increments/22-auto-catchment.md`, "Data flow", "The seed", "The window" and "The fine outline". :func:`delineate` moves the seed point and the lake into the DEM's CRS, plans and assembles a window round them (15a), floods it (`_core.upstream`), grows the window until the catchment lies clearly inside it or is cut by the data's edge or NoData, and traces the outline of -the in-nodes, holes filled and other pieces dropped. +the in-nodes, holes filled and other pieces dropped. The fine outline is then +reduced to the request's tolerance by `_core.reduce_ring`, keeping its area. No paths: the DEM comes through a `DemRepository`, and the lakes arrive as shapely geometries the CLI read. Blocking (the flood releases the GIL); an @@ -50,6 +51,11 @@ class CatchmentError(ValueError): """A catchment that cannot be delineated, in words for the person asking.""" +class LakeError(CatchmentError): + """The lakes cannot seed it: the point in no lake or in two, or a lake + that does not move into the DEM's CRS.""" + + class CatchmentRequest(BaseModel): """The seed point in `seed_crs`, and the lakes (shapely polygons or multipolygons in `lakes_crs`) of which the one containing it is the seed. @@ -92,7 +98,11 @@ class Catchment: """The fine outline (holes filled) in the DEM's CRS `crs`, the seed point there, the lake's area (None without one), node counts, the outline's area and what was left out of it, every window, and the last window's - mask of in-nodes with its `meta`.""" + mask of in-nodes with its `meta`. Then the reduced outline `reduced` (the + CLI's `reduced_vertices` and `reduced_area_m2` are its vertex count and + area), the `tolerance` in metres it was reduced to (`outline_tolerance_m`; + 0 keeps the fine outline less its collinear vertices), and the seconds the + trace and the reduction took.""" fine: Polygon crs: str @@ -177,13 +187,13 @@ def _lake(request: CatchmentRequest, dem_crs: str) -> Polygon | None: found = [p for g in request.lakes for p in shapely.get_parts(g) if p.contains(point)] where = f"({px!r}, {py!r}) in {request.lakes_crs}" if not found: - raise CatchmentError(f"the seed point {where} is in no lake") + raise LakeError(f"the seed point {where} is in no lake") if len(found) > 1: - raise CatchmentError(f"the seed point {where} is in {len(found)} lakes; give one") + raise LakeError(f"the seed point {where} is in {len(found)} lakes; give one") move = reprojector(request.lakes_crs, dem_crs) lake = shapely.transform(found[0], move) if not np.isfinite(shapely.get_coordinates(lake)).all(): - raise CatchmentError(f"the lake at {where} has a vertex with no image in {dem_crs}") + raise LakeError(f"the lake at {where} has a vertex with no image in {dem_crs}") assert isinstance(lake, Polygon) return lake diff --git a/src_python/tin_engine/cli.py b/src_python/tin_engine/cli.py index 860cbc41..af4e5bdf 100644 --- a/src_python/tin_engine/cli.py +++ b/src_python/tin_engine/cli.py @@ -69,7 +69,7 @@ sample, triangulate, ) -from tin_engine.catchment import CatchmentRequest, delineate +from tin_engine.catchment import CatchmentRequest, LakeError, delineate from tin_engine.chains import start_chains from tin_engine.crs import crs_label, parse_crs, transform_description from tin_engine.dem_input import DemInput, DemRequest, open_dem, repository_for @@ -1322,7 +1322,8 @@ def catchment( "--outline-tolerance", metavar="METRES", help="Reduce the outline, keeping its area, to within this of the fine one; " - "0 writes the fine outline. Default: twice the DEM's cell.", + "0 writes the fine outline without its collinear vertices. Default: twice the " + "DEM's cell.", ), ] = None, out_parent: Annotated[ @@ -1331,9 +1332,10 @@ def catchment( ] = None, ) -> None: """Write the catchment of a lake, from the DEM, as a GeoJSON polygon in the - DEM's CRS that ``mesh --domain`` reads (increment 22, PR 1: the fine - outline, drawn between DEM nodes, holes filled). A catchment cut by the - data's edge or by NoData is refused, and nothing is written.""" + DEM's CRS that ``mesh --domain`` reads (increment 22). The fine outline is + drawn between DEM nodes, holes filled, then reduced to ``--outline-tolerance`` + keeping its area, with the seed inside. A catchment cut by the data's edge + or by NoData is refused, and nothing is written.""" if lakes is None and lakes_layer is not None: raise typer.BadParameter("applies only with --lakes", param_hint="--lakes-layer") if outline_tolerance is not None and not ( @@ -1360,13 +1362,14 @@ def catchment( except OSError as exc: raise typer.BadParameter(f"cannot read {exc.filename}: {exc}", param_hint="--dem") from exc except ValueError as exc: - raise typer.BadParameter(_words(exc), param_hint="--dem") from exc + hint = "--lakes" if isinstance(exc, LakeError) else "--dem" + raise typer.BadParameter(_words(exc), param_hint=hint) from exc for k, w in enumerate(result.windows, 1): b = w.bounds typer.echo( f"window {k}: x {b.x_min:.0f}-{b.x_max:.0f}, y {b.y_min:.0f}-{b.y_max:.0f}, " f"{w.rows} x {w.cols} nodes, flood {w.seconds:.2f} s, " - + (f"grown: touches {', '.join(w.grown)}" if w.grown else "contained"), + + (f"grown on {', '.join(w.grown)}" if w.grown else "contained"), err=True, ) cell = result.meta.delta_x * result.meta.delta_y From ad3df66d0fb6c73ec643d924486f45ea8d136d3d Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:37:04 +0200 Subject: [PATCH 07/16] 22 acceptance: Bygdin catchment, NVE overlap and domain meshes (AC) Area -0.206 % against NVE's 305.54 km2 (within the design's 2 %); node overlap 99.12 % / 99.33 %; 740 vertices at 20 m, Hausdorff 19.92 m. Meshes at 10 m and 1 m on the reduced and fine outlines, and with CORINE. First run: no performance baseline. Co-Authored-By: Claude Opus 5.5 --- docs/benchmarks/2026-09-29/bygdin.md | 71 ++++++ docs/benchmarks/2026-09-29/bygdin/README.md | 233 ++++++++++++++++++ docs/benchmarks/2026-09-29/bygdin/analyse.py | 156 ++++++++++++ .../bygdin/bygdin_reduced_t20.geojson | 1 + .../2026-09-29/bygdin/logs/analysis.txt | 48 ++++ .../2026-09-29/bygdin/logs/catchment_t0.err | 25 ++ .../2026-09-29/bygdin/logs/catchment_t0.out | 1 + .../2026-09-29/bygdin/logs/catchment_t10.err | 25 ++ .../2026-09-29/bygdin/logs/catchment_t10.out | 1 + .../2026-09-29/bygdin/logs/catchment_t20.err | 25 ++ .../2026-09-29/bygdin/logs/catchment_t20.out | 1 + .../bygdin/logs/catchment_t20_run1.err | 25 ++ .../bygdin/logs/catchment_t20_run1.out | 1 + .../bygdin/logs/catchment_t20_run2.err | 25 ++ .../bygdin/logs/catchment_t20_run2.out | 1 + .../bygdin/logs/catchment_t20_run3.err | 25 ++ .../bygdin/logs/catchment_t20_run3.out | 1 + .../2026-09-29/bygdin/logs/catchment_t50.err | 25 ++ .../2026-09-29/bygdin/logs/catchment_t50.out | 1 + .../2026-09-29/bygdin/logs/commit.txt | 2 + .../2026-09-29/bygdin/logs/exits.txt | 27 ++ .../bygdin/logs/mesh_feat_t10_ascii.err | 22 ++ .../bygdin/logs/mesh_feat_t10_ascii.out | 2 + .../bygdin/logs/mesh_feat_t10_ascii.stats.md | 66 +++++ .../bygdin/logs/mesh_feat_t10_run1.err | 22 ++ .../bygdin/logs/mesh_feat_t10_run1.out | 2 + .../bygdin/logs/mesh_feat_t10_run1.stats.md | 66 +++++ .../bygdin/logs/mesh_feat_t10_run2.err | 22 ++ .../bygdin/logs/mesh_feat_t10_run2.out | 2 + .../bygdin/logs/mesh_feat_t10_run2.stats.md | 66 +++++ .../bygdin/logs/mesh_feat_t10_run3.err | 22 ++ .../bygdin/logs/mesh_feat_t10_run3.out | 2 + .../bygdin/logs/mesh_feat_t10_run3.stats.md | 66 +++++ .../bygdin/logs/mesh_fine_t10_ascii.err | 20 ++ .../bygdin/logs/mesh_fine_t10_ascii.out | 2 + .../bygdin/logs/mesh_fine_t10_ascii.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t10_run1.err | 20 ++ .../bygdin/logs/mesh_fine_t10_run1.out | 2 + .../bygdin/logs/mesh_fine_t10_run1.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t10_run2.err | 20 ++ .../bygdin/logs/mesh_fine_t10_run2.out | 2 + .../bygdin/logs/mesh_fine_t10_run2.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t10_run3.err | 20 ++ .../bygdin/logs/mesh_fine_t10_run3.out | 2 + .../bygdin/logs/mesh_fine_t10_run3.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t1_ascii.err | 20 ++ .../bygdin/logs/mesh_fine_t1_ascii.out | 2 + .../bygdin/logs/mesh_fine_t1_ascii.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t1_run1.err | 20 ++ .../bygdin/logs/mesh_fine_t1_run1.out | 2 + .../bygdin/logs/mesh_fine_t1_run1.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t1_run2.err | 20 ++ .../bygdin/logs/mesh_fine_t1_run2.out | 2 + .../bygdin/logs/mesh_fine_t1_run2.stats.md | 64 +++++ .../bygdin/logs/mesh_fine_t1_run3.err | 20 ++ .../bygdin/logs/mesh_fine_t1_run3.out | 2 + .../bygdin/logs/mesh_fine_t1_run3.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t10_ascii.err | 20 ++ .../bygdin/logs/mesh_red_t10_ascii.out | 2 + .../bygdin/logs/mesh_red_t10_ascii.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t10_run1.err | 20 ++ .../bygdin/logs/mesh_red_t10_run1.out | 2 + .../bygdin/logs/mesh_red_t10_run1.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t10_run2.err | 20 ++ .../bygdin/logs/mesh_red_t10_run2.out | 2 + .../bygdin/logs/mesh_red_t10_run2.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t10_run3.err | 20 ++ .../bygdin/logs/mesh_red_t10_run3.out | 2 + .../bygdin/logs/mesh_red_t10_run3.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t1_ascii.err | 20 ++ .../bygdin/logs/mesh_red_t1_ascii.out | 2 + .../bygdin/logs/mesh_red_t1_ascii.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t1_run1.err | 20 ++ .../bygdin/logs/mesh_red_t1_run1.out | 2 + .../bygdin/logs/mesh_red_t1_run1.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t1_run2.err | 20 ++ .../bygdin/logs/mesh_red_t1_run2.out | 2 + .../bygdin/logs/mesh_red_t1_run2.stats.md | 64 +++++ .../bygdin/logs/mesh_red_t1_run3.err | 20 ++ .../bygdin/logs/mesh_red_t1_run3.out | 2 + .../bygdin/logs/mesh_red_t1_run3.stats.md | 64 +++++ .../bygdin/logs/pmset_catchment_end.txt | 2 + .../bygdin/logs/pmset_catchment_start.txt | 2 + .../2026-09-29/bygdin/logs/pmset_mesh_end.txt | 2 + .../bygdin/logs/pmset_mesh_start.txt | 2 + .../bygdin/logs/pmset_quality_end.txt | 2 + .../bygdin/nve_delfelt_1187.geojson | 1 + docs/benchmarks/2026-09-29/bygdin/run.sh | 56 +++++ 88 files changed, 2523 insertions(+) create mode 100644 docs/benchmarks/2026-09-29/bygdin.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/README.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/analyse.py create mode 100644 docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/analysis.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/commit.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/exits.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_end.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_start.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_end.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_start.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/logs/pmset_quality_end.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson create mode 100644 docs/benchmarks/2026-09-29/bygdin/run.sh diff --git a/docs/benchmarks/2026-09-29/bygdin.md b/docs/benchmarks/2026-09-29/bygdin.md new file mode 100644 index 00000000..69857cb7 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin.md @@ -0,0 +1,71 @@ +# Increment 22 acceptance: Bygdin (@perf, 2026-09-29): summary + +**Verdict: ACCEPTED** against the design's acceptance, which asks for the +area to be within 2 % of NVE. The reduced catchment is **304.9096 km² +against NVE's 305.54 km², -0.206 %**. + +There is **no performance baseline**: this is the first `catchment` run and +the first mesh of this domain. Its speed figures are the baseline for the +next run. + +- **Run conditions**: AC power at the start and end of every block. The + commit is `6757925`, with a Release `_core`. Method, tables and raw logs: + `bygdin/README.md`. + +**`rasputin catchment`** (seed 8.5425 61.3512, CORINE lake, DTM10): + +- **Windows**: three (4.35 M, 8.15 M and 16.94 M nodes). The design + expected two. +- **Result**: 3,049,095 nodes. The fine outline has 17,812 vertices and the + reduced one 740, at 20 m. Both are 304.909550 km²; they differ by + -3e-07 m². +- **Hausdorff distance, fine to reduced**: 19.92 m at 20 m. +- **Times, median of 3**: flood 4.68 s, trace 0.13 s, reduce 0.04 s, wall + 6.53 s. +- **Memory**: max RSS 989 MB (peak footprint 920 MB). + +`--outline-tolerance` ladder (the area is equal at every step; all four +outlines are valid): + +| tolerance | vertices | Hausdorff | +|---|---|---| +| 0 | 5,513 | 0 m | +| 10 | 1,382 | 9.99 m | +| 20 | 740 | 19.92 m | +| 50 | 295 | 49.85 m | + +**Against NVE's polygon** (delfelt 1187, fetched without GDAL and saved as +`bygdin/nve_delfelt_1187.geojson`), comparing DEM nodes: + +- 99.12 % of NVE's nodes are in ours; +- 99.33 % of ours are in NVE's; +- 2.05 km² are ours only, and 2.67 km² are NVE's only. + +This is the design's prototype figure, reproduced by the built code. + +**`rasputin mesh --domain`** (10 threads, median of 3): + +| domain | tol | triangles | refine | total | max RSS | worst angle | max degree | +|---|---|---|---|---|---|---|---| +| reduced | 10 m | 52,198 | 0.034 s | 0.36 s | 646 MB | 0.356° | 12 | +| fine | 10 m | 72,292 | 0.043 s | 0.39 s | 647 MB | 0.655° | 13 | +| reduced | 1 m | 1,129,026 | 0.497 s | 0.93 s | 925 MB | 0.046° | 18 | +| fine | 1 m | 1,137,703 | 0.483 s | 0.92 s | 942 MB | 0.083° | 16 | +| reduced + CORINE | 10 m | 83,171 | 0.046 s | 1.97 s | 776 MB | 0.0041° | 16 | + +- **Checks**: every mesh is within its tolerance, has 0 uncovered nodes and + 0 constrained-Delaunay violations. +- **The reduction** saves 27.8 % of the triangles at 10 m and 0.8 % at 1 m. +- **Land cover inside the catchment** (CORINE 2018): + - sparse vegetation (333) 42 %; + - bare rock (332) 21 %; + - heath (322) 17 %; + - water (512) 16 %; + - glacier (335) 2.4 %. + +**For the main session**: + +- The design says NVE's polygon is "not committed", but this brief asked for + it to be saved. It is committed, and the conflict is flagged in the + README. +- No defects were found. diff --git a/docs/benchmarks/2026-09-29/bygdin/README.md b/docs/benchmarks/2026-09-29/bygdin/README.md new file mode 100644 index 00000000..07b1cd41 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/README.md @@ -0,0 +1,233 @@ +# Increment 22 acceptance: Bygdin, end to end (@perf, 2026-09-29) + +The summary and the verdict are in `../bygdin.md`. This file has the method, +the tables and where every number comes from. + +## What was run + +- **Commit**: `6757925` on `increment22-autocatchment` (`logs/commit.txt`). +- **Build**: `build-pyext` (Release), rebuilt with + `cmake --build build-pyext -j --target _core` (exit 0, nothing out of + date) and copied into `.venv`; the installed `_core` sha256 is in + `logs/commit.txt`, equal to the build's. +- **Machine**: Apple M1 Max, 10 cores, 32 GB. `mesh` used 10 threads + (hardware concurrency, as `--stats` reports). +- **Power**: AC throughout. `pmset -g batt` was recorded at the start and end + of the catchment block, of the timed mesh block, and after the quality runs + (`logs/pmset_*.txt`): every one reads "AC Power, 80 %, AC attached". +- **Data**: DEM `../rasputin_data/DTM10_UTM33_20260925` (10 m, EPSG:25833). + Lakes and features: the CORINE 2018 Norway extract + `../rasputin_data/corine2018_dtm10_utm33.gpkg`, layer `corine2018`. +- **Seed**: `--seed 8.5425 61.3512` (EPSG:4326), as in the design; it falls + in CORINE's Bygdin polygon (39.936963 km², 399,371 nodes). +- **Reference**: NVE's reservoir catchment ("delfelt") 1187 "BYGDIN", + `delfeltAreal_km2` = 305.54, fetched on 2026-09-29 by curl (no GDAL) from + `https://gis3.nve.no/map/rest/services/Mapservices/VassdragsreguleringVannkraft/MapServer/8/query?where=delfeltNr%3D1187&outFields=*&returnGeometry=true&outSR=25833&f=geojson` + and saved as `nve_delfelt_1187.geojson` (one Polygon, 1376 coordinates, + 305.5393 km² by shapely, valid). The design says this polygon is "fetched + by the URL, not committed"; it is committed here because the brief for + this run asked for it to be saved with the evidence. Remove it if the + design's wording should win. + +Scripts, both in this directory: + +- `run.sh` runs every timed block and writes the raw logs to `logs/`: + - `catchment` three times at the default tolerance, then once each at + `--outline-tolerance` 0, 10, 20 and 50; + - five mesh configurations, each three times with `--binary`; + - then each once more with `--ascii`, for the quality check. + Each command runs under `/usr/bin/time -l`, which gives wall time, + maximum RSS and peak memory footprint at the end of each `.err`. +- `analyse.py` reads the logs and outputs and prints `logs/analysis.txt`. + That is every table below. The quality check calls `tools/bench.py`'s + `quality()` and `read_vtk_ascii()` unchanged: the angles and degree as + `--stats` computes them, the tolerance check, and the constrained Delaunay + check. + +Meshes and catchment GeoJSONs other than the committed one were written to +the session scratchpad and are not committed. To regenerate them, run +`SCRATCH= bash docs/benchmarks/2026-09-29/bygdin/run.sh` from the +repository root with `.venv` active, then `python .../analyse.py `. +The committed `bygdin_reduced_t20.geojson` is `catchment_t20_run1.geojson`, +the default output. All four default-tolerance outputs have the same sha256, +`8083fb5b...`. + +## 1. `rasputin catchment` + +Windows, as every run printed them (`logs/catchment_*.err`; all seven runs +chose the same windows): + +| window | x (m) | y (m) | rows x cols | nodes | flood (s, run 1/2/3) | outcome | +|---|---|---|---|---|---|---| +| 1 | 140240-170470 | 6811200-6825570 | 1438 x 3024 | 4.35 M | 0.62 / 0.61 / 0.61 | grown on north, west, south, east | +| 2 | 136250-172700 | 6807210-6829560 | 2236 x 3646 | 8.15 M | 1.22 / 1.22 / 1.22 | grown on north, west | +| 3 | 128580-176700 | 6802360-6837550 | 3520 x 4813 | 16.94 M | 2.84 / 2.84 / 2.89 | contained | + +The design expected one growth step, to about 4100 x 3000 nodes (12 M). The +loop took two growth steps and ended at 16.9 M nodes. + +The result, identical in every run: + +| item | value | +|---|---| +| catchment nodes | 3,049,095 (304.909500 km² of node area) | +| fine outline | 17,812 vertices, 304.909550 km², 1 hole filled (0.000050 km²), 0 rings dropped | +| reduced outline (20 m) | 740 vertices, 304.909550 km², difference -2.98e-07 m² (-9.8e-16 relative) | +| against NVE's 305.54 km² | **-0.206 %** (fine and reduced alike; -0.630 km²) | +| Hausdorff, fine to reduced | 19.92 m at the 20 m tolerance | + +Times, the median of three runs at the default tolerance, from each run's +stderr and `/usr/bin/time -l`: + +| phase | median (s) | runs | +|---|---|---| +| flood, three windows summed | 4.68 | 4.68, 4.67, 4.72 | +| trace | 0.13 | 0.13, 0.14, 0.11 | +| reduce | 0.04 | 0.04, 0.04, 0.04 | +| the rest of the wall time (start-up, DEM read, seed, write) | 1.68 | by difference | +| **wall** | **6.53** | 6.71, 6.48, 6.53 | +| maximum RSS | 989 MB | 1069, 989, 982 MB | +| peak memory footprint | 920 MB | 923, 920, 861 MB | + +The "rest" row is the wall time less the three reported phases. It is not +broken down further: the command does not report a read time, and nothing was +profiled. + +**`--outline-tolerance` ladder.** One run each. The vertex count, area and +validity come from the output GeoJSON. The Hausdorff distance was measured +two ways between the fine outline's boundary and the reduced one's: + +- shapely `hausdorff_distance(..., densify=0.05)`, as the tests measure it; +- every 1 m sample of one boundary against the other's segments, both ways. + +| tolerance (m) | vertices | area (km²) | vs fine (m²) | vs NVE | Hausdorff, densify 0.05 | 1 m: fine to reduced, reduced to fine | valid | +|---|---|---|---|---|---|---|---| +| 0 | 5,513 | 304.909550 | 0 | -0.206 % | 0.00 m | 0.00, 0.00 m | yes | +| 10 | 1,382 | 304.909550 | 0 | -0.206 % | 9.99 m | 9.99, 9.96 m | yes | +| 20 (default) | 740 | 304.909550 | -2.98e-07 | -0.206 % | 19.92 m | 19.92, 19.62 m | yes | +| 50 | 295 | 304.909550 | -1.43e-06 | -0.206 % | 49.85 m | 49.85, 48.06 m | yes | + +The traced outline has 17,812 vertices. The Hausdorff distance stayed under +the tolerance at every step of the ladder. The design lists that as measured, +not guaranteed. + +## 2. Node overlap with NVE's polygon + +This compares the DEM nodes (10 m lattice, x and y multiples of 10) strictly +inside our fine outline with those inside NVE's polygon, using shapely +`contains_xy` over the union's bounds. + +| item | nodes | +|---|---| +| ours | 3,049,096 | +| NVE's | 3,055,360 | +| both | 3,028,611 | +| **NVE's nodes in ours** | **99.12 %** | +| **ours in NVE's** | **99.33 %** | +| ours only | 20,485 (2.05 km²) | +| NVE's only | 26,749 (2.67 km²) | + +Our count is one more than the command's 3,049,095. The fine outline has +one hole filled, of 50 m² (half a cell, the midpoint diamond around a single +out-node), so one node that is outside the catchment falls inside the filled +ring. + +The largest pieces of disagreement, as area and centroid in EPSG:25833, are +in `logs/analysis.txt`: + +- ours and not NVE's: 0.255 km² at (163743, 6813036); +- NVE's and not ours: 0.577 km² at (149046, 6826265). + +Between the two boundaries, the 1 m-sampled distance is at most 485 m from +ours to NVE's and 782 m from NVE's to ours. Why the divides differ was not +investigated. The prototype's figures in the design (99.1 % and 99.3 %, +-0.21 %) are reproduced by the built code. + +## 3. `rasputin mesh --domain` + +- **Timed runs**: three per configuration, `--binary`, `--stats`. The + medians are shown. +- **Refine and total** are from `--stats`. Total is the command body, without + interpreter start-up. +- **Wall and maximum RSS** are from `/usr/bin/time -l`. +- **Quality** is from the one `--ascii` run per configuration, which has the + same triangle count as the timed runs. +- "reduced" is the default 740-vertex outline; "fine" is + `--outline-tolerance 0` (5,513 vertices). + +| domain | tol | triangles | vertices | start vertices | refine (s) | total (s) | wall (s) | max RSS (MB) | worst angle | max degree | max error (m) | Delaunay checked / ambiguous / violations | +|---|---|---|---|---|---|---|---|---|---|---|---|---| +| reduced | 10 m | 52,198 | 26,576 | 740 | 0.034 | 0.356 | 0.62 | 646 | 0.356° | 12 | 9.9998 | 77,821 / 1,122 / 0 | +| fine | 10 m | 72,292 | 38,914 | 5,513 | 0.043 | 0.388 | 0.66 | 647 | 0.655° | 13 | 10.0000 | 105,671 / 1,546 / 0 | +| reduced | 1 m | 1,129,026 | 565,511 | 740 | 0.497 | 0.927 | 1.38 | 925 | 0.0461° | 18 | 1.0000 | 1,692,542 / 172,872 / 0 | +| fine | 1 m | 1,137,703 | 571,651 | 5,513 | 0.483 | 0.917 | 1.38 | 942 | 0.0834° | 16 | 1.0000 | 1,703,756 / 172,995 / 0 | +| reduced + CORINE | 10 m | 83,171 | 42,110 | 8,242 | 0.046 | 1.972 | 2.24 | 776 | 0.00408° | 16 | 9.9997 | 116,398 / 1,095 / 0 | + +Every run is within its tolerance, with 0 valid DEM nodes uncovered and 0 +Delaunay violations. Per-run figures are in `logs/analysis.txt`. The +refine-time spread between runs is at most 0.008 s. + +**What the reduction saves.** + +- At 10 m it saves 27.8 % of the triangles: 52,198 against 72,292. +- At 1 m it saves 0.8 %: 1,129,026 against 1,137,703. At that tolerance the + interior dominates the triangle count, and the outline does not. +- At both tolerances the reduced outline gives the smaller worst angle: + 0.356° against 0.655° at 10 m, and 0.046° against 0.083° at 1 m. The + maximum degree is 12 against 13 at 10 m, and 18 against 16 at 1 m. The + cause was not measured. + +**Land cover inside the catchment** (`--features +--features-layer corine2018 --features-map corine`, 10 m). stderr reports: + +- 79 features kept, 342 dropped outside, 35 clipped; +- 15,846 input vertices, 8,242 after noding. + +In the mesh: + +- 8,882 constraint edges; +- 7,835 edge cells with `land_cover` = 1; +- 2,005 edge cells with `water` = 1. + +The worst angle, 0.00408°, comes with the CORINE constraints; without them +the same domain gives 0.356°. Of the 1.97 s total, refine takes 0.046 s. The +rest is the feature read, clip and noding, which were not broken down here +(see the `.stats.md` phase table). + +The class areas below come from clipping CORINE to the reduced polygon with +shapely, which is separate from the mesh: + +| CORINE code | class | km² | share | +|---|---|---|---| +| 333 | sparsely vegetated areas | 129.093 | 42.34 % | +| 332 | bare rocks | 65.012 | 21.32 % | +| 322 | moors and heathland | 51.849 | 17.00 % | +| 512 | water bodies | 50.191 | 16.46 % | +| 335 | glaciers and perpetual snow | 7.452 | 2.44 % | +| 412 | peat bogs | 1.021 | 0.33 % | +| 142 | sport and leisure facilities | 0.292 | 0.10 % | + +The classes cover 304.910 km² of the polygon's 304.910 km². + +## No performance baseline + +This is the first run of `catchment`, and the first mesh of this domain. +There is no earlier figure on AC or on battery to compare speed against, so +the speed figures above are the baseline for the next increment. The +increment does not change refine. The 1 m benchmark and the thread sweep +were not run; this brief did not ask for them. + +## Files + +- `run.sh`, `analyse.py`: the scripts above. +- `logs/`: + - `*.err`: stderr with `/usr/bin/time -l`; + - `*.out`: stdout; + - `*.stats.md`: `mesh --stats`; + - `exits.txt`: every command's exit status, all 0; + - `pmset_*.txt`; + - `commit.txt`; + - `analysis.txt`. +- `bygdin_reduced_t20.geojson`: the reduced catchment (740 vertices, + EPSG:25833), which `mesh --domain` reads. +- `nve_delfelt_1187.geojson`: NVE's polygon, as fetched. diff --git a/docs/benchmarks/2026-09-29/bygdin/analyse.py b/docs/benchmarks/2026-09-29/bygdin/analyse.py new file mode 100644 index 00000000..73516b4c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/analyse.py @@ -0,0 +1,156 @@ +"""Increment 22 acceptance on Bygdin: the numbers in README.md, from run.sh's +logs and outputs. Usage, from the repository root with .venv active: + + python docs/benchmarks/2026-09-29/bygdin/analyse.py $SCRATCH > .../logs/analysis.txt +""" + +import json +import re +import sqlite3 +import statistics +import sys +from pathlib import Path + +import numpy as np +import shapely +from shapely.geometry import shape + +sys.path.insert(0, "tools") +import bench # noqa: E402 quality() and read_vtk_ascii(), unchanged + +from tin_engine.io.geopackage import decode_geometry # noqa: E402 + +HERE = Path("docs/benchmarks/2026-09-29/bygdin") +LOG = HERE / "logs" +SCRATCH = Path(sys.argv[1]) +NVE_KM2 = 305.54 # delfeltAreal_km2 of delfelt 1187 +CELL = 10.0 + + +def polygon(path: Path) -> shapely.Polygon: + return shape(json.loads(path.read_text())["features"][0]["geometry"]) + + +def hausdorff_1m(a: shapely.LineString, b: shapely.LineString) -> tuple[float, float]: + """Directed distances a->b and b->a: every 1 m sample of one boundary to + the other boundary's segments (exact point-segment distance).""" + + def directed(p: shapely.LineString, q: shapely.LineString) -> float: + pts = shapely.points(shapely.get_coordinates(shapely.segmentize(p, 1.0))) + c = shapely.get_coordinates(q) + segs = shapely.linestrings(np.stack([c[:-1], c[1:]], axis=1)) + _, d = shapely.STRtree(segs).query_nearest(pts, return_distance=True, all_matches=False) + return float(d.max()) + + return directed(a, b), directed(b, a) + + +print("## catchment") +walls, floods, traces, reduces, rss, foot = [], [], [], [], [], [] +for i in (1, 2, 3): + err = (LOG / f"catchment_t20_run{i}.err").read_text() + floods.append(sum(float(x) for x in re.findall(r"flood ([\d.]+) s", err))) + traces.append(float(re.search(r"traced in ([\d.]+) s", err).group(1))) + reduces.append(float(re.search(r"tolerance 20 m, ([\d.]+) s", err).group(1))) + walls.append(float(re.search(r"([\d.]+) real", err).group(1))) + rss.append(int(re.search(r"(\d+)\s+maximum resident", err).group(1))) + foot.append(int(re.search(r"(\d+)\s+peak memory footprint", err).group(1))) + per_window = re.findall(r"window (\d): .*?flood ([\d.]+) s", err) + print(f"run {i}: windows {per_window}, flood sum {floods[-1]:.2f}, trace {traces[-1]}," + f" reduce {reduces[-1]}, wall {walls[-1]}, maxRSS {rss[-1]/1e6:.0f} MB," + f" peak footprint {foot[-1]/1e6:.0f} MB") +med = statistics.median +print(f"median: flood {med(floods):.2f} s, trace {med(traces):.2f} s, reduce {med(reduces):.2f} s," + f" wall {med(walls):.2f} s, other (read, seed, write, start-up) {med(walls)-med(floods)-med(traces)-med(reduces):.2f} s," + f" maxRSS {med(rss)/1e6:.0f} MB, peak footprint {med(foot)/1e6:.0f} MB") + +fine = polygon(SCRATCH / "catchment_t0.geojson") +nve = polygon(HERE / "nve_delfelt_1187.geojson") +print(f"\nNVE polygon: {nve.area/1e6:.4f} km2 by shapely, {shapely.get_num_coordinates(nve)} coords," + f" valid {nve.is_valid}; published {NVE_KM2} km2") +print("\n| tolerance | vertices | area km2 | vs fine m2 | vs NVE 305.54 | Hausdorff densify=0.05 | 1 m: fine->red, red->fine | valid |") +for t in (0, 10, 20, 50): + red = polygon(SCRATCH / f"catchment_t{t}.geojson") + n = shapely.get_num_coordinates(red.exterior) - 1 + h = shapely.hausdorff_distance(fine.exterior, red.exterior, densify=0.05) + f2r, r2f = hausdorff_1m(fine.exterior, red.exterior) + print(f"| {t} | {n} | {red.area/1e6:.6f} | {red.area-fine.area:+.3g} |" + f" {100*(red.area/1e6-NVE_KM2)/NVE_KM2:+.3f} % | {h:.2f} m | {f2r:.2f}, {r2f:.2f} m | {red.is_valid} |") + props = json.loads((SCRATCH / f"catchment_t{t}.geojson").read_text())["features"][0]["properties"] + if t == 20: + print(f" properties: {props}") + +f2n, n2f = hausdorff_1m(fine.exterior, nve.exterior) +print(f"\nfine outline vs NVE boundary, 1 m samples: fine->NVE {f2n:.0f} m, NVE->fine {n2f:.0f} m") + +print("\n## node overlap with NVE (DEM nodes at multiples of 10 m)") +x0, y0, x1, y1 = shapely.union(fine, nve).bounds +xs = np.arange(np.floor(x0 / CELL) * CELL, x1 + CELL, CELL) +ys = np.arange(np.floor(y0 / CELL) * CELL, y1 + CELL, CELL) +gx, gy = np.meshgrid(xs, ys) +shapely.prepare(fine) +shapely.prepare(nve) +ours = shapely.contains_xy(fine, gx, gy) +theirs = shapely.contains_xy(nve, gx, gy) +both = ours & theirs +print(f"ours {ours.sum()} nodes (catchment reports 3049095), NVE {theirs.sum()} nodes, both {both.sum()}") +print(f"of NVE's nodes in ours: {100*both.sum()/theirs.sum():.2f} %; of ours in NVE's: {100*both.sum()/ours.sum():.2f} %") +print(f"ours only {(ours & ~theirs).sum()} nodes ({(ours & ~theirs).sum()*1e-4:.2f} km2)," + f" NVE only {(theirs & ~ours).sum()} nodes ({(theirs & ~ours).sum()*1e-4:.2f} km2)") +diff = shapely.difference(fine, nve) +parts = sorted(getattr(diff, "geoms", [diff]), key=lambda g: -g.area)[:3] +print("largest ours-not-NVE parts:", [(round(p.area / 1e6, 3), tuple(round(v) for v in p.centroid.coords[0])) for p in parts]) +diff = shapely.difference(nve, fine) +parts = sorted(getattr(diff, "geoms", [diff]), key=lambda g: -g.area)[:3] +print("largest NVE-not-ours parts:", [(round(p.area / 1e6, 3), tuple(round(v) for v in p.centroid.coords[0])) for p in parts]) + +print("\n## meshes (median of 3 binary runs; quality from the ASCII run)") +print("| run | triangles | vertices | start vertices | constraint edges | refine s | total s | wall s | maxRSS MB | worst angle | max degree | max error | within tol | Delaunay checked/ambiguous/violations |") +for tag, tol in (("red_t10", 10), ("fine_t10", 10), ("red_t1", 1), ("fine_t1", 1), ("feat_t10", 10)): + rows = [] + for i in (1, 2, 3): + md = (LOG / f"mesh_{tag}_run{i}.stats.md").read_text() + err = (LOG / f"mesh_{tag}_run{i}.err").read_text() + rows.append(( + float(re.search(r"^\| refine \| ([\d.]+)", md, re.M).group(1)), + float(re.search(r"^\| \*\*total\*\* \| \*\*([\d.]+)", md, re.M).group(1)), + float(re.search(r"([\d.]+) real", err).group(1)), + int(re.search(r"(\d+)\s+maximum resident", err).group(1)), + )) + get = lambda k: med(r[k] for r in rows) # noqa: E731 + md = (LOG / f"mesh_{tag}_ascii.stats.md").read_text() + err = (LOG / f"mesh_{tag}_ascii.err").read_text() + item = lambda name: int(re.search(rf"^\| {name} \| (\d+)", md, re.M).group(1)) # noqa: E731 + max_error = float(re.search(r"achieved max error ([\d.]+) m", err).group(1)) + m = bench.read_vtk_ascii(SCRATCH / f"mesh_{tag}_ascii.vtk") + q = bench.quality(m.points, m.triangles, m.edges, tol, max_error) + print(f"| {tag} | {item('output triangles')} | {item('output vertices')} | {item('start vertices')} |" + f" {item('constraint edges')} | {get(0):.3f} | {get(1):.3f} | {get(2):.2f} | {get(3)/1e6:.0f} |" + f" {q.worst_angle:.4g}° | {q.max_degree} | {max_error:.4f} | {q.within_tolerance} |" + f" {q.delaunay_checked}/{q.delaunay_ambiguous}/{q.delaunay_violations} |") + print(f" runs (refine, total, wall, maxRSS): {rows}") + +print("\n## land cover inside the reduced catchment (CORINE 2018, clipped by shapely)") +red = polygon(SCRATCH / "catchment_t20.geojson") +conn = sqlite3.connect("../rasputin_data/corine2018_dtm10_utm33.gpkg") +bx = red.bounds +ids = [r[0] for r in conn.execute( + "SELECT id FROM rtree_corine2018_geom WHERE maxx >= ? AND minx <= ? AND maxy >= ? AND miny <= ?", + (bx[0], bx[2], bx[1], bx[3]))] +area: dict[str, float] = {} +for fid in ids: + blob, code = conn.execute("SELECT geom, code_18 FROM corine2018 WHERE fid = ?", (fid,)).fetchone() + a = shapely.intersection(decode_geometry(blob)[1], red).area + if a > 0: + area[code] = area.get(code, 0.0) + a +total = sum(area.values()) +print(f"{len(ids)} candidate rows; covered {total/1e6:.3f} km2 of {red.area/1e6:.3f}") +for code, a in sorted(area.items(), key=lambda kv: -kv[1]): + print(f"| {code} | {a/1e6:.3f} km2 | {100*a/red.area:.2f} % |") +m = bench.read_vtk_ascii(SCRATCH / "mesh_feat_t10_ascii.vtk") +text = (SCRATCH / "mesh_feat_t10_ascii.vtk").read_text(encoding="latin-1").split("\n") +for name in ("land_cover", "water"): + i = next(k for k, line in enumerate(text) if line.startswith(f"{name} 1 ")) + n = int(text[i].split()[2]) + vals = np.array(" ".join(text[i + 1 : i + 1 + n]).split()[:n], dtype=int) + print(f"mesh cells with {name}=1: {int(vals.sum())} (of {n} cells: {len(m.edges)} lines + {len(m.triangles)} triangles)") diff --git a/docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson b/docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson new file mode 100644 index 00000000..5a63f92e --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson @@ -0,0 +1 @@ +{"type": "FeatureCollection", "crs": {"type": "name", "properties": {"name": "EPSG:25833"}}, "features": [{"type": "Feature", "properties": {"seed": [8.5425, 61.3512], "seed_crs": "EPSG:4326", "nodes": 3049095, "fine_vertices": 17812, "fine_area_m2": 304909550.0, "reduced_vertices": 740, "reduced_area_m2": 304909549.9999997, "outline_tolerance_m": 20.0, "windows": [[1438, 3024], [2236, 3646], [3520, 4813]]}, "geometry": {"type": "Polygon", "coordinates": [[[137640.0, 6832215.0], [137597.74193548388, 6832243.172043011], [137458.29391628047, 6832099.4653071035], [137272.3691336265, 6832052.957247976], [137184.90341039226, 6831980.659617026], [137201.01864562993, 6831903.119034275], [136903.35714285713, 6831628.214285715], [136951.62316462147, 6831459.250051314], [136817.0754716981, 6831303.773584906], [136830.00038878224, 6831200.698025623], [136684.96189964417, 6831184.071259593], [136435.6189184878, 6831273.199872388], [136395.8257108954, 6831332.053447732], [135865.0, 6831270.0], [135885.40185471406, 6831247.897990727], [135635.0, 6830980.0], [135645.86030125848, 6830903.977891191], [135592.49409990516, 6830828.017502136], [135632.2171388938, 6830610.685336256], [135633.68724762482, 6830376.88081057], [135589.39452549722, 6830304.652792333], [135964.71533574187, 6829950.284664258], [136513.89924577868, 6829517.547606546], [136530.10616212134, 6829406.420685099], [136610.09717929043, 6829289.689134087], [136572.75051124743, 6829053.128834356], [136676.59931295138, 6828870.89274336], [136835.26730887045, 6828688.502266878], [136877.84676395185, 6828693.759262805], [137088.33333333334, 6828465.0], [137057.04474659674, 6828423.815620678], [137186.22954293294, 6828252.867802737], [137245.515878252, 6828247.818994536], [137496.96684188143, 6827874.340167733], [137586.42857142858, 6827642.857142857], [137591.7123287671, 6827533.287671233], [137650.25901788575, 6827515.823043272], [137722.1739130435, 6827398.043478261], [137818.72049870715, 6827340.082686023], [138004.6911740526, 6827112.268315339], [138129.00346474705, 6827096.180873251], [138191.4623733689, 6826868.500055129], [138135.0, 6826840.0], [138257.3819876784, 6826602.968908627], [138238.62261836408, 6826547.302507902], [138328.53135983145, 6826454.936038019], [138303.061546746, 6826412.9448269205], [138419.98539481877, 6826358.102763013], [138385.0, 6826290.0], [138514.84023017494, 6826180.874040825], [138576.58487743896, 6825839.170865189], [138718.05190812348, 6825860.803012173], [138802.4414514038, 6825760.811100701], [138781.7282051282, 6825686.420512821], [138844.19485053458, 6825643.713724635], [138863.115942029, 6825468.115942029], [138808.125, 6825413.125], [138893.70368496655, 6825265.600430721], [138929.1959398952, 6825247.993253361], [138960.0, 6825125.0], [139175.0, 6824910.0], [139116.94111216627, 6824813.235186944], [138949.77487567315, 6824698.088308348], [139098.8443611255, 6824566.148784354], [139065.0, 6824470.0], [139120.51022533225, 6824410.39306786], [138917.7332545976, 6824214.83718576], [138941.75736597166, 6824169.45830872], [139011.99137029288, 6824182.391474895], [139117.5, 6824107.5], [139035.84347231753, 6823966.130352153], [139261.66666666666, 6823673.333333333], [139288.7288135593, 6823534.406779661], [139345.0, 6823450.0], [139371.095371097, 6823292.340466289], [139498.8679245283, 6823156.509433962], [139550.6470393466, 6823014.014679101], [139505.71212772815, 6822934.286085522], [139649.8381673627, 6822703.225457756], [139763.43880354054, 6822680.531590193], [139845.0, 6822563.895642195], [139845.0, 6822510.0], [139933.17778456188, 6822421.822215438], [140025.35194260805, 6822426.207276433], [140062.3109441052, 6822509.62188821], [140168.80157170922, 6822471.640471512], [140210.0, 6822525.0], [140505.9208167372, 6822229.079183263], [140604.20249812232, 6822247.359009317], [140804.2085916462, 6821961.682867177], [140899.38604496184, 6822003.772089924], [140936.32, 6822077.64], [141007.86755289164, 6822086.714784027], [141157.0, 6821912.0], [141098.07046615635, 6821872.320620127], [141142.49400479617, 6821822.482014389], [141112.51491139995, 6821701.06200086], [141355.0, 6821440.0], [141336.42857142858, 6821318.571428572], [141365.0, 6821290.0], [141224.0416141236, 6821147.389659521], [141408.40847831414, 6820937.04239157], [141395.0, 6820870.0], [141499.44444444444, 6820765.555555556], [141465.45526353584, 6820730.716645124], [141593.33333333334, 6820571.666666667], [141569.6974925791, 6820536.908077322], [141659.26065162907, 6820424.260651629], [141651.84448836243, 6820324.296281796], [141772.8260869565, 6820162.173913044], [141816.25, 6820061.25], [142002.6073224465, 6820199.636999488], [142075.57326495965, 6820154.970894979], [142145.0, 6820180.0], [142126.74311926606, 6820253.302752294], [142205.56356523783, 6820277.275755774], [142217.27272727274, 6820382.2727272725], [142326.66666666666, 6820308.333333333], [142388.09997058453, 6820353.3151237555], [142415.0, 6820220.0], [142562.42090701312, 6820072.579092987], [142496.78952869328, 6819997.666816089], [142545.0, 6819670.0], [142500.23076923078, 6819625.230769231], [142605.5162615763, 6819519.49576577], [142622.45828279847, 6819373.558505002], [142764.9480968858, 6819290.017301038], [142802.37950475726, 6819326.935949897], [142957.33098629283, 6819326.905978922], [143108.52788881605, 6819207.685083574], [143320.7669052955, 6819176.611933246], [143329.91124260356, 6819067.061143984], [143440.0, 6818955.0], [143500.0, 6818955.0], [143668.3153049157, 6818845.289064038], [143728.84615384616, 6818876.923076923], [143832.3291834707, 6818814.509030335], [143810.82940272454, 6818728.670749027], [143766.25954198473, 6818729.122137405], [143798.94779576827, 6818644.411090414], [143724.88005997002, 6818546.64167916], [143728.33333333334, 6818373.333333333], [143665.70455182076, 6818215.276290152], [143951.67372638808, 6818022.302804808], [144000.3285880899, 6817943.895927811], [144332.65235579392, 6817745.0], [144365.87257886506, 6817745.0], [144498.42865702798, 6817640.664382635], [144528.09024704012, 6817525.1893014405], [144490.5572646479, 6817377.004492921], [144252.18583446817, 6817108.268572732], [144020.63138396858, 6817064.226122432], [144103.0, 6816882.0], [144078.0923694779, 6816670.361445783], [144277.27753076606, 6816595.668464988], [144557.5, 6816682.5], [144680.52583922824, 6816643.4437025385], [144647.251725348, 6816502.276289624], [144749.5221573919, 6816395.708043541], [144745.86872586873, 6816358.281853282], [144850.0, 6816345.0], [145040.46826041545, 6816171.163978183], [145023.57142857142, 6816067.142857143], [145102.99888205703, 6815921.453325881], [145065.0, 6815880.0], [145013.74513079796, 6815591.887975379], [145075.07522179233, 6815561.809962153], [145124.17391542502, 6815188.856079254], [145175.52631578947, 6815144.736842105], [145195.0, 6815040.0], [145155.0, 6815000.0], [145172.14285714287, 6814947.142857143], [145111.0, 6814886.0], [145139.67534438922, 6814816.195210808], [145468.04373820877, 6814477.181769896], [145615.5698981443, 6814257.181863997], [145604.56123577632, 6814219.313332136], [145914.0, 6813891.8], [146001.37096774194, 6813903.629032258], [146144.44444444444, 6813760.555555556], [146350.0, 6813645.0], [146335.0, 6813620.0], [146401.43835616438, 6813606.712328767], [146405.0, 6813460.0], [146366.15659509707, 6813430.677432868], [146527.69600301265, 6813171.358123098], [146503.70486860673, 6813136.148679506], [146611.05376965224, 6813106.046539101], [146847.60120585765, 6813085.9007122805], [146966.527700098, 6812958.03581416], [147052.18302184745, 6812997.871228581], [147169.1780821918, 6812955.273972603], [147196.0, 6812995.0], [147346.35462052366, 6812939.574463671], [147421.92532942898, 6812993.97510981], [147479.09752825845, 6812965.4824469825], [147558.09108487266, 6813045.788113164], [147519.6041081912, 6813077.697945904], [147608.4982822893, 6813168.276441781], [147667.1334431631, 6813151.573311367], [147790.91385169327, 6813284.549521231], [147842.47737909516, 6813293.602184087], [148220.3207003201, 6813636.607356718], [148361.08580106302, 6813519.130599848], [148460.0, 6813485.0], [148493.6742456259, 6813531.714563435], [148606.3060144191, 6813516.527023941], [148728.69185386883, 6813553.855230316], [148879.0, 6813714.0], [148892.34732105123, 6813769.057699337], [149049.7259037944, 6813832.720556439], [149340.6027820711, 6813827.364760432], [149443.44871181578, 6813949.081502975], [149783.62941755238, 6814088.053182289], [149787.3673469388, 6814149.102040816], [149913.8162243507, 6814447.021466103], [150027.98398457805, 6814525.496019012], [150141.04797171257, 6814506.503744745], [150194.78260869565, 6814540.217391305], [150435.0, 6814530.0], [150360.75, 6814604.25], [150416.25, 6814698.75], [150445.8695652174, 6814670.869565218], [150597.77777777778, 6814822.777777778], [150799.40637330865, 6814738.635584262], [150994.22455816425, 6814787.81637211], [151012.97354325702, 6814963.590357457], [150951.63579225936, 6815083.9067151835], [150962.72727272726, 6815115.0], [151030.48751697718, 6815110.134990859], [151102.07909634485, 6815245.279159011], [151271.65451519756, 6815239.984295697], [151308.7682386734, 6815311.079564392], [151460.57106393375, 6815222.896297737], [151563.125, 6815341.875], [151684.30056524486, 6815324.256142117], [151834.57169459964, 6815543.575418995], [151893.27946552495, 6815707.847712303], [151880.50264550265, 6815858.783068783], [151990.6010042372, 6815986.497164915], [152060.12504789885, 6816209.749904202], [152035.0, 6816290.0], [152084.0909090909, 6816311.515151516], [152092.80805687205, 6816360.912322275], [152299.2829360342, 6816553.41516797], [152422.85714285713, 6816443.571428572], [152477.49710791605, 6816486.557593786], [152521.42857142858, 6816456.428571428], [152592.2393430768, 6816512.669524852], [152596.76479812863, 6816637.450847148], [152549.84938495586, 6816705.217555064], [152762.9001756771, 6816982.259353165], [152932.75378831074, 6816779.468768818], [153164.8737720111, 6816642.538832252], [153165.02191609266, 6816592.614276769], [153287.77621563748, 6816513.054280999], [153606.79801008, 6816526.356951625], [153739.00240476284, 6816450.161591488], [153688.694621263, 6816254.935961682], [153870.23303808243, 6816121.92212627], [153852.40340794573, 6816058.978151899], [154044.0, 6816041.0], [154143.77704498603, 6815941.222955014], [154418.8745345136, 6816050.63404243], [154483.23751478214, 6816023.997543892], [154682.17736722674, 6816036.966404154], [154750.1219986578, 6816065.8835090455], [154933.96544251748, 6816022.994869282], [155041.75461367719, 6816104.826975314], [155126.02943487625, 6816021.415705997], [155364.18056245425, 6815908.366837521], [155473.4518828452, 6815801.338912134], [155576.0787598512, 6815918.459955267], [155673.1800339771, 6815792.479119474], [155757.8260869565, 6815830.217391305], [155829.44544463628, 6815790.353100789], [155879.4713349174, 6815939.219827721], [156068.19036510444, 6815921.3572261715], [156182.09302325582, 6815835.930232558], [156202.26098923897, 6815726.969625716], [156260.625, 6815685.0], [156376.49083068728, 6815772.95243927], [156345.0, 6815850.0], [156415.0, 6815960.0], [156350.33945324935, 6816024.449926077], [156217.47260584292, 6816064.129555845], [156130.1851851852, 6816183.333333333], [156111.44192111958, 6816302.650209459], [156391.81677091605, 6816286.592340345], [156426.4556794017, 6816495.392917727], [156558.90990598194, 6816665.272419229], [156707.94926474956, 6816481.194127676], [157091.39304937274, 6816355.131812555], [157100.0, 6816425.0], [157305.47173560766, 6816362.353989709], [157390.1019995881, 6816381.039124564], [157273.75, 6816628.75], [157337.97297297296, 6816740.540540541], [157316.11111111112, 6816773.333333333], [157380.42857142858, 6816864.571428572], [157345.0, 6816900.0], [157474.2857142857, 6817029.285714285], [157510.0, 6816995.0], [157604.04255319148, 6817011.808510638], [157765.5383315261, 6816937.592302455], [157812.5, 6816973.0], [157925.68808132003, 6816903.948551952], [157895.0, 6816803.333333333], [157951.5116479476, 6816690.878120243], [157875.94978325407, 6816565.120443983], [157930.0, 6816505.0], [158094.7585828061, 6816526.831066811], [158441.32971239905, 6816305.462029215], [158484.7425422148, 6816089.556151916], [158755.6582352538, 6815868.595579216], [158717.7460571056, 6815769.164069157], [158766.58437423463, 6815731.4305657605], [159016.40058227236, 6815653.148119857], [158997.54284853733, 6815563.793145235], [159254.1424648113, 6815392.845778662], [159190.83333333334, 6815303.229166667], [159241.66666666666, 6815233.333333333], [159330.39325842698, 6815254.7191011235], [159444.41583315123, 6815179.457590648], [159518.08560806658, 6815001.26311421], [159610.0, 6814955.0], [159655.0, 6815000.0], [159669.50205278205, 6815115.172084419], [159871.07197186654, 6815149.299696093], [159952.22222222222, 6815107.222222222], [160069.10729420255, 6815179.692141028], [160134.23048735395, 6815139.763186853], [160204.5, 6815139.5], [160398.81895860942, 6815238.118430292], [160522.43506943982, 6815104.396783581], [160624.0604394343, 6815155.430899723], [160830.04368030006, 6814930.551773602], [161066.24030172775, 6814855.777698366], [161350.01393145724, 6814953.180551685], [161510.0, 6814915.0], [161598.26306397247, 6814975.643329668], [161780.19607843139, 6814952.647058823], [161840.0, 6814845.0], [161876.86315789475, 6814861.484210527], [161954.70612328488, 6814750.924636494], [162190.0934019556, 6814568.886670609], [162169.10988743644, 6814521.359502801], [162266.37049742692, 6814460.777893015], [162299.0909090909, 6814490.454545454], [162350.0, 6814465.0], [162403.04314329737, 6814538.998459168], [162378.6015243573, 6814574.734254772], [162522.26744186046, 6814651.220930233], [162516.74387723103, 6814616.8511016825], [162587.29036769632, 6814563.175514227], [162620.0, 6814415.0], [162751.02946044356, 6814479.826216484], [162840.0, 6814475.0], [162961.65952121164, 6814353.340478788], [163276.6738121217, 6814158.326187878], [163248.3261878783, 6814106.673812122], [163318.57142857142, 6814036.428571428], [163410.0, 6814085.0], [163484.88372093023, 6814020.813953488], [163609.2154664364, 6814065.748037321], [163892.62440494008, 6813782.779033474], [163741.62921348313, 6813622.471910113], [163552.71878099983, 6813603.240766249], [163471.222741433, 6813515.288161994], [163552.03058304757, 6813460.807038269], [163423.73463132296, 6813291.364642098], [163644.32819325296, 6813163.016771873], [163567.88869182774, 6812999.652704162], [163698.85066599218, 6812751.698001093], [163635.8378165902, 6812658.929392556], [163821.70311841829, 6812385.71047471], [163808.98071837446, 6812291.979156205], [163917.43243243243, 6812173.513513514], [163923.49914439756, 6812025.5785719445], [164084.99727816283, 6811926.5085076], [164173.68676790822, 6811790.963314203], [164263.90259560433, 6811485.964099594], [164428.94132231225, 6811306.58511767], [164441.15527007577, 6811201.8750435], [164543.2696481575, 6811027.505426047], [164525.81517928684, 6810915.243012267], [164687.76002290647, 6810745.419509138], [164648.83532978804, 6810678.5318045085], [164645.0, 6810520.0], [164790.76588878914, 6810427.424689603], [165041.0616470588, 6810353.613647059], [165255.01829893093, 6810409.439022539], [165355.3015424999, 6810379.725468889], [165807.77777777778, 6810492.777777778], [165862.34620659557, 6810453.302744165], [166164.88580153522, 6810652.314058491], [166357.33333333334, 6810733.222222222], [166504.79644088983, 6810702.478892937], [166738.2570838119, 6810924.066940101], [167062.79121971395, 6810825.960480761], [167499.8949211909, 6810833.021015761], [167622.72727272726, 6810912.2727272725], [167650.0, 6810885.0], [167833.04171334364, 6810991.066069858], [167898.69837386673, 6811139.245934667], [168105.2605042017, 6811334.478991597], [168024.4240491967, 6811467.551301144], [168059.70967741936, 6811663.548387097], [168036.46786122074, 6811744.73555327], [168048.9334216628, 6811997.866843326], [168105.70533241477, 6812299.294667585], [168046.04465184055, 6812358.955348159], [168134.59571527297, 6812469.595715273], [168120.12841631015, 6812599.02955548], [168305.32710050038, 6812698.043566311], [168440.4606585253, 6812940.626835678], [168455.73499161843, 6812932.546442499], [168521.4814814815, 6813123.518518519], [168216.6542235406, 6813511.0663074], [168345.10455040447, 6813676.958179838], [168406.41025641025, 6813652.435897436], [168454.5945945946, 6813719.594594595], [168498.42973970083, 6813714.518389612], [168563.73029364986, 6813866.044721538], [168520.9112981316, 6814040.718329472], [168533.679245283, 6814088.867924528], [168417.0148486697, 6814242.79852416], [168314.74741928652, 6814237.949877645], [168117.49437586972, 6814455.506079413], [168229.12993961514, 6814745.610354501], [168439.9903529381, 6814713.798655271], [168566.31719061558, 6814723.66341645], [168707.1066854023, 6814894.870021078], [168654.27544051432, 6814970.128122582], [168356.7192067656, 6815072.068766756], [168204.62177231206, 6815255.0], [168042.2355061998, 6815234.290297861], [167990.77338129497, 6815283.543165468], [167837.66177546134, 6815323.520864536], [167901.0237011057, 6815437.318948756], [167523.23344481905, 6815822.238529126], [167451.0630340981, 6815801.026141856], [167306.06762680024, 6815847.0788979335], [167190.0, 6815705.0], [167044.6100144439, 6815835.117958594], [167237.1842529126, 6816083.455123402], [167209.17775182932, 6816131.461973824], [167355.66460390794, 6816329.335396092], [167329.44444444444, 6816355.555555556], [167387.96205630354, 6816616.499388005], [167497.21773979685, 6816778.822526856], [167451.0272743894, 6816847.633378786], [167549.15078972108, 6817139.476878677], [167243.98866247002, 6817199.603574305], [167174.06875028307, 6817294.2011025585], [166996.3145335883, 6817359.908163894], [166971.0, 6817472.0], [166884.0909090909, 6817515.454545454], [166775.05378511976, 6817506.144659716], [166566.31215009288, 6817598.261576196], [166549.1625748903, 6817751.680748954], [166492.55927025742, 6817753.93929139], [166233.4210483845, 6818133.2116055], [166115.3551138529, 6818471.641239512], [166302.33333333334, 6818686.666666667], [166341.41249586048, 6818833.392177055], [166734.65753424657, 6819245.0], [166785.99877184516, 6819232.320050565], [167011.5880375926, 6819333.411962408], [166880.89496928419, 6819542.104949057], [166924.33247057255, 6819628.391934455], [167058.63821719208, 6819722.779751538], [167131.96713615023, 6819871.615023474], [167098.4764497793, 6819919.100137919], [167161.05042016806, 6820079.075630252], [167146.6634105652, 6820200.538060977], [167282.5, 6820387.5], [167248.75347801892, 6820436.769616026], [167256.65750346836, 6820618.65411646], [167206.68033090775, 6820686.246072778], [167267.62458181655, 6820786.998011628], [167215.5, 6820851.833333333], [167259.5918349431, 6820946.2756069675], [167216.78021978022, 6821043.472527472], [167354.9859947679, 6821224.385468366], [167282.85714285713, 6821420.238095238], [167235.0, 6821430.0], [167265.53796103233, 6821483.618292585], [166947.04765552984, 6821784.847523488], [166720.31987415714, 6821775.924246227], [166235.69674469688, 6822241.87931986], [165934.52915906822, 6822079.799817318], [165911.88442211057, 6821905.326633166], [165679.2638544093, 6822102.583799018], [165423.29185031544, 6822158.237403855], [165223.8708631178, 6822108.915472963], [165124.66666666666, 6822119.666666667], [165028.29268292684, 6822023.292682927], [164829.50572649855, 6822175.708886004], [164815.0, 6822247.133283693], [163998.30829096286, 6823129.58053719], [163792.85178620127, 6823026.627806563], [163805.71543846332, 6823001.367150615], [163419.6545146307, 6822556.426685418], [163341.53802613975, 6822599.542039724], [163180.67637670063, 6822569.993789102], [163078.72477212505, 6822603.932886762], [162872.4742136763, 6822509.121809682], [162653.07692307694, 6822468.076923077], [162592.44541484717, 6822515.305676856], [162393.33333333334, 6822501.666666667], [162248.53606027988, 6822609.230355221], [162144.63927978635, 6822637.4066103045], [161895.76822711964, 6822882.543396485], [161826.2174817898, 6823161.207075963], [161645.77714427325, 6823342.256978973], [161562.80898876404, 6823267.921348315], [161490.0, 6823285.0], [161266.79030192975, 6822970.667908133], [160971.81136296896, 6822883.205454997], [160959.26763371166, 6822774.73558198], [160806.80737806426, 6822591.108377964], [160412.43243243243, 6822545.810810811], [160232.1669561144, 6822688.572761424], [160162.72727272726, 6822677.337662337], [160116.66826003825, 6822705.004780115], [160145.0, 6822760.0], [159971.1759431634, 6822771.255124691], [159841.0655737705, 6822924.918032787], [159771.92534466315, 6822925.010838464], [159635.0, 6823080.0], [159650.36697247706, 6823161.758409786], [159531.98256241117, 6823235.420569002], [159420.0, 6823155.0], [159301.30434782608, 6823135.217391305], [159260.0, 6823205.0], [159084.67146380994, 6823195.956785547], [158756.73403076752, 6823385.953928209], [158718.37016574584, 6823475.46961326], [158601.9285083186, 6823496.844012202], [158573.4, 6823588.0], [158618.70986920333, 6823633.745541022], [158516.53957800812, 6823634.453374332], [158514.84711211777, 6823750.481313704], [158377.36809690003, 6823777.6319031], [158296.42857142858, 6823858.571428572], [158336.72951870546, 6824128.23415214], [158552.46797385416, 6824354.995921588], [158546.4730999146, 6824410.388556789], [158631.45851142038, 6824504.988622307], [158627.8440081232, 6824573.971657521], [158751.9642857143, 6824783.035714285], [158635.0, 6824900.0], [158610.74017743874, 6825026.858048174], [158528.06397840928, 6825124.598348944], [158584.43570265872, 6825242.722011213], [158517.10595515018, 6825372.10595515], [158679.34782608695, 6825534.347826087], [158626.98421610988, 6825650.749890815], [158247.49005137023, 6826057.537393148], [157917.51924390567, 6826093.847710612], [157786.67985941586, 6826008.639392729], [157378.7747430343, 6826299.240397112], [157280.0, 6826305.0], [156995.2896710623, 6826597.929300996], [156855.0, 6826887.580645162], [156855.0, 6826987.142857143], [156781.4229437834, 6827014.580683301], [156694.18284996282, 6826960.430362051], [156580.0, 6827035.0], [156427.22619874487, 6826978.528233891], [156322.20362574054, 6826978.105847667], [155696.28811047922, 6826444.645677502], [155698.97111913358, 6826325.956678701], [155648.69638047065, 6826220.796272789], [155678.87947168696, 6826174.505945391], [155347.17741730215, 6825877.605460952], [155324.0, 6825899.0], [155239.92119495236, 6825793.362526991], [155329.7510011255, 6825692.161659015], [155235.0, 6825520.0], [155273.1181127542, 6825469.420196538], [155244.836861259, 6825398.880280872], [155275.0, 6825185.998955788], [155144.2457153856, 6825030.026256759], [155146.80368488174, 6824888.237334569], [154966.50743680075, 6824752.349697339], [154940.0, 6824675.0], [154672.96470588236, 6824566.05882353], [154562.5179401709, 6824615.016627856], [154475.2427402175, 6824558.400979183], [154061.17947062736, 6824952.682466841], [153948.3932853717, 6824889.604316547], [153910.0, 6824925.0], [153633.8414655395, 6824675.296095573], [153471.88416499528, 6824573.500874762], [153426.8792042776, 6824646.075720117], [153309.49074264755, 6824577.633097253], [153247.0679408379, 6824447.49916059], [153120.6811731315, 6824334.545884579], [152990.0, 6824405.0], [152808.32406471862, 6824259.991821811], [152677.78021868292, 6824315.803335164], [152626.22609219502, 6824579.0548049705], [152384.7992433376, 6824779.837076458], [152278.09075573212, 6824846.237759655], [152213.0, 6824712.0], [152161.25, 6824706.25], [152091.3911917246, 6824766.539108512], [151782.18418519443, 6824750.416064381], [151575.3332200404, 6824670.751515386], [151482.09492582743, 6824535.961711694], [151237.8509637461, 6824588.767535829], [151048.6855839793, 6824570.609580332], [150772.67070690743, 6824635.934637659], [150647.1180714055, 6824694.774777073], [150517.2738001314, 6824882.720017374], [150490.57487511373, 6824992.36659076], [150368.1541865854, 6825058.422906707], [150143.10841580582, 6825308.745261659], [150059.24676187313, 6825284.784789107], [149914.90366404055, 6825339.163638309], [149866.64025356577, 6825380.546751189], [149803.0982650025, 6825359.3660883345], [149713.27636316314, 6825441.684162582], [149593.7657585323, 6825331.10442416], [149547.14285714287, 6825340.714285715], [149490.0, 6825295.0], [149274.90679094542, 6825546.364846871], [149305.0, 6825590.0], [149096.13636363635, 6825798.863636363], [149137.1672067933, 6825876.02289043], [148925.0, 6826050.0], [148928.31606217616, 6826129.481865285], [148692.09151447949, 6826397.939703955], [148622.70970824885, 6826338.784241047], [148560.55555555556, 6826383.333333333], [148590.88585017837, 6826421.236623067], [148546.1879647242, 6826482.32734796], [148627.16378508764, 6826627.871523432], [148499.26859107963, 6826755.395468691], [148682.25090291057, 6826952.749097089], [148666.00580270792, 6827060.038684719], [148705.0, 6827133.333333333], [148653.18181818182, 6827111.818181818], [148523.04537521815, 6827177.068062827], [148547.29508196723, 6827213.442622951], [148456.21325244984, 6827278.492767149], [148465.0, 6827340.0], [148325.0, 6827520.0], [148315.0, 6827586.0], [148135.7594936709, 6827449.24050633], [148000.12285927028, 6827414.87714073], [147905.08593415626, 6827465.951652011], [147724.3533032997, 6827485.0], [147546.80814456358, 6827709.264580327], [147618.99196179604, 6827783.092181444], [147447.1647981899, 6827912.16479819], [147353.0386189563, 6827864.118452933], [147237.3367831924, 6827982.47275187], [147137.62411810685, 6828023.250587929], [147043.8427676554, 6827940.959470064], [146646.2475308213, 6828237.569703922], [146510.6303528427, 6828282.464883227], [146480.02232370205, 6828326.315199159], [146243.46153846153, 6828433.269230769], [146166.36363636365, 6828385.909090909], [145892.8579279267, 6828449.293074313], [145698.8515359408, 6828273.418585753], [145603.37953091683, 6828313.336886994], [145625.0, 6828380.0], [145239.0185801349, 6828819.417609053], [145283.04689674792, 6828925.031260869], [145148.23529411765, 6829058.529411765], [145163.20202680613, 6829149.718862373], [144653.03129221033, 6829692.410361127], [144531.66666666666, 6829883.333333333], [144568.93858913428, 6829988.851458391], [144377.39666944387, 6830062.1077173175], [143979.65746991846, 6830084.060252395], [143870.0, 6830045.0], [143733.7129986326, 6830163.906018191], [143487.2764752713, 6830596.022720626], [143333.0561105112, 6830726.501611498], [143216.97162517396, 6830920.114947552], [142763.93285686092, 6831292.534366456], [142450.3096744802, 6831305.16582353], [142317.48989381708, 6831369.199303599], [142189.6591747816, 6831304.743607308], [142082.6455026455, 6831372.883597883], [142014.54293797343, 6831352.083016457], [141930.4563679374, 6831434.037702831], [141864.5673076923, 6831389.567307692], [141799.14123013322, 6831405.247070234], [141787.48680984325, 6831323.967080333], [141636.66666666666, 6831181.666666667], [141475.60008149847, 6831197.302349077], [141233.0548628429, 6830976.832917706], [141509.8184635631, 6830604.366053854], [141495.33898305084, 6830560.677966102], [141564.46000643945, 6830509.770019778], [141545.77362714784, 6830458.905365103], [141632.36842105264, 6830352.631578947], [141545.0, 6830250.0], [141623.8377763633, 6830200.622274657], [141686.23654181996, 6829929.228708523], [141649.55835962144, 6829859.681913775], [141715.0, 6829800.0], [141666.44749290444, 6829677.47398297], [141721.66666666666, 6829581.111111111], [141709.79804479572, 6829513.855587176], [141562.36967840735, 6829412.856355283], [141454.0950811691, 6829492.618725208], [141175.23841093152, 6829271.356306608], [141034.58521433573, 6829410.063663613], [140909.11902109734, 6829406.78201276], [140730.0, 6829515.0], [140600.0, 6829435.0], [140618.06886056298, 6829398.782824378], [140532.1081036239, 6829317.67283945], [140470.0, 6829355.0], [140373.125, 6829268.194444444], [140226.71247727206, 6829229.2699248735], [140045.26763457104, 6829090.899232198], [139907.9834137516, 6829228.536762], [139848.93116912674, 6829387.837838343], [139537.18753273966, 6829777.593713987], [139457.36522433392, 6830009.841604134], [139471.75458260515, 6830190.407476903], [139361.69032642775, 6830442.3604914965], [139385.76923076922, 6830663.846153846], [139297.1158099488, 6830979.101254613], [138957.26103517492, 6831621.836906897], [138742.14602379626, 6831882.475498062], [138744.0186756718, 6831907.097402352], [138398.62650113303, 6832267.215780782], [138303.2242100864, 6832205.139652085], [138029.34806321948, 6832279.209163358], [137863.5294117647, 6832232.647058823], [137825.43219565472, 6832281.692015447], [137640.0, 6832215.0]]]}}]} \ No newline at end of file diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/analysis.txt b/docs/benchmarks/2026-09-29/bygdin/logs/analysis.txt new file mode 100644 index 00000000..ae3d06c2 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/analysis.txt @@ -0,0 +1,48 @@ +## catchment +run 1: windows [('1', '0.62'), ('2', '1.22'), ('3', '2.84')], flood sum 4.68, trace 0.13, reduce 0.04, wall 6.71, maxRSS 1069 MB, peak footprint 923 MB +run 2: windows [('1', '0.61'), ('2', '1.22'), ('3', '2.84')], flood sum 4.67, trace 0.14, reduce 0.04, wall 6.48, maxRSS 989 MB, peak footprint 920 MB +run 3: windows [('1', '0.61'), ('2', '1.22'), ('3', '2.89')], flood sum 4.72, trace 0.11, reduce 0.04, wall 6.53, maxRSS 982 MB, peak footprint 861 MB +median: flood 4.68 s, trace 0.13 s, reduce 0.04 s, wall 6.53 s, other (read, seed, write, start-up) 1.68 s, maxRSS 989 MB, peak footprint 920 MB + +NVE polygon: 305.5393 km2 by shapely, 1376 coords, valid True; published 305.54 km2 + +| tolerance | vertices | area km2 | vs fine m2 | vs NVE 305.54 | Hausdorff densify=0.05 | 1 m: fine->red, red->fine | valid | +| 0 | 5513 | 304.909550 | +0 | -0.206 % | 0.00 m | 0.00, 0.00 m | True | +| 10 | 1382 | 304.909550 | +0 | -0.206 % | 9.99 m | 9.99, 9.96 m | True | +| 20 | 740 | 304.909550 | -2.98e-07 | -0.206 % | 19.92 m | 19.92, 19.62 m | True | + properties: {'seed': [8.5425, 61.3512], 'seed_crs': 'EPSG:4326', 'nodes': 3049095, 'fine_vertices': 17812, 'fine_area_m2': 304909550.0, 'reduced_vertices': 740, 'reduced_area_m2': 304909549.9999997, 'outline_tolerance_m': 20.0, 'windows': [[1438, 3024], [2236, 3646], [3520, 4813]]} +| 50 | 295 | 304.909550 | -1.43e-06 | -0.206 % | 49.85 m | 49.85, 48.06 m | True | + +fine outline vs NVE boundary, 1 m samples: fine->NVE 485 m, NVE->fine 782 m + +## node overlap with NVE (DEM nodes at multiples of 10 m) +ours 3049096 nodes (catchment reports 3049095), NVE 3055360 nodes, both 3028611 +of NVE's nodes in ours: 99.12 %; of ours in NVE's: 99.33 % +ours only 20485 nodes (2.05 km2), NVE only 26749 nodes (2.67 km2) +largest ours-not-NVE parts: [(0.255, (163743, 6813036)), (0.134, (156227, 6826776)), (0.086, (148176, 6827409))] +largest NVE-not-ours parts: [(0.577, (149046, 6826265)), (0.439, (158666, 6826076)), (0.242, (167571, 6815768))] + +## meshes (median of 3 binary runs; quality from the ASCII run) +| run | triangles | vertices | start vertices | constraint edges | refine s | total s | wall s | maxRSS MB | worst angle | max degree | max error | within tol | Delaunay checked/ambiguous/violations | +| red_t10 | 52198 | 26576 | 740 | 952 | 0.034 | 0.356 | 0.62 | 646 | 0.3562° | 12 | 9.9998 | True | 77821/1122/0 | + runs (refine, total, wall, maxRSS): [(0.034, 0.356, 0.63, 643219456), (0.034, 0.355, 0.62, 645562368), (0.033, 0.356, 0.62, 647921664)] +| fine_t10 | 72292 | 38914 | 5513 | 5534 | 0.043 | 0.388 | 0.66 | 647 | 0.6548° | 13 | 10.0000 | True | 105671/1546/0 | + runs (refine, total, wall, maxRSS): [(0.043, 0.39, 0.66, 646496256), (0.043, 0.388, 0.66, 648019968), (0.042, 0.388, 0.66, 647200768)] +| red_t1 | 1129026 | 565511 | 740 | 1994 | 0.497 | 0.927 | 1.38 | 925 | 0.04612° | 18 | 1.0000 | True | 1692542/172872/0 | + runs (refine, total, wall, maxRSS): [(0.497, 0.933, 1.39, 923910144), (0.496, 0.927, 1.38, 925401088), (0.504, 0.915, 1.37, 929644544)] +| fine_t1 | 1137703 | 571651 | 5513 | 5597 | 0.483 | 0.917 | 1.38 | 942 | 0.08338° | 16 | 1.0000 | True | 1703756/172995/0 | + runs (refine, total, wall, maxRSS): [(0.481, 0.902, 1.36, 942112768), (0.483, 0.931, 1.39, 942194688), (0.486, 0.917, 1.38, 941096960)] +| feat_t10 | 83171 | 42110 | 8242 | 8882 | 0.046 | 1.972 | 2.24 | 776 | 0.004079° | 16 | 9.9997 | True | 116398/1095/0 | + runs (refine, total, wall, maxRSS): [(0.046, 2.031, 2.3, 777306112), (0.047, 1.972, 2.24, 774995968), (0.046, 1.966, 2.23, 776355840)] + +## land cover inside the reduced catchment (CORINE 2018, clipped by shapely) +187 candidate rows; covered 304.910 km2 of 304.910 +| 333 | 129.093 km2 | 42.34 % | +| 332 | 65.012 km2 | 21.32 % | +| 322 | 51.849 km2 | 17.00 % | +| 512 | 50.191 km2 | 16.46 % | +| 335 | 7.452 km2 | 2.44 % | +| 412 | 1.021 km2 | 0.33 % | +| 142 | 0.292 km2 | 0.10 % | +mesh cells with land_cover=1: 7835 (of 92053 cells: 8882 lines + 83171 triangles) +mesh cells with water=1: 2005 (of 92053 cells: 8882 lines + 83171 triangles) diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.err new file mode 100644 index 00000000..716f1fb9 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.61 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.84 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.12 s +reduced outline: 5513 vertices, 304.909550 km2, difference 0 m2 (0 relative), tolerance 0 m, 0.00 s + 6.54 real 7.44 user 0.36 sys + 1003683840 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 96789 page reclaims + 109 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 1394 involuntary context switches + 68121011463 instructions retired + 24830697181 cycles elapsed + 913196232 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.out new file mode 100644 index 00000000..837b602e --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t0.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.err new file mode 100644 index 00000000..2fec51ba --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.61 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.86 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.12 s +reduced outline: 1382 vertices, 304.909550 km2, difference 0 m2 (0 relative), tolerance 10 m, 0.04 s + 6.48 real 7.41 user 0.36 sys + 982958080 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 86367 page reclaims + 99 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 890 involuntary context switches + 68320843206 instructions retired + 24823885477 cycles elapsed + 914130120 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.out new file mode 100644 index 00000000..3d07a9d8 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t10.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t10.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.err new file mode 100644 index 00000000..f6d3aa2a --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.62 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.23 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.84 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.13 s +reduced outline: 740 vertices, 304.909550 km2, difference -2.98e-07 m2 (-9.8e-16 relative), tolerance 20 m, 0.04 s + 6.51 real 7.44 user 0.34 sys + 983056384 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 95166 page reclaims + 110 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 903 involuntary context switches + 68359231685 instructions retired + 24848591477 cycles elapsed + 912409824 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.out new file mode 100644 index 00000000..5450d1d1 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.err new file mode 100644 index 00000000..2a39c51b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.62 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.84 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.13 s +reduced outline: 740 vertices, 304.909550 km2, difference -2.98e-07 m2 (-9.8e-16 relative), tolerance 20 m, 0.04 s + 6.71 real 7.52 user 0.38 sys + 1069252608 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 100178 page reclaims + 153 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 659 voluntary context switches + 1464 involuntary context switches + 68481504221 instructions retired + 25139539390 cycles elapsed + 922502416 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.out new file mode 100644 index 00000000..c79cf167 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run1.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.err new file mode 100644 index 00000000..62f5838a --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.61 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.84 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.14 s +reduced outline: 740 vertices, 304.909550 km2, difference -2.98e-07 m2 (-9.8e-16 relative), tolerance 20 m, 0.04 s + 6.48 real 7.44 user 0.30 sys + 988905472 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 92259 page reclaims + 109 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 6 voluntary context switches + 1048 involuntary context switches + 68208970319 instructions retired + 24727903521 cycles elapsed + 920290528 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.out new file mode 100644 index 00000000..904554fb --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run2.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run2.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.err new file mode 100644 index 00000000..7cc08914 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.61 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.89 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.11 s +reduced outline: 740 vertices, 304.909550 km2, difference -2.98e-07 m2 (-9.8e-16 relative), tolerance 20 m, 0.04 s + 6.53 real 7.48 user 0.31 sys + 982482944 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 97529 page reclaims + 109 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 942 involuntary context switches + 68267131103 instructions retired + 24858136046 cycles elapsed + 860800248 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.out new file mode 100644 index 00000000..6451577b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t20_run3.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run3.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.err b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.err new file mode 100644 index 00000000..f6a65ba1 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.err @@ -0,0 +1,25 @@ +window 1: x 140240-170470, y 6811200-6825570, 1438 x 3024 nodes, flood 0.64 s, grown on north, west, south, east +window 2: x 136250-172700, y 6807210-6829560, 2236 x 3646 nodes, flood 1.22 s, grown on north, west +window 3: x 128580-176700, y 6802360-6837550, 3520 x 4813 nodes, flood 2.83 s, contained +seed: lake of 39.936963 km2, 399371 nodes +catchment: 3049095 nodes, 304.909500 km2 of node area +fine outline: 17812 vertices, 304.909550 km2, 0 rings dropped (0 nodes), 1 holes filled (0.000050 km2), traced in 0.13 s +reduced outline: 295 vertices, 304.909550 km2, difference -1.43e-06 m2 (-4.7e-15 relative), tolerance 50 m, 0.04 s + 6.54 real 7.46 user 0.34 sys + 985776128 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 92107 page reclaims + 110 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 1134 involuntary context switches + 68289847475 instructions retired + 24862349973 cycles elapsed + 917144800 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.out b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.out new file mode 100644 index 00000000..af39abb7 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/catchment_t50.out @@ -0,0 +1 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t50.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/commit.txt b/docs/benchmarks/2026-09-29/bygdin/logs/commit.txt new file mode 100644 index 00000000..f3f54b56 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/commit.txt @@ -0,0 +1,2 @@ +675792531373ec1f4297d2fd59281a4745376f00 +b6f5b91f43116a4ce978aeb2b27845766cf200ee7239eabb5bcfeb3d74be6ca0 .venv/lib/python3.14/site-packages/tin_engine/_core.cpython-314-darwin.so diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/exits.txt b/docs/benchmarks/2026-09-29/bygdin/logs/exits.txt new file mode 100644 index 00000000..fb84ac05 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/exits.txt @@ -0,0 +1,27 @@ +catchment_t20_run1 exit=0 +catchment_t20_run2 exit=0 +catchment_t20_run3 exit=0 +catchment_t0 exit=0 +catchment_t10 exit=0 +catchment_t20 exit=0 +catchment_t50 exit=0 +mesh_red_t10_run1 exit=0 +mesh_red_t1_run1 exit=0 +mesh_fine_t10_run1 exit=0 +mesh_fine_t1_run1 exit=0 +mesh_feat_t10_run1 exit=0 +mesh_red_t10_run2 exit=0 +mesh_red_t1_run2 exit=0 +mesh_fine_t10_run2 exit=0 +mesh_fine_t1_run2 exit=0 +mesh_feat_t10_run2 exit=0 +mesh_red_t10_run3 exit=0 +mesh_red_t1_run3 exit=0 +mesh_fine_t10_run3 exit=0 +mesh_fine_t1_run3 exit=0 +mesh_feat_t10_run3 exit=0 +mesh_red_t10_ascii exit=0 +mesh_red_t1_ascii exit=0 +mesh_fine_t10_ascii exit=0 +mesh_fine_t1_ascii exit=0 +mesh_feat_t10_ascii exit=0 diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.err new file mode 100644 index 00000000..7482a0b7 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.err @@ -0,0 +1,22 @@ +79 features kept, 342 dropped outside, 35 clipped, 0 empty skipped +15846 input vertices, 8242 noded vertices +15989 start quality nodes inserted, 2442 start quality skips, 534 constraint feet, 0 feet refused, 17 rounds, 17879 points inserted, 39582 flips, 83171 triangles, achieved max error 9.999732907715497 m, 0 valid DEM nodes not covered, 15614 start triangles, 8242 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 2.43 real 2.75 user 0.21 sys + 773390336 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 65759 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 5 voluntary context switches + 572 involuntary context switches + 42096546004 instructions retired + 9407019502 cycles elapsed + 750945312 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.out new file mode 100644 index 00000000..eadeb36b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_ascii.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md new file mode 100644 index 00000000..8e3f5f70 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md @@ -0,0 +1,66 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --features ../rasputin_data/corine2018_dtm10_utm33.gpkg --features-layer corine2018 --features-map corine --ascii --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_ascii.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_ascii.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 8242 | +| start triangles | 15614 | +| output vertices | 42110 | +| output triangles | 83171 | +| constraint edges | 8882 | +| vertices without data dropped | 0 | +| mesh_feat_t10_ascii.vtk | 4.7 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.07 % | 1.70 % | 0.00408° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 31 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999732907715497 m | 17 | 17879 | 0 | 39582 | 0 | 15989 | 2442 | 534 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.2 % | +| decode | 0.305 | 14.2 % | +| features read | 0.125 | 5.8 % | +| features clip | 1.430 | 66.4 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.017 | 0.8 % | +| start mesh: triangulate | 0.005 | 0.2 % | +| start mesh: constraint edges | 0.016 | 0.7 % | +| refine | 0.046 | 2.1 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.015 | 0.7 % | +| refine: scan (parallel) | 0.013 | 0.6 % | +| refine: split + flip (serial) | 0.009 | 0.4 % | +| refine: setup + output | 0.009 | 0.4 % | +| trim | 0.003 | 0.1 % | +| write: encode | 0.194 | 9.0 % | +| write: disk | 0.006 | 0.3 % | +| other | 0.005 | 0.2 % | +| **total** | **2.156** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.016 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.err new file mode 100644 index 00000000..61b2aaa3 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.err @@ -0,0 +1,22 @@ +79 features kept, 342 dropped outside, 35 clipped, 0 empty skipped +15846 input vertices, 8242 noded vertices +15989 start quality nodes inserted, 2442 start quality skips, 534 constraint feet, 0 feet refused, 17 rounds, 17879 points inserted, 39582 flips, 83171 triangles, achieved max error 9.999732907715497 m, 0 valid DEM nodes not covered, 15614 start triangles, 8242 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 2.30 real 2.55 user 0.22 sys + 777306112 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 65508 page reclaims + 57 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 832 voluntary context switches + 883 involuntary context switches + 39082214782 instructions retired + 8809983601 cycles elapsed + 754861088 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.out new file mode 100644 index 00000000..0b2f6070 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run1.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md new file mode 100644 index 00000000..d6a3cae9 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md @@ -0,0 +1,66 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --features ../rasputin_data/corine2018_dtm10_utm33.gpkg --features-layer corine2018 --features-map corine --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run1.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run1.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 8242 | +| start triangles | 15614 | +| output vertices | 42110 | +| output triangles | 83171 | +| constraint edges | 8882 | +| vertices without data dropped | 0 | +| mesh_feat_t10_run1.vtk | 3.3 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.07 % | 1.70 % | 0.00408° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 31 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999732907715497 m | 17 | 17879 | 0 | 39582 | 0 | 15989 | 2442 | 534 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.2 % | +| decode | 0.316 | 15.6 % | +| features read | 0.206 | 10.1 % | +| features clip | 1.405 | 69.2 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.017 | 0.9 % | +| start mesh: triangulate | 0.005 | 0.3 % | +| start mesh: constraint edges | 0.016 | 0.8 % | +| refine | 0.046 | 2.3 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.015 | 0.7 % | +| refine: scan (parallel) | 0.013 | 0.6 % | +| refine: split + flip (serial) | 0.009 | 0.4 % | +| refine: setup + output | 0.009 | 0.4 % | +| trim | 0.003 | 0.1 % | +| write: encode | 0.007 | 0.3 % | +| write: disk | 0.001 | 0.1 % | +| other | 0.004 | 0.2 % | +| **total** | **2.031** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.015 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.err new file mode 100644 index 00000000..9efc60a9 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.err @@ -0,0 +1,22 @@ +79 features kept, 342 dropped outside, 35 clipped, 0 empty skipped +15846 input vertices, 8242 noded vertices +15989 start quality nodes inserted, 2442 start quality skips, 534 constraint feet, 0 feet refused, 17 rounds, 17879 points inserted, 39582 flips, 83171 triangles, achieved max error 9.999732907715497 m, 0 valid DEM nodes not covered, 15614 start triangles, 8242 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 2.24 real 2.56 user 0.21 sys + 774995968 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 66039 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 16 voluntary context switches + 583 involuntary context switches + 39002546617 instructions retired + 8839202639 cycles elapsed + 752550944 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.out new file mode 100644 index 00000000..78bf8056 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run2.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md new file mode 100644 index 00000000..6db15918 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md @@ -0,0 +1,66 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --features ../rasputin_data/corine2018_dtm10_utm33.gpkg --features-layer corine2018 --features-map corine --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run2.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run2.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 8242 | +| start triangles | 15614 | +| output vertices | 42110 | +| output triangles | 83171 | +| constraint edges | 8882 | +| vertices without data dropped | 0 | +| mesh_feat_t10_run2.vtk | 3.3 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.07 % | 1.70 % | 0.00408° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 31 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999732907715497 m | 17 | 17879 | 0 | 39582 | 0 | 15989 | 2442 | 534 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.2 % | +| decode | 0.308 | 15.6 % | +| features read | 0.131 | 6.6 % | +| features clip | 1.432 | 72.6 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.017 | 0.9 % | +| start mesh: triangulate | 0.005 | 0.3 % | +| start mesh: constraint edges | 0.016 | 0.8 % | +| refine | 0.047 | 2.4 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.015 | 0.8 % | +| refine: scan (parallel) | 0.013 | 0.7 % | +| refine: split + flip (serial) | 0.010 | 0.5 % | +| refine: setup + output | 0.009 | 0.4 % | +| trim | 0.003 | 0.2 % | +| write: encode | 0.005 | 0.3 % | +| write: disk | 0.001 | 0.0 % | +| other | 0.004 | 0.2 % | +| **total** | **1.972** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.015 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.err new file mode 100644 index 00000000..da3a2ee0 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.err @@ -0,0 +1,22 @@ +79 features kept, 342 dropped outside, 35 clipped, 0 empty skipped +15846 input vertices, 8242 noded vertices +15989 start quality nodes inserted, 2442 start quality skips, 534 constraint feet, 0 feet refused, 17 rounds, 17879 points inserted, 39582 flips, 83171 triangles, achieved max error 9.999732907715497 m, 0 valid DEM nodes not covered, 15614 start triangles, 8242 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 2.23 real 2.56 user 0.21 sys + 776355840 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 67674 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 564 involuntary context switches + 39056894174 instructions retired + 8846481892 cycles elapsed + 753927224 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.out new file mode 100644 index 00000000..d2dbbacc --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run3.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md new file mode 100644 index 00000000..3315bac0 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md @@ -0,0 +1,66 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --features ../rasputin_data/corine2018_dtm10_utm33.gpkg --features-layer corine2018 --features-map corine --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_feat_t10_run3.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_feat_t10_run3.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 8242 | +| start triangles | 15614 | +| output vertices | 42110 | +| output triangles | 83171 | +| constraint edges | 8882 | +| vertices without data dropped | 0 | +| mesh_feat_t10_run3.vtk | 3.3 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.07 % | 1.70 % | 0.00408° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 31 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999732907715497 m | 17 | 17879 | 0 | 39582 | 0 | 15989 | 2442 | 534 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.2 % | +| decode | 0.312 | 15.9 % | +| features read | 0.125 | 6.4 % | +| features clip | 1.427 | 72.6 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.017 | 0.9 % | +| start mesh: triangulate | 0.005 | 0.3 % | +| start mesh: constraint edges | 0.016 | 0.8 % | +| refine | 0.046 | 2.4 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.015 | 0.8 % | +| refine: scan (parallel) | 0.013 | 0.7 % | +| refine: split + flip (serial) | 0.009 | 0.5 % | +| refine: setup + output | 0.008 | 0.4 % | +| trim | 0.003 | 0.2 % | +| write: encode | 0.005 | 0.3 % | +| write: disk | 0.001 | 0.0 % | +| other | 0.004 | 0.2 % | +| **total** | **1.966** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.015 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.err new file mode 100644 index 00000000..5f481d65 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 21 constraint feet, 0 feet refused, 22 rounds, 22612 points inserted, 54390 flips, 72292 triangles, achieved max error 9.99996099528471 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.78 real 1.20 user 0.14 sys + 662913024 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 48750 page reclaims + 109 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 4 voluntary context switches + 645 involuntary context switches + 16629498139 instructions retired + 4178781410 cycles elapsed + 639992768 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.out new file mode 100644 index 00000000..29b4e3ca --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_ascii.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md new file mode 100644 index 00000000..29187989 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 10 --ascii --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_ascii.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_ascii.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 38914 | +| output triangles | 72292 | +| constraint edges | 5534 | +| vertices without data dropped | 0 | +| mesh_fine_t10_ascii.vtk | 3.8 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.01 % | 1.10 % | 0.655° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 13 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.99996099528471 m | 22 | 22612 | 0 | 54390 | 0 | 10789 | 1841 | 21 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.006 | 1.3 % | +| decode | 0.310 | 61.3 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.006 | 1.2 % | +| start mesh: triangulate | 0.003 | 0.6 % | +| start mesh: constraint edges | 0.007 | 1.4 % | +| refine | 0.043 | 8.5 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 2.2 % | +| refine: scan (parallel) | 0.018 | 3.5 % | +| refine: split + flip (serial) | 0.009 | 1.9 % | +| refine: setup + output | 0.005 | 0.9 % | +| trim | 0.003 | 0.5 % | +| write: encode | 0.121 | 24.0 % | +| write: disk | 0.003 | 0.5 % | +| other | 0.003 | 0.6 % | +| **total** | **0.505** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.013 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.err new file mode 100644 index 00000000..11f93442 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 21 constraint feet, 0 feet refused, 22 rounds, 22612 points inserted, 54390 flips, 72292 triangles, achieved max error 9.99996099528471 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.66 real 1.08 user 0.14 sys + 646496256 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 47915 page reclaims + 66 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 4 voluntary context switches + 630 involuntary context switches + 14655662506 instructions retired + 3816662932 cycles elapsed + 623330216 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.out new file mode 100644 index 00000000..bb851e70 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run1.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md new file mode 100644 index 00000000..2abd0be3 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run1.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run1.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 38914 | +| output triangles | 72292 | +| constraint edges | 5534 | +| vertices without data dropped | 0 | +| mesh_fine_t10_run1.vtk | 2.8 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.01 % | 1.10 % | 0.655° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 13 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.99996099528471 m | 22 | 22612 | 0 | 54390 | 0 | 10789 | 1841 | 21 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.007 | 1.8 % | +| decode | 0.312 | 80.0 % | +| start mesh: build | 0.000 | 0.1 % | +| start mesh: node | 0.006 | 1.6 % | +| start mesh: triangulate | 0.003 | 0.8 % | +| start mesh: constraint edges | 0.007 | 1.8 % | +| refine | 0.043 | 11.0 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 2.8 % | +| refine: scan (parallel) | 0.018 | 4.5 % | +| refine: split + flip (serial) | 0.009 | 2.3 % | +| refine: setup + output | 0.005 | 1.2 % | +| trim | 0.003 | 0.7 % | +| write: encode | 0.004 | 1.1 % | +| write: disk | 0.002 | 0.5 % | +| other | 0.003 | 0.8 % | +| **total** | **0.390** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.013 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.err new file mode 100644 index 00000000..14e4d9a9 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 21 constraint feet, 0 feet refused, 22 rounds, 22612 points inserted, 54390 flips, 72292 triangles, achieved max error 9.99996099528471 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.66 real 1.08 user 0.15 sys + 648019968 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 48218 page reclaims + 126 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 1 voluntary context switches + 643 involuntary context switches + 14673501610 instructions retired + 3810240204 cycles elapsed + 624853928 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.out new file mode 100644 index 00000000..7232208d --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run2.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md new file mode 100644 index 00000000..54b04327 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run2.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run2.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 38914 | +| output triangles | 72292 | +| constraint edges | 5534 | +| vertices without data dropped | 0 | +| mesh_fine_t10_run2.vtk | 2.8 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.01 % | 1.10 % | 0.655° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 13 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.99996099528471 m | 22 | 22612 | 0 | 54390 | 0 | 10789 | 1841 | 21 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.007 | 1.7 % | +| decode | 0.310 | 79.9 % | +| start mesh: build | 0.000 | 0.1 % | +| start mesh: node | 0.006 | 1.6 % | +| start mesh: triangulate | 0.003 | 0.8 % | +| start mesh: constraint edges | 0.007 | 1.8 % | +| refine | 0.043 | 11.1 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 2.8 % | +| refine: scan (parallel) | 0.018 | 4.6 % | +| refine: split + flip (serial) | 0.009 | 2.4 % | +| refine: setup + output | 0.005 | 1.2 % | +| trim | 0.003 | 0.7 % | +| write: encode | 0.004 | 1.1 % | +| write: disk | 0.001 | 0.1 % | +| other | 0.004 | 1.1 % | +| **total** | **0.388** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.013 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.err new file mode 100644 index 00000000..01dbd7b0 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 21 constraint feet, 0 feet refused, 22 rounds, 22612 points inserted, 54390 flips, 72292 triangles, achieved max error 9.99996099528471 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.66 real 1.08 user 0.14 sys + 647200768 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 48746 page reclaims + 126 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 677 involuntary context switches + 14667763218 instructions retired + 3818928580 cycles elapsed + 624051136 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.out new file mode 100644 index 00000000..eab674a8 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run3.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md new file mode 100644 index 00000000..d5ebb3b7 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t10_run3.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t10_run3.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 38914 | +| output triangles | 72292 | +| constraint edges | 5534 | +| vertices without data dropped | 0 | +| mesh_fine_t10_run3.vtk | 2.8 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 36.87° | 0.01 % | 1.10 % | 0.655° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 13 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.99996099528471 m | 22 | 22612 | 0 | 54390 | 0 | 10789 | 1841 | 21 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.006 | 1.6 % | +| decode | 0.313 | 80.5 % | +| start mesh: build | 0.000 | 0.1 % | +| start mesh: node | 0.006 | 1.6 % | +| start mesh: triangulate | 0.003 | 0.8 % | +| start mesh: constraint edges | 0.007 | 1.8 % | +| refine | 0.042 | 10.9 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 2.8 % | +| refine: scan (parallel) | 0.017 | 4.5 % | +| refine: split + flip (serial) | 0.009 | 2.3 % | +| refine: setup + output | 0.005 | 1.2 % | +| trim | 0.003 | 0.7 % | +| write: encode | 0.004 | 1.1 % | +| write: disk | 0.001 | 0.1 % | +| other | 0.003 | 0.8 % | +| **total** | **0.388** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.013 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.err new file mode 100644 index 00000000..0a277980 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 84 constraint feet, 0 feet refused, 33 rounds, 555349 points inserted, 1193738 flips, 1137703 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 3.22 real 4.17 user 0.30 sys + 1163919360 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 111236 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 19 voluntary context switches + 1051 involuntary context switches + 53213996844 instructions retired + 13969867000 cycles elapsed + 1142572400 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.out new file mode 100644 index 00000000..06a01f5b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_ascii.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md new file mode 100644 index 00000000..b5c900ec --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 1 --ascii --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_ascii.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_ascii.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 571651 | +| output triangles | 1137703 | +| constraint edges | 5597 | +| vertices without data dropped | 0 | +| mesh_fine_t1_ascii.vtk | 59.2 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.01 % | 0.27 % | 0.0834° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 80 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 33 | 555349 | 0 | 1193738 | 0 | 10789 | 1841 | 84 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.007 | 0.2 % | +| decode | 0.317 | 11.5 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.006 | 0.2 % | +| start mesh: triangulate | 0.003 | 0.1 % | +| start mesh: constraint edges | 0.007 | 0.2 % | +| refine | 0.498 | 18.0 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 0.4 % | +| refine: scan (parallel) | 0.102 | 3.7 % | +| refine: split + flip (serial) | 0.334 | 12.1 % | +| refine: setup + output | 0.051 | 1.8 % | +| trim | 0.050 | 1.8 % | +| write: encode | 1.850 | 66.8 % | +| write: disk | 0.029 | 1.0 % | +| other | 0.003 | 0.1 % | +| **total** | **2.770** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.196 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.err new file mode 100644 index 00000000..bef677d5 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 84 constraint feet, 0 feet refused, 33 rounds, 555349 points inserted, 1193738 flips, 1137703 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.36 real 2.43 user 0.19 sys + 942112768 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 68222 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 10 voluntary context switches + 921 involuntary context switches + 23941825764 instructions retired + 8019935151 cycles elapsed + 920421576 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.out new file mode 100644 index 00000000..cbf9a9a8 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run1.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md new file mode 100644 index 00000000..22f25387 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run1.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run1.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 571651 | +| output triangles | 1137703 | +| constraint edges | 5597 | +| vertices without data dropped | 0 | +| mesh_fine_t1_run1.vtk | 41.1 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.01 % | 0.27 % | 0.0834° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 80 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 33 | 555349 | 0 | 1193738 | 0 | 10789 | 1841 | 84 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.006 | 0.7 % | +| decode | 0.311 | 34.4 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.006 | 0.7 % | +| start mesh: triangulate | 0.003 | 0.3 % | +| start mesh: constraint edges | 0.007 | 0.8 % | +| refine | 0.481 | 53.3 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 1.2 % | +| refine: scan (parallel) | 0.099 | 11.0 % | +| refine: split + flip (serial) | 0.324 | 36.0 % | +| refine: setup + output | 0.046 | 5.1 % | +| trim | 0.043 | 4.8 % | +| write: encode | 0.023 | 2.6 % | +| write: disk | 0.018 | 2.0 % | +| other | 0.003 | 0.4 % | +| **total** | **0.902** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.204 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.err new file mode 100644 index 00000000..63934c30 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 84 constraint feet, 0 feet refused, 33 rounds, 555349 points inserted, 1193738 flips, 1137703 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.39 real 2.43 user 0.19 sys + 942194688 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 65599 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 17 voluntary context switches + 892 involuntary context switches + 23949464331 instructions retired + 8021673555 cycles elapsed + 920503496 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.out new file mode 100644 index 00000000..4d234678 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run2.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md new file mode 100644 index 00000000..741664a2 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run2.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run2.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 571651 | +| output triangles | 1137703 | +| constraint edges | 5597 | +| vertices without data dropped | 0 | +| mesh_fine_t1_run2.vtk | 41.1 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.01 % | 0.27 % | 0.0834° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 80 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 33 | 555349 | 0 | 1193738 | 0 | 10789 | 1841 | 84 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.006 | 0.7 % | +| decode | 0.312 | 33.5 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.006 | 0.7 % | +| start mesh: triangulate | 0.003 | 0.3 % | +| start mesh: constraint edges | 0.007 | 0.7 % | +| refine | 0.483 | 51.9 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 1.2 % | +| refine: scan (parallel) | 0.100 | 10.7 % | +| refine: split + flip (serial) | 0.326 | 35.1 % | +| refine: setup + output | 0.046 | 4.9 % | +| trim | 0.044 | 4.7 % | +| write: encode | 0.040 | 4.3 % | +| write: disk | 0.026 | 2.8 % | +| other | 0.003 | 0.4 % | +| **total** | **0.931** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.205 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.err new file mode 100644 index 00000000..01c6465a --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.err @@ -0,0 +1,20 @@ +10789 start quality nodes inserted, 1841 start quality skips, 84 constraint feet, 0 feet refused, 33 rounds, 555349 points inserted, 1193738 flips, 1137703 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 5511 start triangles, 5513 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.38 real 2.43 user 0.19 sys + 941096960 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 68102 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 16 voluntary context switches + 920 involuntary context switches + 23952962455 instructions retired + 8052110280 cycles elapsed + 919422176 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.out new file mode 100644 index 00000000..da838706 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run3.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md new file mode 100644 index 00000000..6bd4566b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t0.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_fine_t1_run3.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_fine_t1_run3.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 5513 (1 ring, 0 holes) | +| start vertices | 5513 | +| start triangles | 5511 | +| output vertices | 571651 | +| output triangles | 1137703 | +| constraint edges | 5597 | +| vertices without data dropped | 0 | +| mesh_fine_t1_run3.vtk | 41.1 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.01 % | 0.27 % | 0.0834° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 16 | 80 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 33 | 555349 | 0 | 1193738 | 0 | 10789 | 1841 | 84 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.006 | 0.7 % | +| decode | 0.312 | 34.0 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.006 | 0.7 % | +| start mesh: triangulate | 0.003 | 0.3 % | +| start mesh: constraint edges | 0.007 | 0.8 % | +| refine | 0.486 | 53.0 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.011 | 1.2 % | +| refine: scan (parallel) | 0.100 | 10.9 % | +| refine: split + flip (serial) | 0.329 | 35.9 % | +| refine: setup + output | 0.046 | 5.0 % | +| trim | 0.044 | 4.8 % | +| write: encode | 0.022 | 2.5 % | +| write: disk | 0.027 | 3.0 % | +| other | 0.003 | 0.4 % | +| **total** | **0.917** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.204 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.err new file mode 100644 index 00000000..b1016c5f --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 212 constraint feet, 0 feet refused, 28 rounds, 24634 points inserted, 60095 flips, 52198 triangles, achieved max error 9.999777071593144 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.70 real 1.14 user 0.16 sys + 651853824 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 52354 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 4 voluntary context switches + 623 involuntary context switches + 15748668832 instructions retired + 4038961335 cycles elapsed + 628933568 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.out new file mode 100644 index 00000000..7825f177 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_ascii.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md new file mode 100644 index 00000000..ff652294 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --ascii --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_ascii.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_ascii.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 26576 | +| output triangles | 52198 | +| constraint edges | 952 | +| vertices without data dropped | 0 | +| mesh_red_t10_ascii.vtk | 2.6 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 34.43° | 0.01 % | 1.23 % | 0.356° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 12 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999777071593144 m | 28 | 24634 | 0 | 60095 | 0 | 1202 | 145 | 212 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.7 % | +| decode | 0.309 | 70.0 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.2 % | +| start mesh: triangulate | 0.000 | 0.1 % | +| start mesh: constraint edges | 0.001 | 0.2 % | +| refine | 0.034 | 7.7 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.3 % | +| refine: scan (parallel) | 0.019 | 4.3 % | +| refine: split + flip (serial) | 0.011 | 2.5 % | +| refine: setup + output | 0.002 | 0.5 % | +| trim | 0.002 | 0.4 % | +| write: encode | 0.086 | 19.5 % | +| write: disk | 0.002 | 0.5 % | +| other | 0.003 | 0.7 % | +| **total** | **0.441** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.010 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.err new file mode 100644 index 00000000..6363c56f --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 212 constraint feet, 0 feet refused, 28 rounds, 24634 points inserted, 60095 flips, 52198 triangles, achieved max error 9.999777071593144 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.63 real 1.06 user 0.15 sys + 643219456 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 46851 page reclaims + 109 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 5 voluntary context switches + 718 involuntary context switches + 14372415862 instructions retired + 3738694731 cycles elapsed + 620053416 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.out new file mode 100644 index 00000000..d8dc9061 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run1.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md new file mode 100644 index 00000000..9f73d74a --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run1.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run1.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 26576 | +| output triangles | 52198 | +| constraint edges | 952 | +| vertices without data dropped | 0 | +| mesh_red_t10_run1.vtk | 1.9 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 34.43° | 0.01 % | 1.23 % | 0.356° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 12 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999777071593144 m | 28 | 24634 | 0 | 60095 | 0 | 1202 | 145 | 212 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.004 | 1.0 % | +| decode | 0.306 | 85.9 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.2 % | +| start mesh: triangulate | 0.000 | 0.1 % | +| start mesh: constraint edges | 0.001 | 0.2 % | +| refine | 0.034 | 9.6 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.3 % | +| refine: scan (parallel) | 0.020 | 5.5 % | +| refine: split + flip (serial) | 0.011 | 3.1 % | +| refine: setup + output | 0.003 | 0.7 % | +| trim | 0.002 | 0.5 % | +| write: encode | 0.004 | 1.2 % | +| write: disk | 0.001 | 0.3 % | +| other | 0.003 | 0.9 % | +| **total** | **0.356** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.010 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.err new file mode 100644 index 00000000..0f8d348e --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 212 constraint feet, 0 feet refused, 28 rounds, 24634 points inserted, 60095 flips, 52198 triangles, achieved max error 9.999777071593144 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.62 real 1.06 user 0.15 sys + 645562368 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 50541 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 625 involuntary context switches + 14377516726 instructions retired + 3753778848 cycles elapsed + 622396328 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.out new file mode 100644 index 00000000..7e542f1b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run2.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md new file mode 100644 index 00000000..0ed3a567 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run2.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run2.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 26576 | +| output triangles | 52198 | +| constraint edges | 952 | +| vertices without data dropped | 0 | +| mesh_red_t10_run2.vtk | 1.9 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 34.43° | 0.01 % | 1.23 % | 0.356° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 12 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999777071593144 m | 28 | 24634 | 0 | 60095 | 0 | 1202 | 145 | 212 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.9 % | +| decode | 0.306 | 86.1 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.2 % | +| start mesh: triangulate | 0.000 | 0.1 % | +| start mesh: constraint edges | 0.001 | 0.2 % | +| refine | 0.034 | 9.5 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.3 % | +| refine: scan (parallel) | 0.019 | 5.4 % | +| refine: split + flip (serial) | 0.011 | 3.2 % | +| refine: setup + output | 0.002 | 0.7 % | +| trim | 0.002 | 0.6 % | +| write: encode | 0.004 | 1.2 % | +| write: disk | 0.001 | 0.1 % | +| other | 0.003 | 0.9 % | +| **total** | **0.355** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.009 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.err new file mode 100644 index 00000000..2871afdf --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 212 constraint feet, 0 feet refused, 28 rounds, 24634 points inserted, 60095 flips, 52198 triangles, achieved max error 9.999777071593144 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 0.62 real 1.06 user 0.15 sys + 647921664 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 49962 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 0 voluntary context switches + 672 involuntary context switches + 14395382812 instructions retired + 3755607788 cycles elapsed + 624755624 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.out new file mode 100644 index 00000000..f0d76148 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run3.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md new file mode 100644 index 00000000..bedf4b9b --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 10 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t10_run3.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t10_run3.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 26576 | +| output triangles | 52198 | +| constraint edges | 952 | +| vertices without data dropped | 0 | +| mesh_red_t10_run3.vtk | 1.9 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 34.43° | 0.01 % | 1.23 % | 0.356° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 12 | 3 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 9.999777071593144 m | 28 | 24634 | 0 | 60095 | 0 | 1202 | 145 | 212 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.9 % | +| decode | 0.307 | 86.4 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.2 % | +| start mesh: triangulate | 0.000 | 0.1 % | +| start mesh: constraint edges | 0.001 | 0.2 % | +| refine | 0.033 | 9.3 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.3 % | +| refine: scan (parallel) | 0.019 | 5.4 % | +| refine: split + flip (serial) | 0.011 | 3.0 % | +| refine: setup + output | 0.002 | 0.7 % | +| trim | 0.002 | 0.6 % | +| write: encode | 0.004 | 1.2 % | +| write: disk | 0.001 | 0.1 % | +| other | 0.003 | 0.9 % | +| **total** | **0.356** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.010 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.err new file mode 100644 index 00000000..446c3ee2 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 1254 constraint feet, 0 feet refused, 36 rounds, 563569 points inserted, 1218749 flips, 1129026 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 3.19 real 4.18 user 0.29 sys + 1156923392 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 109434 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 16 voluntary context switches + 995 involuntary context switches + 52887328192 instructions retired + 13971370462 cycles elapsed + 1135576432 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.out new file mode 100644 index 00000000..0530407f --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_ascii.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md new file mode 100644 index 00000000..cd7164a3 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 1 --ascii --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_ascii.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_ascii.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 565511 | +| output triangles | 1129026 | +| constraint edges | 1994 | +| vertices without data dropped | 0 | +| mesh_red_t1_ascii.vtk | 58.6 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.03 % | 0.36 % | 0.0461° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 18 | 102 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 36 | 563569 | 0 | 1218749 | 0 | 1202 | 145 | 1254 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.1 % | +| decode | 0.308 | 11.2 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.0 % | +| start mesh: triangulate | 0.000 | 0.0 % | +| start mesh: constraint edges | 0.001 | 0.0 % | +| refine | 0.508 | 18.5 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.0 % | +| refine: scan (parallel) | 0.108 | 4.0 % | +| refine: split + flip (serial) | 0.353 | 12.9 % | +| refine: setup + output | 0.046 | 1.7 % | +| trim | 0.049 | 1.8 % | +| write: encode | 1.833 | 66.9 % | +| write: disk | 0.034 | 1.2 % | +| other | 0.003 | 0.1 % | +| **total** | **2.741** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.193 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.err new file mode 100644 index 00000000..8b00e0a5 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 1254 constraint feet, 0 feet refused, 36 rounds, 563569 points inserted, 1218749 flips, 1129026 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.39 real 2.42 user 0.21 sys + 923910144 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 66791 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 13 voluntary context switches + 1458 involuntary context switches + 23881754846 instructions retired + 8078512563 cycles elapsed + 902202544 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.out new file mode 100644 index 00000000..5ea95199 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run1.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md new file mode 100644 index 00000000..7df50fe6 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run1.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run1.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 565511 | +| output triangles | 1129026 | +| constraint edges | 1994 | +| vertices without data dropped | 0 | +| mesh_red_t1_run1.vtk | 40.7 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.03 % | 0.36 % | 0.0461° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 18 | 102 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 36 | 563569 | 0 | 1218749 | 0 | 1202 | 145 | 1254 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.4 % | +| decode | 0.310 | 33.3 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.1 % | +| start mesh: triangulate | 0.000 | 0.0 % | +| start mesh: constraint edges | 0.001 | 0.1 % | +| refine | 0.497 | 53.3 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.1 % | +| refine: scan (parallel) | 0.105 | 11.3 % | +| refine: split + flip (serial) | 0.347 | 37.2 % | +| refine: setup + output | 0.044 | 4.7 % | +| trim | 0.043 | 4.6 % | +| write: encode | 0.044 | 4.7 % | +| write: disk | 0.030 | 3.2 % | +| other | 0.003 | 0.3 % | +| **total** | **0.933** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.197 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.err new file mode 100644 index 00000000..f40fa1de --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 1254 constraint feet, 0 feet refused, 36 rounds, 563569 points inserted, 1218749 flips, 1129026 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.38 real 2.46 user 0.20 sys + 925401088 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 70065 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 17 voluntary context switches + 932 involuntary context switches + 23890779174 instructions retired + 8158706283 cycles elapsed + 903709896 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.out new file mode 100644 index 00000000..bdb41d23 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run2.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md new file mode 100644 index 00000000..52b60283 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run2.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run2.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 565511 | +| output triangles | 1129026 | +| constraint edges | 1994 | +| vertices without data dropped | 0 | +| mesh_red_t1_run2.vtk | 40.7 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.03 % | 0.36 % | 0.0461° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 18 | 102 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 36 | 563569 | 0 | 1218749 | 0 | 1202 | 145 | 1254 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.4 % | +| decode | 0.309 | 33.3 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.1 % | +| start mesh: triangulate | 0.000 | 0.0 % | +| start mesh: constraint edges | 0.001 | 0.1 % | +| refine | 0.496 | 53.5 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.1 % | +| refine: scan (parallel) | 0.104 | 11.2 % | +| refine: split + flip (serial) | 0.347 | 37.4 % | +| refine: setup + output | 0.045 | 4.8 % | +| trim | 0.049 | 5.2 % | +| write: encode | 0.032 | 3.5 % | +| write: disk | 0.033 | 3.5 % | +| other | 0.003 | 0.3 % | +| **total** | **0.927** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.195 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.err b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.err new file mode 100644 index 00000000..75285fe3 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.err @@ -0,0 +1,20 @@ +1202 start quality nodes inserted, 145 start quality skips, 1254 constraint feet, 0 feet refused, 36 rounds, 563569 points inserted, 1218749 flips, 1129026 triangles, achieved max error 1 m, 0 valid DEM nodes not covered, 738 start triangles, 740 start vertices off-node, 0 vertices without data dropped +mosaic of 2 tiles, 2195 x 3314 nodes + 1.37 real 2.45 user 0.20 sys + 929644544 maximum resident set size + 0 average shared memory size + 0 average unshared data size + 0 average unshared stack size + 68173 page reclaims + 44 page faults + 0 swaps + 0 block input operations + 0 block output operations + 0 messages sent + 0 messages received + 0 signals received + 11 voluntary context switches + 967 involuntary context switches + 23896562231 instructions retired + 8123335720 cycles elapsed + 907936944 peak memory footprint diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.out b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.out new file mode 100644 index 00000000..11734301 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.out @@ -0,0 +1,2 @@ +/private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run3.vtk +/Users/skavhaug/projects/rasputin/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md new file mode 100644 index 00000000..7cdf21bc --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md @@ -0,0 +1,64 @@ +# rasputin mesh — statistics + +`rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 --domain /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/catchment_t20_run1.geojson --tolerance 1 --binary --out /private/tmp/claude-501/-Users-skavhaug-projects-rasputin/38487caf-f56e-46a3-b05b-867af1fb1619/scratchpad/run/mesh_red_t1_run3.vtk --stats docs/benchmarks/2026-09-29/bygdin/logs/mesh_red_t1_run3.stats.md` + +## Sizes + +| item | count | +|---|---| +| DEM nodes | 2195 × 3314 (10 m) | +| domain vertices | 740 (1 ring, 0 holes) | +| start vertices | 740 | +| start triangles | 738 | +| output vertices | 565511 | +| output triangles | 1129026 | +| constraint edges | 1994 | +| vertices without data dropped | 0 | +| mesh_red_t1_run3.vtk | 40.7 MB | + +## Quality (plan view, x/y) + +| metric | median | < 1° | < 10° | worst | +|---|---|---|---|---| +| minimum angle | 45.00° | 0.03 % | 0.36 % | 0.0461° | + +| metric | median | p99 | max | ≥ 12 | ≥ 20 | +|---|---|---|---|---|---| +| vertex degree (triangles) | 6 | 9 | 18 | 102 | 0 | + +## Refinement + +| tolerance | achieved max error | rounds | inserted | carved | flips | uncovered | quality inserted | quality skipped | feet | +|---|---|---|---|---|---|---|---|---|---| +| 1 m | 1 m | 36 | 563569 | 0 | 1218749 | 0 | 1202 | 145 | 1254 | + +## Timings + +Wall clock, `time.perf_counter_ns` (Python) and `std::chrono::steady_clock` +(inside `refine`), one run, no warm-up. Total is the `mesh` command body, from +argument checks to the last file written; interpreter start-up and imports are +not in it. Threads: 10 (hardware concurrency). + +| phase | seconds | share | +|---|---|---| +| domain read | 0.003 | 0.4 % | +| decode | 0.308 | 33.6 % | +| start mesh: build | 0.000 | 0.0 % | +| start mesh: node | 0.001 | 0.1 % | +| start mesh: triangulate | 0.000 | 0.0 % | +| start mesh: constraint edges | 0.001 | 0.1 % | +| refine | 0.504 | 55.0 % | +| refine: legalise start | 0.000 | 0.0 % | +| refine: start quality | 0.001 | 0.1 % | +| refine: scan (parallel) | 0.104 | 11.4 % | +| refine: split + flip (serial) | 0.353 | 38.6 % | +| refine: setup + output | 0.045 | 4.9 % | +| trim | 0.044 | 4.8 % | +| write: encode | 0.023 | 2.5 % | +| write: disk | 0.029 | 3.2 % | +| other | 0.003 | 0.3 % | +| **total** | **0.915** | **100 %** | + +Sub-rows sum to their parent and are not added to the total. + +Statistics computed in 0.201 s, not included above. diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_end.txt b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_end.txt new file mode 100644 index 00000000..a692306c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_end.txt @@ -0,0 +1,2 @@ +Now drawing from 'AC Power' + -InternalBattery-0 (id=7929955) 80%; AC attached; not charging present: true diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_start.txt b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_start.txt new file mode 100644 index 00000000..a692306c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_catchment_start.txt @@ -0,0 +1,2 @@ +Now drawing from 'AC Power' + -InternalBattery-0 (id=7929955) 80%; AC attached; not charging present: true diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_end.txt b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_end.txt new file mode 100644 index 00000000..a692306c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_end.txt @@ -0,0 +1,2 @@ +Now drawing from 'AC Power' + -InternalBattery-0 (id=7929955) 80%; AC attached; not charging present: true diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_start.txt b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_start.txt new file mode 100644 index 00000000..a692306c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_mesh_start.txt @@ -0,0 +1,2 @@ +Now drawing from 'AC Power' + -InternalBattery-0 (id=7929955) 80%; AC attached; not charging present: true diff --git a/docs/benchmarks/2026-09-29/bygdin/logs/pmset_quality_end.txt b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_quality_end.txt new file mode 100644 index 00000000..a692306c --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/logs/pmset_quality_end.txt @@ -0,0 +1,2 @@ +Now drawing from 'AC Power' + -InternalBattery-0 (id=7929955) 80%; AC attached; not charging present: true diff --git a/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson b/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson new file mode 100644 index 00000000..3c509ff5 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson @@ -0,0 +1 @@ +{"type":"FeatureCollection","crs":{"type":"name","properties":{"name":"EPSG:25833"}},"features":[{"type":"Feature","id":152,"geometry":{"type":"Polygon","coordinates":[[[138194.83000000007,6832236.4899999984],[138065.8200000003,6832255.4499999993],[137902.75,6832232.2899999991],[137753.79000000004,6832253.0899999999],[137475.28000000026,6832167.1600000001],[137415.41999999993,6832096.25],[137321.33000000007,6832057.6600000001],[137234.15000000037,6832028.5],[137194.16000000015,6831965.8099999987],[137014.33000000007,6831772.1900000013],[136961.69000000041,6831735.8099999987],[136901.29000000004,6831669.9800000004],[136903.66000000015,6831554.0799999982],[136824.75999999978,6831364.2199999988],[136827.25,6831195.0100000016],[136781.16999999993,6831186.1900000013],[136670.25999999978,6831183.3599999994],[136548.09999999963,6831232.8599999994],[136390.9299999997,6831284.5899999999],[136267.37000000011,6831308.0700000003],[136144.65000000037,6831286.2199999988],[136027.91999999993,6831274.870000001],[135940.28000000026,6831240.7100000009],[135854.40000000037,6831236.5799999982],[135792.75,6831309.6600000001],[135816.73000000045,6831209.879999999],[135823.62999999989,6831088.5399999991],[135796.83000000007,6830994.4499999993],[135730.59999999963,6830909.0300000012],[135614.33999999985,6830837.3000000007],[135601.20000000019,6830716.7899999991],[135611.79000000004,6830581.0300000012],[135615.46999999974,6830424.7899999991],[135616.84999999963,6830374.3599999994],[135613.24000000022,6830324.3999999985],[135703.25999999978,6830242.6600000001],[135821.36000000034,6830138.2100000009],[135930.94000000041,6830016.2399999984],[135956.34999999963,6829987.9400000013],[136105,6829811.2600000016],[136160.83000000007,6829795.0399999991],[136196.83999999985,6829757.7899999991],[136235.66000000015,6829732.4100000001],[136272.4299999997,6829698.0899999999],[136338.61000000034,6829673.5799999982],[136375.37999999989,6829649.0700000003],[136399.88999999966,6829627.0100000016],[136424.40000000037,6829595.1400000006],[136448.91999999993,6829570.629999999],[136497.94000000041,6829521.6000000015],[136517.54999999981,6829472.5799999982],[136534.70999999996,6829396.5899999999],[136556.76999999955,6829345.1099999994],[136600.88999999966,6829264.2199999988],[136589.70999999996,6829221.3599999994],[136590.45999999996,6829208.1799999997],[136587.30999999959,6829097.8299999982],[136588.48000000045,6829092.9299999997],[136578.83000000007,6829080.3900000006],[136576.37999999989,6829026.4499999993],[136615.59999999963,6828950.4600000009],[136652.36000000034,6828830.3500000015],[136718.54999999981,6828764.1700000018],[136750.28000000026,6828748.8500000015],[136789.63999999966,6828729.8500000015],[136811.70000000019,6828700.4400000013],[136833.12999999989,6828672.1099999994],[136880.33000000007,6828609.7399999984],[136939.16000000015,6828536.1999999993],[137016.55999999959,6828510.8999999985],[137066.62999999989,6828494.5300000012],[137099.63999999966,6828448.0199999996],[137137.71999999974,6828386.6799999997],[137194.08999999985,6828310.6900000013],[137260.29000000004,6828227.3500000015],[137289.70000000019,6828166.0700000003],[137336.26999999955,6828102.3399999999],[137414.70999999996,6828018.9899999984],[137475.99000000022,6827940.5500000007],[137515.20999999996,6827859.6600000001],[137542.16999999993,6827744.4499999993],[137542.16999999993,6827719.9400000013],[137614.80999999959,6827556.5399999991],[137657.37999999989,6827506.6799999997],[137677.5,6827451.1799999997],[137708.4299999997,6827394],[137756.62999999989,6827360.379999999],[137814.79999999981,6827325.8500000015],[137897.53000000026,6827219.629999999],[137963.78000000026,6827158.6099999994],[138014.78000000026,6827095.1499999985],[138061.98000000045,6827061.620000001],[138154.98000000045,6826991.9299999997],[138165.20000000019,6826883.1099999994],[138218.9299999997,6826789.6400000006],[138210.3200000003,6826663.6999999993],[138257.12999999989,6826549.7399999984],[138320.83999999985,6826455.3399999999],[138355.91000000015,6826377.6700000018],[138387.84999999963,6826325.1900000013],[138400.11000000034,6826273.7100000009],[138381.71999999974,6826221.3999999985],[138432.25999999978,6826180.5199999996],[138486.8200000003,6826096.0199999996],[138540.8200000003,6826016.6000000015],[138599.08000000007,6825917.6799999997],[138660.01999999955,6825847.6900000013],[138729.53000000026,6825761.8099999987],[138797.75,6825661.9600000009],[138790.08999999985,6825557.0599999987],[138827.84999999963,6825454],[138846.03000000026,6825356.7600000016],[138935.12999999989,6825265.0500000007],[138952.38999999966,6825157.8500000015],[138987.41999999993,6825112.370000001],[139031.51999999955,6825067.0599999987],[139072.5,6824998.8999999985],[139095.6799999997,6824901.2100000009],[139115.79000000004,6824824.9200000018],[139083.94000000041,6824741.3599999994],[139033.58999999985,6824664.5399999991],[139070.88999999966,6824556.4899999984],[139049.08999999985,6824461.9400000013],[139010.58000000007,6824349.8200000003],[139022.9299999997,6824233.0100000016],[139041.58000000007,6824140.7600000016],[139114.79999999981,6824105.8399999999],[139068.51999999955,6823953.1999999993],[139112.65000000037,6823864.6400000006],[139156.24000000022,6823781.1600000001],[139270.36000000034,6823611.6999999993],[139294.54000000004,6823524.9800000004],[139328.99000000022,6823451.3900000006],[139331.75,6823350.5599999987],[139391.38999999966,6823266.6000000015],[139465.90000000037,6823180.2600000016],[139527.38999999966,6823050.870000001],[139511.03000000026,6822960.8500000015],[139540.20000000019,6822873.6700000018],[139596.99000000022,6822758.7899999991],[139671.96999999974,6822688.5],[139746.94000000041,6822672.5300000012],[139805.95999999996,6822658.0300000012],[139852.84999999963,6822534.0100000016],[139898.28000000026,6822470.4699999988],[139932.44000000041,6822382.8299999982],[140065.04999999981,6822479.2199999988],[140135.49000000022,6822403.3200000003],[140232.65000000037,6822366.1900000013],[140325.8200000003,6822329.4299999997],[140429.96999999974,6822302.7199999988],[140506.24000000022,6822246.4100000001],[140614.36000000034,6822208.2600000016],[140683.05999999959,6822178.7800000012],[140770.3200000003,6822001.75],[140829.27950000018,6821975.5658],[140869.3814000003,6822003.7454999983],[140906.01730000041,6822029.1088999994],[140920.81269999966,6822048.1314000003],[140963.78940000013,6822064.3357999995],[140999.01630000025,6822060.1086000018],[141077.21999999974,6822013.6090999991],[141105.40160000045,6821991.0637999997],[141108.82940000016,6821988.1772000007],[141120.19689999986,6821965.7005000003],[141141.33299999963,6821933.2917000018],[141144.15110000037,6821915.6783000007],[141158.94639999978,6821874.815200001],[141157.53729999997,6821856.497200001],[141152.84470000025,6821822.9904999994],[141149.08279999997,6821814.9294000007],[141139.92389999982,6821769.1343999989],[141169.51439999975,6821716.2941000015],[141205.4458999997,6821638.0905000009],[141222.95000000019,6821576.7699999996],[141258.49000000022,6821504.1000000015],[141303.91999999993,6821440.5500000007],[141367.54000000004,6821345.1600000001],[141364.60059999954,6821303.1295000017],[141323.80829999968,6821249.1856000014],[141272.16050000023,6821147.8040999994],[141297.29590000026,6820993.0223000012],[141343.59810000006,6820805.1680000015],[141376.67109999992,6820739.0218999982],[141450.75449999981,6820699.3343999982],[141589.89549999963,6820606.8977999985],[141591.58000000007,6820524.870000001],[141636.54999999981,6820456.3500000015],[141673.86000000034,6820348.3000000007],[141718.45000000019,6820264.7300000004],[141767.5700000003,6820175.6999999993],[141802.5700000003,6820108.1099999994],[141876.16000000015,6820077.1799999997],[141918.8200000003,6820114.4800000004],[141988.73000000045,6820174.4100000001],[142050.12999999989,6820185.8500000015],[142136.37000000011,6820193.9800000004],[142222.1799999997,6820273.5700000003],[142235.34119999968,6820281.4884000011],[142276.43030000012,6820282.2221999988],[142334.73720000032,6820258.0023000017],[142374.20660000015,6820215.8418999985],[142394.1409,6820162.2602000013],[142394.83829999994,6820048.0973000005],[142431.18499999959,6819981.4151999988],[142441.48379999958,6819890.2201999985],[142441.48379999958,6819854.3388999999],[142462.11550000031,6819765.5329999998],[142497.09970000014,6819691.0795000009],[142521.31940000039,6819636.3605999984],[142559.89169999957,6819510.7765999995],[142602.0521999998,6819421.0736000016],[142626.27190000005,6819382.5012999997],[142695.62999999989,6819354.8399999999],[142754.1799999997,6819335.3599999994],[142828.70000000019,6819325.4600000009],[142898.6799999997,6819309.9499999993],[143016.41000000015,6819266.9100000001],[143129.88999999966,6819243.3599999994],[143261.29000000004,6819173.9100000001],[143374.40000000037,6819135.3099999987],[143428.55999999959,6819152.0700000003],[143458.58000000007,6819143.3599999994],[143500.78000000026,6819128.4600000009],[143543.03639999963,6819134.0240999982],[143595.06410000008,6819098.1427999996],[143663.23840000015,6819049.7030999996],[143734.89300000016,6819025.1972000003],[143752.94139999989,6818942.059700001],[143790.6166000003,6818872.9882999994],[143813.04229999986,6818828.1369000003],[143832,6818786.3299999982],[143825.60080000013,6818744.7131000012],[143787.5493999999,6818700.3295000009],[143771.40000000037,6818659.2199999988],[143756.50999999978,6818587.2199999988],[143714.29999999981,6818477.9800000004],[143714.29999999981,6818401.0100000016],[143704.62999999989,6818336.5199999996],[143699.40000000037,6818301.6999999993],[143689.46999999974,6818224.7399999984],[143705.70000000019,6818186.879999999],[143726.70999999996,6818137.8399999999],[143811.12999999989,6818090.6700000018],[143867.87999999989,6818085],[143930.29999999981,6818043.5],[143962.58000000007,6817996.3200000003],[143991.87999999989,6817990.0799999982],[144004.78000000026,6817941.6999999993],[144054.50999999978,6817919.8200000003],[144066.84999999963,6817914.3900000006],[144126.44000000041,6817884.6000000015],[144148.78000000026,6817864.7399999984],[144183.54000000004,6817837.4299999997],[144277.75,6817763.5500000007],[144297.75,6817745.5700000003],[144415.57770000026,6817721.1469999999],[144519.6054999996,6817531.5841000006],[144474.68410000019,6817446.4699999988],[144429.64690000005,6817386.063000001],[144347.62579999957,6817249.8022999987],[144300.58609999996,6817110.3960999995],[144278.82519999985,6817030.6059000008],[144216.3870000001,6816990.0056000017],[144129.34420000017,6816934.9475000016],[144087.75189999957,6816863.2338000014],[144089.20000000019,6816839.3599999994],[144086.70999999996,6816784.7399999984],[144066.84999999963,6816682.9400000013],[144120.6799999997,6816649.5500000007],[144198.44000000041,6816638.2600000016],[144331.29999999981,6816632.129999999],[144414.44000000041,6816628.3200000003],[144513.75,6816665.5700000003],[144543.20000000019,6816686.8399999999],[144663.49000000022,6816667.870000001],[144764.50999999978,6816620.879999999],[144794.29999999981,6816598.5300000012],[144829.05999999959,6816521.5700000003],[144856.37000000011,6816457.0100000016],[144928.37000000011,6816385.0100000016],[144963.12999999989,6816315.5],[145027.6799999997,6816260.879999999],[145046.71999999974,6816228.5],[145016.24000000022,6816141.5],[145040.40000000037,6816032.6600000001],[145059.19000000041,6815970.5799999982],[145065.86000000034,6815948.8399999999],[145039.78000000026,6815840.6099999994],[145055.44000000041,6815748.629999999],[145011.5700000003,6815621.9400000013],[145091.34999999963,6815472.7699999996],[145103.16000000015,6815349.9800000004],[145131.01999999955,6815226.7100000009],[145169.15000000037,6815116.5899999999],[145145.04999999981,6815073.5500000007],[145162.30999999959,6814955.2899999991],[145194.49000000022,6814894.2300000004],[145233.04999999981,6814826.0100000016],[145295.33999999985,6814751.8500000015],[145372.45999999996,6814680.6600000001],[145425.86000000034,6814609.4699999988],[145485.1799999997,6814553.1099999994],[145505.36000000034,6814465.6900000013],[145560.98579999991,6814359.2221999988],[145609.04000000004,6814313.6400000006],[145600.87000000011,6814209.0199999996],[145639.4299999997,6814117.0700000003],[145669.08999999985,6814054.7800000012],[145743.25,6813980.620000001],[145802.5700000003,6813933.1600000001],[145850.03000000026,6813903.5],[145964.51999999955,6813903.5],[145992.70999999996,6813838.7300000004],[146089.58000000007,6813798.620000001],[146282.16000000015,6813662.1799999997],[146353.87999999989,6813578.120000001],[146414.09999999963,6813380.4699999988],[146401.6799999997,6813311.2100000009],[146462.19000000041,6813269.4200000018],[146512.8200000003,6813218.4899999984],[146529.83999999985,6813141.4800000004],[146605.01999999955,6813138.5799999982],[146767.54999999981,6813101.4600000009],[146893.48000000045,6813092.870000001],[146989.80999999959,6813046.7699999996],[147161.84999999963,6813003.75],[147451.24000000022,6812979.0799999982],[147512.5700000003,6813077.0100000016],[147789.90000000037,6813259.6400000006],[147811.83999999985,6813322.9899999984],[147871.41999999993,6813401.9800000004],[147962.21999999974,6813503.2300000004],[148080.5700000003,6813521.4899999984],[148119.61000000034,6813552.0799999982],[148148.66999999993,6813594.6600000001],[148216.70000000019,6813612.5300000012],[148276.45000000019,6813606.0199999996],[148377.38999999966,6813555.4699999988],[148461.45999999996,6813496.4200000018],[148495.5,6813527.4800000004],[148815.48000000045,6813594.5199999996],[148837.87000000011,6813662.8599999994],[148880.8200000003,6813779.5899999999],[148901.74000000022,6813831.9699999988],[148962.1799999997,6813876.6799999997],[149062.19000000041,6813870.4800000004],[149100.86000000034,6813897.0899999999],[149138.20000000019,6813964.0399999991],[149236.8200000003,6813942.879999999],[149328.46999999974,6813998.8000000007],[149444.28000000026,6813945.879999999],[149545.91000000015,6814000.870000001],[149709.19000000041,6814059.2300000004],[149765.48000000045,6814113.379999999],[149783.79999999981,6814192.1499999985],[149844.46999999974,6814337.4200000018],[149911.79999999981,6814445.8599999994],[149957.28000000026,6814491.9600000009],[150017.1799999997,6814530.6900000013],[150102.30999999959,6814526.8599999994],[150197.11000000034,6814562.3599999994],[150378.71999999974,6814557.6900000013],[150339.45000000019,6814622.6600000001],[150345.51999999955,6814677.4200000018],[150385.08999999985,6814714],[150459.70000000019,6814705.1000000015],[150555.54999999981,6814784.7699999996],[150601.55999999959,6814814.7199999988],[150654.78000000026,6814813.8399999999],[150716.83000000007,6814766.879999999],[150781.19000000041,6814755.9100000001],[150949.95000000019,6814775.5500000007],[150970.04000000004,6814884.3299999982],[150988.37999999989,6814974.1600000001],[150953.24000000022,6815072.9400000013],[150990.98000000045,6815154.9499999993],[151100.44000000041,6815240.3999999985],[151171.62000000011,6815248.9200000018],[151277.79000000004,6815244.1600000001],[151305.54999999981,6815327.0899999999],[151398.37999999989,6815243.0899999999],[151461.73000000045,6815221.1600000001],[151507.21999999974,6815267.2600000016],[151504.69000000041,6815348.9499999993],[151548.09999999963,6815328.8399999999],[151618.83000000007,6815332.3900000006],[151673.8200000003,6815372.5700000003],[151734.19000000041,6815427.3500000015],[151874.25999999978,6815626.6400000006],[151882.75999999978,6815816.9400000013],[151871.12000000011,6815854.2199999988],[151790.49000000022,6815906.9200000018],[151774.23000000045,6815948.6499999985],[152013.40000000037,6816284.629999999],[152055.12999999989,6816300.8900000006],[152089.33000000007,6816388.25],[152149.24000000022,6816438.0399999991],[152182.91000000015,6816530.4800000004],[152306.71999999974,6816706.120000001],[152389.41000000015,6816566.75],[152540.20999999996,6816511.6000000015],[152550.11000000034,6816662.5500000007],[152562.45999999996,6816741.870000001],[152583.40000000037,6816805.3200000003],[152635.25,6816865.8999999985],[152731.13999999966,6816978.75],[152799.55999999959,6816946.2899999991],[152842.51999999955,6816866.8999999985],[152879.95999999996,6816793.0500000007],[152951.51999999955,6816740.1799999997],[153025.84999999963,6816728.3000000007],[153127.33999999985,6816683.7399999984],[153156.55999999959,6816564.379999999],[153218.62000000011,6816528.4800000004],[153426.25999999978,6816533.4699999988],[153497.51999999955,6816531.9200000018],[153594.01999999955,6816487.8200000003],[153712.61000000034,6816410.5100000016],[153690.29000000004,6816332.1099999994],[153680.70999999996,6816282.7100000009],[153695.58999999985,6816226.0199999996],[153718.61000000034,6816148.4699999988],[153746.58999999985,6816124.7699999996],[153803.66999999993,6816089.3299999982],[153855.29999999981,6816049.370000001],[153916.96999999974,6816063.7899999991],[154038.38999999966,6816006.3299999982],[154130.37000000011,6815967.6799999997],[154219.34999999963,6815994.6700000018],[154306.33999999985,6815956.4800000004],[154419.70999999996,6815844.4400000013],[154452.15000000037,6815901.7899999991],[154565.21999999974,6815971.8200000003],[154716.40000000037,6816073.5300000012],[154748.99000000022,6816045.379999999],[154810.21999999974,6816054.8200000003],[154892.20999999996,6816006.0300000012],[154992,6816052.1400000006],[155065.55999999959,6816075.5199999996],[155322.75999999978,6815920.0500000007],[155524.96999999974,6815866.2100000009],[155598.33999999985,6815833.2899999991],[155717.08000000007,6815812.2800000012],[155820.09999999963,6815827.9200000018],[155854.69000000041,6815930.3299999982],[155966.75999999978,6815935.0799999982],[156027.50999999978,6815939.5199999996],[156089.01999999955,6815897.6499999985],[156139.36000000034,6815845.2100000009],[156191.96999999974,6815789.9800000004],[156236.6799999997,6815740],[156276.13999999966,6815711.0700000003],[156315.58999999985,6815718.9600000009],[156360.30999999959,6815771.5599999987],[156357.6799999997,6815861],[156323.48000000045,6815955.6900000013],[156297.1799999997,6816008.2899999991],[156286.66000000015,6816029.3399999999],[156254.87000000011,6816049.3000000007],[156155.13999999966,6816092.4600000009],[156134.09999999963,6816129.2899999991],[156114.6799999997,6816219.120000001],[156134.09999999963,6816289.7399999984],[156170.91999999993,6816289.7399999984],[156228.91000000015,6816297.6600000001],[156251.66000000015,6816275.8900000006],[156301.03000000026,6816266.3000000007],[156374.04999999981,6816294.7600000016],[156396.70999999996,6816327.5199999996],[156444.48000000045,6816381.8000000007],[156518.12999999989,6816400.2100000009],[156568.09999999963,6816450.1900000013],[156623.33999999985,6816484.379999999],[156656.28000000026,6816504.1499999985],[156745.83999999985,6816476.6900000013],[156926.41000000015,6816515.3500000015],[156986.99000000022,6816463.5],[157083.95999999996,6816424.3900000006],[157175.70999999996,6816410.7300000004],[157301.95999999996,6816363.3900000006],[157354.5700000003,6816387.0599999987],[157362.45999999996,6816442.3000000007],[157336.16000000015,6816576.4400000013],[157333.53000000026,6816657.9800000004],[157341.41999999993,6816771.0899999999],[157338.79000000004,6816857.8900000006],[157338.79000000004,6816891.6900000013],[157420.33000000007,6816942.0599999987],[157490.23000000045,6816971.3099999987],[157635.45999999996,6816962.8500000015],[157745.59999999963,6816946.6600000001],[157837.11000000034,6816903.0100000016],[157861.96999999974,6816845.4100000001],[157865.95999999996,6816779.6700000018],[157846.54999999981,6816634.629999999],[157874.54999999981,6816556.629999999],[157988.75,6816530],[158069.44000000041,6816532.6099999994],[158119.30999999959,6816528.0199999996],[158190.04000000004,6816466.1799999997],[158307.70000000019,6816378.8999999985],[158395.13999999966,6816345.6900000013],[158444.00999999978,6816275.8099999987],[158480.51999999955,6816180.9299999997],[158503.99000000022,6816108.370000001],[158608.0700000003,6816037.4200000018],[158661.46999999974,6815962.0899999999],[158730.28000000026,6815890.379999999],[158739.54999999981,6815772.870000001],[158990.59999999963,6815703.4499999993],[159025.36000000034,6815589.620000001],[159083.74000000022,6815513.8399999999],[159166.11000000034,6815480.1000000015],[159222.25999999978,6815434.6900000013],[159233.21860000025,6815401.6790999994],[159249.6353000002,6815385.7707999982],[159283.70629999973,6815277.8627999984],[159356.1799999997,6815240.3099999987],[159447.63999999966,6815141.370000001],[159489.4299999997,6815005.7699999996],[159522.5700000003,6814972.5500000007],[159617.29999999981,6814963.8000000007],[159680.58000000007,6815093.7399999984],[159749.99000000022,6815137.620000001],[159815.71999999974,6815141.620000001],[159924.48000000045,6815110.4699999988],[160001.1799999997,6815178.8200000003],[160061.54999999981,6815168.2199999988],[160112.71999999974,6815123.2800000012],[160146.37999999989,6815215.6999999993],[160209.12000000011,6815285.3500000015],[160277.54999999981,6815263.9499999993],[160348.73000000045,6815272.4699999988],[160430.80999999959,6815224.6799999997],[160454.12000000011,6815161.1799999997],[160487.25999999978,6815127.9600000009],[160546.62999999989,6815117.4499999993],[160596.55999999959,6815168.1600000001],[160659.0700000003,6815137.25],[160721.20999999996,6815091.3000000007],[160775.28000000026,6814979.7100000009],[160892.25999999978,6814928.6900000013],[160993.65000000037,6814883.1499999985],[161073.5,6814865.7199999988],[161121.59999999963,6814896.4800000004],[161258.66000000015,6814954.2399999984],[161354.76999999955,6814960.4499999993],[161619.08000000007,6814980.3299999982],[161680.37000000011,6814979.7100000009],[161744.25999999978,6814963.7600000016],[161843.36000000034,6814893.2699999996],[161892.6799999997,6814828.3999999985],[161929.74000000022,6814739.4899999984],[162023.54000000004,6814709.7300000004],[162202.41999999993,6814577.5700000003],[162217.84119999968,6814497.7908999994],[162245.33029999956,6814496.6763999984],[162321.43360000011,6814480.1169999987],[162358.80470000021,6814486.1068999991],[162381.85720000044,6814568.0910999998],[162410.88889999967,6814611.7419000007],[162464.15720000025,6814640.9871999994],[162504.67949999962,6814636.1823000014],[162518.76109999977,6814604.6928000003],[162541.35859999992,6814570.3445000015],[162560.71729999967,6814569.5089000016],[162585.19799999986,6814560.4015999995],[162604.17980000004,6814547.7470000014],[162596.49670000002,6814515.2063999996],[162601.01630000025,6814470.4633000009],[162611.00129999965,6814421.3827],[162638.07629999984,6814418.0368999988],[162694.11830000021,6814445.1541000009],[162731.65000000037,6814470.5390999988],[162817.95000000019,6814458.4600000009],[162907.0700000003,6814388.8999999985],[163015.37999999989,6814363.8200000003],[163068.76999999955,6814288.5100000016],[163141.71999999974,6814250.6099999994],[163254.96160000004,6814185.1013999991],[163278.82019999996,6814173.0593999997],[163285.96389999986,6814125.4343999997],[163290.98190000001,6814027.1526999995],[163365.74480000045,6814059.1292000003],[163373.0027999999,6814061.5485999994],[163399.6026999997,6814048.2008999996],[163437.98479999974,6814038.5218999982],[163485.3786000004,6814020.8326999992],[163511.16189999972,6814030.9274999984],[163569.72190000024,6814050.4501999989],[163600.28949999996,6814053.2030000016],[163665.90089999977,6814000.4035999998],[163683.79000000004,6813987.1799999997],[163728.54000000004,6813940.0700000003],[163764.17019999959,6813900.4219999984],[163817.08690000046,6813881.9010999985],[163860.08169999998,6813846.8438000008],[163920.27450000029,6813832.2917000018],[163952.68609999958,6813819.7239000015],[163917.62880000006,6813797.8957999982],[163854.79000000004,6813769.4530000016],[163833.62330000009,6813742.3330999985],[163800.36000000034,6813694.0899999999],[163795.25879999995,6813683.4631000012],[163837.86089999974,6813640.7291000001],[163875.9299999997,6813586.5599999987],[163890.45999999996,6813579.2600000016],[163893.4299999997,6813574.9699999988],[163916.98000000045,6813551.4100000001],[163947.59999999963,6813523.1499999985],[163975.87000000011,6813471.3200000003],[163971.16000000015,6813426.5700000003],[163905.20999999996,6813391.2399999984],[163858.08999999985,6813370.0399999991],[163785.0700000003,6813344.129999999],[163719.12000000011,6813306.4400000013],[163693.92300000042,6813285.5291000009],[163658.20419999957,6813213.2976999991],[163645.50420000032,6813162.4976999983],[163639.94799999986,6813078.3599999994],[163656.61670000013,6813032.3222999983],[163676.46050000004,6812945.8033999987],[163719.32309999969,6812864.0469999984],[163733.61060000025,6812779.9094000012],[163789.96700000018,6812693.3905000016],[163811,6812587.9100000001],[163843.29999999981,6812469.2899999991],[163857.03000000026,6812432.8299999982],[163962.41999999993,6812321.5500000007],[163984.98000000045,6812239.0100000016],[163944.87000000011,6812142.1499999985],[163970.09999999963,6812088.5399999991],[164005,6812085.3200000003],[164058.54000000004,6812055.2399999984],[164082.94000000041,6811992.6499999985],[164106.25,6811929.1600000001],[164138.3200000003,6811840.7300000004],[164137.79000000004,6811780.4400000013],[164171.24000000022,6811706.9600000009],[164225.38999999966,6811585.3099999987],[164228.9299999997,6811514.5899999999],[164326.5700000003,6811373.8599999994],[164377.28000000026,6811323.9299999997],[164412.11000000034,6811265.4100000001],[164424.13999999966,6811177.8099999987],[164504.69000000041,6811069.8200000003],[164525.21999999974,6810911.0500000007],[164589.96999999974,6810849.7800000012],[164633.76999999955,6810779.370000001],[164636.6799999997,6810658.4200000018],[164674.5,6810523.1999999993],[164744.23000000045,6810461.4600000009],[164836.73000000045,6810417.7399999984],[164935.88999999966,6810402.5599999987],[165003.86000000034,6810376.1900000013],[165059.70000000019,6810371.0399999991],[165154.04000000004,6810412.620000001],[165214.79000000004,6810417.0799999982],[165324.54999999981,6810396.8999999985],[165410.59999999963,6810403.0500000007],[165474.1799999997,6810427.3599999994],[165558.48000000045,6810414.5599999987],[165673.75999999978,6810454.2100000009],[165776.69000000041,6810479.9200000018],[165842.5,6810473.8500000015],[165919.58000000007,6810491.879999999],[166016.29999999981,6810548.3299999982],[166097.05999999959,6810606.25],[166137.46999999974,6810662.8599999994],[166203.80999999959,6810717.0799999982],[166260.04000000004,6810726.9899999984],[166392.41000000015,6810733.879999999],[166451.3200000003,6810718.3999999985],[166522.95000000019,6810731.9100000001],[166592.04000000004,6810816.0399999991],[166677.87999999989,6810863.4400000013],[166758.1799999997,6810916.370000001],[166823.45000000019,6810915.379999999],[166901,6810873.0399999991],[166967.49000000022,6810830.7100000009],[167031.37999999989,6810814.7600000016],[167203.26999999955,6810814],[167290.33000000007,6810831.1099999994],[167375.0700000003,6810812.2399999984],[167451.23000000045,6810820.3000000007],[167474.8563000001,6810832.4426999986],[167522.53969999962,6810839.4512999989],[167540.80020000041,6810859.7846999988],[167565.26809999999,6810867.2740999982],[167595.87409999967,6810881.6583999991],[167626.91810000036,6810889.4576999992],[167659.85879999958,6810892.6327],[167709.07129999995,6810924.3827],[167731.69330000039,6810938.6702999994],[167746.77450000029,6810953.3546999991],[167753.33270000014,6810956.0872000009],[167765.8245000001,6810961.2921999991],[167788.84339999966,6810950.5766000003],[167819.79970000032,6810976.373399999],[167860.67800000031,6811026.7767000012],[167866.23419999983,6811082.3394000009],[167888.06230000034,6811114.8830999993],[167899.5717000002,6811133.9331],[167934.89379999973,6811172.0331000015],[167955.68420000002,6811223.7734000012],[167969.81879999954,6811258.9490999989],[167985.69379999954,6811287.5240999982],[168011.09379999992,6811318.4805000015],[168028.15950000007,6811333.5617000014],[168057.52830000035,6811355.7866999991],[168073.99670000002,6811355.6169000007],[168059.8069000002,6811371.2983999997],[168055.84420000017,6811417.5577999987],[168000.8200000003,6811545.9499999993],[167986,6811657.9400000013],[168034.76999999955,6811794.2199999988],[168046.80999999959,6811924.8500000015],[168038.8200000003,6812056.3099999987],[168049.40000000037,6812105.6099999994],[168069.04999999981,6812155.0899999999],[168085.45000000019,6812223.9699999988],[168126.94000000041,6812335.7899999991],[168173.71999999974,6812406.8900000006],[168232.70000000019,6812446.7100000009],[168262.75,6812489.1999999993],[168295.5,6812571.6400000006],[168289.37000000011,6812668.75],[168314.98000000045,6812771.9600000009],[168345.41999999993,6812829.5],[168418.58999999985,6812903.1999999993],[168465.99000000022,6813035.5899999999],[168477.41000000015,6813104.9299999997],[168509.76999999955,6813172.3399999999],[168557.25,6813360.0300000012],[168643.20999999996,6813518.0199999996],[168642.51999999955,6813608.5899999999],[168640.76999999955,6813916.4600000009],[168534.86000000034,6813967.4600000009],[168511.91000000015,6814100.3000000007],[168456.38999999966,6814206.9899999984],[168419.73000000045,6814245.5700000003],[168370.3200000003,6814255.1499999985],[168252.04999999981,6814281.1400000006],[168167.38999999966,6814355.3200000003],[168163.45999999996,6814410.9899999984],[168208.87000000011,6814467.129999999],[168202.19000000041,6814558.2600000016],[168184.25,6814636.3399999999],[168239.3200000003,6814731.8200000003],[168358.95999999996,6814720.7800000012],[168509.88999999966,6814710.879999999],[168610.21999999974,6814751.9100000001],[168623.94000000041,6814846.1799999997],[168679.63999999966,6814948.6400000006],[168677.0700000003,6814953.5100000016],[168670.84999999963,6814954.3099999987],[168648.66999999993,6814976.4899999984],[168600.61000000034,6814987.5799999982],[168552.54999999981,6815008.8399999999],[168486.94000000041,6815028.2399999984],[168432.41000000015,6815043.9600000009],[168370.49000000022,6815077.2300000004],[168325.20000000019,6815122.5100000016],[168287.30999999959,6815161.3299999982],[168217.0700000003,6815222.3299999982],[168176.41000000015,6815264.8399999999],[168120.03000000026,6815307.3500000015],[168098.16999999993,6815319.6799999997],[168093.91949999984,6815322.628899999],[168072.24000000022,6815337.6700000018],[168060.87999999989,6815350.7899999991],[167958.29999999981,6815449.6799999997],[167911.16000000015,6815535.629999999],[167898.21999999974,6815604.9400000013],[167870.5,6815676.1099999994],[167833.53000000026,6815729.7100000009],[167804.87999999989,6815747.2699999996],[167742.03000000026,6815865.5700000003],[167717.08000000007,6815936.7300000004],[167687.50999999978,6815970.9200000018],[167651.45999999996,6815989.4100000001],[167634.83000000007,6816016.2100000009],[167627.4299999997,6816022.6799999997],[167602.48000000045,6816051.3299999982],[167549.79999999981,6816073.5100000016],[167512.83000000007,6816054.1000000015],[167475.87000000011,6816018.0599999987],[167422.25999999978,6816004.1900000013],[167372.36000000034,6816027.3000000007],[167351.09999999963,6816048.5599999987],[167274.38999999966,6816074.4299999997],[167243.45000000019,6816078.4699999988],[167245.87000000011,6816163.120000001],[167318.62999999989,6816302.0199999996],[167371.54000000004,6816434.3099999987],[167404.62000000011,6816546.7600000016],[167477.37999999989,6816758.4299999997],[167483.99000000022,6816860.9600000009],[167517.05999999959,6816999.8599999994],[167556.75,6817122.2300000004],[167556.75,6817143.6700000018],[167385.59999999963,6817163.6499999985],[167320.70999999996,6817179.6999999993],[167259.66999999993,6817226.5500000007],[167183.11000000034,6817268.8200000003],[167109.3200000003,6817340.9899999984],[166980.16999999993,6817369],[166897.40000000037,6817507.3599999994],[166595.12999999989,6817566.4200000018],[166487.13999999966,6817758.3900000006],[166447.25999999978,6817762.0700000003],[166329.75,6817959.9600000009],[166251.99000000022,6818043.5599999987],[166225.83000000007,6818141.5],[166191.99000000022,6818265.3000000007],[166138.30999999959,6818391.9299999997],[166115.29000000004,6818469.4800000004],[166132.24000000022,6818533.2800000012],[166244.75999999978,6818608.370000001],[166323.08000000007,6818737.9100000001],[166355.45000000019,6818805.3200000003],[166436.98000000045,6819024.0599999987],[166523.26999999955,6819087.5],[166572.04000000004,6819223.7899999991],[166653.26999999955,6819286.6799999997],[166705.95999999996,6819301.9400000013],[166867.63999999966,6819255.8500000015],[167035.99000000022,6819325.8000000007],[166996.73000000045,6819390.75],[166889.04000000004,6819531.4200000018],[166894.55999999959,6819591.25],[166971.79999999981,6819654.5100000016],[167068.88999999966,6819726],[167107.79999999981,6819864.2100000009],[167117.37999999989,6819913.6000000015],[167142.76999999955,6820123.4299999997],[167161.01999999955,6820212.25],[167241.08999999985,6820360.75],[167326.45999999996,6820675.6700000018],[167327.53000000026,6820730.879999999],[167285.20999999996,6820871.5500000007],[167276.23000000045,6820937.7399999984],[167274.70999999996,6821084.6999999993],[167284.12999999989,6821284.9600000009],[167266.26999999955,6821418.3399999999],[167209.8200000003,6821569.3599999994],[167102.20999999996,6821711.0199999996],[167004.88999999966,6821746.1499999985],[166863.25999999978,6821793.25],[166845.86000000034,6821799.0399999991],[166717.95000000019,6821807.8200000003],[166663.21999999974,6821809.9800000004],[166641.79999999981,6821810.8299999982],[166548.53000000026,6821889.8299999982],[166538.59999999963,6821927.9499999993],[166503.98000000045,6822060.870000001],[166437.01999999955,6822152.5300000012],[166379.20000000019,6822234.2899999991],[166302.19000000041,6822271.5700000003],[166211.05999999959,6822264.8900000006],[166164.91000000015,6822229.9600000009],[166074.83999999985,6822161.8000000007],[165980.41999999993,6822130.3000000007],[165923.66999999993,6822060.120000001],[165919.37000000011,6821970.0100000016],[165806.16000000015,6822050.8599999994],[165709.29999999981,6822090.9600000009],[165690.15000000037,6822099.3099999987],[165592.33000000007,6822141.9800000004],[165404.74000000022,6822179.3999999985],[164888.95999999996,6822161.4200000018],[164867.00999999978,6822160.6499999985],[164797.70999999996,6822223.9899999984],[164575.45000000019,6822427.1400000006],[164541.49000000022,6822458.1799999997],[164446.08999999985,6822568.5500000007],[164361.41000000015,6822631.6900000013],[164214.37999999989,6822782.0100000016],[164009.8200000003,6823028.1799999997],[163907.04999999981,6823058.7699999996],[163863.5700000003,6823062.7800000012],[163852.20999999996,6823063.8299999982],[163787.63999999966,6822974.25],[163712.69000000041,6822870.5500000007],[163594.01999999955,6822739.7100000009],[163561.59999999963,6822729.0199999996],[163322.28000000026,6822650.1400000006],[163315.45999999996,6822638.1900000013],[163292.29000000004,6822597.6000000015],[163230.16000000015,6822578.1900000013],[163201.37999999989,6822578.3900000006],[163053.28000000026,6822579.4299999997],[162969.11000000034,6822546.0500000007],[162810.90000000037,6822482.629999999],[162697.99000000022,6822457.8399999999],[162555.1799999997,6822446.8900000006],[162439.20000000019,6822497.8099999987],[162374.30999999959,6822529.1700000018],[162272.08000000007,6822604.75],[162204.91999999993,6822629.6099999994],[162126.96999999974,6822634.1099999994],[162052.62999999989,6822694.3500000015],[161959.50999999978,6822770.3599999994],[161879.53000000026,6822884.3500000015],[161872.13999999966,6823020.8599999994],[161852.65000000037,6823080.8200000003],[161809.1799999997,6823172.2600000016],[161770.03000000026,6823211.1999999993],[161689.62999999989,6823376.5399999991],[161663.94000000041,6823357.9699999988],[161584.76999999955,6823286.4699999988],[161487.73000000045,6823197.0799999982],[161406.49000000022,6823120.0799999982],[161324.34999999963,6823047.2100000009],[161258.45999999996,6822997.9800000004],[161152.86000000034,6822934.0300000012],[161107.21999999974,6822931.4899999984],[161083.65000000037,6822921.7399999984],[161033.16000000015,6822900.8500000015],[161020.5,6822890.9400000013],[160974.41999999993,6822854.879999999],[160956.54999999981,6822801.25],[160918.24000000022,6822739.9600000009],[160859.5,6822678.6700000018],[160846.74000000022,6822659.5199999996],[160823.75,6822625.0500000007],[160793.48000000045,6822623.5399999991],[160649.20999999996,6822596.629999999],[160495.12000000011,6822561.5700000003],[160382.58999999985,6822551.8399999999],[160357.28000000026,6822604.4600000009],[160267.61000000034,6822668.0500000007],[160194.29000000004,6822690.9100000001],[160095.04000000004,6822770.4600000009],[159892.16000000015,6822860.5899999999],[159867.30999999959,6822918.1900000013],[159732.79000000004,6822948.5199999996],[159708.70000000019,6822953.9499999993],[159695.70999999996,6822976.1999999993],[159684.26999999955,6822995.8099999987],[159693.40000000037,6823049.2899999991],[159660.83999999985,6823093.3399999999],[159660.83999999985,6823166.120000001],[159624.34999999963,6823191.6600000001],[159603.37999999989,6823206.3399999999],[159576.5700000003,6823215.9200000018],[159513.36000000034,6823200.6000000015],[159448.24000000022,6823171.870000001],[159346.73000000045,6823146.9699999988],[159178.1799999997,6823160.370000001],[159021.12999999989,6823223.5799999982],[158971.41999999993,6823264.9600000009],[158885.0700000003,6823324.620000001],[158782.66000000015,6823359.2199999988],[158756.08999999985,6823407.6099999994],[158742.58000000007,6823423.4200000018],[158730.00999999978,6823440.0500000007],[158705.41000000015,6823463.0100000016],[158643.74000000022,6823495.4600000009],[158619.66999999993,6823511.0300000012],[158578.48000000045,6823555.0599999987],[158579.25,6823573.4200000018],[158581.86000000034,6823635.1999999993],[158535.73000000045,6823639.1600000001],[158486.75999999978,6823711.9800000004],[158444.62000000011,6823782.8500000015],[158415.88999999966,6823819.2399999984],[158362.80999999959,6824094.8999999985],[158376.16999999993,6824185.1900000013],[158397.66000000015,6824243.5500000007],[158461.62000000011,6824282.8999999985],[158525.94000000041,6824334.9299999997],[158549.75,6824353.7199999988],[158563.12999999989,6824385.0899999999],[158607.50999999978,6824489.1799999997],[158628.38999999966,6824704.4800000004],[158620.41000000015,6824835.9499999993],[158608.54000000004,6824968.7899999991],[158537.80999999959,6825030.6400000006],[158547.25999999978,6825176.5899999999],[158503.38999999966,6825246.0100000016],[158503.91999999993,6825306.3000000007],[158515.80999999959,6825380.629999999],[158589.08000000007,6825455.3299999982],[158641.78000000026,6825535.9499999993],[158660.11000000034,6825614.7100000009],[158668.00999999978,6825613.8599999994],[158705.37999999989,6825609.8599999994],[158712.88999999966,6825609.5100000016],[158775.09999999963,6825634.7800000012],[158794.25999999978,6825642.5599999987],[158897.0700000003,6825706.8200000003],[158912.69000000041,6825761.4800000004],[158933.05999999959,6825832.7699999996],[158935.62999999989,6825974.1400000006],[158961.33000000007,6826084.6600000001],[158997.3200000003,6826151.4899999984],[159010.16999999993,6826184.9100000001],[158974.1799999997,6826220.8900000006],[158963.66000000015,6826252.4400000013],[158945.91000000015,6826305.7100000009],[158920.20999999996,6826382.8299999982],[158915.0700000003,6826441.9400000013],[158891.9299999997,6826490.7800000012],[158881.65000000037,6826519.0500000007],[158545.09999999963,6826427.8599999994],[158484.26999999955,6826368.1099999994],[158399.8200000003,6826335.6600000001],[158353.49000000022,6826269.5500000007],[158326.1799999997,6826191.620000001],[158304.59999999963,6826132.2600000016],[158266.49000000022,6826100.5799999982],[158196.63999999966,6826073.8399999999],[157986.75,6826077.129999999],[157906.34999999963,6826070.1799999997],[157896.66000000015,6826056.9699999988],[157841.15000000037,6826033.6600000001],[157797.86000000034,6826007.0100000016],[157765.66999999993,6825997.0199999996],[157733.48000000045,6826012.5599999987],[157709.05999999959,6826035.879999999],[157691.54000000004,6826054.5300000012],[157674.63999999966,6826072.5100000016],[157651.33000000007,6826094.7100000009],[157623.58000000007,6826131.3399999999],[157592.79999999981,6826134.6099999994],[157573.62999999989,6826159.0899999999],[157546.55999999959,6826176.3399999999],[157532.54999999981,6826199.0599999987],[157511.45999999996,6826215.7100000009],[157500.41999999993,6826217.9800000004],[157473.71999999974,6826223.4800000004],[157444.88999999966,6826230.9600000009],[157434.44000000041,6826231.8000000007],[157408.40000000037,6826256.5700000003],[157365.45999999996,6826297.3999999985],[157328.21999999974,6826300.8399999999],[157260.75,6826307.0700000003],[157237.87999999989,6826343.3900000006],[157186.0700000003,6826384.8399999999],[157121.8200000003,6826463.6000000015],[157080.37000000011,6826500.8999999985],[157011.79000000004,6826567.8000000007],[156987.11000000034,6826633.5399999991],[156974.66999999993,6826664.629999999],[156937.37000000011,6826712.3000000007],[156918.70999999996,6826751.6700000018],[156888.41000000015,6826819.4200000018],[156883.48000000045,6826830.4299999997],[156856.54000000004,6826925.7600000016],[156855.70999999996,6826933.629999999],[156847.54999999981,6827004.1900000013],[156817.16000000015,6826990.0100000016],[156775.70999999996,6826971.3599999994],[156717.6799999997,6826965.1400000006],[156659.65000000037,6826938.1999999993],[156603.69000000041,6826892.6000000015],[156528.16000000015,6826877.5],[156302.66000000015,6826746.5],[156045.50999999978,6826631.25],[155949.15000000037,6826588.0599999987],[155759.66000000015,6826378.1400000006],[155756.41999999993,6826374.5500000007],[155688.20000000019,6826342.629999999],[155639.75,6826319.9499999993],[155576.62000000011,6826235.25],[155567.1799999997,6826089.2899999991],[155521.30999999959,6826028.1499999985],[155511.5,6826019.2100000009],[155360.98000000045,6825882.0399999991],[155303.20000000019,6825668.7100000009],[155266.37999999989,6825532.7399999984],[155289.46999999974,6825374],[155290.99000000022,6825363.5199999996],[155288.48000000045,6825352.870000001],[155258.61000000034,6825226.2800000012],[155248.03000000026,6825181.4200000018],[155188.49000000022,6825088.620000001],[155185.91999999993,6825084.620000001],[155165.79999999981,6825053.25],[155144.33000000007,6824986.6400000006],[155136.51999999955,6824962.3900000006],[155136.51999999955,6824954.3900000006],[155133.04999999981,6824871.2699999996],[155101.58000000007,6824854.0100000016],[155025.6799999997,6824812.379999999],[155002.3200000003,6824720.370000001],[154935.62000000011,6824677.3000000007],[154779.48000000045,6824605.1600000001],[154675.83999999985,6824573.379999999],[154592.62999999989,6824588.7100000009],[154544.21999999974,6824597.629999999],[154461.08999999985,6824576.8500000015],[154409.74000000022,6824594.9699999988],[154402.20000000019,6824597.629999999],[154393.55999999959,6824625.7199999988],[154362.4299999997,6824684.8900000006],[154334.75,6824710.3399999999],[154288.26999999955,6824753.0899999999],[154232.75999999978,6824792.6700000018],[154121.04000000004,6824872.3399999999],[154093.87999999989,6824891.7100000009],[154045.19000000041,6824909.1400000006],[153957.58999999985,6824940.5],[153883.00999999978,6824914.1499999985],[153878.58000000007,6824912.5899999999],[153869.08999999985,6824902.9200000018],[153760.91000000015,6824792.7100000009],[153616.91999999993,6824660.1700000018],[153538.91000000015,6824632.1700000018],[153516.13999999966,6824638.3399999999],[153454.53000000026,6824655.0500000007],[153311.62000000011,6824577.7199999988],[153303.79999999981,6824564.0399999991],[153262.45000000019,6824491.75],[153181.29000000004,6824484.1499999985],[153130.36000000034,6824433.5399999991],[153098.51999999955,6824361.0500000007],[152957.45000000019,6824369.0399999991],[152810.86000000034,6824317.1999999993],[152664.50999999978,6824311.6000000015],[152650.98000000045,6824433.9299999997],[152642.59999999963,6824509.7300000004],[152631.03000000026,6824602.3200000003],[152569.44000000041,6824643.2100000009],[152428.23000000045,6824747.7699999996],[152291.75999999978,6824805.620000001],[152202.38999999966,6824763.5799999982],[152187.76999999955,6824763.8500000015],[152173.33999999985,6824764.120000001],[151874.08999999985,6824769.75],[151712.08000000007,6824714.3000000007],[151601.83999999985,6824664.129999999],[151546,6824603.9100000001],[151419.91999999993,6824501.8999999985],[151356.11000000034,6824518.8500000015],[151327.5700000003,6824539.2300000004],[151299.5,6824559.2800000012],[151260.16000000015,6824568.9499999993],[151195.6799999997,6824564.4100000001],[151087.04999999981,6824556.7600000016],[151005.98000000045,6824550.1700000018],[150949,6824586.6000000015],[150938.99000000022,6824589.5199999996],[150620.91000000015,6824682.2600000016],[150591.62000000011,6824746.3099999987],[150537.37999999989,6824801.6099999994],[150490.37999999989,6824837.120000001],[150469.99000000022,6824975.7899999991],[150418.13999999966,6825057.0100000016],[150364.8200000003,6825041.4100000001],[150329.25,6825031.0100000016],[150268.74000000022,6825072.8000000007],[150234.47130000032,6825117.1944000013],[150164.24469999969,6825229.2192000002],[150149.08270000014,6825253.4054999985],[149930.37469999958,6825283.1119000018],[149745.16590000037,6825324.122299999],[149633.40000000037,6825404.8599999994],[149559.79000000004,6825506.3200000003],[149518.01999999955,6825542.129999999],[149450.37999999989,6825625.6799999997],[149378.75999999978,6825713.2199999988],[149291.21999999974,6825884.3099999987],[149253.41999999993,6825965.870000001],[149211.65000000037,6826109.1099999994],[149187.76999999955,6826174.7600000016],[149157.9299999997,6826190.6799999997],[149152.9299999997,6826191.629999999],[149240.29999999981,6826276.0799999982],[149305.73000000045,6826374.6400000006],[149340.08000000007,6826507.25],[149320.16000000015,6826650.8999999985],[149250.58980000019,6826722.2837999985],[149168.03969999962,6826741.3337000012],[149012.46439999994,6826703.2338000014],[148763.22640000004,6826806.4213999994],[148654.61000000034,6826893.379999999],[148641.62999999989,6826982.1600000001],[148636.73000000045,6827015.7199999988],[148656.63999999966,6827059.0199999996],[148683.29999999981,6827117.0199999996],[148658.58999999985,6827124.0799999982],[148600.00999999978,6827140.8099999987],[148553.79999999981,6827192],[148518.79999999981,6827230.7699999996],[148455.38999999966,6827273.8399999999],[148324.35059999954,6827301.8869000003],[147822.96439999994,6827402.4288000017],[147699.16000000015,6827470.9100000001],[147689.62999999989,6827529.9499999993],[147685.80999999959,6827566.1600000001],[147627.21999999974,6827683.2100000009],[147618.94000000041,6827789.5799999982],[147588.62000000011,6827809.1499999985],[147533.50999999978,6827844.7399999984],[147499.04999999981,6827859.5599999987],[147456.12000000011,6827878.0300000012],[147430.87999999989,6827878.8000000007],[147390.9299999997,6827880.0300000012],[147284.08999999985,6827921.0700000003],[147280.90000000037,6827936.0500000007],[147270.75999999978,6827983.6700000018],[147207.30999999959,6828004.6000000015],[147116.75,6828014.9600000009],[147028.91999999993,6827986.5599999987],[146986.12000000011,6827972.7199999988],[146912.24000000022,6828042.0399999991],[146839.87000000011,6828109.9400000013],[146768.38999999966,6828152.7399999984],[146652.28000000026,6828245.9400000013],[146512.87000000011,6828312.9699999988],[146462.26999999955,6828337.3000000007],[146411.53000000026,6828361.6999999993],[146329.94000000041,6828382.6000000015],[146239.58000000007,6828405.7399999984],[146171.0700000003,6828402.0199999996],[146081.88999999966,6828397.1700000018],[146023.09999999963,6828425.3999999985],[146014.46999999974,6828429.5399999991],[145915.75999999978,6828449.7199999988],[145909.59999999963,6828448.6400000006],[145898.29000000004,6828446.6700000018],[145844.20000000019,6828437.2199999988],[145760.58999999985,6828370.5100000016],[145752.37999999989,6828336.0700000003],[145740.66999999993,6828319.4200000018],[145716.5,6828285.0599999987],[145695.54999999981,6828282.8500000015],[145665.70999999996,6828279.6999999993],[145639.33999999985,6828311.9299999997],[145632.75999999978,6828319.9699999988],[145606.95999999996,6828351.5],[145540.08999999985,6828378.8000000007],[145518.00999999978,6828412.0199999996],[145427.20000000019,6828506.8999999985],[145344.37999999989,6828677.4899999984],[145311.65000000037,6828711.6900000013],[145253.5700000003,6828772.370000001],[145255.23000000045,6828826.7300000004],[145255.55999999959,6828837.5599999987],[145294.30999999959,6828919.4699999988],[145214.16000000015,6828922.8500000015],[145182.11000000034,6828956.9899999984],[145157.79000000004,6829009.5300000012],[145153.79000000004,6829052.2899999991],[145141.08999999985,6829188.0899999999],[145098.05999999959,6829328.0700000003],[145096.23000000045,6829334.0399999991],[144961.78000000026,6829544.6000000015],[144897.66999999993,6829601.8200000003],[144820.26999999955,6829617.9800000004],[144794.05999999959,6829623.4499999993],[144762.76999999955,6829677.9899999984],[144738.61000000034,6829720.1000000015],[144593.15000000037,6829876.3500000015],[144575.91000000015,6829994.620000001],[144505.71999999974,6830024.9400000013],[144487.3200000003,6830032.8900000006],[144433.73000000045,6830056.0300000012],[144395.5,6830053.2800000012],[144378.03000000026,6830058.5599999987],[144345.15000000037,6830067.2100000009],[144251.69000000041,6830067.2100000009],[144151.3200000003,6830067.2100000009],[144076.90000000037,6830075.870000001],[143992.09999999963,6830075.870000001],[143852.80999999959,6830082.5100000016],[143789.61000000034,6830084.5199999996],[143787.86000000034,6830086.9800000004],[143708.26999999955,6830198.7399999984],[143670.20000000019,6830259.3099999987],[143632.12000000011,6830328.5399999991],[143598.75999999978,6830388.1900000013],[143569.8200000003,6830428.9200000018],[143536.45000000019,6830502.3399999999],[143488.48000000045,6830574.2899999991],[143427.91000000015,6830641.7800000012],[143363.87000000011,6830695.4299999997],[143334.61000000034,6830722.7899999991],[143294.65000000037,6830783.6999999993],[143275.61000000034,6830837.3500000015],[143215.04000000004,6830920.4200000018],[143182.16000000015,6830951.5700000003],[143135.4299999997,6830975.8000000007],[143096.95999999996,6830999.0100000016],[143055.51999999955,6831044.2699999996],[142952,6831132.2800000012],[142852.9299999997,6831224.9100000001],[142766.05999999959,6831286.2399999984],[142714.71999999974,6831274.8900000006],[142661.62999999989,6831298.9100000001],[142591.16999999993,6831298.370000001],[142501.6799999997,6831309.6499999985],[142416.28000000026,6831310.5],[142313.58999999985,6831353.1900000013],[142240.00999999978,6831318.7300000004],[142189.20000000019,6831302.3000000007],[142109.61000000034,6831257.3500000015],[142005.62999999989,6831209.620000001],[141920.21999999974,6831210.4699999988],[141782.16000000015,6831240.3200000003],[141704.50999999978,6831216.3000000007],[141602.75,6831203.5799999982],[141516.87000000011,6831199.4400000013],[141413.80999999959,6831161.6900000013],[141314.34999999963,6831108.5100000016],[141240.75,6831063.0100000016],[141164.78000000026,6831039.370000001],[141253.48000000045,6830961.25],[141300.36000000034,6830902.6000000015],[141351.79000000004,6830784.1999999993],[141417.70000000019,6830713.7399999984],[141476.25,6830628.870000001],[141493.20999999996,6830507.6099999994],[141517.6799999997,6830434.9499999993],[141557.28000000026,6830417.2199999988],[141575.91999999993,6830324.9600000009],[141539.16999999993,6830242.8599999994],[141620.5,6830176.0100000016],[141646.45000000019,6830119.2899999991],[141666,6829971.6600000001],[141687.04000000004,6829839.9600000009],[141717.49000000022,6829701.3599999994],[141713.49000000022,6829570.9699999988],[141675.90000000037,6829468.8299999982],[141632.76999999955,6829426.5500000007],[141627.09999999963,6829397.8999999985],[141536.45999999996,6829418.3299999982],[141461.46999999974,6829423.25],[141377.8200000003,6829388.7300000004],[141293.41999999993,6829335.1700000018],[141226.63999999966,6829309.1499999985],[141148.99000000022,6829350.5199999996],[141064.95000000019,6829366.3299999982],[141061.12000000011,6829366.629999999],[140998.24000000022,6829394.879999999],[140896.08999999985,6829416.6700000018],[140823.90000000037,6829442.5500000007],[140743.54000000004,6829471.1499999985],[140672.71999999974,6829477.9600000009],[140649.5700000003,6829445.2699999996],[140623.65000000037,6829395.4100000001],[140615.51999999955,6829377.1700000018],[140589.63999999966,6829356.7399999984],[140580.11000000034,6829351.2899999991],[140524.25999999978,6829317.2399999984],[140501.33999999985,6829315.9200000018],[140453.44000000041,6829313.1600000001],[140362.19000000041,6829310.4299999997],[140281.83000000007,6829306.3500000015],[140129.29000000004,6829302.2600000016],[140027.13999999966,6829315.879999999],[139946.78000000026,6829318.6000000015],[139880.66000000015,6829309.8599999994],[139873.24000000022,6829358.1000000015],[139833.74000000022,6829405.7699999996],[139784.20999999996,6829454.2899999991],[139467.04999999981,6829893.0799999982],[139432.37000000011,6830051.1799999997],[139408.20000000019,6830268.6600000001],[139354.01999999955,6830433.5899999999],[139363.46999999974,6830699.2600000016],[139282.59999999963,6830901.8599999994],[139205.62999999989,6831103.1000000015],[139112.94000000041,6831275.6099999994],[139103.80999999959,6831296.5700000003],[139042.41000000015,6831415.8999999985],[138774.21999999974,6831822.8900000006],[138696.86000000034,6831943.6900000013],[138613.75,6832034.8599999994],[138510.54000000004,6832147.9899999984],[138419.6799999997,6832209.6900000013],[138294.29000000004,6832224.2899999991],[138194.83000000007,6832236.4899999984]]]},"properties":{"OBJECTID":152,"objektType":"Delfelt","delfeltNr":1187,"delfeltNavn":"BYGDIN","delfeltAreal_km2":305.54000000000002,"vannkraftverkNr":523,"vannkraftverkNavn":"Øvre Vinstra","magasinNr":42,"magasinNavn":"BYGDIN","nesteDelfeltNr":2670,"delfeltFormal":"Kraftproduksjon","vassdragsomradeNr":"002","oppstromDelfeltListe":"1187","QNormalDelfelt6190_Mm3Aar":397.98000000000002,"QNormalDelfelt9120_Mm3Aar":418.07999999999998}}]} \ No newline at end of file diff --git a/docs/benchmarks/2026-09-29/bygdin/run.sh b/docs/benchmarks/2026-09-29/bygdin/run.sh new file mode 100644 index 00000000..36aed324 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin/run.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Increment 22 acceptance on Bygdin (@perf, 2026-09-29). Runs every timed +# block and writes raw logs to ./logs; meshes and GeoJSONs go to $SCRATCH. +# Usage: SCRATCH=/some/dir bash run.sh (from the repository root, .venv active) +set -u +HERE=docs/benchmarks/2026-09-29/bygdin +LOG=$HERE/logs +DEM=../rasputin_data/DTM10_UTM33_20260925 +GPKG=../rasputin_data/corine2018_dtm10_utm33.gpkg +mkdir -p "$LOG" "$SCRATCH" + +catchment() { # $1 tolerance ("" = default), $2 tag + local tol=() + [ -n "$1" ] && tol=(--outline-tolerance "$1") + /usr/bin/time -l rasputin catchment --dem $DEM --seed 8.5425 61.3512 \ + --lakes $GPKG --lakes-layer corine2018 ${tol[@]+"${tol[@]}"} \ + --out "$SCRATCH/$2.geojson" > "$LOG/$2.out" 2> "$LOG/$2.err" + echo "$2 exit=$?" | tee -a "$LOG/exits.txt" +} + +mesh() { # $1 domain geojson, $2 tolerance, $3 tag, $4 ascii|binary, rest: extra args + local dom=$1 tol=$2 tag=$3 fmt=$4; shift 4 + /usr/bin/time -l rasputin mesh --dem $DEM --domain "$dom" --tolerance "$tol" \ + "$@" --"$fmt" --out "$SCRATCH/$tag.vtk" --stats "$LOG/$tag.stats.md" \ + > "$LOG/$tag.out" 2> "$LOG/$tag.err" + echo "$tag exit=$?" | tee -a "$LOG/exits.txt" +} + +# Block 1: the catchment. +pmset -g batt > "$LOG/pmset_catchment_start.txt" +for i in 1 2 3; do catchment "" "catchment_t20_run$i"; done +for t in 0 10 20 50; do catchment "$t" "catchment_t$t"; done +pmset -g batt > "$LOG/pmset_catchment_end.txt" +cp "$SCRATCH/catchment_t20_run1.geojson" "$HERE/bygdin_reduced_t20.geojson" + +# Block 2: meshes. Three timed binary runs each, then one ASCII run for the +# quality check (tools/bench.py's quality(), which reads ASCII only). +RED=$SCRATCH/catchment_t20_run1.geojson +FINE=$SCRATCH/catchment_t0.geojson +pmset -g batt > "$LOG/pmset_mesh_start.txt" +for i in 1 2 3; do + mesh "$RED" 10 "mesh_red_t10_run$i" binary + mesh "$RED" 1 "mesh_red_t1_run$i" binary + mesh "$FINE" 10 "mesh_fine_t10_run$i" binary + mesh "$FINE" 1 "mesh_fine_t1_run$i" binary + mesh "$RED" 10 "mesh_feat_t10_run$i" binary \ + --features $GPKG --features-layer corine2018 --features-map corine +done +pmset -g batt > "$LOG/pmset_mesh_end.txt" +mesh "$RED" 10 mesh_red_t10_ascii ascii +mesh "$RED" 1 mesh_red_t1_ascii ascii +mesh "$FINE" 10 mesh_fine_t10_ascii ascii +mesh "$FINE" 1 mesh_fine_t1_ascii ascii +mesh "$RED" 10 mesh_feat_t10_ascii ascii \ + --features $GPKG --features-layer corine2018 --features-map corine +pmset -g batt > "$LOG/pmset_quality_end.txt" From 1985720cc78e604c85abdd6270b851deef194752 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:38:57 +0200 Subject: [PATCH 08/16] 22 acceptance: NVE's polygon fetched, not committed As the design says: the licence and attribution terms are not recorded. README records the curl command, the date fetched and the sha256; analyse.py takes the polygon's path as an argument. Co-Authored-By: Claude Opus 5.5 --- docs/benchmarks/2026-09-29/bygdin.md | 7 ++---- docs/benchmarks/2026-09-29/bygdin/README.md | 25 ++++++++++++------- docs/benchmarks/2026-09-29/bygdin/analyse.py | 5 ++-- .../bygdin/nve_delfelt_1187.geojson | 1 - 4 files changed, 21 insertions(+), 17 deletions(-) delete mode 100644 docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson diff --git a/docs/benchmarks/2026-09-29/bygdin.md b/docs/benchmarks/2026-09-29/bygdin.md index 69857cb7..b2de0823 100644 --- a/docs/benchmarks/2026-09-29/bygdin.md +++ b/docs/benchmarks/2026-09-29/bygdin.md @@ -34,8 +34,8 @@ outlines are valid): | 20 | 740 | 19.92 m | | 50 | 295 | 49.85 m | -**Against NVE's polygon** (delfelt 1187, fetched without GDAL and saved as -`bygdin/nve_delfelt_1187.geojson`), comparing DEM nodes: +**Against NVE's polygon** (delfelt 1187, fetched without GDAL and not +committed; the README gives the command and the sha256), comparing DEM nodes: - 99.12 % of NVE's nodes are in ours; - 99.33 % of ours are in NVE's; @@ -65,7 +65,4 @@ This is the design's prototype figure, reproduced by the built code. **For the main session**: -- The design says NVE's polygon is "not committed", but this brief asked for - it to be saved. It is committed, and the conflict is flagged in the - README. - No defects were found. diff --git a/docs/benchmarks/2026-09-29/bygdin/README.md b/docs/benchmarks/2026-09-29/bygdin/README.md index 07b1cd41..88f79592 100644 --- a/docs/benchmarks/2026-09-29/bygdin/README.md +++ b/docs/benchmarks/2026-09-29/bygdin/README.md @@ -21,13 +21,20 @@ the tables and where every number comes from. - **Seed**: `--seed 8.5425 61.3512` (EPSG:4326), as in the design; it falls in CORINE's Bygdin polygon (39.936963 km², 399,371 nodes). - **Reference**: NVE's reservoir catchment ("delfelt") 1187 "BYGDIN", - `delfeltAreal_km2` = 305.54, fetched on 2026-09-29 by curl (no GDAL) from - `https://gis3.nve.no/map/rest/services/Mapservices/VassdragsreguleringVannkraft/MapServer/8/query?where=delfeltNr%3D1187&outFields=*&returnGeometry=true&outSR=25833&f=geojson` - and saved as `nve_delfelt_1187.geojson` (one Polygon, 1376 coordinates, - 305.5393 km² by shapely, valid). The design says this polygon is "fetched - by the URL, not committed"; it is committed here because the brief for - this run asked for it to be saved with the evidence. Remove it if the - design's wording should win. + `delfeltAreal_km2` = 305.54. Its polygon was fetched on 2026-09-29 (by + curl, no GDAL) with exactly this command: + + ```sh + curl -sS --max-time 60 -o nve_delfelt_1187.geojson 'https://gis3.nve.no/map/rest/services/Mapservices/VassdragsreguleringVannkraft/MapServer/8/query?where=delfeltNr%3D1187&outFields=*&returnGeometry=true&outSR=25833&f=geojson' + ``` + + The file as fetched has sha256 + `fe6d298b08573e50e50e711f1be9f70e9459ea823a0626516a539cd62cc4e76c`: one + Polygon, 1376 coordinates, 305.5393 km² by shapely, valid. As the design + says, it is **not committed**: its licence and attribution terms are not + recorded. The service can change, so a later fetch may not match the + sha256. If it does not, the overlap figures below apply to the file with + this hash, not to the new one. Scripts, both in this directory: @@ -47,7 +54,8 @@ Scripts, both in this directory: Meshes and catchment GeoJSONs other than the committed one were written to the session scratchpad and are not committed. To regenerate them, run `SCRATCH= bash docs/benchmarks/2026-09-29/bygdin/run.sh` from the -repository root with `.venv` active, then `python .../analyse.py `. +repository root with `.venv` active, fetch NVE's polygon as above, then +`python .../analyse.py `. The committed `bygdin_reduced_t20.geojson` is `catchment_t20_run1.geojson`, the default output. All four default-tolerance outputs have the same sha256, `8083fb5b...`. @@ -230,4 +238,3 @@ were not run; this brief did not ask for them. - `analysis.txt`. - `bygdin_reduced_t20.geojson`: the reduced catchment (740 vertices, EPSG:25833), which `mesh --domain` reads. -- `nve_delfelt_1187.geojson`: NVE's polygon, as fetched. diff --git a/docs/benchmarks/2026-09-29/bygdin/analyse.py b/docs/benchmarks/2026-09-29/bygdin/analyse.py index 73516b4c..75b8a43d 100644 --- a/docs/benchmarks/2026-09-29/bygdin/analyse.py +++ b/docs/benchmarks/2026-09-29/bygdin/analyse.py @@ -1,7 +1,7 @@ """Increment 22 acceptance on Bygdin: the numbers in README.md, from run.sh's logs and outputs. Usage, from the repository root with .venv active: - python docs/benchmarks/2026-09-29/bygdin/analyse.py $SCRATCH > .../logs/analysis.txt + python docs/benchmarks/2026-09-29/bygdin/analyse.py $SCRATCH $NVE_GEOJSON > .../logs/analysis.txt """ import json @@ -23,6 +23,7 @@ HERE = Path("docs/benchmarks/2026-09-29/bygdin") LOG = HERE / "logs" SCRATCH = Path(sys.argv[1]) +NVE = Path(sys.argv[2]) # NVE's delfelt 1187 GeoJSON, fetched as README.md says NVE_KM2 = 305.54 # delfeltAreal_km2 of delfelt 1187 CELL = 10.0 @@ -65,7 +66,7 @@ def directed(p: shapely.LineString, q: shapely.LineString) -> float: f" maxRSS {med(rss)/1e6:.0f} MB, peak footprint {med(foot)/1e6:.0f} MB") fine = polygon(SCRATCH / "catchment_t0.geojson") -nve = polygon(HERE / "nve_delfelt_1187.geojson") +nve = polygon(NVE) print(f"\nNVE polygon: {nve.area/1e6:.4f} km2 by shapely, {shapely.get_num_coordinates(nve)} coords," f" valid {nve.is_valid}; published {NVE_KM2} km2") print("\n| tolerance | vertices | area km2 | vs fine m2 | vs NVE 305.54 | Hausdorff densify=0.05 | 1 m: fine->red, red->fine | valid |") diff --git a/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson b/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson deleted file mode 100644 index 3c509ff5..00000000 --- a/docs/benchmarks/2026-09-29/bygdin/nve_delfelt_1187.geojson +++ /dev/null @@ -1 +0,0 @@ -{"type":"FeatureCollection","crs":{"type":"name","properties":{"name":"EPSG:25833"}},"features":[{"type":"Feature","id":152,"geometry":{"type":"Polygon","coordinates":[[[138194.83000000007,6832236.4899999984],[138065.8200000003,6832255.4499999993],[137902.75,6832232.2899999991],[137753.79000000004,6832253.0899999999],[137475.28000000026,6832167.1600000001],[137415.41999999993,6832096.25],[137321.33000000007,6832057.6600000001],[137234.15000000037,6832028.5],[137194.16000000015,6831965.8099999987],[137014.33000000007,6831772.1900000013],[136961.69000000041,6831735.8099999987],[136901.29000000004,6831669.9800000004],[136903.66000000015,6831554.0799999982],[136824.75999999978,6831364.2199999988],[136827.25,6831195.0100000016],[136781.16999999993,6831186.1900000013],[136670.25999999978,6831183.3599999994],[136548.09999999963,6831232.8599999994],[136390.9299999997,6831284.5899999999],[136267.37000000011,6831308.0700000003],[136144.65000000037,6831286.2199999988],[136027.91999999993,6831274.870000001],[135940.28000000026,6831240.7100000009],[135854.40000000037,6831236.5799999982],[135792.75,6831309.6600000001],[135816.73000000045,6831209.879999999],[135823.62999999989,6831088.5399999991],[135796.83000000007,6830994.4499999993],[135730.59999999963,6830909.0300000012],[135614.33999999985,6830837.3000000007],[135601.20000000019,6830716.7899999991],[135611.79000000004,6830581.0300000012],[135615.46999999974,6830424.7899999991],[135616.84999999963,6830374.3599999994],[135613.24000000022,6830324.3999999985],[135703.25999999978,6830242.6600000001],[135821.36000000034,6830138.2100000009],[135930.94000000041,6830016.2399999984],[135956.34999999963,6829987.9400000013],[136105,6829811.2600000016],[136160.83000000007,6829795.0399999991],[136196.83999999985,6829757.7899999991],[136235.66000000015,6829732.4100000001],[136272.4299999997,6829698.0899999999],[136338.61000000034,6829673.5799999982],[136375.37999999989,6829649.0700000003],[136399.88999999966,6829627.0100000016],[136424.40000000037,6829595.1400000006],[136448.91999999993,6829570.629999999],[136497.94000000041,6829521.6000000015],[136517.54999999981,6829472.5799999982],[136534.70999999996,6829396.5899999999],[136556.76999999955,6829345.1099999994],[136600.88999999966,6829264.2199999988],[136589.70999999996,6829221.3599999994],[136590.45999999996,6829208.1799999997],[136587.30999999959,6829097.8299999982],[136588.48000000045,6829092.9299999997],[136578.83000000007,6829080.3900000006],[136576.37999999989,6829026.4499999993],[136615.59999999963,6828950.4600000009],[136652.36000000034,6828830.3500000015],[136718.54999999981,6828764.1700000018],[136750.28000000026,6828748.8500000015],[136789.63999999966,6828729.8500000015],[136811.70000000019,6828700.4400000013],[136833.12999999989,6828672.1099999994],[136880.33000000007,6828609.7399999984],[136939.16000000015,6828536.1999999993],[137016.55999999959,6828510.8999999985],[137066.62999999989,6828494.5300000012],[137099.63999999966,6828448.0199999996],[137137.71999999974,6828386.6799999997],[137194.08999999985,6828310.6900000013],[137260.29000000004,6828227.3500000015],[137289.70000000019,6828166.0700000003],[137336.26999999955,6828102.3399999999],[137414.70999999996,6828018.9899999984],[137475.99000000022,6827940.5500000007],[137515.20999999996,6827859.6600000001],[137542.16999999993,6827744.4499999993],[137542.16999999993,6827719.9400000013],[137614.80999999959,6827556.5399999991],[137657.37999999989,6827506.6799999997],[137677.5,6827451.1799999997],[137708.4299999997,6827394],[137756.62999999989,6827360.379999999],[137814.79999999981,6827325.8500000015],[137897.53000000026,6827219.629999999],[137963.78000000026,6827158.6099999994],[138014.78000000026,6827095.1499999985],[138061.98000000045,6827061.620000001],[138154.98000000045,6826991.9299999997],[138165.20000000019,6826883.1099999994],[138218.9299999997,6826789.6400000006],[138210.3200000003,6826663.6999999993],[138257.12999999989,6826549.7399999984],[138320.83999999985,6826455.3399999999],[138355.91000000015,6826377.6700000018],[138387.84999999963,6826325.1900000013],[138400.11000000034,6826273.7100000009],[138381.71999999974,6826221.3999999985],[138432.25999999978,6826180.5199999996],[138486.8200000003,6826096.0199999996],[138540.8200000003,6826016.6000000015],[138599.08000000007,6825917.6799999997],[138660.01999999955,6825847.6900000013],[138729.53000000026,6825761.8099999987],[138797.75,6825661.9600000009],[138790.08999999985,6825557.0599999987],[138827.84999999963,6825454],[138846.03000000026,6825356.7600000016],[138935.12999999989,6825265.0500000007],[138952.38999999966,6825157.8500000015],[138987.41999999993,6825112.370000001],[139031.51999999955,6825067.0599999987],[139072.5,6824998.8999999985],[139095.6799999997,6824901.2100000009],[139115.79000000004,6824824.9200000018],[139083.94000000041,6824741.3599999994],[139033.58999999985,6824664.5399999991],[139070.88999999966,6824556.4899999984],[139049.08999999985,6824461.9400000013],[139010.58000000007,6824349.8200000003],[139022.9299999997,6824233.0100000016],[139041.58000000007,6824140.7600000016],[139114.79999999981,6824105.8399999999],[139068.51999999955,6823953.1999999993],[139112.65000000037,6823864.6400000006],[139156.24000000022,6823781.1600000001],[139270.36000000034,6823611.6999999993],[139294.54000000004,6823524.9800000004],[139328.99000000022,6823451.3900000006],[139331.75,6823350.5599999987],[139391.38999999966,6823266.6000000015],[139465.90000000037,6823180.2600000016],[139527.38999999966,6823050.870000001],[139511.03000000026,6822960.8500000015],[139540.20000000019,6822873.6700000018],[139596.99000000022,6822758.7899999991],[139671.96999999974,6822688.5],[139746.94000000041,6822672.5300000012],[139805.95999999996,6822658.0300000012],[139852.84999999963,6822534.0100000016],[139898.28000000026,6822470.4699999988],[139932.44000000041,6822382.8299999982],[140065.04999999981,6822479.2199999988],[140135.49000000022,6822403.3200000003],[140232.65000000037,6822366.1900000013],[140325.8200000003,6822329.4299999997],[140429.96999999974,6822302.7199999988],[140506.24000000022,6822246.4100000001],[140614.36000000034,6822208.2600000016],[140683.05999999959,6822178.7800000012],[140770.3200000003,6822001.75],[140829.27950000018,6821975.5658],[140869.3814000003,6822003.7454999983],[140906.01730000041,6822029.1088999994],[140920.81269999966,6822048.1314000003],[140963.78940000013,6822064.3357999995],[140999.01630000025,6822060.1086000018],[141077.21999999974,6822013.6090999991],[141105.40160000045,6821991.0637999997],[141108.82940000016,6821988.1772000007],[141120.19689999986,6821965.7005000003],[141141.33299999963,6821933.2917000018],[141144.15110000037,6821915.6783000007],[141158.94639999978,6821874.815200001],[141157.53729999997,6821856.497200001],[141152.84470000025,6821822.9904999994],[141149.08279999997,6821814.9294000007],[141139.92389999982,6821769.1343999989],[141169.51439999975,6821716.2941000015],[141205.4458999997,6821638.0905000009],[141222.95000000019,6821576.7699999996],[141258.49000000022,6821504.1000000015],[141303.91999999993,6821440.5500000007],[141367.54000000004,6821345.1600000001],[141364.60059999954,6821303.1295000017],[141323.80829999968,6821249.1856000014],[141272.16050000023,6821147.8040999994],[141297.29590000026,6820993.0223000012],[141343.59810000006,6820805.1680000015],[141376.67109999992,6820739.0218999982],[141450.75449999981,6820699.3343999982],[141589.89549999963,6820606.8977999985],[141591.58000000007,6820524.870000001],[141636.54999999981,6820456.3500000015],[141673.86000000034,6820348.3000000007],[141718.45000000019,6820264.7300000004],[141767.5700000003,6820175.6999999993],[141802.5700000003,6820108.1099999994],[141876.16000000015,6820077.1799999997],[141918.8200000003,6820114.4800000004],[141988.73000000045,6820174.4100000001],[142050.12999999989,6820185.8500000015],[142136.37000000011,6820193.9800000004],[142222.1799999997,6820273.5700000003],[142235.34119999968,6820281.4884000011],[142276.43030000012,6820282.2221999988],[142334.73720000032,6820258.0023000017],[142374.20660000015,6820215.8418999985],[142394.1409,6820162.2602000013],[142394.83829999994,6820048.0973000005],[142431.18499999959,6819981.4151999988],[142441.48379999958,6819890.2201999985],[142441.48379999958,6819854.3388999999],[142462.11550000031,6819765.5329999998],[142497.09970000014,6819691.0795000009],[142521.31940000039,6819636.3605999984],[142559.89169999957,6819510.7765999995],[142602.0521999998,6819421.0736000016],[142626.27190000005,6819382.5012999997],[142695.62999999989,6819354.8399999999],[142754.1799999997,6819335.3599999994],[142828.70000000019,6819325.4600000009],[142898.6799999997,6819309.9499999993],[143016.41000000015,6819266.9100000001],[143129.88999999966,6819243.3599999994],[143261.29000000004,6819173.9100000001],[143374.40000000037,6819135.3099999987],[143428.55999999959,6819152.0700000003],[143458.58000000007,6819143.3599999994],[143500.78000000026,6819128.4600000009],[143543.03639999963,6819134.0240999982],[143595.06410000008,6819098.1427999996],[143663.23840000015,6819049.7030999996],[143734.89300000016,6819025.1972000003],[143752.94139999989,6818942.059700001],[143790.6166000003,6818872.9882999994],[143813.04229999986,6818828.1369000003],[143832,6818786.3299999982],[143825.60080000013,6818744.7131000012],[143787.5493999999,6818700.3295000009],[143771.40000000037,6818659.2199999988],[143756.50999999978,6818587.2199999988],[143714.29999999981,6818477.9800000004],[143714.29999999981,6818401.0100000016],[143704.62999999989,6818336.5199999996],[143699.40000000037,6818301.6999999993],[143689.46999999974,6818224.7399999984],[143705.70000000019,6818186.879999999],[143726.70999999996,6818137.8399999999],[143811.12999999989,6818090.6700000018],[143867.87999999989,6818085],[143930.29999999981,6818043.5],[143962.58000000007,6817996.3200000003],[143991.87999999989,6817990.0799999982],[144004.78000000026,6817941.6999999993],[144054.50999999978,6817919.8200000003],[144066.84999999963,6817914.3900000006],[144126.44000000041,6817884.6000000015],[144148.78000000026,6817864.7399999984],[144183.54000000004,6817837.4299999997],[144277.75,6817763.5500000007],[144297.75,6817745.5700000003],[144415.57770000026,6817721.1469999999],[144519.6054999996,6817531.5841000006],[144474.68410000019,6817446.4699999988],[144429.64690000005,6817386.063000001],[144347.62579999957,6817249.8022999987],[144300.58609999996,6817110.3960999995],[144278.82519999985,6817030.6059000008],[144216.3870000001,6816990.0056000017],[144129.34420000017,6816934.9475000016],[144087.75189999957,6816863.2338000014],[144089.20000000019,6816839.3599999994],[144086.70999999996,6816784.7399999984],[144066.84999999963,6816682.9400000013],[144120.6799999997,6816649.5500000007],[144198.44000000041,6816638.2600000016],[144331.29999999981,6816632.129999999],[144414.44000000041,6816628.3200000003],[144513.75,6816665.5700000003],[144543.20000000019,6816686.8399999999],[144663.49000000022,6816667.870000001],[144764.50999999978,6816620.879999999],[144794.29999999981,6816598.5300000012],[144829.05999999959,6816521.5700000003],[144856.37000000011,6816457.0100000016],[144928.37000000011,6816385.0100000016],[144963.12999999989,6816315.5],[145027.6799999997,6816260.879999999],[145046.71999999974,6816228.5],[145016.24000000022,6816141.5],[145040.40000000037,6816032.6600000001],[145059.19000000041,6815970.5799999982],[145065.86000000034,6815948.8399999999],[145039.78000000026,6815840.6099999994],[145055.44000000041,6815748.629999999],[145011.5700000003,6815621.9400000013],[145091.34999999963,6815472.7699999996],[145103.16000000015,6815349.9800000004],[145131.01999999955,6815226.7100000009],[145169.15000000037,6815116.5899999999],[145145.04999999981,6815073.5500000007],[145162.30999999959,6814955.2899999991],[145194.49000000022,6814894.2300000004],[145233.04999999981,6814826.0100000016],[145295.33999999985,6814751.8500000015],[145372.45999999996,6814680.6600000001],[145425.86000000034,6814609.4699999988],[145485.1799999997,6814553.1099999994],[145505.36000000034,6814465.6900000013],[145560.98579999991,6814359.2221999988],[145609.04000000004,6814313.6400000006],[145600.87000000011,6814209.0199999996],[145639.4299999997,6814117.0700000003],[145669.08999999985,6814054.7800000012],[145743.25,6813980.620000001],[145802.5700000003,6813933.1600000001],[145850.03000000026,6813903.5],[145964.51999999955,6813903.5],[145992.70999999996,6813838.7300000004],[146089.58000000007,6813798.620000001],[146282.16000000015,6813662.1799999997],[146353.87999999989,6813578.120000001],[146414.09999999963,6813380.4699999988],[146401.6799999997,6813311.2100000009],[146462.19000000041,6813269.4200000018],[146512.8200000003,6813218.4899999984],[146529.83999999985,6813141.4800000004],[146605.01999999955,6813138.5799999982],[146767.54999999981,6813101.4600000009],[146893.48000000045,6813092.870000001],[146989.80999999959,6813046.7699999996],[147161.84999999963,6813003.75],[147451.24000000022,6812979.0799999982],[147512.5700000003,6813077.0100000016],[147789.90000000037,6813259.6400000006],[147811.83999999985,6813322.9899999984],[147871.41999999993,6813401.9800000004],[147962.21999999974,6813503.2300000004],[148080.5700000003,6813521.4899999984],[148119.61000000034,6813552.0799999982],[148148.66999999993,6813594.6600000001],[148216.70000000019,6813612.5300000012],[148276.45000000019,6813606.0199999996],[148377.38999999966,6813555.4699999988],[148461.45999999996,6813496.4200000018],[148495.5,6813527.4800000004],[148815.48000000045,6813594.5199999996],[148837.87000000011,6813662.8599999994],[148880.8200000003,6813779.5899999999],[148901.74000000022,6813831.9699999988],[148962.1799999997,6813876.6799999997],[149062.19000000041,6813870.4800000004],[149100.86000000034,6813897.0899999999],[149138.20000000019,6813964.0399999991],[149236.8200000003,6813942.879999999],[149328.46999999974,6813998.8000000007],[149444.28000000026,6813945.879999999],[149545.91000000015,6814000.870000001],[149709.19000000041,6814059.2300000004],[149765.48000000045,6814113.379999999],[149783.79999999981,6814192.1499999985],[149844.46999999974,6814337.4200000018],[149911.79999999981,6814445.8599999994],[149957.28000000026,6814491.9600000009],[150017.1799999997,6814530.6900000013],[150102.30999999959,6814526.8599999994],[150197.11000000034,6814562.3599999994],[150378.71999999974,6814557.6900000013],[150339.45000000019,6814622.6600000001],[150345.51999999955,6814677.4200000018],[150385.08999999985,6814714],[150459.70000000019,6814705.1000000015],[150555.54999999981,6814784.7699999996],[150601.55999999959,6814814.7199999988],[150654.78000000026,6814813.8399999999],[150716.83000000007,6814766.879999999],[150781.19000000041,6814755.9100000001],[150949.95000000019,6814775.5500000007],[150970.04000000004,6814884.3299999982],[150988.37999999989,6814974.1600000001],[150953.24000000022,6815072.9400000013],[150990.98000000045,6815154.9499999993],[151100.44000000041,6815240.3999999985],[151171.62000000011,6815248.9200000018],[151277.79000000004,6815244.1600000001],[151305.54999999981,6815327.0899999999],[151398.37999999989,6815243.0899999999],[151461.73000000045,6815221.1600000001],[151507.21999999974,6815267.2600000016],[151504.69000000041,6815348.9499999993],[151548.09999999963,6815328.8399999999],[151618.83000000007,6815332.3900000006],[151673.8200000003,6815372.5700000003],[151734.19000000041,6815427.3500000015],[151874.25999999978,6815626.6400000006],[151882.75999999978,6815816.9400000013],[151871.12000000011,6815854.2199999988],[151790.49000000022,6815906.9200000018],[151774.23000000045,6815948.6499999985],[152013.40000000037,6816284.629999999],[152055.12999999989,6816300.8900000006],[152089.33000000007,6816388.25],[152149.24000000022,6816438.0399999991],[152182.91000000015,6816530.4800000004],[152306.71999999974,6816706.120000001],[152389.41000000015,6816566.75],[152540.20999999996,6816511.6000000015],[152550.11000000034,6816662.5500000007],[152562.45999999996,6816741.870000001],[152583.40000000037,6816805.3200000003],[152635.25,6816865.8999999985],[152731.13999999966,6816978.75],[152799.55999999959,6816946.2899999991],[152842.51999999955,6816866.8999999985],[152879.95999999996,6816793.0500000007],[152951.51999999955,6816740.1799999997],[153025.84999999963,6816728.3000000007],[153127.33999999985,6816683.7399999984],[153156.55999999959,6816564.379999999],[153218.62000000011,6816528.4800000004],[153426.25999999978,6816533.4699999988],[153497.51999999955,6816531.9200000018],[153594.01999999955,6816487.8200000003],[153712.61000000034,6816410.5100000016],[153690.29000000004,6816332.1099999994],[153680.70999999996,6816282.7100000009],[153695.58999999985,6816226.0199999996],[153718.61000000034,6816148.4699999988],[153746.58999999985,6816124.7699999996],[153803.66999999993,6816089.3299999982],[153855.29999999981,6816049.370000001],[153916.96999999974,6816063.7899999991],[154038.38999999966,6816006.3299999982],[154130.37000000011,6815967.6799999997],[154219.34999999963,6815994.6700000018],[154306.33999999985,6815956.4800000004],[154419.70999999996,6815844.4400000013],[154452.15000000037,6815901.7899999991],[154565.21999999974,6815971.8200000003],[154716.40000000037,6816073.5300000012],[154748.99000000022,6816045.379999999],[154810.21999999974,6816054.8200000003],[154892.20999999996,6816006.0300000012],[154992,6816052.1400000006],[155065.55999999959,6816075.5199999996],[155322.75999999978,6815920.0500000007],[155524.96999999974,6815866.2100000009],[155598.33999999985,6815833.2899999991],[155717.08000000007,6815812.2800000012],[155820.09999999963,6815827.9200000018],[155854.69000000041,6815930.3299999982],[155966.75999999978,6815935.0799999982],[156027.50999999978,6815939.5199999996],[156089.01999999955,6815897.6499999985],[156139.36000000034,6815845.2100000009],[156191.96999999974,6815789.9800000004],[156236.6799999997,6815740],[156276.13999999966,6815711.0700000003],[156315.58999999985,6815718.9600000009],[156360.30999999959,6815771.5599999987],[156357.6799999997,6815861],[156323.48000000045,6815955.6900000013],[156297.1799999997,6816008.2899999991],[156286.66000000015,6816029.3399999999],[156254.87000000011,6816049.3000000007],[156155.13999999966,6816092.4600000009],[156134.09999999963,6816129.2899999991],[156114.6799999997,6816219.120000001],[156134.09999999963,6816289.7399999984],[156170.91999999993,6816289.7399999984],[156228.91000000015,6816297.6600000001],[156251.66000000015,6816275.8900000006],[156301.03000000026,6816266.3000000007],[156374.04999999981,6816294.7600000016],[156396.70999999996,6816327.5199999996],[156444.48000000045,6816381.8000000007],[156518.12999999989,6816400.2100000009],[156568.09999999963,6816450.1900000013],[156623.33999999985,6816484.379999999],[156656.28000000026,6816504.1499999985],[156745.83999999985,6816476.6900000013],[156926.41000000015,6816515.3500000015],[156986.99000000022,6816463.5],[157083.95999999996,6816424.3900000006],[157175.70999999996,6816410.7300000004],[157301.95999999996,6816363.3900000006],[157354.5700000003,6816387.0599999987],[157362.45999999996,6816442.3000000007],[157336.16000000015,6816576.4400000013],[157333.53000000026,6816657.9800000004],[157341.41999999993,6816771.0899999999],[157338.79000000004,6816857.8900000006],[157338.79000000004,6816891.6900000013],[157420.33000000007,6816942.0599999987],[157490.23000000045,6816971.3099999987],[157635.45999999996,6816962.8500000015],[157745.59999999963,6816946.6600000001],[157837.11000000034,6816903.0100000016],[157861.96999999974,6816845.4100000001],[157865.95999999996,6816779.6700000018],[157846.54999999981,6816634.629999999],[157874.54999999981,6816556.629999999],[157988.75,6816530],[158069.44000000041,6816532.6099999994],[158119.30999999959,6816528.0199999996],[158190.04000000004,6816466.1799999997],[158307.70000000019,6816378.8999999985],[158395.13999999966,6816345.6900000013],[158444.00999999978,6816275.8099999987],[158480.51999999955,6816180.9299999997],[158503.99000000022,6816108.370000001],[158608.0700000003,6816037.4200000018],[158661.46999999974,6815962.0899999999],[158730.28000000026,6815890.379999999],[158739.54999999981,6815772.870000001],[158990.59999999963,6815703.4499999993],[159025.36000000034,6815589.620000001],[159083.74000000022,6815513.8399999999],[159166.11000000034,6815480.1000000015],[159222.25999999978,6815434.6900000013],[159233.21860000025,6815401.6790999994],[159249.6353000002,6815385.7707999982],[159283.70629999973,6815277.8627999984],[159356.1799999997,6815240.3099999987],[159447.63999999966,6815141.370000001],[159489.4299999997,6815005.7699999996],[159522.5700000003,6814972.5500000007],[159617.29999999981,6814963.8000000007],[159680.58000000007,6815093.7399999984],[159749.99000000022,6815137.620000001],[159815.71999999974,6815141.620000001],[159924.48000000045,6815110.4699999988],[160001.1799999997,6815178.8200000003],[160061.54999999981,6815168.2199999988],[160112.71999999974,6815123.2800000012],[160146.37999999989,6815215.6999999993],[160209.12000000011,6815285.3500000015],[160277.54999999981,6815263.9499999993],[160348.73000000045,6815272.4699999988],[160430.80999999959,6815224.6799999997],[160454.12000000011,6815161.1799999997],[160487.25999999978,6815127.9600000009],[160546.62999999989,6815117.4499999993],[160596.55999999959,6815168.1600000001],[160659.0700000003,6815137.25],[160721.20999999996,6815091.3000000007],[160775.28000000026,6814979.7100000009],[160892.25999999978,6814928.6900000013],[160993.65000000037,6814883.1499999985],[161073.5,6814865.7199999988],[161121.59999999963,6814896.4800000004],[161258.66000000015,6814954.2399999984],[161354.76999999955,6814960.4499999993],[161619.08000000007,6814980.3299999982],[161680.37000000011,6814979.7100000009],[161744.25999999978,6814963.7600000016],[161843.36000000034,6814893.2699999996],[161892.6799999997,6814828.3999999985],[161929.74000000022,6814739.4899999984],[162023.54000000004,6814709.7300000004],[162202.41999999993,6814577.5700000003],[162217.84119999968,6814497.7908999994],[162245.33029999956,6814496.6763999984],[162321.43360000011,6814480.1169999987],[162358.80470000021,6814486.1068999991],[162381.85720000044,6814568.0910999998],[162410.88889999967,6814611.7419000007],[162464.15720000025,6814640.9871999994],[162504.67949999962,6814636.1823000014],[162518.76109999977,6814604.6928000003],[162541.35859999992,6814570.3445000015],[162560.71729999967,6814569.5089000016],[162585.19799999986,6814560.4015999995],[162604.17980000004,6814547.7470000014],[162596.49670000002,6814515.2063999996],[162601.01630000025,6814470.4633000009],[162611.00129999965,6814421.3827],[162638.07629999984,6814418.0368999988],[162694.11830000021,6814445.1541000009],[162731.65000000037,6814470.5390999988],[162817.95000000019,6814458.4600000009],[162907.0700000003,6814388.8999999985],[163015.37999999989,6814363.8200000003],[163068.76999999955,6814288.5100000016],[163141.71999999974,6814250.6099999994],[163254.96160000004,6814185.1013999991],[163278.82019999996,6814173.0593999997],[163285.96389999986,6814125.4343999997],[163290.98190000001,6814027.1526999995],[163365.74480000045,6814059.1292000003],[163373.0027999999,6814061.5485999994],[163399.6026999997,6814048.2008999996],[163437.98479999974,6814038.5218999982],[163485.3786000004,6814020.8326999992],[163511.16189999972,6814030.9274999984],[163569.72190000024,6814050.4501999989],[163600.28949999996,6814053.2030000016],[163665.90089999977,6814000.4035999998],[163683.79000000004,6813987.1799999997],[163728.54000000004,6813940.0700000003],[163764.17019999959,6813900.4219999984],[163817.08690000046,6813881.9010999985],[163860.08169999998,6813846.8438000008],[163920.27450000029,6813832.2917000018],[163952.68609999958,6813819.7239000015],[163917.62880000006,6813797.8957999982],[163854.79000000004,6813769.4530000016],[163833.62330000009,6813742.3330999985],[163800.36000000034,6813694.0899999999],[163795.25879999995,6813683.4631000012],[163837.86089999974,6813640.7291000001],[163875.9299999997,6813586.5599999987],[163890.45999999996,6813579.2600000016],[163893.4299999997,6813574.9699999988],[163916.98000000045,6813551.4100000001],[163947.59999999963,6813523.1499999985],[163975.87000000011,6813471.3200000003],[163971.16000000015,6813426.5700000003],[163905.20999999996,6813391.2399999984],[163858.08999999985,6813370.0399999991],[163785.0700000003,6813344.129999999],[163719.12000000011,6813306.4400000013],[163693.92300000042,6813285.5291000009],[163658.20419999957,6813213.2976999991],[163645.50420000032,6813162.4976999983],[163639.94799999986,6813078.3599999994],[163656.61670000013,6813032.3222999983],[163676.46050000004,6812945.8033999987],[163719.32309999969,6812864.0469999984],[163733.61060000025,6812779.9094000012],[163789.96700000018,6812693.3905000016],[163811,6812587.9100000001],[163843.29999999981,6812469.2899999991],[163857.03000000026,6812432.8299999982],[163962.41999999993,6812321.5500000007],[163984.98000000045,6812239.0100000016],[163944.87000000011,6812142.1499999985],[163970.09999999963,6812088.5399999991],[164005,6812085.3200000003],[164058.54000000004,6812055.2399999984],[164082.94000000041,6811992.6499999985],[164106.25,6811929.1600000001],[164138.3200000003,6811840.7300000004],[164137.79000000004,6811780.4400000013],[164171.24000000022,6811706.9600000009],[164225.38999999966,6811585.3099999987],[164228.9299999997,6811514.5899999999],[164326.5700000003,6811373.8599999994],[164377.28000000026,6811323.9299999997],[164412.11000000034,6811265.4100000001],[164424.13999999966,6811177.8099999987],[164504.69000000041,6811069.8200000003],[164525.21999999974,6810911.0500000007],[164589.96999999974,6810849.7800000012],[164633.76999999955,6810779.370000001],[164636.6799999997,6810658.4200000018],[164674.5,6810523.1999999993],[164744.23000000045,6810461.4600000009],[164836.73000000045,6810417.7399999984],[164935.88999999966,6810402.5599999987],[165003.86000000034,6810376.1900000013],[165059.70000000019,6810371.0399999991],[165154.04000000004,6810412.620000001],[165214.79000000004,6810417.0799999982],[165324.54999999981,6810396.8999999985],[165410.59999999963,6810403.0500000007],[165474.1799999997,6810427.3599999994],[165558.48000000045,6810414.5599999987],[165673.75999999978,6810454.2100000009],[165776.69000000041,6810479.9200000018],[165842.5,6810473.8500000015],[165919.58000000007,6810491.879999999],[166016.29999999981,6810548.3299999982],[166097.05999999959,6810606.25],[166137.46999999974,6810662.8599999994],[166203.80999999959,6810717.0799999982],[166260.04000000004,6810726.9899999984],[166392.41000000015,6810733.879999999],[166451.3200000003,6810718.3999999985],[166522.95000000019,6810731.9100000001],[166592.04000000004,6810816.0399999991],[166677.87999999989,6810863.4400000013],[166758.1799999997,6810916.370000001],[166823.45000000019,6810915.379999999],[166901,6810873.0399999991],[166967.49000000022,6810830.7100000009],[167031.37999999989,6810814.7600000016],[167203.26999999955,6810814],[167290.33000000007,6810831.1099999994],[167375.0700000003,6810812.2399999984],[167451.23000000045,6810820.3000000007],[167474.8563000001,6810832.4426999986],[167522.53969999962,6810839.4512999989],[167540.80020000041,6810859.7846999988],[167565.26809999999,6810867.2740999982],[167595.87409999967,6810881.6583999991],[167626.91810000036,6810889.4576999992],[167659.85879999958,6810892.6327],[167709.07129999995,6810924.3827],[167731.69330000039,6810938.6702999994],[167746.77450000029,6810953.3546999991],[167753.33270000014,6810956.0872000009],[167765.8245000001,6810961.2921999991],[167788.84339999966,6810950.5766000003],[167819.79970000032,6810976.373399999],[167860.67800000031,6811026.7767000012],[167866.23419999983,6811082.3394000009],[167888.06230000034,6811114.8830999993],[167899.5717000002,6811133.9331],[167934.89379999973,6811172.0331000015],[167955.68420000002,6811223.7734000012],[167969.81879999954,6811258.9490999989],[167985.69379999954,6811287.5240999982],[168011.09379999992,6811318.4805000015],[168028.15950000007,6811333.5617000014],[168057.52830000035,6811355.7866999991],[168073.99670000002,6811355.6169000007],[168059.8069000002,6811371.2983999997],[168055.84420000017,6811417.5577999987],[168000.8200000003,6811545.9499999993],[167986,6811657.9400000013],[168034.76999999955,6811794.2199999988],[168046.80999999959,6811924.8500000015],[168038.8200000003,6812056.3099999987],[168049.40000000037,6812105.6099999994],[168069.04999999981,6812155.0899999999],[168085.45000000019,6812223.9699999988],[168126.94000000041,6812335.7899999991],[168173.71999999974,6812406.8900000006],[168232.70000000019,6812446.7100000009],[168262.75,6812489.1999999993],[168295.5,6812571.6400000006],[168289.37000000011,6812668.75],[168314.98000000045,6812771.9600000009],[168345.41999999993,6812829.5],[168418.58999999985,6812903.1999999993],[168465.99000000022,6813035.5899999999],[168477.41000000015,6813104.9299999997],[168509.76999999955,6813172.3399999999],[168557.25,6813360.0300000012],[168643.20999999996,6813518.0199999996],[168642.51999999955,6813608.5899999999],[168640.76999999955,6813916.4600000009],[168534.86000000034,6813967.4600000009],[168511.91000000015,6814100.3000000007],[168456.38999999966,6814206.9899999984],[168419.73000000045,6814245.5700000003],[168370.3200000003,6814255.1499999985],[168252.04999999981,6814281.1400000006],[168167.38999999966,6814355.3200000003],[168163.45999999996,6814410.9899999984],[168208.87000000011,6814467.129999999],[168202.19000000041,6814558.2600000016],[168184.25,6814636.3399999999],[168239.3200000003,6814731.8200000003],[168358.95999999996,6814720.7800000012],[168509.88999999966,6814710.879999999],[168610.21999999974,6814751.9100000001],[168623.94000000041,6814846.1799999997],[168679.63999999966,6814948.6400000006],[168677.0700000003,6814953.5100000016],[168670.84999999963,6814954.3099999987],[168648.66999999993,6814976.4899999984],[168600.61000000034,6814987.5799999982],[168552.54999999981,6815008.8399999999],[168486.94000000041,6815028.2399999984],[168432.41000000015,6815043.9600000009],[168370.49000000022,6815077.2300000004],[168325.20000000019,6815122.5100000016],[168287.30999999959,6815161.3299999982],[168217.0700000003,6815222.3299999982],[168176.41000000015,6815264.8399999999],[168120.03000000026,6815307.3500000015],[168098.16999999993,6815319.6799999997],[168093.91949999984,6815322.628899999],[168072.24000000022,6815337.6700000018],[168060.87999999989,6815350.7899999991],[167958.29999999981,6815449.6799999997],[167911.16000000015,6815535.629999999],[167898.21999999974,6815604.9400000013],[167870.5,6815676.1099999994],[167833.53000000026,6815729.7100000009],[167804.87999999989,6815747.2699999996],[167742.03000000026,6815865.5700000003],[167717.08000000007,6815936.7300000004],[167687.50999999978,6815970.9200000018],[167651.45999999996,6815989.4100000001],[167634.83000000007,6816016.2100000009],[167627.4299999997,6816022.6799999997],[167602.48000000045,6816051.3299999982],[167549.79999999981,6816073.5100000016],[167512.83000000007,6816054.1000000015],[167475.87000000011,6816018.0599999987],[167422.25999999978,6816004.1900000013],[167372.36000000034,6816027.3000000007],[167351.09999999963,6816048.5599999987],[167274.38999999966,6816074.4299999997],[167243.45000000019,6816078.4699999988],[167245.87000000011,6816163.120000001],[167318.62999999989,6816302.0199999996],[167371.54000000004,6816434.3099999987],[167404.62000000011,6816546.7600000016],[167477.37999999989,6816758.4299999997],[167483.99000000022,6816860.9600000009],[167517.05999999959,6816999.8599999994],[167556.75,6817122.2300000004],[167556.75,6817143.6700000018],[167385.59999999963,6817163.6499999985],[167320.70999999996,6817179.6999999993],[167259.66999999993,6817226.5500000007],[167183.11000000034,6817268.8200000003],[167109.3200000003,6817340.9899999984],[166980.16999999993,6817369],[166897.40000000037,6817507.3599999994],[166595.12999999989,6817566.4200000018],[166487.13999999966,6817758.3900000006],[166447.25999999978,6817762.0700000003],[166329.75,6817959.9600000009],[166251.99000000022,6818043.5599999987],[166225.83000000007,6818141.5],[166191.99000000022,6818265.3000000007],[166138.30999999959,6818391.9299999997],[166115.29000000004,6818469.4800000004],[166132.24000000022,6818533.2800000012],[166244.75999999978,6818608.370000001],[166323.08000000007,6818737.9100000001],[166355.45000000019,6818805.3200000003],[166436.98000000045,6819024.0599999987],[166523.26999999955,6819087.5],[166572.04000000004,6819223.7899999991],[166653.26999999955,6819286.6799999997],[166705.95999999996,6819301.9400000013],[166867.63999999966,6819255.8500000015],[167035.99000000022,6819325.8000000007],[166996.73000000045,6819390.75],[166889.04000000004,6819531.4200000018],[166894.55999999959,6819591.25],[166971.79999999981,6819654.5100000016],[167068.88999999966,6819726],[167107.79999999981,6819864.2100000009],[167117.37999999989,6819913.6000000015],[167142.76999999955,6820123.4299999997],[167161.01999999955,6820212.25],[167241.08999999985,6820360.75],[167326.45999999996,6820675.6700000018],[167327.53000000026,6820730.879999999],[167285.20999999996,6820871.5500000007],[167276.23000000045,6820937.7399999984],[167274.70999999996,6821084.6999999993],[167284.12999999989,6821284.9600000009],[167266.26999999955,6821418.3399999999],[167209.8200000003,6821569.3599999994],[167102.20999999996,6821711.0199999996],[167004.88999999966,6821746.1499999985],[166863.25999999978,6821793.25],[166845.86000000034,6821799.0399999991],[166717.95000000019,6821807.8200000003],[166663.21999999974,6821809.9800000004],[166641.79999999981,6821810.8299999982],[166548.53000000026,6821889.8299999982],[166538.59999999963,6821927.9499999993],[166503.98000000045,6822060.870000001],[166437.01999999955,6822152.5300000012],[166379.20000000019,6822234.2899999991],[166302.19000000041,6822271.5700000003],[166211.05999999959,6822264.8900000006],[166164.91000000015,6822229.9600000009],[166074.83999999985,6822161.8000000007],[165980.41999999993,6822130.3000000007],[165923.66999999993,6822060.120000001],[165919.37000000011,6821970.0100000016],[165806.16000000015,6822050.8599999994],[165709.29999999981,6822090.9600000009],[165690.15000000037,6822099.3099999987],[165592.33000000007,6822141.9800000004],[165404.74000000022,6822179.3999999985],[164888.95999999996,6822161.4200000018],[164867.00999999978,6822160.6499999985],[164797.70999999996,6822223.9899999984],[164575.45000000019,6822427.1400000006],[164541.49000000022,6822458.1799999997],[164446.08999999985,6822568.5500000007],[164361.41000000015,6822631.6900000013],[164214.37999999989,6822782.0100000016],[164009.8200000003,6823028.1799999997],[163907.04999999981,6823058.7699999996],[163863.5700000003,6823062.7800000012],[163852.20999999996,6823063.8299999982],[163787.63999999966,6822974.25],[163712.69000000041,6822870.5500000007],[163594.01999999955,6822739.7100000009],[163561.59999999963,6822729.0199999996],[163322.28000000026,6822650.1400000006],[163315.45999999996,6822638.1900000013],[163292.29000000004,6822597.6000000015],[163230.16000000015,6822578.1900000013],[163201.37999999989,6822578.3900000006],[163053.28000000026,6822579.4299999997],[162969.11000000034,6822546.0500000007],[162810.90000000037,6822482.629999999],[162697.99000000022,6822457.8399999999],[162555.1799999997,6822446.8900000006],[162439.20000000019,6822497.8099999987],[162374.30999999959,6822529.1700000018],[162272.08000000007,6822604.75],[162204.91999999993,6822629.6099999994],[162126.96999999974,6822634.1099999994],[162052.62999999989,6822694.3500000015],[161959.50999999978,6822770.3599999994],[161879.53000000026,6822884.3500000015],[161872.13999999966,6823020.8599999994],[161852.65000000037,6823080.8200000003],[161809.1799999997,6823172.2600000016],[161770.03000000026,6823211.1999999993],[161689.62999999989,6823376.5399999991],[161663.94000000041,6823357.9699999988],[161584.76999999955,6823286.4699999988],[161487.73000000045,6823197.0799999982],[161406.49000000022,6823120.0799999982],[161324.34999999963,6823047.2100000009],[161258.45999999996,6822997.9800000004],[161152.86000000034,6822934.0300000012],[161107.21999999974,6822931.4899999984],[161083.65000000037,6822921.7399999984],[161033.16000000015,6822900.8500000015],[161020.5,6822890.9400000013],[160974.41999999993,6822854.879999999],[160956.54999999981,6822801.25],[160918.24000000022,6822739.9600000009],[160859.5,6822678.6700000018],[160846.74000000022,6822659.5199999996],[160823.75,6822625.0500000007],[160793.48000000045,6822623.5399999991],[160649.20999999996,6822596.629999999],[160495.12000000011,6822561.5700000003],[160382.58999999985,6822551.8399999999],[160357.28000000026,6822604.4600000009],[160267.61000000034,6822668.0500000007],[160194.29000000004,6822690.9100000001],[160095.04000000004,6822770.4600000009],[159892.16000000015,6822860.5899999999],[159867.30999999959,6822918.1900000013],[159732.79000000004,6822948.5199999996],[159708.70000000019,6822953.9499999993],[159695.70999999996,6822976.1999999993],[159684.26999999955,6822995.8099999987],[159693.40000000037,6823049.2899999991],[159660.83999999985,6823093.3399999999],[159660.83999999985,6823166.120000001],[159624.34999999963,6823191.6600000001],[159603.37999999989,6823206.3399999999],[159576.5700000003,6823215.9200000018],[159513.36000000034,6823200.6000000015],[159448.24000000022,6823171.870000001],[159346.73000000045,6823146.9699999988],[159178.1799999997,6823160.370000001],[159021.12999999989,6823223.5799999982],[158971.41999999993,6823264.9600000009],[158885.0700000003,6823324.620000001],[158782.66000000015,6823359.2199999988],[158756.08999999985,6823407.6099999994],[158742.58000000007,6823423.4200000018],[158730.00999999978,6823440.0500000007],[158705.41000000015,6823463.0100000016],[158643.74000000022,6823495.4600000009],[158619.66999999993,6823511.0300000012],[158578.48000000045,6823555.0599999987],[158579.25,6823573.4200000018],[158581.86000000034,6823635.1999999993],[158535.73000000045,6823639.1600000001],[158486.75999999978,6823711.9800000004],[158444.62000000011,6823782.8500000015],[158415.88999999966,6823819.2399999984],[158362.80999999959,6824094.8999999985],[158376.16999999993,6824185.1900000013],[158397.66000000015,6824243.5500000007],[158461.62000000011,6824282.8999999985],[158525.94000000041,6824334.9299999997],[158549.75,6824353.7199999988],[158563.12999999989,6824385.0899999999],[158607.50999999978,6824489.1799999997],[158628.38999999966,6824704.4800000004],[158620.41000000015,6824835.9499999993],[158608.54000000004,6824968.7899999991],[158537.80999999959,6825030.6400000006],[158547.25999999978,6825176.5899999999],[158503.38999999966,6825246.0100000016],[158503.91999999993,6825306.3000000007],[158515.80999999959,6825380.629999999],[158589.08000000007,6825455.3299999982],[158641.78000000026,6825535.9499999993],[158660.11000000034,6825614.7100000009],[158668.00999999978,6825613.8599999994],[158705.37999999989,6825609.8599999994],[158712.88999999966,6825609.5100000016],[158775.09999999963,6825634.7800000012],[158794.25999999978,6825642.5599999987],[158897.0700000003,6825706.8200000003],[158912.69000000041,6825761.4800000004],[158933.05999999959,6825832.7699999996],[158935.62999999989,6825974.1400000006],[158961.33000000007,6826084.6600000001],[158997.3200000003,6826151.4899999984],[159010.16999999993,6826184.9100000001],[158974.1799999997,6826220.8900000006],[158963.66000000015,6826252.4400000013],[158945.91000000015,6826305.7100000009],[158920.20999999996,6826382.8299999982],[158915.0700000003,6826441.9400000013],[158891.9299999997,6826490.7800000012],[158881.65000000037,6826519.0500000007],[158545.09999999963,6826427.8599999994],[158484.26999999955,6826368.1099999994],[158399.8200000003,6826335.6600000001],[158353.49000000022,6826269.5500000007],[158326.1799999997,6826191.620000001],[158304.59999999963,6826132.2600000016],[158266.49000000022,6826100.5799999982],[158196.63999999966,6826073.8399999999],[157986.75,6826077.129999999],[157906.34999999963,6826070.1799999997],[157896.66000000015,6826056.9699999988],[157841.15000000037,6826033.6600000001],[157797.86000000034,6826007.0100000016],[157765.66999999993,6825997.0199999996],[157733.48000000045,6826012.5599999987],[157709.05999999959,6826035.879999999],[157691.54000000004,6826054.5300000012],[157674.63999999966,6826072.5100000016],[157651.33000000007,6826094.7100000009],[157623.58000000007,6826131.3399999999],[157592.79999999981,6826134.6099999994],[157573.62999999989,6826159.0899999999],[157546.55999999959,6826176.3399999999],[157532.54999999981,6826199.0599999987],[157511.45999999996,6826215.7100000009],[157500.41999999993,6826217.9800000004],[157473.71999999974,6826223.4800000004],[157444.88999999966,6826230.9600000009],[157434.44000000041,6826231.8000000007],[157408.40000000037,6826256.5700000003],[157365.45999999996,6826297.3999999985],[157328.21999999974,6826300.8399999999],[157260.75,6826307.0700000003],[157237.87999999989,6826343.3900000006],[157186.0700000003,6826384.8399999999],[157121.8200000003,6826463.6000000015],[157080.37000000011,6826500.8999999985],[157011.79000000004,6826567.8000000007],[156987.11000000034,6826633.5399999991],[156974.66999999993,6826664.629999999],[156937.37000000011,6826712.3000000007],[156918.70999999996,6826751.6700000018],[156888.41000000015,6826819.4200000018],[156883.48000000045,6826830.4299999997],[156856.54000000004,6826925.7600000016],[156855.70999999996,6826933.629999999],[156847.54999999981,6827004.1900000013],[156817.16000000015,6826990.0100000016],[156775.70999999996,6826971.3599999994],[156717.6799999997,6826965.1400000006],[156659.65000000037,6826938.1999999993],[156603.69000000041,6826892.6000000015],[156528.16000000015,6826877.5],[156302.66000000015,6826746.5],[156045.50999999978,6826631.25],[155949.15000000037,6826588.0599999987],[155759.66000000015,6826378.1400000006],[155756.41999999993,6826374.5500000007],[155688.20000000019,6826342.629999999],[155639.75,6826319.9499999993],[155576.62000000011,6826235.25],[155567.1799999997,6826089.2899999991],[155521.30999999959,6826028.1499999985],[155511.5,6826019.2100000009],[155360.98000000045,6825882.0399999991],[155303.20000000019,6825668.7100000009],[155266.37999999989,6825532.7399999984],[155289.46999999974,6825374],[155290.99000000022,6825363.5199999996],[155288.48000000045,6825352.870000001],[155258.61000000034,6825226.2800000012],[155248.03000000026,6825181.4200000018],[155188.49000000022,6825088.620000001],[155185.91999999993,6825084.620000001],[155165.79999999981,6825053.25],[155144.33000000007,6824986.6400000006],[155136.51999999955,6824962.3900000006],[155136.51999999955,6824954.3900000006],[155133.04999999981,6824871.2699999996],[155101.58000000007,6824854.0100000016],[155025.6799999997,6824812.379999999],[155002.3200000003,6824720.370000001],[154935.62000000011,6824677.3000000007],[154779.48000000045,6824605.1600000001],[154675.83999999985,6824573.379999999],[154592.62999999989,6824588.7100000009],[154544.21999999974,6824597.629999999],[154461.08999999985,6824576.8500000015],[154409.74000000022,6824594.9699999988],[154402.20000000019,6824597.629999999],[154393.55999999959,6824625.7199999988],[154362.4299999997,6824684.8900000006],[154334.75,6824710.3399999999],[154288.26999999955,6824753.0899999999],[154232.75999999978,6824792.6700000018],[154121.04000000004,6824872.3399999999],[154093.87999999989,6824891.7100000009],[154045.19000000041,6824909.1400000006],[153957.58999999985,6824940.5],[153883.00999999978,6824914.1499999985],[153878.58000000007,6824912.5899999999],[153869.08999999985,6824902.9200000018],[153760.91000000015,6824792.7100000009],[153616.91999999993,6824660.1700000018],[153538.91000000015,6824632.1700000018],[153516.13999999966,6824638.3399999999],[153454.53000000026,6824655.0500000007],[153311.62000000011,6824577.7199999988],[153303.79999999981,6824564.0399999991],[153262.45000000019,6824491.75],[153181.29000000004,6824484.1499999985],[153130.36000000034,6824433.5399999991],[153098.51999999955,6824361.0500000007],[152957.45000000019,6824369.0399999991],[152810.86000000034,6824317.1999999993],[152664.50999999978,6824311.6000000015],[152650.98000000045,6824433.9299999997],[152642.59999999963,6824509.7300000004],[152631.03000000026,6824602.3200000003],[152569.44000000041,6824643.2100000009],[152428.23000000045,6824747.7699999996],[152291.75999999978,6824805.620000001],[152202.38999999966,6824763.5799999982],[152187.76999999955,6824763.8500000015],[152173.33999999985,6824764.120000001],[151874.08999999985,6824769.75],[151712.08000000007,6824714.3000000007],[151601.83999999985,6824664.129999999],[151546,6824603.9100000001],[151419.91999999993,6824501.8999999985],[151356.11000000034,6824518.8500000015],[151327.5700000003,6824539.2300000004],[151299.5,6824559.2800000012],[151260.16000000015,6824568.9499999993],[151195.6799999997,6824564.4100000001],[151087.04999999981,6824556.7600000016],[151005.98000000045,6824550.1700000018],[150949,6824586.6000000015],[150938.99000000022,6824589.5199999996],[150620.91000000015,6824682.2600000016],[150591.62000000011,6824746.3099999987],[150537.37999999989,6824801.6099999994],[150490.37999999989,6824837.120000001],[150469.99000000022,6824975.7899999991],[150418.13999999966,6825057.0100000016],[150364.8200000003,6825041.4100000001],[150329.25,6825031.0100000016],[150268.74000000022,6825072.8000000007],[150234.47130000032,6825117.1944000013],[150164.24469999969,6825229.2192000002],[150149.08270000014,6825253.4054999985],[149930.37469999958,6825283.1119000018],[149745.16590000037,6825324.122299999],[149633.40000000037,6825404.8599999994],[149559.79000000004,6825506.3200000003],[149518.01999999955,6825542.129999999],[149450.37999999989,6825625.6799999997],[149378.75999999978,6825713.2199999988],[149291.21999999974,6825884.3099999987],[149253.41999999993,6825965.870000001],[149211.65000000037,6826109.1099999994],[149187.76999999955,6826174.7600000016],[149157.9299999997,6826190.6799999997],[149152.9299999997,6826191.629999999],[149240.29999999981,6826276.0799999982],[149305.73000000045,6826374.6400000006],[149340.08000000007,6826507.25],[149320.16000000015,6826650.8999999985],[149250.58980000019,6826722.2837999985],[149168.03969999962,6826741.3337000012],[149012.46439999994,6826703.2338000014],[148763.22640000004,6826806.4213999994],[148654.61000000034,6826893.379999999],[148641.62999999989,6826982.1600000001],[148636.73000000045,6827015.7199999988],[148656.63999999966,6827059.0199999996],[148683.29999999981,6827117.0199999996],[148658.58999999985,6827124.0799999982],[148600.00999999978,6827140.8099999987],[148553.79999999981,6827192],[148518.79999999981,6827230.7699999996],[148455.38999999966,6827273.8399999999],[148324.35059999954,6827301.8869000003],[147822.96439999994,6827402.4288000017],[147699.16000000015,6827470.9100000001],[147689.62999999989,6827529.9499999993],[147685.80999999959,6827566.1600000001],[147627.21999999974,6827683.2100000009],[147618.94000000041,6827789.5799999982],[147588.62000000011,6827809.1499999985],[147533.50999999978,6827844.7399999984],[147499.04999999981,6827859.5599999987],[147456.12000000011,6827878.0300000012],[147430.87999999989,6827878.8000000007],[147390.9299999997,6827880.0300000012],[147284.08999999985,6827921.0700000003],[147280.90000000037,6827936.0500000007],[147270.75999999978,6827983.6700000018],[147207.30999999959,6828004.6000000015],[147116.75,6828014.9600000009],[147028.91999999993,6827986.5599999987],[146986.12000000011,6827972.7199999988],[146912.24000000022,6828042.0399999991],[146839.87000000011,6828109.9400000013],[146768.38999999966,6828152.7399999984],[146652.28000000026,6828245.9400000013],[146512.87000000011,6828312.9699999988],[146462.26999999955,6828337.3000000007],[146411.53000000026,6828361.6999999993],[146329.94000000041,6828382.6000000015],[146239.58000000007,6828405.7399999984],[146171.0700000003,6828402.0199999996],[146081.88999999966,6828397.1700000018],[146023.09999999963,6828425.3999999985],[146014.46999999974,6828429.5399999991],[145915.75999999978,6828449.7199999988],[145909.59999999963,6828448.6400000006],[145898.29000000004,6828446.6700000018],[145844.20000000019,6828437.2199999988],[145760.58999999985,6828370.5100000016],[145752.37999999989,6828336.0700000003],[145740.66999999993,6828319.4200000018],[145716.5,6828285.0599999987],[145695.54999999981,6828282.8500000015],[145665.70999999996,6828279.6999999993],[145639.33999999985,6828311.9299999997],[145632.75999999978,6828319.9699999988],[145606.95999999996,6828351.5],[145540.08999999985,6828378.8000000007],[145518.00999999978,6828412.0199999996],[145427.20000000019,6828506.8999999985],[145344.37999999989,6828677.4899999984],[145311.65000000037,6828711.6900000013],[145253.5700000003,6828772.370000001],[145255.23000000045,6828826.7300000004],[145255.55999999959,6828837.5599999987],[145294.30999999959,6828919.4699999988],[145214.16000000015,6828922.8500000015],[145182.11000000034,6828956.9899999984],[145157.79000000004,6829009.5300000012],[145153.79000000004,6829052.2899999991],[145141.08999999985,6829188.0899999999],[145098.05999999959,6829328.0700000003],[145096.23000000045,6829334.0399999991],[144961.78000000026,6829544.6000000015],[144897.66999999993,6829601.8200000003],[144820.26999999955,6829617.9800000004],[144794.05999999959,6829623.4499999993],[144762.76999999955,6829677.9899999984],[144738.61000000034,6829720.1000000015],[144593.15000000037,6829876.3500000015],[144575.91000000015,6829994.620000001],[144505.71999999974,6830024.9400000013],[144487.3200000003,6830032.8900000006],[144433.73000000045,6830056.0300000012],[144395.5,6830053.2800000012],[144378.03000000026,6830058.5599999987],[144345.15000000037,6830067.2100000009],[144251.69000000041,6830067.2100000009],[144151.3200000003,6830067.2100000009],[144076.90000000037,6830075.870000001],[143992.09999999963,6830075.870000001],[143852.80999999959,6830082.5100000016],[143789.61000000034,6830084.5199999996],[143787.86000000034,6830086.9800000004],[143708.26999999955,6830198.7399999984],[143670.20000000019,6830259.3099999987],[143632.12000000011,6830328.5399999991],[143598.75999999978,6830388.1900000013],[143569.8200000003,6830428.9200000018],[143536.45000000019,6830502.3399999999],[143488.48000000045,6830574.2899999991],[143427.91000000015,6830641.7800000012],[143363.87000000011,6830695.4299999997],[143334.61000000034,6830722.7899999991],[143294.65000000037,6830783.6999999993],[143275.61000000034,6830837.3500000015],[143215.04000000004,6830920.4200000018],[143182.16000000015,6830951.5700000003],[143135.4299999997,6830975.8000000007],[143096.95999999996,6830999.0100000016],[143055.51999999955,6831044.2699999996],[142952,6831132.2800000012],[142852.9299999997,6831224.9100000001],[142766.05999999959,6831286.2399999984],[142714.71999999974,6831274.8900000006],[142661.62999999989,6831298.9100000001],[142591.16999999993,6831298.370000001],[142501.6799999997,6831309.6499999985],[142416.28000000026,6831310.5],[142313.58999999985,6831353.1900000013],[142240.00999999978,6831318.7300000004],[142189.20000000019,6831302.3000000007],[142109.61000000034,6831257.3500000015],[142005.62999999989,6831209.620000001],[141920.21999999974,6831210.4699999988],[141782.16000000015,6831240.3200000003],[141704.50999999978,6831216.3000000007],[141602.75,6831203.5799999982],[141516.87000000011,6831199.4400000013],[141413.80999999959,6831161.6900000013],[141314.34999999963,6831108.5100000016],[141240.75,6831063.0100000016],[141164.78000000026,6831039.370000001],[141253.48000000045,6830961.25],[141300.36000000034,6830902.6000000015],[141351.79000000004,6830784.1999999993],[141417.70000000019,6830713.7399999984],[141476.25,6830628.870000001],[141493.20999999996,6830507.6099999994],[141517.6799999997,6830434.9499999993],[141557.28000000026,6830417.2199999988],[141575.91999999993,6830324.9600000009],[141539.16999999993,6830242.8599999994],[141620.5,6830176.0100000016],[141646.45000000019,6830119.2899999991],[141666,6829971.6600000001],[141687.04000000004,6829839.9600000009],[141717.49000000022,6829701.3599999994],[141713.49000000022,6829570.9699999988],[141675.90000000037,6829468.8299999982],[141632.76999999955,6829426.5500000007],[141627.09999999963,6829397.8999999985],[141536.45999999996,6829418.3299999982],[141461.46999999974,6829423.25],[141377.8200000003,6829388.7300000004],[141293.41999999993,6829335.1700000018],[141226.63999999966,6829309.1499999985],[141148.99000000022,6829350.5199999996],[141064.95000000019,6829366.3299999982],[141061.12000000011,6829366.629999999],[140998.24000000022,6829394.879999999],[140896.08999999985,6829416.6700000018],[140823.90000000037,6829442.5500000007],[140743.54000000004,6829471.1499999985],[140672.71999999974,6829477.9600000009],[140649.5700000003,6829445.2699999996],[140623.65000000037,6829395.4100000001],[140615.51999999955,6829377.1700000018],[140589.63999999966,6829356.7399999984],[140580.11000000034,6829351.2899999991],[140524.25999999978,6829317.2399999984],[140501.33999999985,6829315.9200000018],[140453.44000000041,6829313.1600000001],[140362.19000000041,6829310.4299999997],[140281.83000000007,6829306.3500000015],[140129.29000000004,6829302.2600000016],[140027.13999999966,6829315.879999999],[139946.78000000026,6829318.6000000015],[139880.66000000015,6829309.8599999994],[139873.24000000022,6829358.1000000015],[139833.74000000022,6829405.7699999996],[139784.20999999996,6829454.2899999991],[139467.04999999981,6829893.0799999982],[139432.37000000011,6830051.1799999997],[139408.20000000019,6830268.6600000001],[139354.01999999955,6830433.5899999999],[139363.46999999974,6830699.2600000016],[139282.59999999963,6830901.8599999994],[139205.62999999989,6831103.1000000015],[139112.94000000041,6831275.6099999994],[139103.80999999959,6831296.5700000003],[139042.41000000015,6831415.8999999985],[138774.21999999974,6831822.8900000006],[138696.86000000034,6831943.6900000013],[138613.75,6832034.8599999994],[138510.54000000004,6832147.9899999984],[138419.6799999997,6832209.6900000013],[138294.29000000004,6832224.2899999991],[138194.83000000007,6832236.4899999984]]]},"properties":{"OBJECTID":152,"objektType":"Delfelt","delfeltNr":1187,"delfeltNavn":"BYGDIN","delfeltAreal_km2":305.54000000000002,"vannkraftverkNr":523,"vannkraftverkNavn":"Øvre Vinstra","magasinNr":42,"magasinNavn":"BYGDIN","nesteDelfeltNr":2670,"delfeltFormal":"Kraftproduksjon","vassdragsomradeNr":"002","oppstromDelfeltListe":"1187","QNormalDelfelt6190_Mm3Aar":397.98000000000002,"QNormalDelfelt9120_Mm3Aar":418.07999999999998}}]} \ No newline at end of file From e1656eabfaefe3a8f9428ee998cdbb1cf992c8a0 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:39:28 +0200 Subject: [PATCH 09/16] ROADMAP: increment 22 built (Bygdin accepted) Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index c70a0225..9157c95e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -49,7 +49,7 @@ increment that most needs a picture to check against | 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | planned: ruled by Ola 2026-09-28 (16b's Q2) as a separate increment, next after 16b | `docs/increments/16b-terrain-polygons.md` (Q2, R6) | | 16d | A record of the input polylines in the mesh. Today a constraint edge keeps only its type bits; the source feature, its attributes (a river's name or order, a road's class) and which edges belong to one polyline are lost, and where edges from two features merge only the union of their bits survives. Proposed: a feature table (one row per input feature: source ID and attributes) and, per constraint edge, the list of features it came from (a list, since a merged edge belongs to several). The noder already tracks each piece's input chain; the open part is the output: a per-edge list in `.vtk`/`.ply`, or a side file | proposed by Ola 2026-09-28; to be designed by `@architect` once Ola has said what it is for; after 16b | - | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | -| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **designed as increment 22** (`@architect`, 2026-09-29), Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | +| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **built as increment 22 on branch `increment22-autocatchment`, not yet pushed** (2026-09-29, overnight): PR 1, the fine catchment, net 619 lines; PR 2, the outline reduction, net 403 lines. Bygdin: 304.91 km² against NVE's 305.54 km² (−0.21 %), 99.1/99.3 % node overlap with NVE's polygon, 17 812 → 740 outline vertices at 20 m with the area kept, catchment in 6.5 s (`docs/benchmarks/2026-09-29/bygdin.md`). Designed by `@architect`, 2026-09-29, Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | | — | `raster/`: grid-to-world geometry and bilinear sampling | shipped (`7785fea`), **no record** | none — predates the protocol | ## What stands between here and an operational MVP From 79432e35ade0d33f131add71b474465bab302fab Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:43:14 +0200 Subject: [PATCH 10/16] ROADMAP: increment 22 row, PR 2 at 406 lines; no push state in the row Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 9157c95e..71e3a2cd 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -49,7 +49,7 @@ increment that most needs a picture to check against | 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | planned: ruled by Ola 2026-09-28 (16b's Q2) as a separate increment, next after 16b | `docs/increments/16b-terrain-polygons.md` (Q2, R6) | | 16d | A record of the input polylines in the mesh. Today a constraint edge keeps only its type bits; the source feature, its attributes (a river's name or order, a road's class) and which edges belong to one polyline are lost, and where edges from two features merge only the union of their bits survives. Proposed: a feature table (one row per input feature: source ID and attributes) and, per constraint edge, the list of features it came from (a list, since a merged edge belongs to several). The noder already tracks each piece's input chain; the open part is the output: a per-edge list in `.vtk`/`.ply`, or a side file | proposed by Ola 2026-09-28; to be designed by `@architect` once Ola has said what it is for; after 16b | - | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | -| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **built as increment 22 on branch `increment22-autocatchment`, not yet pushed** (2026-09-29, overnight): PR 1, the fine catchment, net 619 lines; PR 2, the outline reduction, net 403 lines. Bygdin: 304.91 km² against NVE's 305.54 km² (−0.21 %), 99.1/99.3 % node overlap with NVE's polygon, 17 812 → 740 outline vertices at 20 m with the area kept, catchment in 6.5 s (`docs/benchmarks/2026-09-29/bygdin.md`). Designed by `@architect`, 2026-09-29, Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | +| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **built as increment 22** (2026-09-29, overnight), in two PRs: PR 1, the fine catchment, net 619 lines; PR 2, the outline reduction, net 406 lines. Bygdin: 304.91 km² against NVE's 305.54 km² (−0.21 %), 99.1/99.3 % node overlap with NVE's polygon, 17 812 → 740 outline vertices at 20 m with the area kept, catchment in 6.5 s (`docs/benchmarks/2026-09-29/bygdin.md`). Designed by `@architect`, 2026-09-29, Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | | — | `raster/`: grid-to-world geometry and bilinear sampling | shipped (`7785fea`), **no record** | none — predates the protocol | ## What stands between here and an operational MVP From e7f9fe2cadee4543771460e1b5914baf78327724 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:43:14 +0200 Subject: [PATCH 11/16] Retrospective agenda: behaviour added in a review fix without a red test Co-Authored-By: Claude Opus 5.5 --- docs/retrospectives/next.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/retrospectives/next.md b/docs/retrospectives/next.md index f9ce6834..75683827 100644 --- a/docs/retrospectives/next.md +++ b/docs/retrospectives/next.md @@ -67,3 +67,7 @@ Useful evidence, but it is implementation work in the design step. green step (the design's rule never stopped early) without stopping to hand back; the design amendment and the pinning test came after the code. The fix was right and disclosed, but the order was code first. + +2026-09-29, night: `@developer` added user-visible behaviour (lake refusals +reported under `--lakes`) in a review-fix commit with no failing test first; +`@reviewer` caught it and `@tester` pinned it afterwards. From 84e10746dd33e24383dbc77214493ad726eab023 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:44:26 +0200 Subject: [PATCH 12/16] 22 tests: lake refusals are reported under --lakes test_cli_catchment.py: a seed in no lake, and a seed in two overlapping lakes (a GeoJSON of the lake box and the box grown by one cell), are refused with Typer's 'Invalid value for --lakes:' and without 'Invalid value for --dem:' (6757925, LakeError). The whole phrase is asserted, as plain() flattens it (Typer prints the hint unquoted): with the old --dem hint the line reads 'Invalid value for --dem: ...', which a bare '--lakes' substring would still match when the message names the flag, and this phrase does not (checked on a scratch Typer app raising the same BadParameter with param_hint '--dem'). Replaces test_a_seed_in_no_lake_is_refused, whose refusal it keeps. Co-Authored-By: Claude Opus 5.5 --- tests/python/test_cli_catchment.py | 41 ++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/python/test_cli_catchment.py b/tests/python/test_cli_catchment.py index a73d312c..8bdf8ae4 100644 --- a/tests/python/test_cli_catchment.py +++ b/tests/python/test_cli_catchment.py @@ -262,8 +262,45 @@ def test_lakes_layer_without_lakes_is_refused(tmp_path: Path, dem_dir: Path) -> assert "--lakes" in output -def test_a_seed_in_no_lake_is_refused(tmp_path: Path, dem_dir: Path, lakes: Path) -> None: - refused(tmp_path, "--dem", str(dem_dir), *utm_seed(*lat(60, 60)), "--lakes", str(lakes)) +#: Typer's line for a BadParameter with param_hint "--lakes" (6757925, +#: LakeError), as `plain` flattens it. The whole phrase, because the old hint's +#: line, "Invalid value for --dem:", also contains "--lakes" wherever the +#: message quotes the flag. +LAKES_HINT = "Invalid value for --lakes:" +DEM_HINT = "Invalid value for --dem:" + + +def test_a_seed_in_no_lake_is_refused_under_lakes( + tmp_path: Path, dem_dir: Path, lakes: Path +) -> None: + output = refused( + tmp_path, "--dem", str(dem_dir), *utm_seed(*lat(60, 60)), "--lakes", str(lakes) + ) + assert LAKES_HINT in output, output + assert DEM_HINT not in output, output + + +def test_a_seed_in_two_lakes_is_refused_under_lakes(tmp_path: Path, dem_dir: Path) -> None: + doc = { + "type": "FeatureCollection", + "crs": {"type": "name", "properties": {"name": EPSG}}, + "features": [ + { + "type": "Feature", + "properties": {"name": name}, + "geometry": {"type": "Polygon", "coordinates": [list(lake.exterior.coords)]}, + } + for name, lake in ( + ("one", lake_box()), + ("two", lake_box().buffer(D, join_style="mitre")), + ) + ], + } + path = tmp_path / "two.geojson" + path.write_text(json.dumps(doc)) + output = refused(tmp_path, "--dem", str(dem_dir), *utm_seed(), "--lakes", str(path)) + assert LAKES_HINT in output, output + assert DEM_HINT not in output, output def test_a_catchment_cut_by_the_data_edge_writes_nothing(tmp_path: Path) -> None: From 647f1ccbd281d05c6f872c5bd5c149ab562d7892 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:44:57 +0200 Subject: [PATCH 13/16] 22 docs: as-built counts exact; RichDEM back under consideration Co-Authored-By: Claude Opus 5.5 --- docs/increments/22-auto-catchment.md | 11 ++++++----- project_structure.md | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/increments/22-auto-catchment.md b/docs/increments/22-auto-catchment.md index 7fd8fa6b..1e577768 100644 --- a/docs/increments/22-auto-catchment.md +++ b/docs/increments/22-auto-catchment.md @@ -689,19 +689,20 @@ request and the result are frozen; the CLI is the only place with paths. | `bindings/core.cpp` | `upstream`, `reduce_ring`, two outcome classes | 80 | 48 | 46 | | `src_python/tin_engine/_core.pyi` | their stubs | 30 | 19 | 22 | | `src_python/tin_engine/outline.py` | the tracer | 70 | 50 | | -| `src_python/tin_engine/catchment.py` | request, seed, window loop, result | 170 | 244, plus later edits | 23 | +| `src_python/tin_engine/catchment.py` | request, seed, window loop, result | 170 | 244 | 24 | | `src_python/tin_engine/dem_input.py` | repository helper split out | 10 | 1 | | | `src_python/tin_engine/feature_input.py` | `read_source` split out of `_Tally.source`, `read_lakes` | 30 | 38 net | | -| `src_python/tin_engine/cli.py` | `catchment` command, report, writer | 100 | 113 | 26 | -| **Total** | | about 860 | **619 net** (674 added, 55 removed) | **403 net** | +| `src_python/tin_engine/cli.py` | `catchment` command, report, writer | 100 | 113 | 28 | +| **Total** | | about 860 | **619 net** (674 added, 55 removed) | **406 net** | | `project_structure.md` | the two C++ modules and two Python modules | docs | | | "As built" is `@reviewer`'s count at review (2026-09-29), by CLAUDE.md §2's rule (blank lines, comments and docstrings not counted), over PR 1 = `master..608e366` and PR 2 = `039cf3c..ac778e4`. The totals are `@reviewer`'s; the per-file numbers are theirs too and were not recounted -here, and `catchment.py`'s PR 1 figure predates the window-loop edits, so the -PR 1 column need not sum to 619. Both PRs are under the 700 ceiling. +here. The PR 1 column, counted at `58f6904`, sums to 619. PR 2's figures +include `6757925`'s review fix (`catchment.py` +1, `cli.py` +2), taking it +from 403 to 406. Both PRs are under the 700 ceiling. ### The PR split diff --git a/project_structure.md b/project_structure.md index 7d45609c..0bb4d6e2 100644 --- a/project_structure.md +++ b/project_structure.md @@ -408,7 +408,7 @@ CMake-based, building the header-only core plus one Python extension. C++20 requ - **Detria** (header-only) is vendored at a pinned SHA in `lib/detria/`. CMake does **not** search for a system copy: version skew in a geometry kernel across machines is a reproducibility hazard and vendoring a header costs nothing. -- **External deps under consideration:** Eigen (if linear algebra needs grow beyond what we want to hand-roll). +- **External deps under consideration:** RichDEM (optional, MIT; increment 22 does not use it), Eigen (if linear algebra needs grow beyond what we want to hand-roll). - **No CGAL and no GDAL** in the new core: prohibited by `CLAUDE.md` §2. **No Boost.Geometry** either, which is a scope choice and not a prohibition — reading this line as one is what put an unauthored ban in §2 for six From f53fa69cfff6a1734c0cebcab26e4e884eaa0b4d Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 07:56:08 +0200 Subject: [PATCH 14/16] 22 acceptance: data credits (NVE under NLOD, Kartverket, Copernicus) Co-Authored-By: Claude Opus 5.5 --- docs/benchmarks/2026-09-29/bygdin/README.md | 30 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/benchmarks/2026-09-29/bygdin/README.md b/docs/benchmarks/2026-09-29/bygdin/README.md index 88f79592..9bd4517e 100644 --- a/docs/benchmarks/2026-09-29/bygdin/README.md +++ b/docs/benchmarks/2026-09-29/bygdin/README.md @@ -31,10 +31,10 @@ the tables and where every number comes from. The file as fetched has sha256 `fe6d298b08573e50e50e711f1be9f70e9459ea823a0626516a539cd62cc4e76c`: one Polygon, 1376 coordinates, 305.5393 km² by shapely, valid. As the design - says, it is **not committed**: its licence and attribution terms are not - recorded. The service can change, so a later fetch may not match the - sha256. If it does not, the overlap figures below apply to the file with - this hash, not to the new one. + says, it is **not committed** (see "Data and licence" below). The + service can change, so a later fetch may not match the sha256. If it + does not, the overlap figures below apply to the file with this hash, + not to the new one. Scripts, both in this directory: @@ -225,6 +225,28 @@ the speed figures above are the baseline for the next increment. The increment does not change refine. The 1 m benchmark and the thread sweep were not run; this brief did not ask for them. +## Data and licence + +- **NVE's catchment polygon** (REGINE delfelt 1187, "BYGDIN") is © Norges + vassdrags- og energidirektorat (NVE). It is used under the Norwegian + Licence for Open Government Data (NLOD), which is compatible with + CC BY 4.0. NVE asks users to credit the rights holder and link to its + services: https://konto.nve.no/Information and + https://temakart.nve.no/tema/nedborfelt. + - The polygon was committed in `ad3df66` and removed in `1985720`, so it + stays in the history. + - To reproduce the overlap figures, fetch it with the curl command under + "What was run". + - The main session checked these terms on 2026-09-29. +- **The DEM**, DTM10 (`DTM10_UTM33_20260925`), is © Kartverket. +- **CORINE**, the lake polygon and the `--features` land cover, is credited + as increment 16b's NOTICE words it (`CORINE_NOTICE` in + `src_python/tin_engine/feature_input.py`): "Contains modified CORINE Land + Cover 2018 data (version 2020_20u1), (c) European Union, Copernicus Land + Monitoring Service 2018, European Environment Agency (EEA): clipped and + re-encoded, produced with funding by the European Union, not endorsed by + the EU". + ## Files - `run.sh`, `analyse.py`: the scripts above. From 25eb8fdcd906cef1ac253e5a4772fa6938212fd1 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 07:56:33 +0200 Subject: [PATCH 15/16] ROADMAP: an enforced outline tolerance parked (Ola) Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 71e3a2cd..0629cb4c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -49,7 +49,7 @@ increment that most needs a picture to check against | 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | planned: ruled by Ola 2026-09-28 (16b's Q2) as a separate increment, next after 16b | `docs/increments/16b-terrain-polygons.md` (Q2, R6) | | 16d | A record of the input polylines in the mesh. Today a constraint edge keeps only its type bits; the source feature, its attributes (a river's name or order, a road's class) and which edges belong to one polyline are lost, and where edges from two features merge only the union of their bits survives. Proposed: a feature table (one row per input feature: source ID and attributes) and, per constraint edge, the list of features it came from (a list, since a merged edge belongs to several). The noder already tracks each piece's input chain; the open part is the output: a per-edge list in `.vtk`/`.ply`, or a side file | proposed by Ola 2026-09-28; to be designed by `@architect` once Ola has said what it is for; after 16b | - | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | -| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **built as increment 22** (2026-09-29, overnight), in two PRs: PR 1, the fine catchment, net 619 lines; PR 2, the outline reduction, net 406 lines. Bygdin: 304.91 km² against NVE's 305.54 km² (−0.21 %), 99.1/99.3 % node overlap with NVE's polygon, 17 812 → 740 outline vertices at 20 m with the area kept, catchment in 6.5 s (`docs/benchmarks/2026-09-29/bygdin.md`). Designed by `@architect`, 2026-09-29, Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | +| — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; **a requirement, not an option (Ola, 2026-09-29): "we will get an extreme amount of points in the catchment polygon. This must be taken into account, or the number of triangles will be overwhelming."** A cell outline has a vertex at every cell step (tens of thousands for a mid-sized catchment at 10 m, far more for Glomma), and every boundary edge is a constraint the mesh must honour; 16b measured what dense constraints cost (CORINE borders make the 10 m mesh 3.6x larger). The design must state how the outline is reduced, to what horizontal tolerance, and what it guarantees (a simple polygon, the pour point inside); the fine outline is defined on the DEM's node lattice (Ola, 2026-09-29: "Isn't the DEM points, really?"), but the reduced polygon's vertices need not be DEM nodes: off-node domain vertices are supported since 16 (Ola: "We have aleady established that the polygons and DEM don't need to match"), so vertices may be placed to keep the area; and the reduced polygon keeps approximately the fine one's area (Ola, 2026-09-29: "the resulting polygon has approximately the same area as the fine one"), since catchment area drives runoff volume. Literature to check: area-preserving polyline simplification (e.g. Bose et al. 2006; Kronenfeld et al. 2020, segment collapse); and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | **built as increment 22** (2026-09-29, overnight), in two PRs: PR 1, the fine catchment, net 619 lines; PR 2, the outline reduction, net 406 lines. Bygdin: 304.91 km² against NVE's 305.54 km² (−0.21 %), 99.1/99.3 % node overlap with NVE's polygon, 17 812 → 740 outline vertices at 20 m with the area kept, catchment in 6.5 s (`docs/benchmarks/2026-09-29/bygdin.md`). The reduction bounds the distance to the fine outline at its vertices only (measured on Bygdin: 19.92 m at 20 m). Parked for later (Ola, 2026-09-29): "It could be useful to have the option of some tolerance, doing more adaptive coarsening, but write it down and leave it for now": an enforced whole-outline tolerance as an option, which also belongs with 20c's input coarsening. Designed by `@architect`, 2026-09-29, Bygdin first, moved ahead of 20c for Ola's night run: a lake polygon (CORINE) as the seed, a Priority-Flood labelling the lake's catchment, a marching-squares outline, area-preserving segment collapse to `--outline-tolerance` (default twice the cell). Two PRs on one branch; defaults for Ola to confirm are marked in the file | `docs/increments/22-auto-catchment.md` | | — | `raster/`: grid-to-world geometry and bilinear sampling | shipped (`7785fea`), **no record** | none — predates the protocol | ## What stands between here and an operational MVP From b169d2046fbd616c06f2db5c7975fa017382125d Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 08:01:22 +0200 Subject: [PATCH 16/16] 22 acceptance: the NVE source named correctly Co-Authored-By: Claude Opus 5.5 --- docs/benchmarks/2026-09-29/bygdin/README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/benchmarks/2026-09-29/bygdin/README.md b/docs/benchmarks/2026-09-29/bygdin/README.md index 9bd4517e..75b845eb 100644 --- a/docs/benchmarks/2026-09-29/bygdin/README.md +++ b/docs/benchmarks/2026-09-29/bygdin/README.md @@ -227,12 +227,15 @@ were not run; this brief did not ask for them. ## Data and licence -- **NVE's catchment polygon** (REGINE delfelt 1187, "BYGDIN") is © Norges - vassdrags- og energidirektorat (NVE). It is used under the Norwegian - Licence for Open Government Data (NLOD), which is compatible with - CC BY 4.0. NVE asks users to credit the rights holder and link to its - services: https://konto.nve.no/Information and - https://temakart.nve.no/tema/nedborfelt. +- **NVE's catchment polygon** is © Norges vassdrags- og energidirektorat + (NVE). It is NVE's reservoir sub-catchment ("delfelt") 1187, "BYGDIN", + from the map service VassdragsreguleringVannkraft, layer 8 + (https://gis3.nve.no/map/rest/services/Mapservices/VassdragsreguleringVannkraft/MapServer/8). + It is not a REGINE unit. + - It is used under the Norwegian Licence for Open Government Data + (NLOD), which is compatible with CC BY 4.0. NVE asks users to credit + the rights holder and link to its services: + https://konto.nve.no/Information. - The polygon was committed in `ad3df66` and removed in `1985720`, so it stays in the history. - To reproduce the overlap figures, fetch it with the curl command under