{ "cells": [ { "cell_type": "markdown", "id": "2aa721bd", "metadata": { "papermill": { "duration": 0.002549, "end_time": "2025-06-18T09:40:14.966885", "exception": false, "start_time": "2025-06-18T09:40:14.964336", "status": "completed" }, "tags": [] }, "source": [ "# Future climate discharge workflow\n", "\n", "This example shows how to update **Wflow** model forcing with monthly change factors derived from differences between present and future climate model simulations to the effect of climate change on discharge. This example can be combined with the fluvial risk example to simulate flood risk for present and future climate scenarios. " ] }, { "cell_type": "code", "execution_count": 1, "id": "0328ce6a", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:14.971718Z", "iopub.status.busy": "2025-06-18T09:40:14.971535Z", "iopub.status.idle": "2025-06-18T09:40:20.007980Z", "shell.execute_reply": "2025-06-18T09:40:20.007476Z" }, "papermill": { "duration": 5.039741, "end_time": "2025-06-18T09:40:20.008785", "exception": false, "start_time": "2025-06-18T09:40:14.969044", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO - log - hydroflows version: 0.1.0\n" ] } ], "source": [ "# Import packages\n", "from pathlib import Path\n", "\n", "from hydroflows import Workflow \n", "from hydroflows.log import setuplog\n", "from hydroflows.methods import climate, raster, wflow\n", "from hydroflows.methods.utils.example_data import fetch_data\n", "from hydroflows.workflow.workflow_config import WorkflowConfig\n", "\n", "logger = setuplog(level=\"INFO\")" ] }, { "cell_type": "code", "execution_count": 2, "id": "d26710e5", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:20.014113Z", "iopub.status.busy": "2025-06-18T09:40:20.013774Z", "iopub.status.idle": "2025-06-18T09:40:20.016657Z", "shell.execute_reply": "2025-06-18T09:40:20.016260Z" }, "papermill": { "duration": 0.006171, "end_time": "2025-06-18T09:40:20.017378", "exception": false, "start_time": "2025-06-18T09:40:20.011207", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "# Set the parent directory\n", "pwd = Path().resolve()\n", "# Case directory\n", "name = \"climate_discharge\" # for now\n", "case_root=Path(pwd, \"cases\", name)" ] }, { "cell_type": "markdown", "id": "365edcee", "metadata": { "papermill": { "duration": 0.002052, "end_time": "2025-06-18T09:40:20.021539", "exception": false, "start_time": "2025-06-18T09:40:20.019487", "status": "completed" }, "tags": [] }, "source": [ "\n", "## Workflow inputs\n", "\n", "This example uses a predefined Wflow model and CMIP6 data. \n", "You can define the CMIP6 climate models to use as well as the climate scenarios/horizons." ] }, { "cell_type": "code", "execution_count": 3, "id": "61445b3f", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:20.026376Z", "iopub.status.busy": "2025-06-18T09:40:20.026051Z", "iopub.status.idle": "2025-06-18T09:40:23.716392Z", "shell.execute_reply": "2025-06-18T09:40:23.715996Z" }, "papermill": { "duration": 3.693629, "end_time": "2025-06-18T09:40:23.717221", "exception": false, "start_time": "2025-06-18T09:40:20.023592", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Downloading file 'cmip6-data.tar.gz' from 'doi:10.5281/zenodo.14967510/cmip6-data.tar.gz' to '/home/runner/.cache/hydroflows'.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Untarring contents of '/home/runner/.cache/hydroflows/cmip6-data.tar.gz' to '/home/runner/.cache/hydroflows/cmip6-data'\n" ] } ], "source": [ "# Fetch the climate build data\n", "cmip6_dir = fetch_data(data=\"cmip6-data\")" ] }, { "cell_type": "code", "execution_count": 4, "id": "7edffe22", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:23.723337Z", "iopub.status.busy": "2025-06-18T09:40:23.723173Z", "iopub.status.idle": "2025-06-18T09:40:23.726675Z", "shell.execute_reply": "2025-06-18T09:40:23.726263Z" }, "papermill": { "duration": 0.007767, "end_time": "2025-06-18T09:40:23.727436", "exception": false, "start_time": "2025-06-18T09:40:23.719669", "status": "completed" }, "tags": [] }, "outputs": [], "source": [ "# Setup the config file\n", "\n", "model_dir = \"models/wflow\" # wflow model directory (input)\n", "clim_dir = \"data/climatology\" # climatology data (intermediate results)\n", "change_dir = \"data/change_factor\" # change factor data (intermediate results) \n", "\n", "config = WorkflowConfig(\n", " region=Path(model_dir, \"staticgeoms\", \"region.geojson\"),\n", " catalog_path=Path(cmip6_dir, \"data_catalog.yml\"),\n", " cmip6_models=[\n", " \"NOAA-GFDL_GFDL-ESM4\",\n", " \"INM_INM-CM5-0\",\n", " \"CSIRO-ARCCSS_ACCESS-CM2\",\n", " ],\n", " cmip6_scenarios=[\"ssp245\", \"ssp585\"],\n", " historical=[[2000, 2010]],\n", " future_horizons=[[2050, 2060], [2090, 2100]],\n", " plot_fig=True,\n", " clim_dir = clim_dir,\n", " change_dir = change_dir,\n", ")" ] }, { "cell_type": "markdown", "id": "22a7bde4", "metadata": { "papermill": { "duration": 0.00214, "end_time": "2025-06-18T09:40:23.731747", "exception": false, "start_time": "2025-06-18T09:40:23.729607", "status": "completed" }, "tags": [] }, "source": [ "## Create the workflow\n", "\n", "Note that we initialize the workflow with a wildcards for the climate models and scenarios used to scale the wflow forcing." ] }, { "cell_type": "code", "execution_count": 5, "id": "2ee38606", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:23.736712Z", "iopub.status.busy": "2025-06-18T09:40:23.736562Z", "iopub.status.idle": "2025-06-18T09:40:23.740222Z", "shell.execute_reply": "2025-06-18T09:40:23.739840Z" }, "papermill": { "duration": 0.007048, "end_time": "2025-06-18T09:40:23.740978", "exception": false, "start_time": "2025-06-18T09:40:23.733930", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO - wildcards - Added wildcard 'clim_models' with values: ['NOAA-GFDL_GFDL-ESM4', 'INM_INM-CM5-0', 'CSIRO-ARCCSS_ACCESS-CM2']\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - wildcards - Added wildcard 'clim_scenarios' with values: ['ssp245', 'ssp585']\n" ] } ], "source": [ "# Create a workflow\n", "wf = Workflow(config=config, name=name, root=case_root)\n", "# Set wildcards\n", "wf.wildcards.set(\"clim_models\", config.cmip6_models)\n", "wf.wildcards.set(\"clim_scenarios\", config.cmip6_scenarios)" ] }, { "cell_type": "markdown", "id": "f3503a9a", "metadata": { "papermill": { "duration": 0.002176, "end_time": "2025-06-18T09:40:23.745410", "exception": false, "start_time": "2025-06-18T09:40:23.743234", "status": "completed" }, "tags": [] }, "source": [ "### Prepare a Wflow model and forcing\n", "\n", "Here we use an pre-developed model. You can replace this by your model or use the BuildWflow method to build your model as part of the workflow. " ] }, { "cell_type": "code", "execution_count": 6, "id": "4eb4b9b5", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:23.750437Z", "iopub.status.busy": "2025-06-18T09:40:23.750289Z", "iopub.status.idle": "2025-06-18T09:40:34.522159Z", "shell.execute_reply": "2025-06-18T09:40:34.521740Z" }, "papermill": { "duration": 10.775317, "end_time": "2025-06-18T09:40:34.522925", "exception": false, "start_time": "2025-06-18T09:40:23.747608", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Downloading file 'wflow-model.tar.gz' from 'doi:10.5281/zenodo.14967510/wflow-model.tar.gz' to '/home/runner/.cache/hydroflows'.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Untarring contents of '/home/runner/.cache/hydroflows/wflow-model.tar.gz' to '/home/runner/work/HydroFlows/HydroFlows/examples/cases/climate_discharge/models/wflow'\n" ] }, { "data": { "text/plain": [ "PosixPath('/home/runner/work/HydroFlows/HydroFlows/examples/cases/climate_discharge/models/wflow')" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Fetch a pre-build wflow-model\n", "fetch_data(\n", " data=\"wflow-model\",\n", " output_dir=Path(case_root, model_dir),\n", " sub_dir=False\n", ")" ] }, { "cell_type": "markdown", "id": "1a163521", "metadata": { "papermill": { "duration": 0.00244, "end_time": "2025-06-18T09:40:34.528029", "exception": false, "start_time": "2025-06-18T09:40:34.525589", "status": "completed" }, "tags": [] }, "source": [ "### Derive Climate statistics\n", "\n", "First, we derive monthly spatially distributed climatology for present (hist) and future climate conditions. Both are derived for multiple climate models and the future climatology is derived for multiple climate scenarios / horizons. \n", "Then change factors between the present and future climate conditions are calculated for each month of the climatology. \n", "The multi-model ensemble of change factors can be reduced to a single median change factor as shown here, or propagated to the Wflow simulations. " ] }, { "cell_type": "code", "execution_count": 7, "id": "098f0f19", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.533512Z", "iopub.status.busy": "2025-06-18T09:40:34.533358Z", "iopub.status.idle": "2025-06-18T09:40:34.541042Z", "shell.execute_reply": "2025-06-18T09:40:34.540640Z" }, "papermill": { "duration": 0.011372, "end_time": "2025-06-18T09:40:34.541751", "exception": false, "start_time": "2025-06-18T09:40:34.530379", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Rule(id=future_climatology, method=monthly_climatology, runs=6, repeat=['clim_models', 'clim_scenarios'])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Derive climate data statistics\n", "hist_climatology = climate.MonthlyClimatology(\n", " region=wf.get_ref(\"$config.region\"),\n", " catalog_path=wf.get_ref(\"$config.catalog_path\"),\n", " model=\"{clim_models}\",\n", " scenario=\"historical\",\n", " horizon=wf.get_ref(\"$config.historical\"),\n", " output_dir=wf.get_ref(\"$config.clim_dir\"),\n", ")\n", "wf.create_rule(hist_climatology, rule_id=\"hist_climatology\")\n", "\n", "future_climatology = climate.MonthlyClimatology(\n", " region=wf.get_ref(\"$config.region\"),\n", " catalog_path=wf.get_ref(\"$config.catalog_path\"),\n", " model=\"{clim_models}\",\n", " scenario=\"{clim_scenarios}\",\n", " horizon=wf.get_ref(\"$config.future_horizons\"),\n", " output_dir=wf.get_ref(\"$config.clim_dir\"),\n", ")\n", "wf.create_rule(future_climatology, rule_id=\"future_climatology\")" ] }, { "cell_type": "code", "execution_count": 8, "id": "584a527d", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.547502Z", "iopub.status.busy": "2025-06-18T09:40:34.547171Z", "iopub.status.idle": "2025-06-18T09:40:34.554239Z", "shell.execute_reply": "2025-06-18T09:40:34.553739Z" }, "papermill": { "duration": 0.010781, "end_time": "2025-06-18T09:40:34.555033", "exception": false, "start_time": "2025-06-18T09:40:34.544252", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO - wildcards - Added wildcard 'horizons' with values: ['2050-2060', '2090-2100']\n" ] }, { "data": { "text/plain": [ "Rule(id=change_factors, method=climate_change_factors, runs=6, repeat=['clim_models', 'clim_scenarios'], expand=['horizons'])" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Derive change factors from the statistics\n", "change_factors = climate.ClimateChangeFactors(\n", " hist_climatology=hist_climatology.output.climatology,\n", " future_climatology=future_climatology.output.climatology,\n", " model=\"{clim_models}\",\n", " scenario=\"{clim_scenarios}\",\n", " horizon=wf.get_ref(\"$config.future_horizons\"),\n", " wildcard=\"horizons\",\n", " output_dir=wf.get_ref(\"$config.change_dir\"),\n", ")\n", "wf.create_rule(change_factors, rule_id=\"change_factors\")" ] }, { "cell_type": "code", "execution_count": 9, "id": "ebe7ca99", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.560954Z", "iopub.status.busy": "2025-06-18T09:40:34.560632Z", "iopub.status.idle": "2025-06-18T09:40:34.565689Z", "shell.execute_reply": "2025-06-18T09:40:34.565273Z" }, "papermill": { "duration": 0.008787, "end_time": "2025-06-18T09:40:34.566395", "exception": false, "start_time": "2025-06-18T09:40:34.557608", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Rule(id=change_factors_median, method=merge_gridded_datasets, runs=4, repeat=['clim_scenarios', 'horizons'], reduce=['clim_models'])" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a model ensemble of the change factors\n", "change_factors_median = raster.MergeGriddedDatasets( \n", " datasets=change_factors.output.change_factors,\n", " reduce_dim=\"model\",\n", " quantile=0.5,\n", " output_name=\"change_{clim_scenarios}_{horizons}_q50.nc\",\n", " output_dir=wf.get_ref(\"$config.change_dir\"),\n", ")\n", "wf.create_rule(change_factors_median, rule_id=\"change_factors_median\")" ] }, { "cell_type": "markdown", "id": "48c56411", "metadata": { "papermill": { "duration": 0.002583, "end_time": "2025-06-18T09:40:34.571686", "exception": false, "start_time": "2025-06-18T09:40:34.569103", "status": "completed" }, "tags": [] }, "source": [ "### Update and run the Wflow model\n", "\n", "The ensemble median change factors are downscaled to the Wflow model grid and added to the Wflow settings file. To run the Wflow model with these change factors a tailored Wflow run script is required as this is not yet standard Wflow functionality." ] }, { "cell_type": "code", "execution_count": 10, "id": "d97a74e4", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.577590Z", "iopub.status.busy": "2025-06-18T09:40:34.577427Z", "iopub.status.idle": "2025-06-18T09:40:34.582121Z", "shell.execute_reply": "2025-06-18T09:40:34.581714Z" }, "papermill": { "duration": 0.008503, "end_time": "2025-06-18T09:40:34.582852", "exception": false, "start_time": "2025-06-18T09:40:34.574349", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Rule(id=wflow_change_factors, method=wflow_update_factors, runs=4, repeat=['clim_scenarios', 'horizons'])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Downscale the ensemble change factors to wflow model resolution\n", "wflow_change_factors = wflow.WflowUpdateChangeFactors(\n", " change_factor_dataset=change_factors_median.output.merged_dataset,\n", " wflow_toml=Path(model_dir, \"simulations\", \"default\", \"wflow_sbm.toml\"),\n", " output_dir=Path(model_dir, \"simulations\", \"{clim_scenarios}_{horizons}\"),\n", " copy_model=True\n", ")\n", "wf.create_rule(wflow_change_factors, rule_id=\"wflow_change_factors\")" ] }, { "cell_type": "code", "execution_count": 11, "id": "67cea457", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.589163Z", "iopub.status.busy": "2025-06-18T09:40:34.588973Z", "iopub.status.idle": "2025-06-18T09:40:34.593762Z", "shell.execute_reply": "2025-06-18T09:40:34.593389Z" }, "papermill": { "duration": 0.008777, "end_time": "2025-06-18T09:40:34.594497", "exception": false, "start_time": "2025-06-18T09:40:34.585720", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "text/plain": [ "Rule(id=wflow_run, method=wflow_run, runs=4, repeat=['clim_scenarios', 'horizons'])" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Run the wflow model\n", "wflow_run = wflow.WflowRun(\n", " wflow_toml=wflow_change_factors.output.wflow_out_toml,\n", " run_method=\"script\",\n", " wflow_run_script=\"run_wflow_change_factors.jl\",\n", ")\n", "wf.create_rule(wflow_run, rule_id=\"wflow_run\")" ] }, { "cell_type": "markdown", "id": "4f9284b6", "metadata": { "papermill": { "duration": 0.002817, "end_time": "2025-06-18T09:40:34.600247", "exception": false, "start_time": "2025-06-18T09:40:34.597430", "status": "completed" }, "tags": [] }, "source": [ "## Visualize and execute the workflow" ] }, { "cell_type": "code", "execution_count": 12, "id": "d2b3e5cb", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.606534Z", "iopub.status.busy": "2025-06-18T09:40:34.606379Z", "iopub.status.idle": "2025-06-18T09:40:34.638425Z", "shell.execute_reply": "2025-06-18T09:40:34.638019Z" }, "papermill": { "duration": 0.036091, "end_time": "2025-06-18T09:40:34.639162", "exception": false, "start_time": "2025-06-18T09:40:34.603071", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "climate_discharge\n", "\n", "\n", "\n", "hist_climatology\n", "\n", "hist_climatology\n", " runs=3\n", " repeat=['clim_models']\n", "\n", "\n", "\n", "change_factors\n", "\n", "change_factors\n", " runs=6\n", " repeat=['clim_models'\n", " 'clim_scenarios']\n", " expand=['horizons']\n", "\n", "\n", "\n", "hist_climatology->change_factors\n", "\n", "\n", "\n", "\n", "\n", "future_climatology\n", "\n", "future_climatology\n", " runs=6\n", " repeat=['clim_models'\n", " 'clim_scenarios']\n", "\n", "\n", "\n", "future_climatology->change_factors\n", "\n", "\n", "\n", "\n", "\n", "change_factors_median\n", "\n", "change_factors_median\n", " runs=4\n", " repeat=['clim_scenarios'\n", " 'horizons']\n", " reduce=['clim_models']\n", "\n", "\n", "\n", "change_factors->change_factors_median\n", "\n", "\n", "\n", "\n", "\n", "wflow_change_factors\n", "\n", "wflow_change_factors\n", " runs=4\n", " repeat=['clim_scenarios'\n", " 'horizons']\n", "\n", "\n", "\n", "change_factors_median->wflow_change_factors\n", "\n", "\n", "\n", "\n", "\n", "wflow_run\n", "\n", "wflow_run\n", " runs=4\n", " repeat=['clim_scenarios'\n", " 'horizons']\n", "\n", "\n", "\n", "wflow_change_factors->wflow_run\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# plot the rulegraph using graphviz\n", "wf.plot_rulegraph(filename=\"rulegraph.svg\", plot_rule_attrs=True)" ] }, { "cell_type": "code", "execution_count": 13, "id": "3fa9df17", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.646243Z", "iopub.status.busy": "2025-06-18T09:40:34.645933Z", "iopub.status.idle": "2025-06-18T09:40:34.652303Z", "shell.execute_reply": "2025-06-18T09:40:34.651844Z" }, "papermill": { "duration": 0.010571, "end_time": "2025-06-18T09:40:34.653016", "exception": false, "start_time": "2025-06-18T09:40:34.642445", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 1/6: hist_climatology (3 runs)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 2/6: future_climatology (6 runs)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 3/6: change_factors (6 runs)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 4/6: change_factors_median (4 runs)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 5/6: wflow_change_factors (4 runs)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "INFO - workflow - Dryrun rule 6/6: wflow_run (4 runs)\n" ] } ], "source": [ "# Test the workflow\n", "wf.dryrun()" ] }, { "cell_type": "code", "execution_count": 14, "id": "fb15a429", "metadata": { "execution": { "iopub.execute_input": "2025-06-18T09:40:34.660161Z", "iopub.status.busy": "2025-06-18T09:40:34.659851Z", "iopub.status.idle": "2025-06-18T09:40:34.674983Z", "shell.execute_reply": "2025-06-18T09:40:34.674479Z" }, "papermill": { "duration": 0.019519, "end_time": "2025-06-18T09:40:34.675769", "exception": false, "start_time": "2025-06-18T09:40:34.656250", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cases/climate_discharge:\n", "- Snakefile\n", "- models\n", "- rulegraph.svg\n", "- Snakefile.config.yml\n" ] } ], "source": [ "# Write the workflow to a Snakefile and snakefile.config.yml\n", "wf.to_snakemake()\n", "\n", "# show the files in the case directory\n", "print(f\"{wf.root.relative_to(pwd)}:\")\n", "for f in wf.root.iterdir():\n", " print(f\"- {f.name}\")" ] } ], "metadata": { "kernelspec": { "display_name": "full", "language": "python", "name": "python3" }, "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.12" }, "papermill": { "default_parameters": {}, "duration": 21.857432, "end_time": "2025-06-18T09:40:35.294534", "environment_variables": {}, "exception": null, "input_path": "/home/runner/work/HydroFlows/HydroFlows/docs/../examples/climate_discharge.ipynb", "output_path": "/home/runner/work/HydroFlows/HydroFlows/docs/_examples/climate_discharge.ipynb", "parameters": {}, "start_time": "2025-06-18T09:40:13.437102", "version": "2.6.0" } }, "nbformat": 4, "nbformat_minor": 5 }