Group project¶
Instructions¶
Objectives
In this final project, you should pick two glacierized basins and analyse the projected future of glaciers for different climate scenarios, and compare them to each other. For this you should use tools and knowledge you gained during the practical sessions in the last weeks.
Deadline
Please submit your project via OLAT before Monday June 17 at 00H (in the night from Monday to Tuesday).
Formal requirements
You will work in groups of two. If we are an odd number of students, one group can have three participants. (Tip: I recommend that students who have not followed a programming class to team up with students who have).
Each group will submit one (executed) jupyter notebook containing the code, plots, and answers to the questions (text in the markdown format) on OLAT. Please also submit an HTML version of the notebook. Please ensure that your HTML file is smaller than 10 MB. This helps us provide you with more detailed and readable feedback.
Each group member must contribute to the notebook. The notebook should be self-contained and the answers must be well structured. The plots must be as understandable as possible (title, units, x and y axis labels, appropriate colors…).
Please be concise in your answer. We expect a few sentences per answer at most - there is no need to write a new text book in this project! Use links and references to the literature or your class slides where appropriate.
Grading
We will give one grade per project, according to the following table (total 10 points):
- correctness of the code and the plots: content, legends, colors, units, etc. (3 points)
- quality of the answers: correctness, preciseness, appropriate use of links and references to literature or external resources (5 points)
- contextualise your findings with literature (2 points)
# Imports
import os
import urllib.request
import xarray as xr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt # added by Jakob
import seaborn as sns
from scipy.stats import scoreatpercentile
International Network for Alpine Research Catchment Hydrology INARCH¶
We aim, with this group project, to contribute to the INARCH initiative. The goal of INARCH is to better understand hydrological processes in alpine cold regions, improve their prediction, diagnose their sensitivity to global change, and develop consistent measurement strategies. You can find more information on the INARCH website.
In this project, our focus will be on glacier projections from OGGM in glacierized basins of INARCH. You are expected to analyze how glaciers are evolving and how their contribution to total runoff is changing. The final outcome will be a dataset of OGGM projections for all CMIP5 and CMIP6 scenarios (which I have prepared), along with a first analysis of this dataset, this is the core of your work in the group project.
For your individual group analysis, you should compare two basins. As a whole class, we aim to ensure that each basin is covered by at least one group. The selection of basins is up to each group and should be done using the link to the spreadsheet shared in the presentation (first come, first served).
In your analysis, make sure to incorporate the background knowledge about how OGGM is working and how the data was generated, as we discussed during the practical sessions.
Selecting Temperature Scenarios for Analysis¶
To analyze how glaciers are evolving, we will group the projections by temperature warming levels, as done in a previous session. For this, you will again need the table containing the actual warming levels for each climate model realization. You can download it here and save it at the same location as this notebook.
Below is the code used to select the model realizations based on temperature targets. Our analysis focuses on three temperature goals: 1.5°C, 2.7°C, and 4.0°C, each with a tolerance of ±0.2°C.
# reading the csv file
df_warming_levels = pd.read_csv('cmip5_and_cmip6_warming_compared_to_preindustrial.csv', index_col=0)
# the function takes a target temperature and a range, e.g. 2.7+/-0.2°C
def get_models_from_temp(temp, temp_range):
pi_l = temp - temp_range # our lower temperature limit
pi_u = temp + temp_range # our higher temperature limit
# select only those which are inside of our temperature limit
pd_cmip_sel = df_warming_levels.loc[
# select all which have a larger temperature as our lower limit AND
(df_warming_levels['global_temp_ch_2071-2100_preindustrial']>=pi_l) &
# those having a smaller temperature as our higher limit
(df_warming_levels['global_temp_ch_2071-2100_preindustrial']<=pi_u)
]
return pd_cmip_sel
# define the models for each temperature goal in a dictionary
temp_scenarios = {
'4°C': get_models_from_temp(4, 0.2),
'2.7°C': get_models_from_temp(2.7, 0.2),
'1.5°C': get_models_from_temp(1.5, 0.2),
}
print(temp_scenarios)
{'4°C': cmip gcm rcp ssp \
CCSM4_RCP85 CMIP5 CCSM4 rcp85 NaN
MPI-ESM-LR_RCP85 CMIP5 MPI-ESM-LR rcp85 NaN
CNRM-CM5_RCP85 CMIP5 CNRM-CM5 rcp85 NaN
NORESM2-MM_SSP585 CMIP6 NorESM2-MM NaN ssp585
BCC-CSM2-MR_SSP585 CMIP6 BCC-CSM2-MR NaN ssp585
FGOALS-F3-L_SSP585 CMIP6 FGOALS-f3-L NaN ssp585
CESM2-WACCM_SSP370 CMIP6 CESM2-WACCM NaN ssp370
CESM2_SSP370 CMIP6 CESM2 NaN ssp370
EC-EARTH3-VEG_SSP370 CMIP6 EC-Earth3-Veg NaN ssp370
global_temp_ch_2071-2100_preindustrial
CCSM4_RCP85 4.101378
MPI-ESM-LR_RCP85 4.058044
CNRM-CM5_RCP85 3.960476
NORESM2-MM_SSP585 3.873557
BCC-CSM2-MR_SSP585 4.035788
FGOALS-F3-L_SSP585 4.197179
CESM2-WACCM_SSP370 4.081607
CESM2_SSP370 4.125652
EC-EARTH3-VEG_SSP370 4.143629 , '2.7°C': cmip gcm rcp ssp \
CNRM-CM5_RCP45 CMIP5 CNRM-CM5 rcp45 NaN
CSIRO-MK3-6-0_RCP45 CMIP5 CSIRO-Mk3-6-0 rcp45 NaN
IPSL-CM5A-LR_RCP45 CMIP5 IPSL-CM5A-LR rcp45 NaN
CCSM4_RCP60 CMIP5 CCSM4 rcp60 NaN
GFDL-CM3_RCP26 CMIP5 GFDL-CM3 rcp26 NaN
ACCESS-CM2_SSP126 CMIP6 ACCESS-CM2 NaN ssp126
CANESM5_SSP126 CMIP6 CanESM5 NaN ssp126
CAMS-CSM1-0_SSP370 CMIP6 CAMS-CSM1-0 NaN ssp370
CESM2-WACCM_SSP534-OVER CMIP6 CESM2-WACCM NaN ssp534-over
IPSL-CM6A-LR_SSP534-OVER CMIP6 IPSL-CM6A-LR NaN ssp534-over
MRI-ESM2-0_SSP245 CMIP6 MRI-ESM2-0 NaN ssp245
BCC-CSM2-MR_SSP245 CMIP6 BCC-CSM2-MR NaN ssp245
FGOALS-F3-L_SSP245 CMIP6 FGOALS-f3-L NaN ssp245
MRI-ESM2-0_SSP434 CMIP6 MRI-ESM2-0 NaN ssp434
global_temp_ch_2071-2100_preindustrial
CNRM-CM5_RCP45 2.544987
CSIRO-MK3-6-0_RCP45 2.870632
IPSL-CM5A-LR_RCP45 2.783992
CCSM4_RCP60 2.706795
GFDL-CM3_RCP26 2.626000
ACCESS-CM2_SSP126 2.781735
CANESM5_SSP126 2.646456
CAMS-CSM1-0_SSP370 2.731656
CESM2-WACCM_SSP534-OVER 2.853885
IPSL-CM6A-LR_SSP534-OVER 2.792142
MRI-ESM2-0_SSP245 2.844054
BCC-CSM2-MR_SSP245 2.641109
FGOALS-F3-L_SSP245 2.568657
MRI-ESM2-0_SSP434 2.609334 , '1.5°C': cmip gcm rcp ssp \
GFDL-ESM2G_RCP45 CMIP5 GFDL-ESM2G rcp45 NaN
NORESM1-M_RCP26 CMIP5 NorESM1-M rcp26 NaN
MPI-ESM-LR_RCP26 CMIP5 MPI-ESM-LR rcp26 NaN
CCSM4_RCP26 CMIP5 CCSM4 rcp26 NaN
MPI-ESM1-2-HR_SSP126 CMIP6 MPI-ESM1-2-HR NaN ssp126
GFDL-ESM4_SSP126 CMIP6 GFDL-ESM4 NaN ssp126
INM-CM4-8_SSP126 CMIP6 INM-CM4-8 NaN ssp126
INM-CM5-0_SSP126 CMIP6 INM-CM5-0 NaN ssp126
CAMS-CSM1-0_SSP126 CMIP6 CAMS-CSM1-0 NaN ssp126
GFDL-ESM4_SSP119 CMIP6 GFDL-ESM4 NaN ssp119
MRI-ESM2-0_SSP119 CMIP6 MRI-ESM2-0 NaN ssp119
global_temp_ch_2071-2100_preindustrial
GFDL-ESM2G_RCP45 1.666789
NORESM1-M_RCP26 1.545585
MPI-ESM-LR_RCP26 1.487504
CCSM4_RCP26 1.579428
MPI-ESM1-2-HR_SSP126 1.669876
GFDL-ESM4_SSP126 1.692323
INM-CM4-8_SSP126 1.534266
INM-CM5-0_SSP126 1.589314
CAMS-CSM1-0_SSP126 1.510336
GFDL-ESM4_SSP119 1.307174
MRI-ESM2-0_SSP119 1.619736 }
df_warming_levels.gcm.unique() # print the unique RCPs in the dataframe
array(['CCSM4', 'MPI-ESM-LR', 'CNRM-CM5', 'GFDL-ESM2G', 'CSIRO-Mk3-6-0',
'IPSL-CM5A-LR', 'GFDL-CM3', 'NorESM1-M', 'GISS-E2-R', 'CanESM2',
'CESM1-CAM5', 'CESM2-WACCM', 'MPI-ESM1-2-HR', 'GFDL-ESM4',
'NorESM2-MM', 'ACCESS-ESM1-5', 'ACCESS-CM2', 'INM-CM4-8',
'CanESM5', 'IPSL-CM6A-LR', 'INM-CM5-0', 'MRI-ESM2-0', 'CESM2',
'EC-Earth3', 'EC-Earth3-Veg', 'CAMS-CSM1-0', 'BCC-CSM2-MR',
'FGOALS-f3-L', 'CMCC-CM2-SR5', 'TaiESM1'], dtype=object)
# Evaluate and plot the selection of climate model realizations for each temperature target
stats = []
for temp, df in temp_scenarios.items():
temps = df['global_temp_ch_2071-2100_preindustrial']
mean_val = temps.mean()
median_val = temps.median()
std_val = temps.std()
count_val = temps.count()
stats.append({'Scenario': temp, 'Mean': mean_val, 'Median': median_val, 'Std': std_val, 'Count': count_val})
print(f"{temp}:")
print(f" Number of realizations: {count_val}")
print(f" Mean warming: {mean_val:.2f}°C")
print(f" Median warming: {median_val:.2f}°C")
4°C: Number of realizations: 9 Mean warming: 4.06°C Median warming: 4.08°C 2.7°C: Number of realizations: 14 Mean warming: 2.71°C Median warming: 2.72°C 1.5°C: Number of realizations: 11 Mean warming: 1.56°C Median warming: 1.58°C
fig, axes = plt.subplots(2, 4, figsize=(24, 12), sharey=True)
df_warming_levels.iloc[0:15].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[0, 0])
df_warming_levels.iloc[15:30].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[0, 1])
df_warming_levels.iloc[30:45].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[0, 2])
df_warming_levels.iloc[45:60].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[0, 3])
df_warming_levels.iloc[60:75].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[1, 0])
df_warming_levels.iloc[75:90].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[1, 1])
df_warming_levels.iloc[90:105].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[1, 2])
df_warming_levels.iloc[105:].plot(x='gcm', y='global_temp_ch_2071-2100_preindustrial', kind='bar', ax=axes[1, 3])
for ax in axes.flatten():
ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right')
ax.set_xlabel('GCM')
ax.set_ylabel('Warming (°C)')
ax.grid(axis='y')
axes[0, 0].set_title('Projected Warming by GCM (Part 1)')
axes[0, 1].set_title('Projected Warming by GCM (Part 2)')
axes[0, 2].set_title('Projected Warming by GCM (Part 3)')
axes[1, 0].set_title('Projected Warming by GCM (Part 4)')
plt.tight_layout()
plt.show()
Getting the Data for Your Basins¶
To begin your analysis, you need to load the glacier and runoff projection data for your assigned basins. Make sure you know which basins your group is working on (as selected in the shared spreadsheet). The data for each basin is stored in individual files, which you can load using the provided code template. You can choose where to store the data locally by setting the variable local_data_dir. By default, this will create a new folder called glacier_projection_data in the same location as your notebook.
# add here your the basin_id of your selected basins (e.g. ['basin_1', 'basin_2'])
basin_ids = ['rofental', 'langtang' ]
# you can select here a location on your computer to store the glacier data
local_data_dir = 'glacier_projection_data'
# create the directory, if it does not exist
os.makedirs(local_data_dir, exist_ok=True)
# the url where all the data is stored
base_url = 'https://cluster.klima.uni-bremen.de/~pschmitt/teaching/cryo_in_climate/INARCH/data/'
# in this structure we will save the opened data
ds_all = {}
# Code for downloading the data, if data already downloaded this will be skipped
for basin in basin_ids:
# create a directory for each basin
basin_url = os.path.join(base_url, basin, '2100')
local_basin_dir = os.path.join(local_data_dir, basin)
os.makedirs(local_basin_dir, exist_ok=True)
ds_all[basin] = {}
for temp_level in temp_scenarios:
ds_tmp_all = []
for i, realization in temp_scenarios[temp_level].iterrows():
# depending on the CMIP, different names for scenarios
scenario_column = 'ssp' if realization['cmip'] == 'CMIP6' else 'rcp'
filename = f"basin_{basin}_run_hydro_w5e5_gcm_merged_bc_2000_2019_{realization['gcm']}_{realization[scenario_column]}.nc"
# only download if file not already downloaded
if os.path.isfile(os.path.join(local_basin_dir, filename)):
print(f"File already downloaded: {filename}")
else:
print(f"Downloading {filename}")
urllib.request.urlretrieve(
os.path.join(basin_url, filename),
os.path.join(local_basin_dir, filename))
# open individual dataset and combine gcma and scenario in new variable
with xr.open_dataset(os.path.join(local_basin_dir, filename)) as ds:
ds_stacked = ds.stack(gcm_scenario=("gcm", "scenario"))
ds_tmp_all.append(ds_stacked)
print(f'{basin}: combining data for {temp_level}')
ds_all[basin][temp_level] = xr.combine_by_coords(ds_tmp_all, fill_value=np.nan)
File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp85.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM-LR_rcp85.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CNRM-CM5_rcp85.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_NorESM2-MM_ssp585.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_BCC-CSM2-MR_ssp585.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_FGOALS-f3-L_ssp585.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2-WACCM_ssp370.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2_ssp370.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_EC-Earth3-Veg_ssp370.nc rofental: combining data for 4°C File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CNRM-CM5_rcp45.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CSIRO-Mk3-6-0_rcp45.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_IPSL-CM5A-LR_rcp45.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp60.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-CM3_rcp26.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_ACCESS-CM2_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CanESM5_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CAMS-CSM1-0_ssp370.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2-WACCM_ssp534-over.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_IPSL-CM6A-LR_ssp534-over.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp245.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_BCC-CSM2-MR_ssp245.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_FGOALS-f3-L_ssp245.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp434.nc rofental: combining data for 2.7°C File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM2G_rcp45.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_NorESM1-M_rcp26.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM-LR_rcp26.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp26.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM1-2-HR_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM4_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_INM-CM4-8_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_INM-CM5-0_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_CAMS-CSM1-0_ssp126.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM4_ssp119.nc File already downloaded: basin_rofental_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp119.nc rofental: combining data for 1.5°C File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp85.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM-LR_rcp85.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CNRM-CM5_rcp85.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_NorESM2-MM_ssp585.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_BCC-CSM2-MR_ssp585.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_FGOALS-f3-L_ssp585.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2-WACCM_ssp370.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2_ssp370.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_EC-Earth3-Veg_ssp370.nc langtang: combining data for 4°C File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CNRM-CM5_rcp45.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CSIRO-Mk3-6-0_rcp45.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_IPSL-CM5A-LR_rcp45.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp60.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-CM3_rcp26.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_ACCESS-CM2_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CanESM5_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CAMS-CSM1-0_ssp370.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CESM2-WACCM_ssp534-over.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_IPSL-CM6A-LR_ssp534-over.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp245.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_BCC-CSM2-MR_ssp245.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_FGOALS-f3-L_ssp245.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp434.nc langtang: combining data for 2.7°C File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM2G_rcp45.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_NorESM1-M_rcp26.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM-LR_rcp26.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CCSM4_rcp26.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MPI-ESM1-2-HR_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM4_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_INM-CM4-8_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_INM-CM5-0_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_CAMS-CSM1-0_ssp126.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_GFDL-ESM4_ssp119.nc File already downloaded: basin_langtang_run_hydro_w5e5_gcm_merged_bc_2000_2019_MRI-ESM2-0_ssp119.nc langtang: combining data for 1.5°C
After downloading and processing the data, everything will be stored in the variable ds_all. You can access the data for a specific basin and temperature level using the syntax ds_all[basin_name][temperature_level]. Below you can find one example:
print(ds_all)
{'rofental': {'4°C': <xarray.Dataset> Size: 6MB
Dimensions: (time: 101, rgi_id: 23, gcm_scenario: 9,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 1kB 'RGI60-11.00719' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 72B MultiIndex
* gcm (gcm_scenario) <U13 468B 'BCC-CSM2-MR' ... ...
* scenario (gcm_scenario) <U6 216B 'ssp585' ... 'ssp585'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 84kB 4...
area (time, rgi_id, gcm_scenario) float32 84kB 8...
on_area (time, rgi_id, gcm_scenario) float32 84kB 8...
off_area (time, rgi_id, gcm_scenario) float32 84kB 3...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 84kB 3...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 84kB 1...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
runoff (time, rgi_id, gcm_scenario) float32 84kB 2...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Rofental
basin_area_km2: 98.64030351364737
glaciers_in_basin: 23
glacierised_area_perc: 35.36384090218637
glacierised_area_km2: 34.883, '2.7°C': <xarray.Dataset> Size: 9MB
Dimensions: (time: 101, rgi_id: 23, gcm_scenario: 14,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 1kB 'RGI60-11.00719' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 112B MultiIndex
* gcm (gcm_scenario) <U13 728B 'ACCESS-CM2' ... '...
* scenario (gcm_scenario) <U11 616B 'ssp126' ... 'ssp434'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 130kB ...
area (time, rgi_id, gcm_scenario) float32 130kB ...
on_area (time, rgi_id, gcm_scenario) float32 130kB ...
off_area (time, rgi_id, gcm_scenario) float32 130kB ...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 130kB ...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 130kB ...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 2MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 2MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 2MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 2MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 2MB ...
runoff (time, rgi_id, gcm_scenario) float32 130kB ...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Rofental
basin_area_km2: 98.64030351364737
glaciers_in_basin: 23
glacierised_area_perc: 35.36384090218637
glacierised_area_km2: 34.883, '1.5°C': <xarray.Dataset> Size: 7MB
Dimensions: (time: 101, rgi_id: 23, gcm_scenario: 11,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 1kB 'RGI60-11.00719' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 88B MultiIndex
* gcm (gcm_scenario) <U13 572B 'CAMS-CSM1-0' ... ...
* scenario (gcm_scenario) <U6 264B 'ssp126' ... 'rcp26'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 102kB ...
area (time, rgi_id, gcm_scenario) float32 102kB ...
on_area (time, rgi_id, gcm_scenario) float32 102kB ...
off_area (time, rgi_id, gcm_scenario) float32 102kB ...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 102kB ...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 102kB ...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 1MB ...
runoff (time, rgi_id, gcm_scenario) float32 102kB ...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Rofental
basin_area_km2: 98.64030351364737
glaciers_in_basin: 23
glacierised_area_perc: 35.36384090218637
glacierised_area_km2: 34.883}, 'langtang': {'4°C': <xarray.Dataset> Size: 26MB
Dimensions: (time: 101, rgi_id: 102, gcm_scenario: 9,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 6kB 'RGI60-15.03954' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 72B MultiIndex
* gcm (gcm_scenario) <U13 468B 'BCC-CSM2-MR' ... ...
* scenario (gcm_scenario) <U6 216B 'ssp585' ... 'ssp585'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 371kB ...
area (time, rgi_id, gcm_scenario) float32 371kB ...
on_area (time, rgi_id, gcm_scenario) float32 371kB ...
off_area (time, rgi_id, gcm_scenario) float32 371kB ...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 371kB ...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 371kB ...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 4MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 4MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 4MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 4MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 4MB ...
runoff (time, rgi_id, gcm_scenario) float32 371kB ...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Langtang
basin_area_km2: 583.8048947612463
glaciers_in_basin: 102
glacierised_area_perc: 21.129661828280465
glacierised_area_km2: 123.356, '2.7°C': <xarray.Dataset> Size: 40MB
Dimensions: (time: 101, rgi_id: 102, gcm_scenario: 14,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 6kB 'RGI60-15.03954' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 112B MultiIndex
* gcm (gcm_scenario) <U13 728B 'ACCESS-CM2' ... '...
* scenario (gcm_scenario) <U11 616B 'ssp126' ... 'ssp434'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 577kB ...
area (time, rgi_id, gcm_scenario) float32 577kB ...
on_area (time, rgi_id, gcm_scenario) float32 577kB ...
off_area (time, rgi_id, gcm_scenario) float32 577kB ...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 577kB ...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 577kB ...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 7MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 7MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 7MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 7MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 7MB ...
runoff (time, rgi_id, gcm_scenario) float32 577kB ...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Langtang
basin_area_km2: 583.8048947612463
glaciers_in_basin: 102
glacierised_area_perc: 21.129661828280465
glacierised_area_km2: 123.356, '1.5°C': <xarray.Dataset> Size: 31MB
Dimensions: (time: 101, rgi_id: 102, gcm_scenario: 11,
month_2d: 12)
Coordinates:
* time (time) float64 808B 2e+03 ... 2.1e+03
* rgi_id (rgi_id) <U14 6kB 'RGI60-15.03954' ... 'RGI...
hydro_year (time) int64 808B 2000 2001 2002 ... 2099 2100
hydro_month (time) int64 808B 4 4 4 4 4 4 ... 4 4 4 4 4 4
calendar_year (time) int64 808B 2000 2001 2002 ... 2099 2100
calendar_month (time) int64 808B 1 1 1 1 1 1 ... 1 1 1 1 1 1
* month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
calendar_month_2d (month_2d) int64 96B 1 2 3 4 5 ... 9 10 11 12
* gcm_scenario (gcm_scenario) object 88B MultiIndex
* gcm (gcm_scenario) <U13 572B 'CAMS-CSM1-0' ... ...
* scenario (gcm_scenario) <U6 264B 'ssp126' ... 'rcp26'
Data variables: (12/14)
volume (time, rgi_id, gcm_scenario) float32 453kB ...
area (time, rgi_id, gcm_scenario) float32 453kB ...
on_area (time, rgi_id, gcm_scenario) float32 453kB ...
off_area (time, rgi_id, gcm_scenario) float32 453kB ...
melt_off_glacier (time, rgi_id, gcm_scenario) float32 453kB ...
melt_on_glacier (time, rgi_id, gcm_scenario) float32 453kB ...
... ...
melt_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 5MB ...
melt_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 5MB ...
liq_prcp_off_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 5MB ...
liq_prcp_on_glacier_monthly (time, month_2d, rgi_id, gcm_scenario) float32 5MB ...
runoff_monthly (time, month_2d, rgi_id, gcm_scenario) float32 5MB ...
runoff (time, rgi_id, gcm_scenario) float32 453kB ...
Attributes:
description: OGGM model output
oggm_version: 1.6.1.dev26+gf8a1745
calendar: 365-day no leap
creation_date: 2025-05-07 20:14:15
bias_correction: bc_2000_2019
basin_name: Langtang
basin_area_km2: 583.8048947612463
glaciers_in_basin: 102
glacierised_area_perc: 21.129661828280465
glacierised_area_km2: 123.356}}
Summarize key points for discussion
- Data is stored as an xarray.Dataset with dimensions: time, rgi_id, gcm_scenario (or scenario/gcm)
- Variables include glacier volume, area, runoff, melt, precipitation, etc.
- Units are typically km³ for volume, km² for area, mm or m³ for runoff, etc.
- Temporal resolution: yearly (time from 2000 to 2100) and monthly (month_2d)
- Attributes provide basin metadata: name, area, glacierized fraction, etc.
Common running glaciers¶
Some individual glacier projections may be missing or not available for certain scenarios. To avoid introducing errors due to differing glacier counts across scenarios, we will first extract only those glaciers that are available in all scenarios:
# in this variable the common running glaciers will be saved for each basin
not_nan_rgi_ids_all = {}
# loop through your basins
for basin in basin_ids:
not_nan_rgi_ids = None
# loop though the temperature scenarios, only glaciers which are available in all temperature scenarios are selected
for temp in temp_scenarios:
not_nan_rgi_ids_temp = ~ds_all[basin][temp].volume.isnull().any(dim=["time", "gcm_scenario"])
if not_nan_rgi_ids is None:
not_nan_rgi_ids = not_nan_rgi_ids_temp
else:
not_nan_rgi_ids &= not_nan_rgi_ids_temp
# save the working rgi_ids for each basin
not_nan_rgi_ids_all[basin] = not_nan_rgi_ids.rgi_id[not_nan_rgi_ids].values
for id in basin_ids:
print('for the basin: ', id)
for temp in temp_scenarios:
print(f"{temp}: {not_nan_rgi_ids_all[id].size} glaciers available")
for the basin: rofental 4°C: 23 glaciers available 2.7°C: 23 glaciers available 1.5°C: 23 glaciers available for the basin: langtang 4°C: 102 glaciers available 2.7°C: 102 glaciers available 1.5°C: 102 glaciers available
We can now use this list of valid rgi_ids to filter our data and include only those glaciers that are available across all scenarios.
basin_example = basin_ids[1]
ds_all[basin_example]['4°C'].sel(rgi_id=not_nan_rgi_ids_all[basin_example]).coords['rgi_id']
#########see if the 23 and 102 glacies fromt the file are available in the data for all scenarios
for id in basin_ids:
print('for the basin: ', id)
for temp in temp_scenarios:
print(f"{temp}: {not_nan_rgi_ids_all[id].size} glaciers available")
for the basin: rofental 4°C: 23 glaciers available 2.7°C: 23 glaciers available 1.5°C: 23 glaciers available for the basin: langtang 4°C: 102 glaciers available 2.7°C: 102 glaciers available 1.5°C: 102 glaciers available
When comparing the number of glaciers with the one given in the slides we can see that all are avaliable 23 for Rofental and 102 for LangTang
Your answer here:
- since all galciers are all available in all scenarios skip this task
Describe your basins¶
Before starting your data analysis, take some time to explore your selected basins and do a bit of background research. You can download all basin shapefiles here to examine them more closely.
- Where are the basins located, and what are their climate conditions?
- How large is each basin, and what proportion of the area is glacierized? (Tip: check the Attribues of ds_all for the individual basins)
Your answer here: Rofental is located in the Ötztal Alps in Austria, Central Europe. The climate is temperate alpine, characterized by cold winters, cool summers, and significant precipitation, much of it as snow at higher elevations.
- Basin area: 98.64 km²
- Glacierized area: 34.88 km²
- Glacierized fraction: 35.36%
Langtang is situated in the Himalayas of Nepal. The climate is high-mountain continental, with cold winters, a pronounced monsoon season (June–September), and dry winters.
- Basin area: 583.80 km²
- Glacierized area: 123.36 km²
- Glacierized fraction: 21.13%
Volume and area evolution¶
Analyze the volume and area evolution of all glaciers in your basin. Tip: use ds.sum(dim='rgi_id') to sum over all glaciers.
For each basin, create:
- One plot showing total glacier volume evolution (in km³) from 2020 to 2100
- One plot showing total glacier area evolution (in km²) from 2020 to 2100
Each plot should include all three temperature scenarios, displayed as the median with interquartile range (17th to 83rd percentile). The title of each plot should include the name of the basin and the glacierized area fraction (in percent).
# Plot glacier volume and area evolution for each basin and scenario
years = ds_all[basin_ids[0]]['1.5°C'].time.values
start_idx = np.where(years >= 2020)[0][0]
years = years[start_idx:]
percentiles = [17, 50, 83]
for basin in basin_ids:
attrs = ds_all[basin]['1.5°C'].attrs
frac = attrs['glacierised_area_perc']
basin_name = attrs['basin_name']
fig, axes = plt.subplots(1, 2, figsize=(14, 5), sharex=True)
for var, ax, ylabel in zip(['volume', 'area'], axes, ['Glacier Volume (km³)', 'Glacier Area (km²)']):
for scenario, color in zip(['1.5°C', '2.7°C', '4°C'], ['tab:blue', 'tab:orange', 'tab:red']):
ds = ds_all[basin][scenario].sel(rgi_id=not_nan_rgi_ids_all[basin])
# Sum over glaciers, then compute percentiles over gcm_scenario
data = ds[var].sum(dim='rgi_id').transpose('time', 'gcm_scenario').values[start_idx:]
p17 = np.percentile(data, percentiles[0], axis=1)
p50 = np.percentile(data, percentiles[1], axis=1)
p83 = np.percentile(data, percentiles[2], axis=1)
ax.plot(years, p50, label=scenario, color=color)
ax.fill_between(years, p17, p83, color=color, alpha=0.2)
ax.set_ylabel(ylabel)
ax.set_xlabel('Year')
ax.grid(True)
ax.set_xlim([2020, 2100])
axes[0].set_title(f"{basin_name}: Glacier Volume\nGlacierized Fraction: {frac:.1f}%")
axes[1].set_title(f"{basin_name}: Glacier Area\nGlacierized Fraction: {frac:.1f}%")
axes[1].legend(title='Scenario')
plt.tight_layout()
plt.show()
- What do you observe when comparing the different scenarios within each basin?
- Do the two basins react similarly or differently?
- Is there a noticeable difference in the behavior between glacier area and volume?
Your answers here:
Hydrological output¶
Analyze the hydrological output of your basins. For guidance, you can refer to the plots of this tutorial.
For each basin, create the following plots:
- Total runoff for all temperature scenarios in one plot (showing median and interquartile range)
- Runoff components (only median), one plot per temperature scenario
- Monthly runoff (median only), as a 2D plot (x-axis: Months, y-axis: Years), one plot per temperature scenario
- Annual runoff at three time steps (e.g. 2020, 2060, 2100), showing median and interquartile range, one plot per temperature scenario
The title of each plot should include the name of the basin and the glacierized area fraction (in percent), and, if needed, the temperature scenario.
# ---------- variable preparation --------- #
ds_rofental = ds_all['rofental']
ds_langtang = ds_all['langtang']
temperatures = ['1.5°C', '2.7°C', '4°C']
runoff_vars = ['melt_off_glacier', 'melt_on_glacier', 'liq_prcp_off_glacier', 'liq_prcp_on_glacier']
years = [2020, 2060, 2099]
# ############ Plot 1) #############
# Time setup
years = ds_all[basin_ids[0]]['1.5°C'].time.values
start_idx = np.where(years >= 2020)[0][0]
years = years[start_idx:]
percentiles = [17, 50, 83]
colors = {'1.5°C': 'tab:blue', '2.7°C': 'tab:orange', '4°C': 'tab:red'}
for basin in basin_ids:
attrs = ds_all[basin]['1.5°C'].attrs
basin_name = attrs['basin_name']
fig, ax = plt.subplots(figsize=(10, 4))
for scenario in ['1.5°C', '2.7°C', '4°C']:
ds = ds_all[basin][scenario].sel(rgi_id=not_nan_rgi_ids_all[basin])
# Sum all runoff components and convert from m³ to Gt (1e9 m³)
runoff = sum(ds[comp] for comp in runoff_vars) * 1e-9 # Gt
# Sum over rgi_ids
total_runoff = runoff.sum(dim='rgi_id') # Dimensions: time, gcm_scenario
# Convert to numpy array and trim to 2020 onward
data = total_runoff.values[start_idx:] # shape: (years, gcm_scenario)
# Compute percentiles across GCMs
p17 = np.percentile(data, percentiles[0], axis=1)
p50 = np.percentile(data, percentiles[1], axis=1)
p83 = np.percentile(data, percentiles[2], axis=1)
ax.plot(years, p50, label=scenario, color=colors[scenario])
ax.fill_between(years, p17, p83, color=colors[scenario], alpha=0.2)
ax.set_title(f"{basin_name}: Total Annual Runoff (2020–2100)\nGlaciered Fraction: {attrs['glacierised_area_perc']:.1f}%")
ax.set_ylabel("Total Runoff [Mt/year]")
ax.set_xlabel("Year")
ax.grid(True)
ax.set_xlim([2020, 2100])
ax.legend(title="Scenario")
plt.tight_layout()
plt.show()
############# Plot 2) ############
# --------- variable preparation --------- #
df_annual_r = {}
df_annual_l = {}
for temp in temperatures:
# Only use glaciers available in all scenarios
sel_vars_r = [v for v in ds_rofental[temp].data_vars if 'month_2d' not in ds_rofental[temp][v].dims]
sel_vars_l = [v for v in ds_langtang[temp].data_vars if 'month_2d' not in ds_langtang[temp][v].dims]
df_annual_r[temp] = ds_rofental[temp][sel_vars_r].sel(rgi_id=not_nan_rgi_ids_all['rofental']).sum(dim='rgi_id').to_dataframe()
df_annual_l[temp] = ds_langtang[temp][sel_vars_l].sel(rgi_id=not_nan_rgi_ids_all['langtang']).sum(dim='rgi_id').to_dataframe()
# Convert to Gt if not already done
for temp in temperatures:
df_annual_r[temp] = df_annual_r[temp][runoff_vars] * 1e-9
df_annual_l[temp] = df_annual_l[temp][runoff_vars] * 1e-9
frac_rofental = ds_rofental['1.5°C'].attrs['glacierised_area_perc']
frac_langtang = ds_langtang['1.5°C'].attrs['glacierised_area_perc']
# Plot 1: Median only (no running mean)
fig1, axs1 = plt.subplots(2, 3, figsize=(18, 10), sharex=True, sharey=False)
colors = sns.color_palette("mako", n_colors=len(runoff_vars))
for col, temp in enumerate(temperatures):
# --- Rofental ---
ax_r = axs1[0, col]
df_r = df_annual_r[temp].reset_index()
df_r_median = df_r.groupby('time')[runoff_vars].median()
df_r_median.plot.area(ax=ax_r, color=colors, alpha=0.95)
ax_r.set_title(f'Rofental - {temp}\nGlacierized Fraction: {frac_rofental:.1f}%')
ax_r.set_ylabel('Runoff [Mt]')
ax_r.set_xlabel('Year')
ax_r.set_ylim(0, 250)
if col == 2:
ax_r.legend(loc='upper right', fontsize='small')
else:
ax_r.get_legend().remove()
# --- Langtang ---
ax_l = axs1[1, col]
df_l = df_annual_l[temp].reset_index()
df_l_median = df_l.groupby('time')[runoff_vars].median()
df_l_median.plot.area(ax=ax_l, color=colors, alpha=0.95)
ax_l.set_title(f'Langtang - {temp}\nGlacierized Fraction: {frac_langtang:.1f}%')
ax_l.set_ylabel('Runoff [Mt]')
ax_l.set_xlabel('Year')
# Do not set ylim for Langtang
if col == 2:
ax_l.legend(loc='upper right', fontsize='small')
else:
ax_l.get_legend().remove()
plt.suptitle("Annual runoff components (median, no smoothing)", y=1.02, fontsize=16)
plt.tight_layout()
plt.show()
# Plot 2: Median with running mean
fig2, axs2 = plt.subplots(2, 3, figsize=(18, 10), sharex=True, sharey=False)
for col, temp in enumerate(temperatures):
# --- Rofental ---
ax_r = axs2[0, col]
df_r = df_annual_r[temp].reset_index()
df_r_median = df_r.groupby('time')[runoff_vars].median()
df_r_median_smooth = df_r_median.rolling(window=24, min_periods=1, center=True).mean()
df_r_median_smooth.plot.area(ax=ax_r, color=colors, alpha=0.95)
ax_r.set_title(f'Rofental - {temp}\nGlacierized Fraction: {frac_rofental:.1f}%')
ax_r.set_ylabel('Runoff [Mt]')
ax_r.set_xlabel('Year')
ax_r.set_ylim(0, 250)
if col == 2:
ax_r.legend(loc='upper right', fontsize='small')
else:
ax_r.get_legend().remove()
# --- Langtang ---
ax_l = axs2[1, col]
df_l = df_annual_l[temp].reset_index()
df_l_median = df_l.groupby('time')[runoff_vars].median()
df_l_median_smooth = df_l_median.rolling(window=24, min_periods=1, center=True).mean()
df_l_median_smooth.plot.area(ax=ax_l, color=colors, alpha=0.95)
ax_l.set_title(f'Langtang - {temp}\nGlacierized Fraction: {frac_langtang:.1f}%')
ax_l.set_ylabel('Runoff [Mt]')
ax_l.set_xlabel('Year')
# Do not set ylim for Langtang
if col == 2:
ax_l.legend(loc='upper right', fontsize='small')
else:
ax_l.get_legend().remove()
plt.suptitle("Annual runoff components (median, 24-year running mean)", y=1.02, fontsize=16)
plt.tight_layout()
plt.show()
############ Plot 3) ############
# 1. Extract DataFrames
for temp in temperatures:
df_annual_r[temp] = ds_rofental[temp].sel(rgi_id=not_nan_rgi_ids_all['rofental']).sum(dim='rgi_id').to_dataframe()
df_annual_l[temp] = ds_langtang[temp].sel(rgi_id=not_nan_rgi_ids_all['langtang']).sum(dim='rgi_id').to_dataframe()
# 2. Calculate monthly medians
for temp in temperatures:
var = 'runoff_monthly'
df_annual_r[temp] = (
df_annual_r[temp]
.groupby(['calendar_year', 'calendar_month_2d'])[var]
.median()
.unstack()
* 1e-9
)
df_annual_l[temp] = (
df_annual_l[temp]
.groupby(['calendar_year', 'calendar_month_2d'])[var]
.median()
.unstack()
* 1e-9
)
# Get glacierized area fractions for titles
frac_rofental = ds_rofental['1.5°C'].attrs['glacierised_area_perc']
frac_langtang = ds_langtang['1.5°C'].attrs['glacierised_area_perc']
# 3. Plotting heatmaps
fig, axs = plt.subplots(2, 3, figsize=(18, 12), sharex=True, sharey=True)
for col, temp in enumerate(temperatures):
# Rofental
ax_r = axs[0, col]
sns.heatmap(
df_annual_r[temp], ax=ax_r, cmap=sns.color_palette("vlag", as_cmap=True), cbar=col == 2,
linewidths=0.1, linecolor='gray'
)
ax_r.set_title(f'Rofental - {temp}\nGlacierized Fraction: {frac_rofental:.1f}%')
ax_r.set_xlabel('Month')
ax_r.set_ylabel('Year')
# Langtang
ax_l = axs[1, col]
sns.heatmap(
df_annual_l[temp], ax=ax_l, cmap=sns.color_palette("vlag", as_cmap=True), cbar=col == 2,
linewidths=0.1, linecolor='gray'
)
ax_l.set_title(f'Langtang - {temp}\nGlacierized Fraction: {frac_langtang:.1f}%')
ax_l.set_xlabel('Month')
ax_l.set_ylabel('Year')
plt.tight_layout()
plt.show()
############## Plot 4 ##############
# Constants
month_labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
key_years = [2020, 2060, 2099]
colors = ['tab:blue', 'tab:orange', 'tab:green']
# Get glacierized fractions for titles
frac_rofental = ds_rofental['1.5°C'].attrs['glacierised_area_perc']
frac_langtang = ds_langtang['1.5°C'].attrs['glacierised_area_perc']
for temp in temperatures:
df_annual_r[temp] = ds_rofental[temp].sel(rgi_id=not_nan_rgi_ids_all['rofental']).sum(dim='rgi_id').to_dataframe()
df_annual_l[temp] = ds_langtang[temp].sel(rgi_id=not_nan_rgi_ids_all['langtang']).sum(dim='rgi_id').to_dataframe()
def get_monthly_stats(df, year):
monthly = df[df['calendar_year'] == year].groupby('calendar_month_2d')['runoff_monthly']
median = monthly.median().values * 1e-9
q17 = monthly.quantile(0.17).values * 1e-9
q83 = monthly.quantile(0.83).values * 1e-9
return median, q17, q83
fig, axs = plt.subplots(2, 3, figsize=(18, 10), sharex=True, sharey=False)
for row, (basin, dfs, frac) in enumerate(zip(['rofental', 'langtang'], [df_annual_r, df_annual_l], [frac_rofental, frac_langtang])):
for col, temp in enumerate(temperatures):
ax = axs[row, col]
for idx, year in enumerate(key_years):
median, q17, q83 = get_monthly_stats(dfs[temp], year)
ax.plot(range(1, 13), median, label=f'{year}', color=colors[idx])
ax.fill_between(range(1, 13), q17, q83, color=colors[idx], alpha=0.2)
ax.set_title(f"{basin.capitalize()} - {temp}\nGlacierized Fraction: {frac:.1f}%")
ax.set_xticks(range(1, 13))
ax.set_xticklabels(month_labels)
if col == 0:
ax.set_ylabel('Runoff [Mt]')
if row == 1:
ax.set_xlabel('Month')
if row == 0:
ax.set_ylim(0, 100)
else:
ax.set_ylim(0, 500)
if row == 0 and col == 2:
ax.legend(title="Year")
plt.tight_layout()
plt.show()
- What do you observe when comparing the different scenarios within each basin?
- Do the two basins react similarly or differently?
- Can you identify any evidence of peak water?
Comparison of scenarios within each basin:
For both Rofental and Langtang, higher warming scenarios (2.7°C and 4°C) lead to a much stronger and faster decline in glacier volume and area, as well as a more pronounced reduction in glacier melt contribution to runoff. Under the 1.5°C scenario, glacier loss is slower and a larger glacierized area persists by 2100, resulting in a more sustained glacier melt contribution to runoff.
Similarities and differences between basins:
Both basins show qualitatively similar responses to warming: accelerated glacier loss and declining glacier melt runoff with increasing temperature. However, Langtang (with a lower glacierized fraction) shows a larger absolute runoff and a more pronounced shift from glacier melt to liquid precipitation as the dominant runoff source. Rofental, being more glacierized, exhibits a steeper decline in glacier melt runoff and a more rapid transition to rainfall-dominated runoff under high warming.
Evidence of peak water:
Yes, both basins show evidence of "peak water": annual runoff initially increases or remains stable as glacier melt accelerates due to warming, but then declines in the second half of the century as glacier area shrinks and meltwater supply diminishes. This is especially clear in the 2.7°C and 4°C scenarios, where peak runoff is reached before 2050, followed by a steady decrease towards 2100. The timing and magnitude of peak water differ between basins and scenarios, but the pattern is consistent.
For better visibility of the 'peak water' we additionally plotted the running mean in the second plot
Contextualize your results with the literature¶
Your answer here:
Cites for Rofental¶
Hanzer, F., Förster, K., Nemec, J., & Strasser, U. (2018). Projected cryospheric and hydrological impacts of 21st century climate change in the Ötztal Alps (Austria) simulated using a physically based approach. Hydrology and Earth System Sciences, 22(2), 1593-1614. https://doi.org/10.5194/hess-22-1593-2018
Strasser, U., Marke, T., Braun, L., Escher-Vetter, H., Juen, I., Kuhn, M., ... & Kaser, G. (2018). The Rofental: a high Alpine research basin (1890–3770 m asl) in the Ötztal Alps (Austria) with over 150 years of hydrometeorological and glaciological observations. Earth System Science Data, 10(1), 151-171. https://doi.org/10.5194/essd-10-151-2018
Together, these two articles provide a comprehensive overview of the current state of the Rofental basin and projections for its future development. Strasser et al. present an extensive and unique dataset spanning 150 years of observations, offering a robust foundation for conducting cryospheric, hydrological, and atmospheric research. Hanzer et al. focus on future projections, specifically assessing the anticipated impacts of climate change on the cryosphere and hydrology of the Ötztal Alps (Austria) through the year 2100.
For the projections, Hanzer et.al uses the a physically based hydroclimatological model (AMUNDSEN) to assess future climate change impacts on the cryosphere and hydrology. The model is run in 100 m spatial and 3 h temporal resolution using in total 31 down-scaled, bias-corrected, and temporally disaggregated EURO-CORDEX climate projections for the representative concentration pathways (RCPs) 2.6, 4.5, and 8.5 scenarios as forcing data.
Mains findings Hanzer et.al
- RCP2.6 snow amounts then stay relatively constant during the remainder of the century at 6 mm (−31 %), while RCP4.5 and RCP8.5 snow amounts continue to decrease strongly, amounting to −63 and −80 %, respectively, at the end of the century.
- Until 2050, glacier volume will de-cline by approx. 60–65 % largely independent of the emission scenario, whereas by the end of the century 80–96 %.
- Temperature is projected to increase by all models and for all seasons, with average values of 1.1 (RCP2.6) to 3.8 ∘C (RCP8.5) in the annual mean and a maximum spread of 2.2 ∘C between individual models for a given scenario. The highest warming is projected for the winter season, with up to 4.5 ∘C in the RCP8.5 scenario, while the smallest increases are projected for spring.
- Projected multi-model average increases in winter precipitation are between 8 % (RCP2.6) and 21 % (RCP8.5), with individual models projecting increases of up to 57 %.
- Summer runoff strongly decreases with simultaneously increasing spring runoff, indicating a shift from glacial/glacio-nival to nivo-glacial runoff regimes
- While in the RCP2.6 scenario the month of peak runoff remains unchanged, in the RCP4.5 and RCP8.5 scenarios the peak gradually shifts from July towards June for all catchments, with the exception of Vernagtbach in the RCP4.5 scenario
Although this article does not focus specifically on our study basin, but rather on the Ötztal Alps region as a whole, it serves as a valuable comparison for our results, since the climatic conditions and the glaciers analyzed are relatively similar to those in our area. In addition, the article presents its results in a very clear and structured form, organized into distinct categories: changes in snow cover, glaciers, and hydrology.
When comparing the results from both models, it is evident that, although the projected temperatures differ between the simulations, this does not significantly affect the overall conclusion regarding glacier loss. Regardless of the climate scenario, both models project an almost complete disappearance of glaciers by the end of the century. In our project, we did not explicitly analyze seasonal variations in runoff, but we concur with the findings of the referenced article that a shift in the timing of peak runoff is expected. Specifically, the current summer peak in runoff is projected to move toward the spring months, reflecting earlier snowmelt and glacier melt as a consequence of rising temperatures.
It is particularly interesting to compare the results from both models, as they are designed for different scales and purposes. OGGM is a model developed primarily for regional to global applications, focusing on the long-term evolution of glacier geometry, including changes in area, volume, and length under varying climate scenarios. In contrast, AMUNDSEN is a high-resolution, physically based model tailored for mountainous catchments, particularly in the Alpine region, where complex topography and microclimatic effects play a significant role.
While OGGM simplifies surface processes by using a temperature-index approach calibrated individually for each glacier, AMUNDSEN simulates the full surface energy and mass balance, including detailed representations of radiation fluxes, turbulent heat exchange, snow accumulation and redistribution, and meltwater runoff. This allows AMUNDSEN to capture fine-scale spatial and temporal variability, which is essential in small, glacierized catchments with heterogeneous terrain such as Rofental. While OGGM require precipitation and temperature as a input, AMUNDSEN require temperature, precipitatio, relative humidity, radiation and wind speed. Another key distinction is that OGGM includes a dynamic ice flow model (or flux based model), enabling projections of glacier advance or retreat over decades to centuries. AMUNDSEN, on the other hand, focuses more on short- to medium-term hydrological and cryospheric processes, and requires coupling with external models (e.g., OGGM or GloGEM) if long-term glacier geometry changes are to be simulated.
Cites for Langtang¶
Immerzeel, W.W., van Beek, L.P.H., Konz, M. et al. Hydrological response to climate change in a glacierized catchment in the Himalayas. Climatic Change 110, 721–736 (2012). https://doi.org/10.1007/s10584-011-0143-4
Pradhananga, N. S., Kayastha, R. B., Bhattarai, B. C., Adhikari, T. R., Pradhan, S. C., Devkota, L. P., ... & Mool, P. K. (2014). Estimation of discharge from Langtang River basin, Rasuwa, Nepal, using a glacio-hydrological model. Annals of Glaciology, 55(66), 223-230. DOI 10.3189/2014AoG66A123
Although Immerzel et.al was published more than 10 years ago, we consider it a good example and a starting point for comparing our results. The authors conducted their study following the striking findings of the IPCC Fourth Assessment Report, which suggested that glaciers in the Himalayas could disappear by 2035.
To begin with, we will summarize their main findings and then compare them with our own results, highlighting the similarities and differences between the two models.
Mains findings Immerzzel, et al:
- By 2035 the glacier area has decreased by 32%, by 50% in 2055 and by 75% in 2088.
- From 2075 onwards permanent ice can only be found at the highest elevations in the catchment.
- On average a positive precipitation trend is predicted of 1.9 mm y-1 consistent with the acceleration of the hydrological cycle due to the increased atmospheric temperature.
- Annual ensemble discharge increases by 0.05 m³/s (~4 mm), reaching a 32% rise by 2050, though the rate slows by century’s end. This is due to increased precipitation and a shift from meltwater- to rainfall-dominated runoff, raising the runoff coefficient from 50% to 65%.
- After 2040 the decrease in glacier area becomes dominant leading to runoff decline
In terms of glacier area decrease, our results are very similar for the 2.7 °C and 4 °C scenarios, both showing a strong reduction in glacier surface area—approaching nearly 100% loss under the 4 °C scenario and around 75% under the 2.7 °C scenario. Under the 1.5 °C scenario, approximately 50% of the glacier area is projected to persist. Regarding runoff, our findings also align closely with those presented in the article. Runoff tends to decline by mid-century due to the reduced contribution from glacier melt. Meanwhile, precipitation shows an increasing trend, consistent with the patterns reported by the authors. For the seasonal runoff, our results shows that from April to August runoff increase until a maximun, this is also show in the article where it explains by peak of the melt and moonson season.
The article employed the older climate scenario framework (A1B SRES), using data from five different models provided by the IPCC to simulate future temperature and precipitation. This scenario projects a likely temperature increase of about 2.8 °C by the end of the century, which is comparable to our intermediate scenario.In our project, we used three projections based on different global temperature targets, derived from carbon emission pathways. The model used in the article incorporates key glacio-hydrological processes, including glacier dynamics (assuming basal sliding), snow and ice ablation using a degree-day approach influenced by aspect and debris cover, evapotranspiration (both reference and actual), surface runoff (via the curve number method), and baseflow (groundwater contribution) thougth a downscaling process. OGGM differs in some aspect, use geometrical centerlines and uses temperature and precipitation as mains input.
Pradhananga et.al points out the contribution of the glacier and ice melting in terms of discharge to the Langtang basin, projection until 2050. "The main objective is to estimate the present and future discharge from the glacierized Langtang River basin using a PDD model (PDDM) " --> The contribution of snow and ice melt from glaciers and snowmelt from rocks and vegetation will decrease in the future: in 2040– 50 it will be just 50% of the total discharge. The PDDM is sensitive to monthly average temperature, as a 28C temperature increase will increase the discharge by 31.9%. Changes in glacier area are less sensitive, as glacier area decreases of 25% and 50% result in a change in the total discharge of –5.7% and –11.4%, respectively. Our results shows similarities, due to increased temperature, more rain than snow is observed in precipitation, and melting of snow and ice from glaciers also increases. Despite the similarities in the results, both model has some differences. WEhile PDD, whe main output is ice melting, where it requires only temperature as a input, OGGM has more details in terms on mass balance and geometry (area, volume) where precipitation is also a require input.