From 6fc043f90716153972d79bf53ac752517b33d812 Mon Sep 17 00:00:00 2001 From: gantian127 Date: Mon, 15 Jul 2024 09:57:18 -0600 Subject: [PATCH] Add a rainfall-runoff modeling tutorial (#138) * add rainfall_runoff tutorial * clean code * refine reference * clean code --- lessons/python/data/scs_unit_hydrograph.csv | 29 ++ lessons/python/rainfall_runoff.ipynb | 479 ++++++++++++++++++++ 2 files changed, 508 insertions(+) create mode 100644 lessons/python/data/scs_unit_hydrograph.csv create mode 100644 lessons/python/rainfall_runoff.ipynb diff --git a/lessons/python/data/scs_unit_hydrograph.csv b/lessons/python/data/scs_unit_hydrograph.csv new file mode 100644 index 0000000..3c74ff7 --- /dev/null +++ b/lessons/python/data/scs_unit_hydrograph.csv @@ -0,0 +1,29 @@ +time_ratio,discharge_ratio +0,0 +0.1,0.015 +0.2,0.075 +0.3,0.16 +0.4,0.28 +0.5,0.43 +0.6,0.6 +0.7,0.77 +0.8,0.89 +0.9,0.97 +1,1 +1.1,0.98 +1.2,0.92 +1.3,0.84 +1.4,0.75 +1.5,0.66 +1.6,0.56 +1.8,0.42 +2,0.32 +2.2,0.24 +2.4,0.18 +2.6,0.13 +2.8,0.098 +3,0.075 +3.5,0.036 +4,0.018 +4.5,0.009 +5,0.004 diff --git a/lessons/python/rainfall_runoff.ipynb b/lessons/python/rainfall_runoff.ipynb new file mode 100644 index 0000000..b591e56 --- /dev/null +++ b/lessons/python/rainfall_runoff.ipynb @@ -0,0 +1,479 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e283aaec-48e5-4cfd-92cc-b84e72311172", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "id": "d6c6a98e-7d6e-438e-855f-c610ba743869", + "metadata": {}, + "source": [ + "# Rainfall-Runoff Modeling" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "cfd79182-1f2c-42eb-8dfd-0c0bec41def1", + "metadata": {}, + "source": [ + "One of the principal applications of hydrology is in the forecasting and prediction of flood peaks and runoff volumes due to large rain and snowmelt events. To do this, researchers use models that simulate the stream response to a water-input event of a given magnitude and spatial and temporal distribution on a drainage basin. These models are usually referred to as rainfall-runoff models.\n", + "\n", + "Rainfall-runoff models include conceptual models and physically based models. These models are used to generate design floods or forecast floods from actual storms. A design flood is used in the design of bridges, levees, or floodplain-management plans. Flood forecasting is the process of predicting the occurrence, magnitude, timing, and duration of floods in a specific area. It is usually done via complex hydrologic models that are varying degrees physically based, rather than the simple conceptual models.\n", + "\n", + "In this tutorial, we will learn to write a simple rainfall-runoff model in Python and use it to estimate runoff hydrograph for an example small watershed.\n", + "\n", + "\"\"\n" + ] + }, + { + "cell_type": "markdown", + "id": "2c930396-243e-4f3c-8572-313581da0689", + "metadata": {}, + "source": [ + "## SCS Curve-Number Method\n", + "One of the most widely used rainfall-runoff models for routine design purposes is the SCS Curve-Number method. This method was developed by the U.S. **S**oil **C**onservation **S**ervice (now the U.S. Natural Resources Conservation Service, NRCS). In this method, there are three basic computations: 1) estimation of the effective rainfall, 2) estimation of the peak discharge, and 3) estimation of the runoff hydrograph." + ] + }, + { + "cell_type": "markdown", + "id": "d149144e-ef6d-4389-a75a-4c9584bb0fe8", + "metadata": {}, + "source": [ + "### 1) Estimate effective rainfall\n", + "Effective rainfall (or excess rainfall) is the amount of precipitation that contributes to runoff, as opposed to the total rainfall which includes both runoff and infiltration. The SCS method estimates the effective rainfall using total rainfall and watershed storage capacity via an empirical relation: \n", + "\n", + "\n", + "$$ w_{eff} = \\frac{(w - 0.2 * v_{max})^2}{w + 0.8 * v_{max}}$$\n", + "\n", + "- $w_{eff}$ is the effective rainfall (units: inch).\n", + "- $w$ is the total rainfall (units: inch).\n", + "- $v_{max}$ is the watershed maximum retention capacity, the maximum ability of a watershed to capture and store water from rainfall events (units: inch).\n", + "\n", + "$v_{max}$ could be estimated using curve numbers ($cn$), which are numbers assigned to each [hydrologic soil group](https://engineering.purdue.edu/mapserve/LTHIA7/documentation/hsg.html) under various land use types. You could learn more about the curve number tables at [here](https://www.hec.usace.army.mil/confluence/hmsdocs/hmstrm/cn-tables).\n", + "\n", + "$$ v_{max} = \\frac{1000}{cn} - 10 $$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78528e3e-82e1-4612-88c9-458cad27c254", + "metadata": {}, + "outputs": [], + "source": [ + "# estimate effective rainfall\n", + "def calculate_effective_rainfall(cn, w):\n", + " \"\"\"Calculate the effective rainfall using the SCS-CN method.\n", + "\n", + " Parameters\n", + " ----------\n", + " cn : float\n", + " Curve Number\n", + " w : float\n", + " Total rainfall (units: inch)\n", + "\n", + " Returns\n", + " -------\n", + " float\n", + " Effective rainfall (units: inch), rounded to 2 decimal places.\n", + " \"\"\"\n", + "\n", + " v_max = 1000 / cn - 10\n", + " w_eff = (w - 0.2 * v_max) ** 2 / (w + 0.8 * v_max)\n", + "\n", + " return round(w_eff, 2)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f2396122-6468-4197-9534-be3c4ff4f82b", + "metadata": {}, + "source": [ + "### 2) Estimate peak discharge\n", + "Peak discharge is estimated using effective rainfall, watershed area, and time to peak (the duration of hydrograph rise).\n", + "\n", + "$$ q_{peak} = \\frac{484 * w_{eff}* area}{t_{peak}}$$\n", + "\n", + "- $q_{peak}$ is the peak discharge (units: cfs).\n", + "- $area$ is the watershed area (units: $mi^2$).\n", + "- $t_{peak}$ is time to peak (units: hr).\n", + "\n", + "
\n", + "$t_{peak}$ could be estimated with the function below using duration of effective rainfall and the watershed time of concentration.\n", + "\n", + "$$ t_{peak} = 0.5*t_{rain} + 0.6*t_{con}$$\n", + "\n", + "\n", + "- $t_{rain}$ is the duration of effective rainfall (units: hr).\n", + "- $t_{con}$ is the watershed time of concentration (units: hr).\n", + "\n", + "
\n", + "$t_{con}$ is the time which takes water to travel from the hydraulically most distant part of the contributing area to the outlet. $t_{con}$ could be estimated with functions using watershed characteristics.\n", + "\n", + "$$ t_{peak} = 0.128*(\\frac{l}{s^{0.5}})^{0.79}$$\n", + "\n", + "\n", + "- $l$ is mainstream length (units: km).\n", + "- $s$ is the sine of main-channel slope angle (dimensionless).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3690150-d25b-4224-814e-ec166ff46fa5", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate time to peak\n", + "def calculate_time_to_peak(length, slope, t_rain):\n", + " \"\"\"\n", + " Calculate the time to peak of a hydrograph using the SCS-CN method..\n", + "\n", + " Parameters\n", + " ----------\n", + " length : float\n", + " mainstream length (units: km).\n", + " slope : float\n", + " sine of main-channel slope angle (dimensionless).\n", + " t_rain : float\n", + " Duration of effective rainfall (units: hr).\n", + "\n", + " Returns\n", + " -------\n", + " float\n", + " Time to peak (units: hr), rounded to 2 decimal places.\n", + " \"\"\"\n", + "\n", + " t_con = 0.128 * (length / slope**0.5) ** 0.79\n", + " t_peak = 0.5 * t_rain + 0.6 * t_con\n", + "\n", + " return round(t_peak, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f88b2d39-5caf-4568-be95-4bf116bb8ec9", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate peak discharge\n", + "def calculate_peak_discharge(area, w_eff, t_peak):\n", + " \"\"\"\n", + " Calculate the peak discharge of a hydrograph using the SCS-CN method.\n", + "\n", + " Parameters\n", + " ----------\n", + " area : float\n", + " Area of the watershed (units: mi^2).\n", + " w_eff : float\n", + " Effective rainfall (units: inch).\n", + " t_peak : float\n", + " Time to peak (units: hr).\n", + "\n", + " Returns\n", + " -------\n", + " float\n", + " Peak discharge (units: cfs), rounded to 2 decimal places.\n", + " \"\"\"\n", + "\n", + " q_peak = 484 * w_eff * area / t_peak\n", + "\n", + " return round(q_peak, 2)" + ] + }, + { + "cell_type": "markdown", + "id": "2113ca01-a56e-4fc1-a5ae-e66217aade4b", + "metadata": {}, + "source": [ + "### 3) Estimate runoff hydrograph\n", + "SCS method utilizes a dimensionless unit hydrograph to generate the runoff hydrograph. This unit hydrograph was developed with the analysis of a large number of hydrographs developed for watersheds over a range of sizes and locations. Let's first have a look at the SCS dimensionless unit hydrograph. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e69f09-b300-4410-9b29-75470f790cce", + "metadata": {}, + "outputs": [], + "source": [ + "# load scs unit hydrograph\n", + "import pandas as pd\n", + "\n", + "unit_hydrograph = pd.read_csv(\"./data/scs_unit_hydrograph.csv\")\n", + "print(unit_hydrograph)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a5e205e-bfd4-4490-a8e8-d39dc320e81d", + "metadata": {}, + "outputs": [], + "source": [ + "# plot SCS unit hydrograph\n", + "import matplotlib.pyplot as plt\n", + "\n", + "unit_hydrograph.plot(x=\"time_ratio\", y=\"discharge_ratio\")\n", + "plt.xlabel(\"Time Ratio (t/t_peak)\")\n", + "plt.ylabel(\"Discharge Ratio (q/q_peak)\")\n", + "plt.title(\"SCS Dimensionless Unit Hydrograph\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "48ff05ee-f69e-4586-a9e3-88f8757be0ad", + "metadata": {}, + "source": [ + "In the dimensionless unit hydrograph, it has a characteristic shape with a rising limb, peak, and recession limb. The SCS method estimates the time to peak and peak discharge values (discussed above) and use them to scale the time and discharge axes as indicated in the unit hydrograph. With this idea, we could define a function to generate the runoff hydrograph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a911b79-9caa-47b6-8788-c8064473db66", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate the runoff hydrograph\n", + "def calculate_runoff_hydrograph(q_peak, t_peak, unit_hydrograph):\n", + " \"\"\"\n", + " Calculate the runoff hydrograph using the SCS-CN method.\n", + "\n", + " Parameters\n", + " ----------\n", + " q_peak : float\n", + " Peak discharge of the hydrograph (units: cfs).\n", + " t_peak : float\n", + " Time to peak of the hydrograph (units: hr).\n", + " unit_hydrograph : pd.DataFrame\n", + " A DataFrame containing the SCS unit hydrograph data.\n", + "\n", + " Returns\n", + " -------\n", + " pd.DataFrame\n", + " DataFrame with columns 'time' and 'discharge' representing the computed runoff hydrograph.\n", + "\n", + " \"\"\"\n", + "\n", + " hydrograph = pd.DataFrame(columns=[\"time\", \"discharge\"])\n", + " hydrograph[\"time\"] = unit_hydrograph[\"time_ratio\"] * t_peak\n", + " hydrograph[\"discharge\"] = unit_hydrograph[\"discharge_ratio\"] * q_peak\n", + "\n", + " return hydrograph" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f16dc8e3-e76a-4a30-b9e3-e7216befd86d", + "metadata": {}, + "source": [ + "Using all the functions above, we could define a model function which will first do the model calculation and then show model results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b6070b2-bd4c-4bb5-8cd7-edf9f901e03d", + "metadata": {}, + "outputs": [], + "source": [ + "# create a model function\n", + "def scs_curve_number_model(cn, area, length, slope, t_rain, w, unit_hydrograph):\n", + " \"\"\"\n", + " Model the runoff hydrograph using the SCS-CN method.\n", + "\n", + " Parameters\n", + " ----------\n", + " cn : float\n", + " Curve number.\n", + " area : float\n", + " Area of the watershed (units: mi^2).\n", + " length : float\n", + " Mainstream length (units: km).\n", + " slope : float\n", + " Sine of main-channel slope angle (dimensionless).\n", + " t_rain : float\n", + " Duration of effective rainfall (units: hr).\n", + " w : float\n", + " Total rainfall (units: inch).\n", + " unit_hydrograph : pd.DataFrame\n", + " A DataFrame containing the SCS unit hydrograph data.\n", + "\n", + " Returns\n", + " -------\n", + " pd.DataFrame\n", + " DataFrame with columns 'time' and 'discharge' representing the computed runoff hydrograph.\n", + " \"\"\"\n", + "\n", + " # model calculation\n", + " w_eff = calculate_effective_rainfall(cn, w)\n", + " t_peak = calculate_time_to_peak(length, slope, t_rain)\n", + " q_peak = calculate_peak_discharge(area, w_eff, t_peak)\n", + " hydrograph = calculate_runoff_hydrograph(q_peak, t_peak, unit_hydrograph)\n", + "\n", + " # print results\n", + " print(\n", + " f\"SCS Model Results:\\n\"\n", + " f\"Effective rainfall: {w_eff} in\\n\"\n", + " f\"Time to peak: {t_peak} hr\\n\"\n", + " f\"Peak discharge: {q_peak} cfs\\n\"\n", + " )\n", + "\n", + " # plot hydrograph\n", + " hydrograph.plot(x=\"time\", y=\"discharge\")\n", + " plt.xlabel(\"Time (hr)\")\n", + " plt.ylabel(\"Discharge (cfs)\")\n", + " plt.title(\"Runoff Hydrograph\")\n", + "\n", + " return hydrograph" + ] + }, + { + "cell_type": "markdown", + "id": "22c05068-a842-4ab1-8224-6029c732c409", + "metadata": {}, + "source": [ + "## Model Application" + ] + }, + { + "cell_type": "markdown", + "id": "34f7e39f-98e5-468f-89df-5333f3cf886e", + "metadata": {}, + "source": [ + "### 1) Example\n", + "Let's use the SCS model to estimate the runoff hydrograph for a rain event of 4.2in in 3.4hr for antecedent wetness conditions II on a 1.24 $mi^2$ watershed with a mainstream length of 1.35 km, a main-channel slope of 0.08, and the following land-cover characteristics:\n", + "\n", + "| Land Cover Type| Fraction of total area | Curve number |\n", + "|----------|----------|----------|\n", + "| Forest (soil group B) | 0.58 | 58 |\n", + "| Forest (soil group C) | 0.12 | 72 |\n", + "| Meadow (soil group A) | 0.21 | 30 |\n", + "| Meadow (soil group B) | 0.09 | 58 |\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fba5b5bc-a345-4940-b9cb-4b6d92dc8e50", + "metadata": {}, + "outputs": [], + "source": [ + "# show model documentation\n", + "help(scs_curve_number_model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "491c509c-cb30-4f25-bbf2-8f49151808b3", + "metadata": {}, + "outputs": [], + "source": [ + "# calculate weighted-average curve number\n", + "cn = 0.58 * 58 + 0.12 * 72 + 0.21 * 30 + 0.09 * 58\n", + "\n", + "# define input variables\n", + "area = 1.24\n", + "length = 1.35\n", + "slope = 0.08\n", + "t_rain = 3.4\n", + "w = 4.2\n", + "\n", + "# run model for condition II\n", + "hydrograph_ii = scs_curve_number_model(\n", + " cn, area, length, slope, t_rain, w, unit_hydrograph\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5c497dcc-c0ad-48c0-a68a-a7a85f102e0e", + "metadata": {}, + "source": [ + "### 2) Hands on Exercise\n", + "\n", + "Now please use the model to estimate the runoff hydrograph with antecedent wetness conditions I and III. The table below shows their corresponding soil wetness condition and weighted curve number for the watershed. How does the soil wetness condition impact the runoff hydrograph?\n", + "| Condition| Soil Wetness | Curve number |\n", + "|----------|----------|----------|\n", + "| I | Dry but above wilting point | 35 |\n", + "| II | Average | 54 |\n", + "| III | Near saturation | 72 |" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c42d4639-8df0-498d-a6e0-82716e8605a0", + "metadata": {}, + "outputs": [], + "source": [ + "# run model for condition I" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e26d18f2-8f4d-4792-9e7c-ea4a37ce51e7", + "metadata": {}, + "outputs": [], + "source": [ + "# run model for condition III" + ] + }, + { + "cell_type": "markdown", + "id": "83136885-f10b-4540-a7ea-92db08ad15d6", + "metadata": {}, + "source": [ + "### 3) Bonus Exercise\n", + "\n", + "Please use the SCS model to explore how the watershed characteristics (e.g., watershed area, mainstream length and slope) and various land use types impact the runoff hydrograph. " + ] + }, + { + "cell_type": "markdown", + "id": "ca66a10c-cd2e-4495-8067-06ff59b662e9", + "metadata": {}, + "source": [ + "## References\n", + "- Dingman, S. L. (2002) Physical Hydrology. 2nd Edition, Waveland Press, Long Grove. Chapter 9.\n", + "- Sitterson, J., Chris Knightes, R. Parmar, K. Wolfe, M. Muche, and B. Avant. (2017) An Overview of Rainfall-Runoff Model Types. U.S. Environmental Protection Agency, Washington, DC, EPA/600/R-17/482, https://cfpub.epa.gov/si/si_public_file_download.cfm?p_download_id=533906&Lab=NERL" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Ivy", + "language": "python", + "name": "ivy" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}