Future climate discharge workflow#

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.

[1]:
# Import packages
from pathlib import Path

from hydroflows import Workflow
from hydroflows.log import setuplog
from hydroflows.methods import climate, raster, wflow
from hydroflows.methods.utils.example_data import fetch_data
from hydroflows.workflow.workflow_config import WorkflowConfig

logger = setuplog(level="INFO")
INFO - log - hydroflows version: 0.1.0
[2]:
# Set the parent directory
pwd = Path().resolve()
# Case directory
name = "climate_discharge"  # for now
case_root=Path(pwd, "cases", name)

Workflow inputs#

This example uses a predefined Wflow model and CMIP6 data. You can define the CMIP6 climate models to use as well as the climate scenarios/horizons.

[3]:
# Fetch the climate build data
cmip6_dir = fetch_data(data="cmip6-data")
Downloading file 'cmip6-data.tar.gz' from 'doi:10.5281/zenodo.14967510/cmip6-data.tar.gz' to '/home/runner/.cache/hydroflows'.
Untarring contents of '/home/runner/.cache/hydroflows/cmip6-data.tar.gz' to '/home/runner/.cache/hydroflows/cmip6-data'
[4]:
# Setup the config file

model_dir = "models/wflow"          # wflow model directory (input)
clim_dir = "data/climatology"       # climatology data (intermediate results)
change_dir = "data/change_factor"   # change factor data (intermediate results)

config = WorkflowConfig(
    region=Path(model_dir, "staticgeoms", "region.geojson"),
    catalog_path=Path(cmip6_dir, "data_catalog.yml"),
    cmip6_models=[
        "NOAA-GFDL_GFDL-ESM4",
        "INM_INM-CM5-0",
        "CSIRO-ARCCSS_ACCESS-CM2",
    ],
    cmip6_scenarios=["ssp245", "ssp585"],
    historical=[[2000, 2010]],
    future_horizons=[[2050, 2060], [2090, 2100]],
    plot_fig=True,
    clim_dir = clim_dir,
    change_dir = change_dir,
)

Create the workflow#

Note that we initialize the workflow with a wildcards for the climate models and scenarios used to scale the wflow forcing.

[5]:
# Create a workflow
wf = Workflow(config=config, name=name, root=case_root)
# Set wildcards
wf.wildcards.set("clim_models", config.cmip6_models)
wf.wildcards.set("clim_scenarios", config.cmip6_scenarios)
INFO - wildcards - Added wildcard 'clim_models' with values: ['NOAA-GFDL_GFDL-ESM4', 'INM_INM-CM5-0', 'CSIRO-ARCCSS_ACCESS-CM2']
INFO - wildcards - Added wildcard 'clim_scenarios' with values: ['ssp245', 'ssp585']

Prepare a Wflow model and forcing#

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.

[6]:
# Fetch a pre-build wflow-model
fetch_data(
    data="wflow-model",
    output_dir=Path(case_root, model_dir),
    sub_dir=False
)
Downloading file 'wflow-model.tar.gz' from 'doi:10.5281/zenodo.14967510/wflow-model.tar.gz' to '/home/runner/.cache/hydroflows'.
Untarring contents of '/home/runner/.cache/hydroflows/wflow-model.tar.gz' to '/home/runner/work/HydroFlows/HydroFlows/examples/cases/climate_discharge/models/wflow'
[6]:
PosixPath('/home/runner/work/HydroFlows/HydroFlows/examples/cases/climate_discharge/models/wflow')

Derive Climate statistics#

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. Then change factors between the present and future climate conditions are calculated for each month of the climatology. 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.

[7]:
# Derive climate data statistics
hist_climatology = climate.MonthlyClimatology(
    region=wf.get_ref("$config.region"),
    catalog_path=wf.get_ref("$config.catalog_path"),
    model="{clim_models}",
    scenario="historical",
    horizon=wf.get_ref("$config.historical"),
    output_dir=wf.get_ref("$config.clim_dir"),
)
wf.create_rule(hist_climatology, rule_id="hist_climatology")

