Run this notebook in your web browser:
The link opens bedload-exercise.py as an interactive marimo notebook that runs entirely in your web browser, without installing anything. Like the Jupyter notebook, the marimo notebook is the original French version of this page.
Wait a moment until Python has loaded in the browser (this takes longer only at the first start).
Click the yellow run button (▶︎, bottom right) to run all cells.
Edit and re-run any code cell to experiment: marimo automatically updates all cells that depend on it.
Your edits remain in the browser tab only. To keep them, save the notebook through the menu (☰, top right) > Export… > Python.
Run this marimo notebook locally
To run bedload-exercise.py on your own machine, get it from the marimo
Clone the repository:
git clone https://github.com/sschwindt/marimo-python-course.gitCreate and activate the course environment:
conda env create -f environment.ymlandconda activate hy-marimoIn the repository folder, start the notebook with
marimo edit bedload-exercise.py
Run this notebook with Jupyter
This page is also available as the Jupyter notebook bedload-exercise.ipynb in the jupyter or
, or download the notebook
and install Python and JupyterLab locally to run bedload-exercise.ipynb on your own machine. The notebook on Binder, Colab and GitHub is the original French version of this page.
Structure of the Exercise¶
| part | who does the work | |
|---|---|---|
| I | setup: load the script and the data | given: run the cells |
| II | data: grain sizes, roughness coefficients , threshold | you: complete two lists |
| III | equations: roughness ratio and critical roughness | you: complete two lines of code |
| IV | results: MPM bedload calculation, Shields diagram, active cross-sections | given: run and interpret |
Programming experience is not required. Run a cell with Shift + Enter. The spots to complete are marked with the comment # >>> À COMPLÉTER (to complete): replace each text ">>> COMPLETER" (quotes included) with your value or equation. In the code, decimal numbers are written with a point (0.047, not 0,047).
Part I: Setup¶
The following cell loads the script fun/charriage.py under the short name ch and displays the data from data/hecras-arbogne.csv. On Google Colab, it first downloads these two files. This cell carries no course content.
import os, sys, urllib.request
if "google.colab" in sys.modules: # uniquement sur Google Colab : télécharge le script et les données
for f in ("fun/charriage.py", "data/hecras-arbogne.csv"):
os.makedirs(os.path.dirname(f), exist_ok=True)
urllib.request.urlretrieve(
f"https://raw.githubusercontent.com/hydro-informatics/jupyter-python-course/main/{f}", f)
from fun import charriage as ch # fun/charriage.py, lit data/hecras-arbogne.csv
ch.lire_hecras()For each cross-section (from upstream to downstream), the table shows the station, the discharge, the bed and water surface elevations, the energy slope , the hydraulic radius and the water depth . The slope drops from 0.0042 upstream to 0.0007 downstream: this is where the gravel risks being deposited.
Part II: Data¶
Task 1: Grain Size and Roughness¶
Two grain size distributions describe the Arbogne (line-count analysis after Fehr, March 2014):
“Sable (fin)” (sand, fine) to estimate the supply from upstream;
“Gravier (complet)” (gravel, complete) to characterize the bed in place.
Four characteristic diameters are sufficient for the exercise:
| grain size distribution | [mm] | [mm] | [mm] |
|---|---|---|---|
| “Sable (fin)” | 0.23 | not needed | not needed |
| “Gravier (complet)” | 8.88 | 13.56 | 23.06 |
of the sand represents the fine supply passing through;
of the gravel represents the gravel in transit;
of the gravel represents the bed in place (armor layer);
of the gravel sets the grain roughness (Meyer-Peter & Müller 1948).
Enter these values, in millimeters, in the list melanges. The order matters: first the sand, then the gravel (the last mixture in the list represents the bed).
Then enter in the list k_st the two Strickler coefficients [m/s] to be compared, from the smoothest to the roughest:
without vegetation: the flow stays in the gravel bed (reference state);
with vegetation: the flood overtops into the densely vegetated floodplain.
The list may contain further values: every added becomes an additional roughness case.
# >>> À COMPLÉTER : diamètres caractéristiques en mm
# nom d_m d_84 d_90
melanges = [["Sable (fin)", ">>> COMPLETER"],
["Gravier (complet)", ">>> COMPLETER", ">>> COMPLETER", ">>> COMPLETER"]]
# >>> À COMPLÉTER : coefficients de Strickler k_st [m^(1/3)/s], sans végétation en premier
k_st = [">>> COMPLETER", ">>> COMPLETER"]Threshold of Motion ¶
MPM was calibrated with a critical Shields parameter of = 0.047, the value used throughout the exercise. The list tau_cr nevertheless accepts several values, for example to test how sensitive the result is to the threshold (see the homework). To do so, delete the # in front of the second line: every figure then shows all values of the list.
tau_cr = [0.047]
# tau_cr = [0.030, 0.040, 0.047, 0.056, 0.070] # variante, pas utilisée dans l'exercicePart III: Equations¶
Task 2: Roughness Ratio ¶
MPM does not compare the entire dimensionless bed shear stress with the threshold, but only the share that acts on the grains. Part of the energy is dissipated by bedforms, vegetation and banks. The roughness ratio corrects accordingly:
: total Strickler coefficient of the reach (the list
k_stfrom Task 1);: grain Strickler coefficient of a plane bed, calculated by charriage.py with of the gravel in meters.
Complete the marked line. In Python, a power is written **: for example, is written a ** (3 / 2).
def rapport_rugosite(k_st, k_r):
# >>> À COMPLÉTER : k' = (k_st / k_r)^(3/2)
return ">>> COMPLETER"Task 3: Critical Roughness ¶
The more vegetation slows down the flow, the lower , and with it the effective shear stress . A cross-section stops transporting when . Substituting with its definition and solving for yields the critical roughness of each cross-section:
A cross-section transports as long as . This equation makes it possible to plot the number of active cross-sections as roughness increases (last figure). Complete the marked line.
def k_st_critique(tau, k_r, tau_cr):
# >>> À COMPLÉTER : k_st,cr = k_r * (tau_cr / tau)^(2/3)
return ">>> COMPLETER"Check¶
Run this cell after each task. [OK] means that the value matches the lecture, [XX] that it is wrong, and [ ] that it has not been completed yet. Correct the value, then re-run the cell of the task and this one.
ch.verifier(melanges, k_st, rapport_rugosite, k_st_critique)Part IV: Results¶
MPM Calculation for the 16 Cross-Sections¶
charriage.py combines your data and applies MPM to every cross-section: each roughness case of the list k_st is calculated for both grain classes.
| grain | what it represents | roughness cases |
|---|---|---|
| gravel, | the gravel in transit | without and with vegetation |
| sand, | the fine supply from upstream | without and with vegetation |
| gravel, | the armored bed in place | without vegetation only |
For each combination, the table summarizes the roughness ratio , the number of cross-sections that still transport (), as well as the largest and the largest bedload transport rate [kg/(sm)] of the reach.
resultats = ch.calculer(melanges, k_st, tau_cr, rapport_rugosite)
ch.resume(resultats)Shields Diagram¶
The diagram compares the two roughness cases, without vegetation (flow in the channel) and with vegetation (flood overtopping into the floodplain), for both grain classes, gravel and sand. Every point is a cross-section. The vertical axis is the effective shear stress on the grains , the one that MPM compares with . Below the critical Shields curve, there is no bedload transport.
color: roughness case (blue without vegetation, red with vegetation);
symbol: grain class (circle: gravel; triangle: sand);
size: , one decade per class (the largest gives the largest marker).
ch.diagramme_shields(resultats)Active Cross-Sections as Roughness Increases¶
The following figure lowers step by step (from left to right, vegetation increases) and counts, for the gravel, the cross-sections where . The vertical lines are your values from the list k_st. The total extinction is the below which no cross-section transports anymore.
ch.profils_actifs(melanges, k_st, tau_cr, k_st_critique)