Skip to content

River-Route

river-route routes runoff and discharge through large river networks with Muskingum routing kernels, in C and its numba twin, that route each river's whole time series in turn, from upstream to downstream.

Describe your routing

All routing runs through rr.Router, which routes Muskingum with static coefficients: each river's k and x, from the muskingumK and muskingumX columns of the network file, are fixed for the whole run. You describe what is routed with the forcing selector.

Selector Options Default
forcing 'channel' (channel routing only), or the form of the forcing_files routed into the rivers: 'catchment', 'grid', or 'ecmwf_grib'. Each step's runoff enters its river at a constant rate over the step. 'channel'

Three compute options set how each river computes, in two jobs: dt_routing sets each river's step, and use_subreaches and use_subcycles then make every coefficient non-negative at it, the condition for valid Muskingum routing.

Compute option Options Default
dt_routing 'auto' gives each river its own valid step, the runoff step over the fewest subcycles that keep its coefficients non-negative; a step in seconds, a divisor of the runoff step, is the step of every river. 'auto'
use_subreaches True splits each river too long for its step into sub-reaches; False routes it whole at its step. 'auto' requires it. True
use_subcycles True routes each river too short for its step in subcycles of it; False routes it whole at its step. 'auto' requires it. True

Every forcing routes, and dt_routing: auto with use_subreaches makes every coefficient of every river non-negative.

import river_route as rr

configs = rr.Configs.from_json("/path/to/config.json")  # sets the forcing and the other options
rr.Router(configs).route()

Quick Start

pip install river-route
import river_route as rr

configs = rr.Configs.from_json("/path/to/config.json")
rr.Router(configs).route()

Options are held by a frozen Configs object, built from keyword arguments or read from a JSON file with Configs.from_json, and validated when they are used. Configs is frozen and set once when it is built, so an option is changed by building the Configs you want. to_json writes the options to a file that Configs.from_json reads back.

import river_route as rr

configs = rr.Configs(
    network_file="/path/to/network.parquet",
    forcing="catchment",
    forcing_files=["/path/to/catchment_runoff.nc"],
    discharge_dir="/path/to/output/",
)
configs.to_json("/path/to/config.json")
rr.Router(configs).route()

Classes

Class Owns
Configs Every option, frozen. Built once and given to the classes below.
Network The river network: ids, topology, k and x, the concurrent partition, coefficient signs, sub-reaches, subcycles.
Router One simulation over a Network: coefficients, time options, channel state, the routing loop.
GridRunoff Gridded runoff to catchment runoff, reusing one weight table across many forcing files.

Router takes only a Configs, and builds its Network and the RunoffForcing class its forcing names from it. Network and GridRunoff take the options they need as ordinary arguments and each has a from_configs classmethod that reads those same values off a Configs. examples/config.json lists every option.

A Router reads and partitions its network table once, and every route() call on it reuses the result.

Start Here

  1. Basics
  2. Routing Ensembles
  3. Advanced Uses

Core References

  1. Configuration File
  2. Input/Output File Schemas
  3. Time Variables
  4. Math Derivations
  5. Parallelism