riverarchitect.riverbuilder
River Builder: synthetic river valleys from a handful of parameters.
The open-source replacement for the River Builder half of the ArcGIS ModifyTerrain
module. The other half - threshold-based grading and widening of an existing DEM - is
riverarchitect.terraforming; this one starts from nothing and invents a valley.
Why invent a valley
A restoration design needs a target, and a reach that has been channelised for a century no longer contains one. River Builder generates a reach with the geometry a natural one would have - a meandering centreline, a thalweg that rises and falls, bankfull width that varies along it, floodplain and terrace benches - from parameters an engineer can defend: valley slope, bankfull width and depth, median grain size, and a critical Shields stress. The result is a DEM you can run a 2D model over and compare against the existing one.
The construction
A centreline along the valley, displaced sideways by the meandering function. Its arc length
sand the sinuosity follow from that displacement.Bankfull width at each station, from the width function about the base width, floored at the minimum width.
Curvature, from the curvature function - and, for an asymmetric cross-section, the analytical curvature of the sine that defines it.
Channel slope by least squares of curvature against arc length, plus valley slope over sinuosity. When the bankfull depth is not given it follows from it:
H = 165 * D50 * tau_cr / S- a regime relation, so depth is not a free parameter.Thalweg and bank tops at each station, and a cross-section between them: a symmetric U, an asymmetric U leaning into the bend, or a trapezoid.
Floodplain and terrace benches either side, offset by their own functions.
The points are triangulated onto a regular grid, clipped to the valley boundary, and written as a GeoTIFF.
Relation to the original
The original called an R script (riverbuilder.r, 1189 lines) through rpy2, wrote a
point cloud to CSV, and rebuilt it as a TIN and a raster with arcpy 3D Analyst - so it
needed R, rpy2, ArcGIS and a 3D Analyst licence. The geometry is reproduced here in numpy
and the interpolation is scipy.interpolate.LinearNDInterpolator, which is what a TIN
is. Nothing else is required.
The parameter file format is unchanged, so an existing RiverBuilder input runs as it is.
- class riverarchitect.riverbuilder.RiverBuilder(parameters, unit='si', seed=0)[source]
Bases:
objectBuild a synthetic river valley from a
RiverBuilderInput.- Parameters:
parameters (RiverBuilderInput or str) – the parameters, or a file to read them from.
unit (str) –
"us"or"si". The parameter file is metric by convention; this only labels the result.seed (int) – seed for the
PERLnoise, so a run is reproducible.
- Variables:
error (bool) – True when a step could not be completed.
- build()[source]
Compute the whole geometry.
- Returns:
the station arrays (
x,centreline,arc_length,bankfull_width,curvature,thalweg,top_of_bank) and the cross-section grids (xs_x,xs_y,xs_z).- Return type:
- to_raster(path=None, cell_size=1.0, crs=None)[source]
Triangulate the point cloud onto a regular grid and write a GeoTIFF.
The original built an ArcGIS TIN and sampled it, which needed a 3D Analyst licence. A Delaunay triangulation with linear interpolation is the same surface;
scipy.interpolate.LinearNDInterpolatorreturns NaN outside the convex hull, which is exactly the clip to the valley boundary that the original did with a soft-clip polygon.
- class riverarchitect.riverbuilder.RiverBuilderInput(**overrides)[source]
Bases:
objectThe parameters of a synthetic valley, and the file they are read from and written to.
Every attribute has a default, so a partially specified input still builds. Sub-reach variability parameters hold a list of
UserFunction, which are summed.- Parameters:
**overrides – any attribute in
DEFAULTS, or a variability parameter as a list ofUserFunction.
- VARIABILITY = ('meander', 'curvature', 'width_function', 'thalweg', 'left_floodplain', 'right_floodplain')
The variability parameters, and the independent variable each is evaluated against.
- class riverarchitect.riverbuilder.UserFunction(kind, arguments)[source]
Bases:
objectOne term of a sub-reach variability parameter.
- Parameters:
kind (str) – one of
FUNCTION_TYPES.arguments (list) – the numbers in the call, in the order the format writes them.
SIN,COSandSIN_SQtake amplitude, frequency and phase shift and are evaluated against the radian station;LINEtakes a slope and an intercept and is evaluated against the station in length units;PERLtakes an amplitude and a wavelength and produces smooth pseudo-random noise against the station index.- classmethod parse(text)[source]
Read
SIN(0, 1, 0)into aUserFunction.
- riverarchitect.riverbuilder.PARAMETERS = {'Bankfull Depth (Hbf, A)': 'bankfull_depth', 'Bankfull Width (Wbf)': 'bankfull_width', 'Bankfull Width Function': 'width_function', 'Bankfull Width Minimum': 'bankfull_width_min', 'Boundary Width': 'boundary_width', 'Centerline Curvature Function': 'curvature', 'Channel XS Points': 'xs_points', 'Critical Shields Stress (t*50)': 'tau_cr', 'Cross-Sectional Shape': 'xs_shape', 'Datum': 'datum', 'Floodplain Width': 'floodplain_width', 'Left Floodplain Function': 'left_floodplain', 'Length': 'length', 'Meandering Centerline Function': 'meander', 'Median Sediment Size (D50)': 'd50', 'Outer Floodplain Edge Height': 'floodplain_outer_height', 'Outer Terrace Edge Height': 'terrace_outer_height', 'Right Floodplain Function': 'right_floodplain', 'Terrace Width': 'terrace_width', 'Thalweg Elevation Function': 'thalweg', 'Trapezoidal Base': 'trapezoid_base', 'Valley Slope (Sv)': 'valley_slope', 'X Resolution': 'x_resolution'}
Parameter name in the input file -> attribute name. The file format is the original’s, so an existing RiverBuilder input runs unchanged.
- riverarchitect.riverbuilder.CROSS_SECTIONS = ('SU', 'AU', 'TZ')
symmetric U, asymmetric U, trapezoid.
- Type:
Cross-sectional shapes
- riverarchitect.riverbuilder.FUNCTION_TYPES = ('SIN_SQ', 'SIN', 'COS', 'LINE', 'PERL')
The function forms a sub-reach variability parameter may take, longest name first so that
SIN_SQis not matched asSIN.
- riverarchitect.riverbuilder.DEFAULTS = {'bankfull_depth': 0.0, 'bankfull_width': 10.0, 'bankfull_width_min': 5.0, 'boundary_width': 0.0, 'd50': 0.01, 'datum': 1.0, 'floodplain_outer_height': 0.0, 'floodplain_width': 0.0, 'length': 100.0, 'tau_cr': 0.047, 'terrace_outer_height': 0.0, 'terrace_width': 0.0, 'trapezoid_base': 0, 'valley_slope': 0.001, 'x_resolution': 100, 'xs_points': 23, 'xs_shape': 'SU'}
Defaults, reproducing
cRiverBuilderConstruct.InputFile.get_par_defaults.