Version: v17c Date: July 2026 (data exported May 2026) Base version: SWORD v17b (March 2025, UNC)
- Node coordinates restored to v17b for SWOT D0-D2 continuity. The
344 reaches whose nodes were rederived in 0.0.8 and 0.0.10 now use v17b
node
x/y,node_length,cl_id_min, andcl_id_maxvalues again. A small OC split-revert residue on reach 51111300061 was included in the same pass. Post-revert validation found zero node coordinate differences relative to v17b NetCDF across all 11,112,454 nodes. - Centerline-to-node assignments resynchronized.
centerlines.node_idwas restored from v17b on affected reaches so node geometry and centerline allocation remain internally consistent. - Node order repaired on restored-coordinate reaches. Projection-based
ordering over the v17b-restored node coordinates updates
node_orderon 3,725 nodes across 221 of the 344 affected reaches, and updates 45dn_node_id/up_node_idboundary pairs. The repair preserves node coordinates and recomputes the six node distance fields from the current midpoint convention across all 344 restored-coordinate reaches, including a distance-only cleanup for 289 inheriteddist_outrows on five unchanged-order reaches. AS reach 35301100891 now encodes POM's intended downstream-to-upstream sequence (2, 3, ..., 23, 25, 26, 24, 27, ..., 73, 75, 74, 1) without moving node lat/lon. - SWOT slope observations recomputed from node WSE.
slope_obs_*on reaches now comes from pass-level regressions of RiverSP nodewse_smagainst the current beta12nodes.dist_out, then reach-level percentiles of those pass slopes. This replaces stale reach-level signed RiverSP slopes whose sign could reflect older node ordering. Medianslope_obs_p50coverage is 173,732 reaches; negative medians decrease from 18,805 before the correction to 9,988 after the correction. POM's canonical AS reach 35301100891 changes fromslope_obs_p50=-0.0136548684to+0.0142687580m/m while retainingslope=15.121399m/km. Downstream users should read bothslopeandslope_obs_*directly from 0.0.12; no external sign flip is needed for reaches whose node order changed. The 221 operation-999node_orderrepair reaches are all in first-digit POM regions 1-6. Test 16 slope-observation and endpoint-WSE diagnostics in first-digit regions 7-9 are separate warning populations, not additional 0.0.12 node-order repairs. - Static MERIT/SRTM slope semantics unchanged. The legacy
reaches/slopevariable remains the nonnegative MERIT/SRTM slope magnitude in m/km; it is not a signed flow-direction variable. 0.0.12 does not changereaches/slope,reaches/wse, ornodes/wserelative to v17b. Endpoint-WSE sign checks may still warn where inherited MERIT/SRTM node WSE values are locally inconsistent with flow direction; those warnings are data-quality diagnostics, not instructions to flipreaches/slopeorslope_obs_*. MERIT/SRTM WSE resampling was evaluated after POM's Test 16 feedback and deferred: the legacy SWORD workflow used UPA-filtered nearest MERIT river pixels and centerline aggregation, so a faithful correction is a science-data reconstruction task rather than a final beta patch. - v17c analytic fields preserved. The revert does not undo the v17c midpoint node-distance convention, lakeflag/type reconciliation, SWOT WSE/width observation statistics, metadata restoration, or export ordering changes. No RiverObs software change or coordinate-substitution mapping table is required for D0-D2 coordinate continuity.
- Exports regenerated and verified. NetCDF, GeoPackage, and GeoParquet artifacts were regenerated for all six regions. Export row counts match the DuckDB source for reaches and nodes in GeoPackage/GeoParquet, and for reaches, nodes, and centerlines in NetCDF.
- Node ordering normalized in NetCDF export. About 7.5% of reaches
(18,553 globally) had nodes stored in descending dist_out order in all
previous SWORD versions due to arbitrary GRWL centerline digitization
direction. Nodes are now exported in ascending node_order
(downstream-first) for all reaches. This fixes slope sign reversals
in processing code that assumes the first node in the array is the
downstream end. The underlying data is unchanged; only the file
ordering is corrected. This is an export storage-order fix, not a retained
topology reversal and not the smaller 0.0.12 operation-999
node_orderrepair set. - Restored metadata variables in NetCDF export. river_name_local, river_name_en, version, add_flag, and swot_obs_source were accidentally omitted from the export spec in 0.0.4. Restored in 0.0.11 on both reaches and nodes where applicable.
- Node lakeflag propagated from reach. Node lakeflag now matches parent reach lakeflag across all 11,112,454 nodes, per JPL request.
- Node geolocation fixed on 303 additional reaches.
rederive_nodesrecomputes node x/y from centerline spatial partitioning for reaches where consecutive node gaps exceed 3x the reach's median spacing and 0.4 km absolute (POM test 6b criteria). By region: AS:189, SA:38, AF:29, EU:21, OC:19, NA:7. Combined with the 41 reaches fixed in 0.0.8, total rederived: 344 reaches (0.14%). 10 reaches were fixed but still trigger the scrambled-node detector (centerline geometry issues inherited from v17b); see Known Limitations. - Reach 35301100891 node_order rotation and geolocation fixed. This AS
reach had node_ids rotated by one position (2, 3, ..., 75, 1 instead of
1, 2, ..., 75) since v17b. Additionally, 74 of 75 nodes were rederived
from the centerline (max shift ~12.6 km on node 35301100890011) due to
scrambled geolocation detected by POM test 6b.
node_ordernow matchesnode_idascending,dn_node_id= 0011,up_node_id= 0751. Node dist_out and all interpolated distance columns recalculated to match the corrected node_order. - Reach lakeflag and type reconciled for lake classification. About 6,200 reaches had inconsistent lakeflag and type fields (lakeflag=1 with type=1, or lakeflag=0 with type=3). Inconsistent reaches were either orphaned (skipped by both river and lake processing) or double-counted. Resolution combined three methods: 1,015 manual reviews through the Streamlit QA app, a gradient-boosted classifier trained on those reviews (82% precision, 6% FPR, applied at high confidence only: p>0.8 for lake, p<0.2 for river), and direct corrections from HarP v1.1 lake classifications. Lakeflag and type are now 100% consistent across all 248,673 reaches. The type column now diverges from the reach ID last digit on 2,316 reaches (0.9%) as of 0.0.10 (2,648 by 0.0.12 after subsequent type corrections); the type column is authoritative.
- Six reaches fixed for N013 closure-bug damage. Reaches 14278900061 (AF), 31241401301, 48294000081, 45570000125, 34100005185, 42211000503 (AS) had corrupted x/y and cl_id_min/cl_id_max from the N013 closure bug (documented in 0.0.8). Nodes rederived from their own centerlines using the fixed code; node_length restored from v17b NetCDF to preserve exact sum-equals-reach_length consistency. Full sweep confirms zero reaches with node_length mismatch above 0.1% globally.
- SWOT reach filters aligned with node filters.
build_reach_filter_sqlnow includes cross-track distance (10-60 km) and valid time_str filters, matching the node-level filters. Code change only; no DB data affected. rederive_scrambled_nodes.pysafeguards added. After rederiving nodes, the script now automatically recalculates node dist_out (prevents N004 violations) and verifies node_length sums (catches G002 regressions).
- Flow correction fully reverted. All 810 flow-corrected reaches restored
to v17b topology after discovering a scoring tautology in
score_section_confidence(slope_from_upstream = -slope_from_downstream). Topology diff vs v17b: 0.v17c_flow_correctionstable is empty. - dist_out corruption fixed. 1,976 reaches had BFS-corrupted
dist_outfrom a JanuaryCALCULATE_DIST_OUTbug (wrong outlet, 93–99% error). Restored v17b values. T017 violations: 701 → 557. - Pipeline re-run with
skip_flow_correction=True. All 6 regions pass all gates.
- Node
dist_outunified to midpoint convention. All six node-level distance columns (dist_out,hydro_dist_out,dist_out_dijkstra,hydro_dist_hw,pathlen_hw,pathlen_out) now use midpoint interpolation from reach-level values. Previouslydist_outpreserved v17b endpoint values while the other five used midpoint, causing a systematic ~100 m discrepancy on single-path networks. On such networks the three outlet-distance columns are now exactly equal at every node. Breaking change from v17b convention; all POM release gate checks pass. - Node geolocation fixed on 41 reaches.
rederive_nodesrecomputes node x/y from centerline spatial partitioning for 41 reaches (SA:5, EU:5, AF:8, AS:20, OC:3) where source NetCDF had contradictory CL-to-node mappings causing >2 km geographic jumps between consecutive nodes. Node lengths now use boundary-to-boundary distances (sum equalsreach_lengthexactly). POM example reach 13341000591: max consecutive node jump drops from 13.9 km to 223 m. rederive_nodesclosure bug fixed. The N013 centerline sync insiderederive_nodescaptured a stalereach_idfrom an outer loop instead of the current plan's reach. Fixed to useplan["reach_id"].
- Flow-corrected reach node_order and dist_out restored. When pipeline runs
skip the facc stage (e.g. when only rerunning distance and mainstem
computations),
flipped_reach_idswas empty, soupdate_node_columnsnever reversednode_orderor swappeddn_node_id/up_node_idfor the 810 flow-corrected reaches.0.0.6shipped withnode_ordermatching v17bnode_idorder on those reaches (Pierre-Olivier Malaterre detected 389 inverted reaches).0.0.7loads the flipped reach IDs from thev17c_flow_correctionstable when facc is skipped, so the node-order reversal anddn_node_id/up_node_idswap always run. - Node
dist_outrecomputed for flow-corrected reaches. After reversingnode_order, v17b nodedist_outvalues are stale on 810 reaches (computed from the wrong flow direction).0.0.7recomputes them using cumulativenode_lengthmidpoint offsets from the corrected downstream boundary, sodist_outis monotonic withnode_orderand matches the new flow direction. - New lint check N018. Catches the regression above automatically: flags
flow-corrected reaches whose
node_orderis not reversed from v17bnode_idorder. ERROR severity, wired into the POM release gate.
- Canonical export ordering restored. The
0.0.5flat-file exports were written in DuckDB storage order through the generic maintenance exporter, which does not preserve a stable logical row order unless it is told to do so. As a result, node arrays in the distributed files could be split across multiple blocks for the same reach even though the per-row attributes stayed aligned. - Nodes now export in reach-contiguous logical order.
0.0.6writes nodes ordered byreach_id, thennode_order, thennode_idacross NetCDF, GeoPackage, and Parquet. This restores a clean in-file node sequence for downstream consumers that expect each reach to occupy one contiguous node block and to run from downstream (node_order=1) to upstream (node_order=n). - Observed regression removed. In Africa, the count of reaches whose NetCDF
node rows were split into multiple file blocks dropped from 536 in
0.0.5to 0 in0.0.6. Pierre-Olivier Malaterre's example reach13341000591now appears as one contiguous node block withnode_order = 1..93.
- Promoted corrected bundle to a new version. The corrected April 2
reissue is now published as
0.0.5instead of reusing0.0.4, so testers do not keep hitting stale downloads or cached copies under the same name. This0.0.5bundle supersedes all prior0.0.4beta uploads on Drive. - Release regressions from distributed
0.0.4artifacts fixed. The0.0.5bundle corrects the specific tester-reported regressions from the prior0.0.4uploads: reversed nodedist_outrelative tonode_order, staledn_node_id/up_node_idorientation on flow-corrected reaches, missingnodeslayers in some GeoPackages, NULLdist_out_dijkstraat isolated ghost coastal outlets, and inconsistent single-node node-distance convention relative to the other node-level outlet distances. - Release artifacts renamed and resynced. NetCDF, GeoPackage, Parquet,
release notes, and SHA256 manifests now use
0.0.5filenames consistently in the local beta folder and Google Drive beta folder.
- Node propagation to 11.1M nodes. Five v17c columns (
best_headwater,best_outlet,pathlen_hw,pathlen_out,subnetwork_id) were NULL on all nodes. Now propagated from parent reaches. - Dijkstra ghost outlet fix. Ghost reaches (type=6) with out_degree=0 no
longer report
dist_out_dijkstra=0. All sinks are used as Dijkstra sources for full coverage, with outlet values following the v17b convention (dist_out_dijkstra = reach_length). Real outlet counts: NA=7, SA=1, EU=2, AF=1, AS=11, OC=1. - Bifurcation routing fix. At multi-successor nodes,
rch_id_dn_mainnow follows the mainstem chain unconditionally (was falling back to score-based ranking, which could disagree withis_mainstem). V023 (pathlen_out step consistency) violations: 2 → 0. hydro_dist_hwcomputed. Mainstem distance from headwater viarch_id_up_mainchain walk (mirror ofhydro_dist_out). Was stale from a prior pipeline run.- facc monotonicity fix (T003). 419 reaches corrected via iterative downstream propagation. T003 violations: 392 → 0.
- Routing weights retrained on
effective_slope. SWOTslope_obs_p50(where reliable and n>=5) replaces MERIT DEM slope in routing score training. Slope share: 8% → 3%, width: 67% → 71%. CV accuracy unchanged (88.5%). - Node-level distance interpolation.
hydro_dist_out,hydro_dist_hw, anddist_out_dijkstraadded to nodes table, interpolated by node position within reach (using v17bdist_outoffset).pathlen_hwandpathlen_outchanged from flat reach copies to per-node interpolated values. hydro_dist_hwconvention fix. Reach-level values shifted from downstream-end reporting to upstream-end reporting, matchingdist_out,hydro_dist_out, anddist_out_dijkstra. Headwater reaches now report 0 (wasreach_length). Node-level values unchanged.- F006 junction conservation fix. 2 remaining junction violations (OC 53130100215, AS 45311901585) resolved by setting downstream facc to sum of upstream facc. F006 violations: 2 → 0.
- 13 AS
main_sidereverted to v17b. Reaches hadmain_sidechanged from 0 (main) to 1 (side) by an undetermined prior operation; 9 of 13 are linear reaches where side-channel classification is impossible. - Distance convention documented. Variable reference now includes a convention table specifying endpoint reporting, zero-point, and ghost reach behavior for all distance variables, plus node-level interpolation formulas.
- Variable reference updated. 7 missing variables added, 8 type mismatches
fixed, multi-dimensional array documentation corrected. (The NetCDF export
itself uses the v17b
cl_ids [2, N]format.) - Node dist_out reactive recalc fix.
CALCULATE_DIST_OUToperations now correctly recalculate node-leveldist_outby sorting nodes vianode_order(geometric position) instead ofnode_id. This fixes an issue where flow-corrected reaches had incorrect node distances after recalculation (e.g., downstream-most node getting downstream reach'sdist_out). - April 2 artifact reissue. The published April 1
0.0.4files still had stale orientation metadata on 639 flow-corrected reaches. The reissued NetCDF, GeoPackage, and Parquet bundles recomputenode_order,dn_node_id, andup_node_idfrom nodedist_out, restoredist_out_dijkstra = reach_lengthfor isolated ghost coastal outlets, and align single-nodenode.dist_outwith the same midpoint convention used by the other node-level outlet distances. - Ghost coastal outlet dist_out_dijkstra fix. Isolated ghost coastal
outlets (type=6 sinks with upstream neighbors but no path to real
hydrologic outlets) now receive
dist_out_dijkstra = reach_length, matching v17bdist_outbehavior. Previously these 703 reaches in NA (similar counts in other regions) had NULL, but they should report the reach-level outlet distance to the ocean outlet point. - Exports regenerated (April 1 and 2, 2026). The initial April 1 bundle
captured the code fixes above. The April 2 reissue refreshed NetCDF,
GeoPackage, and Parquet artifacts plus SHA256SUMS after repairing published
node_order/ boundary-node orientation metadata, ghost coastal outletdist_out_dijkstra, and single-node node-distance conventions.
- Routing weights learned from human labels. Replaced the handcrafted
lexicographic 3-tuple
(effective_width, log_facc, pathlen)with a weighted scalar score trained on 1,967 human-labeled junction decisions: `1.97log1p(ew) + 0.23log1p(facc) - 0.23log1p(slope) + 0.23log1p(pathlen)- 0.29*stream_order`. Two new signals vs prior releases: slope (negative = prefer lower gradient) and stream_order. All routing functions use the same score to prevent divergence. (Weights retrained in 0.0.4; see above.)
- Caroline's reviewer fixes synced. 127 C001 lakeflag fixes (NA) and 10 C004 type fixes (NA) from the Streamlit reviewer app.
- NA PostgreSQL geometry fix. 38,696 NA reaches had NULL geometry in PostgreSQL — v17b geometry overwrite via dblink silently failed. Rewrote to read from v17b GPKG files on disk with verification gates.
- Fixed
dn_node_id,up_node_id, andnode_orderfor 810 flow-corrected reaches where node ordering was stale (based on v17bdist_out). 639 reaches now havedn_node_id != min(node_id), reflecting the inverted flow direction. - Removed redundant
rch_id_up_1..4/rch_id_dn_1..4vector variables from NetCDF export. Only the[4, N]matricesrch_id_upandrch_id_dnare exported, matching v17b format. - Fixed
swot_orbitsNetCDF type from string back to int64 (matching v17b). - Fixed 15 integer columns widened from int32 to int64 in NetCDF export
(
n_nodes,lakeflag,n_rch_up,stream_order, etc.). All shared variable types now match v17b exactly. - Synced
rch_id_up_1..4/rch_id_dn_1..4DB columns fromreach_topologytable (694 were stale after flow corrections).
- Renamed
is_mainstem_edgetois_mainstem - Mainstem algorithm refactored: computed per
main_path_idgroup (see Section 2.1) - Fixed
n_rch_up/n_rch_downstale counts at 148 flow-corrected reaches - Recomputed
faccat 807 flow-corrected reaches using topological propagation; resolves all F006 junction conservation violations and T003 monotonicity violations at these reaches - OC reach 51111300061 incomplete split fully reverted to v17b state (434 orphan centerlines, 73 orphan nodes restored)
- Initial v17c beta release
- Includes
node_order,dn_node_id,up_node_id(node ordering within reaches)
v17c retains every v17b variable (no columns removed) and adds new columns
on top. v17b's static fields are preserved unchanged; the only changes to
existing variables are the specific corrections noted below (facc, node
dist_out, and lakeflag/type). The additions fall into three groups:
- Actual SWOT-derived observational data (new). For the first time,
SWORD carries measured SWOT water-surface elevation, width, and slope
aggregated per reach and node from real overpasses — percentile summaries
(
wse_obs_*,width_obs_*,slope_obs_*), spread statistics, and observation counts (n_obs). These are new columns derived from SWOT observations; they do not modify the v17b staticwse,width, orslopevariables, which are retained as-is. - Computed mainstem topology and routing (new): mainstem identification,
best-headwater/outlet routing, new distance metrics, and a globally
unique
subnetwork_id. - One correction to an existing v17b variable: a global
flow-accumulation (
facc) denoise that fixes systematic MERIT Hydro D8 routing artifacts on 95,880 reaches (38.6%).
Alongside these, v17c ships data-quality fixes to v17b-inherited fields (node geolocation and ordering repairs, lakeflag/type reconciliation). No reaches, nodes, or centerlines were added or removed. v17c contains the same 248,673 reaches, 11.1M nodes, and 66.9M centerline points as v17b across all six regions (NA, SA, EU, AF, AS, OC).
Each region is distributed as a single NetCDF4 file
({region}_sword_v17c.nc). The group structure matches v17b
(centerlines, nodes, reaches), and the area_fits and discharge_models
subgroups under reaches pass through from v17b unchanged. Reach and
centerline arrays match v17b canonical row ordering. Node arrays are
grouped contiguously by reach_id and ordered within each reach by
node_order (downstream to upstream).
Reach coordinate columns (x, y, x_min, x_max, y_min, y_max)
match v17b values across all formats (NetCDF, DuckDB, PostgreSQL). Node
coordinate columns (x, y) also match v17b after the v17c continuity
revert.
All new variables use a fill value of -9999 where no observation or computation produced a value.
For a complete variable catalog, see v17c_variable_reference.md.
is_mainstem is computed per main_path_id group: each group (a mainstem
path plus its tributary branches) gets one canonical chain, identified by a
greedy walk from the group's shared best_headwater. At each junction the
algorithm selects the upstream branch with the highest weighted routing
score: 2.02*log1p(ew) + 0.17*log1p(facc) - 0.08*log1p(slope) + 0.35*log1p(pathlen) + 0.23*stream_order. These weights were learned from 1,967
human-labeled junction decisions via logistic regression on pairwise
log1p-difference features, using SWOT slope where reliable (else MERIT DEM).
The negative slope weight captures the
geomorphic pattern that mainstem channels have lower gradients than
tributaries. Mainstem reaches within each group are then assigned
rch_id_up_main / rch_id_dn_main from the chain; non-mainstem reaches
use the same weighted score. Ghost reaches (type=6) are excluded from
mainstem but still participate in routing topology. ~10% of mainstem
reaches have no mainstem neighbor at main_path_id group boundaries —
this is expected by design.
| Variable | Type | Units | Description |
|---|---|---|---|
dist_out_dijkstra |
float64 | meters | Dijkstra shortest-path outlet distance reported at the reach upstream endpoint (outlet = reach_length; values retained for ghost reaches) |
hydro_dist_out |
float64 | meters | Mainstem outlet distance reported at the reach upstream endpoint via rch_id_dn_main chain (outlet = reach_length) |
hydro_dist_hw |
float64 | meters | Mainstem headwater distance reported at the reach upstream endpoint via rch_id_up_main chain (headwater = 0) |
rch_id_up_main |
int64 | — | Main upstream neighbor reach_id (mainstem-preferred) |
rch_id_dn_main |
int64 | — | Main downstream neighbor reach_id (mainstem-preferred) |
best_headwater |
int64 | — | Routing-score-prioritized headwater reach_id for the network component |
best_outlet |
int64 | — | Routing-score-prioritized outlet reach_id for the network component |
pathlen_hw |
float64 | meters | Cumulative path length from best_headwater |
pathlen_out |
float64 | meters | Cumulative path length to best_outlet |
is_mainstem |
int32 | — | 1 if reach is on a mainstem path, 0 otherwise |
main_path_id |
int64 | — | Unique identifier for each mainstem path group |
subnetwork_id |
int32 | — | Connected component ID (Pfafstetter-offset, globally unique; see Section 4) |
dn_node_id |
int64 | — | Node ID at the downstream end of the reach (lowest dist_out) |
up_node_id |
int64 | — | Node ID at the upstream end of the reach (highest dist_out) |
Eight of these variables also appear at node level. Six are interpolated
by node position within the reach: dist_out, hydro_dist_out,
hydro_dist_hw, dist_out_dijkstra, pathlen_hw, and pathlen_out.
Three are flat copies from the parent reach: subnetwork_id,
best_headwater, and best_outlet. Historical flow-correction topology
flips were fully reverted in 0.0.9, so the current v17c database does not
retain a flow-corrected reach set requiring reversed node_id order.
All six interpolated distance columns use midpoint offsets:
offset = cumsum(node_length) - 0.5 * node_length. dist_out,
hydro_dist_out, dist_out_dijkstra, and pathlen_hw use
reach_value - reach_length + offset; hydro_dist_hw and pathlen_out
use reach_value + reach_length - offset. This places each node at the
geometric center of its node_length segment. On a single-path network
(no junctions), node-level dist_out, hydro_dist_out, and
dist_out_dijkstra are exactly equal.
node_order is a node-level variable (not in the reaches table): 1-based
position within a reach, ordered by dist_out ascending (1 = downstream
end, n = upstream end).
Distance convention note. These are endpoint-reported reach scalars, not
traversal-origin claims. dist_out, hydro_dist_out, and
dist_out_dijkstra are outlet-distance values reported at the reach
upstream endpoint and assign reach_length at the outlet. hydro_dist_hw
is also reported at the reach upstream endpoint, but measures distance from
best_headwater and assigns 0 at the headwater.
See the variable reference for the full convention table.
These are v17c's new SWOT-derived observational columns: water-surface
elevation, width, and slope measured by SWOT and aggregated per reach and
node across all qualifying overpasses, as percentile summaries with spread
statistics and observation counts. They are distinct from — and do not modify
— the v17b static wse, width, and slope variables, which are retained
unchanged. All percentile, range, and MAD variables share the units of the
underlying measurement.
SWOT slope derivation. Reach slope_obs_* values are computed from
pass-level linear regressions of RiverSP node water-surface elevation
(wse_sm) against node dist_out, then aggregated to reach-level percentiles
across passes. slope_obs_p50 is populated on 173,732 reaches (the remainder
lack sufficient SWOT slope observations); of these, 9,988 have a negative
median slope, flagged via slope_obs_quality / slope_obs_q.
Reaches and nodes:
| Variable | Type | Units | Description |
|---|---|---|---|
wse_obs_p10–wse_obs_p90 |
float64 | meters | WSE percentiles (10th through 90th, in steps of 10) |
wse_obs_range |
float64 | meters | WSE observation range (p90 - p10) |
wse_obs_mad |
float64 | meters | WSE median absolute deviation |
width_obs_p10–width_obs_p90 |
float64 | meters | Width percentiles |
width_obs_range |
float64 | meters | Width observation range |
width_obs_mad |
float64 | meters | Width median absolute deviation |
n_obs |
int32 | — | Total SWOT observation count |
Reaches only:
| Variable | Type | Units | Description |
|---|---|---|---|
slope_obs_p10–slope_obs_p90 |
float64 | m/m | Slope percentiles |
slope_obs_range |
float64 | m/m | Slope observation range |
slope_obs_mad |
float64 | m/m | Slope median absolute deviation |
slope_obs_adj |
float64 | m/m | Adjusted slope |
slope_obs_slopeF |
float64 | — | Slope F-statistic |
slope_obs_reliable |
int32 | — | 0 = unreliable, 1 = reliable |
slope_obs_quality |
int32 | — | Integer quality category (0–8; see Section 3) |
slope_obs_n |
int32 | — | Number of RiverSP node observations used in pass-level slope fits |
slope_obs_n_passes |
int32 | — | Number of SWOT passes used |
slope_obs_q |
int32 | — | Bitfield quality flag (see Section 3) |
A two-stage denoise pipeline corrected flow accumulation (facc) values
to address three systematic error modes in MERIT Hydro's D8
(eight-direction flow routing) upstream area: bifurcation cloning,
junction inflation, and raster-vector misalignment. In the v17c database,
95,880 of 248,673 reaches (38.6%) carry corrected values
(facc_quality = denoise_v3). Uncorrected reaches retain v17b values.
Relative to v17b, the correction raised facc on 80,538 reaches
(median +60%) and lowered it on 15,342 (median 30% decrease). The largest changes
are at bifurcation children, which under D8 each inherit the full parent
drainage area; v17c splits that area width-proportionally among the
distributary branches. See
facc_correction_methodology.md
for the full algorithm description.
| Variable | Type | Group | Description |
|---|---|---|---|
facc |
float64 | reaches, nodes | Flow accumulation (km^2). Corrected values where applicable; v17b values otherwise. |
facc_quality |
int32 | reaches, nodes | 1 = corrected by denoise_v3; fill_value = not flagged |
After correction, junction conservation violations (downstream facc < sum of upstream facc) are resolved in all regions. (In 0.0.2, facc was additionally recomputed at 807 then-flow-corrected reaches via topological propagation; the flow-correction topology itself was fully reverted to v17b in 0.0.9.)
dl_grod_id integrates a newly added obstruction dataset, DL-GROD (Deep
Learning Global River Obstruction Database; He et al. 2025), populated on
26,120 reaches.
| Variable | Type | Group | Description |
|---|---|---|---|
type |
int32 | reaches | Reach classification (1=river, 3=lake_on_river, 4=dam, 5=unreliable, 6=ghost). Not present in v17b NetCDF; added in v17c so NetCDF users can filter by reach type without needing the database. |
dl_grod_id |
int64 | reaches | DL-GROD (Deep Learning Global River Obstruction Database; He et al. 2025) dam/obstruction ID; populated on 26,120 reaches |
edit_flag |
string | reaches | Comma-delimited edit provenance tags (e.g., harp_lake,clf_reconcile). Also contains v17b numeric codes and the literal string NaN (v17b's no-edit placeholder); see the variable reference for the full value list |
| Value | Meaning |
|---|---|
| 1 | denoise_v3 — corrected by the facc denoising pipeline |
| -9999 (fill) | Not flagged; facc unchanged from v17b |
CF attributes: flag_values = [1], flag_meanings = "denoise_v3".
| Value | Meaning |
|---|---|
| 0 | reliable |
| 1 | small_negative |
| 2 | moderate_negative |
| 3 | large_negative |
| 4 | negative |
| 5 | below_ref_uncertainty |
| 6 | high_uncertainty |
| 7 | noise_high_nobs |
| 8 | flat_water_noise |
CF attributes: flag_values = [0,1,2,3,4,5,6,7,8],
flag_meanings = "reliable small_negative moderate_negative large_negative negative below_ref_uncertainty high_uncertainty noise_high_nobs flat_water_noise".
The working database also carries a below_noise category (427 reaches, slope
below the SWOT noise floor) that is not among codes 0-8; in this release those
reaches export with the slope_obs_quality fill value (-9999). They still
carry a slope_obs_p50 value, so filter on slope_obs_p50 != -9999 rather
than on slope_obs_quality alone when selecting reaches with a computed slope.
| Value | Meaning |
|---|---|
| 0 | Unreliable |
| 1 | Reliable |
| -9999 (fill) | Not computed (no SWOT slope observations) |
| Bit | Value | Meaning |
|---|---|---|
| 1 | 1 | Negative slope |
| 2 | 2 | Low number of passes |
| 3 | 4 | High variance |
| 4 | 8 | Extreme value |
| 5 | 16 | Clipped |
Flags combine by addition. A value of 0 indicates no quality issues. Example: 5 = negative slope (1) + high variance (4).
| Value | Meaning |
|---|---|
| 0 | Not on mainstem |
| 1 | On mainstem path |
-
SWOT observation coverage: SWOT statistics are fill_value (-9999) for reaches and nodes lacking SWOT data.
-
facc correction scope: 95,880 reaches corrected (38.6%); the remaining 152,793 retain v17b values. Node-level facc propagates from the parent reach.
-
Lake sandwich corrections: 1,252 reaches were reclassified to
lakeflag = 1where a narrow, shorter-than-neighbor reach sat between lake reaches. The later lakeflag/type reconciliation (0.0.10) rewrote manyedit_flagtags, so 483 reaches carry alake_sandwichtag in the v17c database. ~1,755 similar cases remain (narrow connecting channels, chains). -
HarP lake corrections: 7,425 reaches were reclassified from
lakeflag = 0(river) tolakeflag = 1(lake) based on HarP v1.1 (Hydrography and River Planform) lake classification data, with child node lakeflag updated to match (node lakeflag matches the parent reach on all 11,112,454 nodes as of 0.0.11). The later lakeflag/type reconciliation (0.0.10) folded many of these into combined tags, so 3,981 reaches carry aharp_laketag in the v17c database; nodeedit_flagis not tagged. Tags are comma-delimited when multiple apply. -
Neighbor-array fill convention (vs v17b): In the NetCDF
rch_id_upandrch_id_dn[4, N]arrays, empty neighbor slots use the fill value-9999, whereas v17b used0. The neighbor relationships themselves are identical to v17b; only the empty-slot sentinel differs. Code that diffs the raw arrays against v17b, or that treats0as "no neighbor," should account for this. -
Swapped neighbor vectors on two reaches (GeoParquet/DuckDB only): Reaches 45220300256 (AS) and 71382000696 (NA) have their denormalized
rch_id_up_1..4/rch_id_dn_1..4columns swapped in the GeoParquet and DuckDB exports (the single downstream neighbor appears inrch_id_up_1, andrch_id_dn_1..4are 0). The canonical NetCDFrch_id_up/rch_id_dnarrays and the topology table are correct for both reaches; only the denormalized vector columns in these two formats are affected. -
area_fits and discharge_models: Direct copies from v17b. Not recomputed against v17c facc or SWOT values.
-
subnetwork_idvsnetwork:subnetwork_iduses Pfafstetter- offset enumeration (globally unique). v17bnetworkuses per-region 1-based IDs. Different component counts (v17c finds more via weakly connected components; 19 subnetworks span multiple v17b networks).networkis retained unchanged from v17b. -
Flow correction fully reverted: experimental flow-direction corrections (810 reaches at peak, including 389 with ambiguous oscillating WSE slope signals) were fully reverted to v17b topology in 0.0.9 after a scoring tautology was found. v17c topology is identical to v17b;
v17c_flow_correctionsis empty. -
main_path_id consistency: 19
(best_headwater, best_outlet)tuples map to more than onemain_path_id(V015 lint check), affecting 230 reaches. The related continuity checks (V013, V014) andbest_headwatervalidity (V007) report zero violations. -
River naming: 51.2% of reaches are unnamed (NODATA), ranging from 26% (AF) to 69% (OC). 2.6% of mainstem 1:1 links have local name discontinuities (name changes between adjacent reaches with no junction).
-
Width fill values (A003): ~1,266 reaches have
width=0(unmeasured) orwidth=-1(GRWL lake fill). These are v17b fill values, not data errors. Present in both v17b and v17c unchanged. The A003 lint check is downgraded to WARNING for this reason. -
Node geolocation corrections deferred: The 0.0.8 and 0.0.10 rederived-node coordinate edits were reverted in v17c to preserve SWOT D0-D2 time-series continuity. Some node sequence/geolocation anomalies inherited from v17b therefore remain. Treat full geolocation repair as a v18 or separately approved release-envelope change.
Validation checks performed on the v17c data:
| Audit | Finding |
|---|---|
| Flow accumulation (facc) | 95,880 reaches (38.6%) corrected via the denoise pipeline (facc_quality = denoise_v3); the flag exactly matches the set whose facc differs from v17b (0 flagged-but-unchanged, 0 changed-but-unflagged). Raised on 80,538 reaches (median +60%), lowered on 15,342 (median 30% decrease). Junction-conservation (F006) and downstream-monotonicity (T003) violations resolved in all regions. |
SWOT observation data (wse_obs_*, width_obs_*, slope_obs_*) |
New SWOT-derived columns aggregated from real overpasses; they do not modify the v17b static wse/width/slope. slope_obs_p50 populated on 173,732 reaches (9,988 negative, flagged via slope_obs_quality); derived from pass-level node-WSE regressions (see §2.2). |
| Geometry | DuckDB geometries (rebuilt from NetCDF) lack endpoint overlap vertices present in v17b (210,533 reaches affected: 173K +1 point, 37K +2 points). reach_length unchanged. Reach coordinate columns (x, y, x_min, x_max, y_min, y_max) copied from v17b to ensure consistency across all formats. |
| Node coordinate continuity | v17c restores v17b node x/y, node_length, cl_id_min, and cl_id_max for the 344 previously rederived reaches plus OC reach 51111300061 split-revert residue. Global node coordinate diff vs v17b NetCDF: 0. |
| n_nodes / reach_length | Internally consistent. Zero N008/G002/G003 violations. |
| path_freq gaps | v17b had 4,952 connected non-ghost reaches with invalid path_freq (0 or -9999). Resolved in v17c; remaining nodata values are correctly attributed to ghost reaches (type=6). |
| subnetwork_id | 3,027 components across 248,673 reaches verified. Pfafstetter banding correct. Zero cross-region collisions. 19 subnetworks (0.6%) span multiple v17b networks (expected). |
| Topology integrity | T001 (dist_out_dijkstra monotonicity), T012 (referential integrity), T013 (self-reference), T014 (bidirectional): all pass. T005/T007: zero non-reciprocal edges (151 from incomplete flow correction revert resolved in beta 0.0.1). Historical flow-correction topology flips were fully reverted in 0.0.9; v17c_flow_corrections is empty in the current database. Node-level dist_out is recomputed for all reaches using the midpoint interpolation convention. |
| n_rch_up/n_rch_down | 148 scalar count mismatches corrected (flow corrections flipped reach_topology but did not recalculate counts). Zero mismatches across all 248,673 reaches. |
| OC reach split revert | Incomplete break_reaches() split of OC reach 51111300061 (434 orphan centerlines, 73 orphan nodes) fully reverted to v17b state. |
| River name formatting | 291 formatting issues corrected (separators, whitespace). Automated checks now enforce "; " separator and alphabetical ordering. |
| Flow direction | Experimental topology flips were ultimately reverted. The 1,112-flip experiment caused ~30K disconnected reaches and was rolled back; the later retained flow-correction family was also fully reverted in 0.0.9 after the scoring tautology was found. Current v17c does not retain topology that differs from v17b because of this flow-correction pipeline. |
| HarP lake corrections | 7,425 reaches reclassified lakeflag 0 to 1 from HarP v1.1 data, with node lakeflag propagated from parent reaches. After the 0.0.10 lakeflag/type reconciliation rewrote tags, 3,981 reaches carry a harp_lake tag in v17c. |
| lakeflag/type consistency | ~6,200 inconsistent reaches reconciled (0.0.10) via 1,015 manual reviews, a gradient-boosted classifier (82% precision, applied only at high confidence), and HarP v1.1 corrections. All 248,673 reaches are consistent under the two primary rules (no lakeflag=1 with type=1, no lakeflag=0 with type=3); some rarer lakeflag/type edge combinations (e.g. lakeflag=3 tidal with type=1) remain and are expected. type is authoritative and diverges from the reach ID last digit on 2,648 reaches (1.1%) in v17c due to in-place corrections. |
For POM (Pierre-Olivier Malaterre) validation results, see pom_validation_report.md.
v17c is distributed in five formats. The NetCDF files are the canonical release artifact (full group structure including centerlines and v17b subgroups); the other formats carry reaches and nodes with geometry.
- NetCDF4:
{region}_sword_v17c.nc, one file per region, where region isna,sa,eu,af,as,oc- Groups:
centerlines,nodes,reaches(plusreaches/area_fitsandreaches/discharge_modelssubgroups from v17b) - Ordering: reach and centerline arrays match v17b canonical ordering;
node arrays are reach-contiguous and sorted by
node_order - Fill value: -9999 for all numeric variables (int32, int64, float64)
- Groups:
- GeoPackage:
sword_{REGION}_v17c.gpkgper region (reaches and nodes layers) - Shapefile:
{region}_sword_{reaches,nodes}_hb{XX}_v17c.shp, split by HydroBASINS Pfafstetter level-2 basin within each region (v17b convention; required by the shapefile 2 GB size limit). Shapefile DBF format truncates attribute names to 10 characters, so fields use abbreviated names; the mapping tableshapefile_field_name_mapping.csvships alongside the shapefiles. The NetCDF/GeoPackage/GeoParquet names are authoritative. - GeoParquet:
sword_{REGION}_v17c_{reaches,nodes}.parquetper region - DuckDB:
sword_{REGION}_v17c.duckdbper region, withreachesandnodestables (geometry stored as GEOMETRY type; written with DuckDB 1.3 — open with DuckDB >= 1.3 and the spatial extension) - Global files: in addition to the per-region files, whole-planet
merged tables are provided as
sword_global_v17c_{reaches,nodes}.parquetandsword_global_v17c_{reaches,nodes}.duckdb(all six regions combined; each table carries aregioncolumn). - Checksums: SHA256 hashes for all distributed files listed in
SHA256SUMS.txt
| Document | Description |
|---|---|
| facc_correction_methodology.md | Facc denoise algorithm, detection rules, correction model |
| pom_requests_summary.md | POM validation check tracker (19 checks, production results) |
| pom_validation_report.md | POM validation production results |
| v17c_variable_reference.md | Complete variable catalog for NetCDF export |
| SWORD_v17b_Technical_Documentation.md | v17b baseline reference |