ERA5 Data generation & merging#
ERA5 data will be generated from 1970-2025, loaded and merged into one file
Start-up#
# general python
import warnings
warnings.filterwarnings("ignore", category=UserWarning)
import numpy as np
from pathlib import Path
import pandas as pd
import matplotlib.pyplot as plt
import xarray as xr
#niceties
from rich import print
import yaml
# General eWaterCycle
import ewatercycle
import ewatercycle.models
import ewatercycle.forcing
# Fix ERA5 config error
from esmvalcore.config import CFG # older: from esmvalcore.experimental import CFG
from pathlib import Path
home = Path.home()
OBS6 = Path('/data/shared/climate-data/obs6')
default = Path('/data/shared/climate-data')
CMIP_path = Path(f'{str(home)}/.esmvaltool/downloads/CMIP6')
CFG['rootpath'] = {'OBS6': [OBS6], 'default': [default], 'CMIP6': [CMIP_path]}
print(CFG['rootpath'])
print(CFG['drs'])
{ 'OBS6': [PosixPath('/data/shared/climate-data/obs6')], 'default': [PosixPath('/data/shared/climate-data')], 'CMIP6': [PosixPath('/home/maxime/.esmvaltool/downloads/CMIP6')] }
{'CMIP3': 'ESGF', 'CMIP5': 'ESGF', 'CMIP6': 'ESGF', 'CORDEX': 'ESGF', 'obs4MIPs': 'ESGF'}
Define range & forcing paths#
# Defining period range
# periods = [
# [1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020],
# [1974, 1979, 1984, 1989, 1994, 1999, 2004, 2009, 2014, 2019, 2024]]
# For calibration 1:
# periods = [
# [1990, 1995, 2000, 2005, 2010],
# [1994, 1999, 2004, 2009, 2017]]
# For calibration 2:
# periods = [
# [1999, 2005, 2010, 2018],
# [2004, 2009, 2017, 2019]]
# For validation 1:
# periods = [
# [2018, 2022],
# [2021, 2024]]
# For validation 2:
# periods = [
# [2019, 2022],
# [2021, 2024]]
# For CMIP trial:
# periods = [
# [1995, 2000, 2005, 2010],
# [1999, 2004, 2009, 2014]]
# For CMIP trial 2
# periods = [
# [1970, 1975, 1980, 1985, 1990, 1995],
# [1974, 1979, 1984, 1989, 1994, 1999]]
periods = [
[1989, 1995, 2000, 2005, 2010],
[1994, 1999, 2004, 2009, 2014]]
folder_names = []
forcing_paths = {}
# Generate file names & paths
for i in range(len(periods[0])):
start_year = periods[0][i]
end_year = periods[1][i]
folder_name = f'ERA5-{start_year}-{end_year}' # Add -cal or -val for cal and val folder name
# forcing_path = Path.home() / "BEP-maxime" / "Workyard" / "forcings" / folder_name
forcing_path = Path.home() / "BEP-maxime" / "book" / "thesis_projects" / "BSc" / "2026_Q4_MaximedeBekker_CEG" / "Workyard" / "forcings" / "ERA5" / folder_name
folder_names.append(folder_name)
forcing_paths[folder_name] = forcing_path
forcing_path.mkdir(exist_ok=True, parents=True)
# Call path test
print(forcing_paths[folder_names[0]])
start_year = periods[0][0]
end_year = periods[1][-1]
print(start_year)
print(end_year)
/home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994
1989
2014
Generate ERA5 data#
# Shapefile path
# shape_file = Path.home() / "BEP-maxime" / "Workyard" / "Shapefiles" / "07DA001_basin.shp"
shape_file = Path.home() / "BEP-maxime" / "book" / "thesis_projects" / "BSc" / "2026_Q4_MaximedeBekker_CEG" / "Workyard" / "Shapefiles" / "07DA001_basin.shp"
# Generate forcing data
# for i in range(1):
for i in range(len(folder_names)):
# Change start year to start time so ERA5 can interpret it
start_time_ERA5 = f'{periods[0][i]}-01-01T00:00:00Z'
end_time_ERA5 = f'{periods[1][i]}-12-31T00:00:00Z'
# Skip if folder is not empty to prevent errors
if any(forcing_paths[folder_names[i]].iterdir()):
print(f"Skipping {folder_names[i]} because the folder is not empty.")
continue
# Load forcings
ERA5_forcing = ewatercycle.forcing.sources['LumpedMakkinkForcing'].generate(
dataset="ERA5",
start_time=start_time_ERA5,
end_time=end_time_ERA5,
shape=shape_file,
directory=forcing_paths[folder_names[i]])
# I need info because my patience is limited
print(f"Finished {folder_names[i]}")
Finished ERA5-1989-1994
Skipping ERA5-1995-1999 because the folder is not empty.
Skipping ERA5-2000-2004 because the folder is not empty.
Skipping ERA5-2005-2009 because the folder is not empty.
Skipping ERA5-2010-2014 because the folder is not empty.
Load ERA5 data#
ERA5_forcings = {}
for i in range(len(folder_names)):
# Create new path to find relevant files in the folder
directory_new = forcing_paths[folder_names[i]] / "work" / "diagnostic" / "script"
# Load forcings
ERA5_forcings[folder_names[i]] = ewatercycle.forcing.sources["LumpedMakkinkForcing"].load(directory=directory_new)
# Test to see if it works
print(folder_names)
ERA5_forcings[folder_names[0]]
['ERA5-1989-1994', 'ERA5-1995-1999', 'ERA5-2000-2004', 'ERA5-2005-2009', 'ERA5-2010-2014']
LumpedMakkinkForcing(start_time='1989-01-01T00:00:00Z', end_time='1994-12-31T00:00:00Z', directory=PosixPath('/home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/work/diagnostic/script'), shape=PosixPath('/home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/work/diagnostic/script/07DA001_basin.shp'), filenames={'pr': 'OBS6_ERA5_reanaly_1_day_pr_1989-1994.nc', 'tas': 'OBS6_ERA5_reanaly_1_day_tas_1989-1994.nc', 'rsds': 'OBS6_ERA5_reanaly_1_day_rsds_1989-1994.nc', 'evspsblpot': 'Derived_Makkink_evspsblpot.nc'})
Merge ERA5 data#
# Function to seperate files based on file names and combine same file names from different folders
def combine_variable(var_name):
datasets = []
for i in range(len(folder_names)):
if var_name == "evspsblpot":
file_name = "Derived_Makkink_evspsblpot.nc"
else:
file_name = f"OBS6_ERA5_reanaly_1_day_{var_name}_{periods[0][i]}-{periods[1][i]}.nc"
directory = forcing_paths[folder_names[i]] / "work" / "diagnostic" / "script" / file_name
print(f"Loading {directory}")
datasets.append(xr.open_dataset(directory))
combined = xr.concat(datasets, dim="time")
return combined
# Running function for various file name types
combined_variables = {}
var_names = ["pr", "tas", "rsds", "evspsblpot"]
# combined_path = Path.home() / "BEP-maxime" / "Workyard" / "forcings" / f'ERA5-{periods[0][0]}-{periods[1][-1]}'
combined_path = Path.home() / "BEP-maxime" / "book" / "thesis_projects" / "BSc" / "2026_Q4_MaximedeBekker_CEG" / "Workyard" / "forcings" / "ERA5" / f'ERA5-{start_year}-{end_year}'
combined_path.mkdir(parents=True, exist_ok=True)
for i in range(len(var_names)):
combined_variables[var_names[i]] = combine_variable(var_names[i])
combined_variables[var_names[i]].to_netcdf(combined_path / f'combined_ERA5_{start_year}_{end_year}_{var_names[i]}.nc')
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_pr_1989-1994.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1995-1999/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_pr_1995-1999.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2000-2004/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_pr_2000-2004.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2005-2009/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_pr_2005-2009.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2010-2014/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_pr_2010-2014.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_tas_1989-1994.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1995-1999/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_tas_1995-1999.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2000-2004/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_tas_2000-2004.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2005-2009/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_tas_2005-2009.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2010-2014/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_tas_2010-2014.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_rsds_1989-1994.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1995-1999/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_rsds_1995-1999.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2000-2004/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_rsds_2000-2004.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2005-2009/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_rsds_2005-2009.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2010-2014/w ork/diagnostic/script/OBS6_ERA5_reanaly_1_day_rsds_2010-2014.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1989-1994/w ork/diagnostic/script/Derived_Makkink_evspsblpot.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-1995-1999/w ork/diagnostic/script/Derived_Makkink_evspsblpot.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2000-2004/w ork/diagnostic/script/Derived_Makkink_evspsblpot.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2005-2009/w ork/diagnostic/script/Derived_Makkink_evspsblpot.nc
Loading /home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forcings/ERA5/ERA5-2010-2014/w ork/diagnostic/script/Derived_Makkink_evspsblpot.nc
Create YAML file#
# Create yaml
forcing_yaml = {
"start_time": f"{start_year}-01-01T00:00:00Z",
"end_time": f"{end_year}-12-31T00:00:00Z",
"shape": str(shape_file),
"filenames": {
"pr": f"combined_ERA5_{start_year}_{end_year}_pr.nc",
"tas": f"combined_ERA5_{start_year}_{end_year}_tas.nc",
"rsds": f"combined_ERA5_{start_year}_{end_year}_rsds.nc",
"evspsblpot": f"combined_ERA5_{start_year}_{end_year}_evspsblpot.nc"}}
# Save the YAML file
yaml_file_path = combined_path / "ewatercycle_forcing.yaml"
with open(yaml_file_path, "w") as yaml_file:
yaml.dump(forcing_yaml, yaml_file, default_flow_style=False)
ERA5_combined_forcing = ewatercycle.forcing.sources["LumpedMakkinkForcing"].load(directory=combined_path)
print(ERA5_combined_forcing)
LumpedMakkinkForcing( start_time='1989-01-01T00:00:00Z', end_time='2014-12-31T00:00:00Z', directory=PosixPath('/home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/forci ngs/ERA5/ERA5-1989-2014'), shape=PosixPath('/home/maxime/BEP-maxime/book/thesis_projects/BSc/2026_Q4_MaximedeBekker_CEG/Workyard/Shapefile s/07DA001_basin.shp'), filenames={ 'evspsblpot': 'combined_ERA5_1989_2014_evspsblpot.nc', 'pr': 'combined_ERA5_1989_2014_pr.nc', 'rsds': 'combined_ERA5_1989_2014_rsds.nc', 'tas': 'combined_ERA5_1989_2014_tas.nc' } )