future_climatology = climate.MonthlyClimatology(
    region=wf.get_ref("$config.region"),
    catalog_path=wf.get_ref("$config.catalog_path"),
    model="{clim_models}",
    scenario="{clim_scenarios}",
    horizon=wf.get_ref("$config.future_horizons"),
    output_dir=wf.get_ref("$config.clim_dir"),
)
wf.create_rule(future_climatology, rule_id="future_climatology")
[7]:
Rule(id=future_climatology, method=monthly_climatology, runs=6, repeat=['clim_models', 'clim_scenarios'])
[8]:
# Derive change factors from the statistics
change_factors = climate.ClimateChangeFactors(
    hist_climatology=hist_climatology.output.climatology,
    future_climatology=future_climatology.output.climatology,
    model="{clim_models}",
    scenario="{clim_scenarios}",
    horizon=wf.get_ref("$config.future_horizons"),
    wildcard="horizons",
    output_dir=wf.get_ref("$config.change_dir"),
)
wf.create_rule(change_factors, rule_id="change_factors")
INFO - wildcards - Added wildcard 'horizons' with values: ['2050-2060', '2090-2100']
[8]:
Rule(id=change_factors, method=climate_change_factors, runs=6, repeat=['clim_models', 'clim_scenarios'], expand=['horizons'])
[9]:
# Create a model ensemble of the change factors
change_factors_median = raster.MergeGriddedDatasets(
    datasets=change_factors.output.change_factors,
    reduce_dim="model",
    quantile=0.5,
    output_name="change_{clim_scenarios}_{horizons}_q50.nc",
    output_dir=wf.get_ref("$config.change_dir"),
)
wf.create_rule(change_factors_median, rule_id="change_factors_median")
[9]:
Rule(id=change_factors_median, method=merge_gridded_datasets, runs=4, repeat=['clim_scenarios', 'horizons'], reduce=['clim_models'])

Update and run the Wflow model#

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.

[10]:
# Downscale the ensemble change factors to wflow model resolution
wflow_change_factors = wflow.WflowUpdateChangeFactors(
    change_factor_dataset=change_factors_median.output.merged_dataset,
    wflow_toml=Path(model_dir, "simulations", "default", "wflow_sbm.toml"),
    output_dir=Path(model_dir, "simulations", "{clim_scenarios}_{horizons}"),
    copy_model=True
)
wf.create_rule(wflow_change_factors, rule_id="wflow_change_factors")
[10]:
Rule(id=wflow_change_factors, method=wflow_update_factors, runs=4, repeat=['clim_scenarios', 'horizons'])
[11]:
# Run the wflow model
wflow_run = wflow.WflowRun(
    wflow_toml=wflow_change_factors.output.wflow_out_toml,
    run_method="script",
    wflow_run_script="run_wflow_change_factors.jl",
)
wf.create_rule(wflow_run, rule_id="wflow_run")
[11]:
Rule(id=wflow_run, method=wflow_run, runs=4, repeat=['clim_scenarios', 'horizons'])

Visualize and execute the workflow#

[12]:
# plot the rulegraph using graphviz
wf.plot_rulegraph(filename="rulegraph.svg", plot_rule_attrs=True)
[12]:
../_images/_examples_climate_discharge_18_0.svg
[13]:
# Test the workflow
wf.dryrun()
INFO - workflow - Dryrun rule 1/6: hist_climatology (3 runs)
INFO - workflow - Dryrun rule 2/6: future_climatology (6 runs)
INFO - workflow - Dryrun rule 3/6: change_factors (6 runs)
INFO - workflow - Dryrun rule 4/6: change_factors_median (4 runs)
INFO - workflow - Dryrun rule 5/6: wflow_change_factors (4 runs)
INFO - workflow - Dryrun rule 6/6: wflow_run (4 runs)
[14]:
# Write the workflow to a Snakefile and snakefile.config.yml
wf.to_snakemake()

# show the files in the case directory
print(f"{wf.root.relative_to(pwd)}:")
for f in wf.root.iterdir():
    print(f"- {f.name}")
cases/climate_discharge:
- Snakefile
- models
- rulegraph.svg
- Snakefile.config.yml