Skip to contents

The invariant suite tests the structural properties a consumer depends on. Every phase records its results; nextgen_fabric() stops the build when a gating check fails, and merge_vpus() runs its own suite on each merged domain, adding cross-VPU conservation and completeness checks.

Gate or information

A gate is a property the build guarantees; its failure stops the build. An informational check reports a desirable property without stopping the build. A check becomes a gate once it holds everywhere and a step in the build keeps it that way.

What each phase checks

Phase What is checked
Refactor unique ids, every reference reach reconciled, reconciled reaches with members, flow direction, a divide for every reconciled reach, landscape divides kept, valid divide polygons, POIs at reach outlets
Aggregate unique flowpaths and divides, resolvable toids and divide links, single-part flowpaths each lying in its divide, reconciled reaches assigned to exactly one flowpath, divide area conserved, no overlap, no shared divide
Nextgen fp- and cat- ids, acyclic flowpath-nexus and flowline graphs, resolvable toids, at most one divide per flowpath and an owner for every network divide, divide coverage and area conserved with no overlaps or gaps, flowpath members contiguous and agreeing with the flowlines, a closed routeable network, every reference id traceable, drainage area accumulating downstream, mainstem_id present, positive slope, integer ids, lake membership, the POI contract, the upstream index
Merge id uniqueness and prefixes across the domain, resolvable toids, divide ownership, an acyclic graph, every VPU present, feature counts, divide area and drainage area conserved, consistent CRS, and the nextgen network checks on the merged network

The POI contract is described in vignette("poi-contract").

Running the suite

The same suite runs on any GeoPackage:

check_invariants(
  "ngen",
  flowpaths = sf::read_sf("ngen_vpu01.gpkg", "flowpaths"),
  divides   = sf::read_sf("ngen_vpu01.gpkg", "divides"),
  nexus     = sf::read_sf("ngen_vpu01.gpkg", "nexus"),
  flowlines = sf::read_sf("ngen_vpu01.gpkg", "flowlines"),
  strict    = TRUE
)

strict = FALSE collects every result instead of stopping at the first failure.

The record

Each build writes invariants.json with every check’s name, level and result: in work/ for a VPU, and beside each merged domain product. A check whose input was not supplied leaves no entry, so absence means not evaluated, not passed; the record’s inputs block lists what the suite was given.

To compare a rebuild against the product it replaces, use hf_compare_products(), which names any flowpath that lost its divide.