Comparing ERA5 sources - CDS vs EDH

Note

Download this example as a Jupyter notebook here: pypsa/atlite

Comparing ERA5 sources - CDS vs EDH#

atlite offers two sources for the ERA5 dataset - CDS and EDH. ERA5 is natively hosted on the Copernicus Data Store, which offers many convenient options for server-side processing of the raw ERA5 data. However, it can only serve 400 simultaneous requests, so you often have to wait for hours in order to download data, which can be annoying and difficult to debug.

The Earth Data Hub hosts a mirror of ERA5. They do not have a queue (although they still require authentication, see installation instructions). However, they offer limited server-side processing, which means you need to download a bit more data, and do more processing on device. Some of the CDS server-side processing is difficult to mimic locally, especially interpolation to grids other than the default 0.25x0.25 deg grid, so, for a small subset of users, CDS is the best source.

This table summarises the differences between the two datasets:

CDS

EDH

atlite API

same

same

authentication/setup

API + accept license

API + accept license

download queue

5min-8hrs

none

download size

1x

~1.5x

data storage grid

parameter-dependent

0.25 deg. grid

results at 0.25 deg. grid

reference

within 1.5% (worst-case)

So which source should I use? For workflows that already used CDS, and when you require data at resolution that is not a multiple of the default 0.25/0.25 deg. grid, use CDS. For everything else, EDH is more convenient.

In this example we will demonstrate how to use the EDH mirror of the ERA5 dataset within atlite and the minimal numerical differences between them. For a more detailed explanation on how to build cutouts, follow this notebook.

We start with the setup:

[1]:
import warnings
from pathlib import Path

import numpy as np
import xarray as xr

import atlite

warnings.filterwarnings("ignore")

Let’s download cutouts from both sources:

[2]:
TIME = "2013-01-01"
BOUNDS = (-4, 56, 1.5, 62)
CACHE = Path(".cache")
CACHE.mkdir(exist_ok=True)

cutout_CDS = atlite.Cutout(
    path=CACHE / "era5-cds-native.nc",
    module="era5",
    bounds=BOUNDS,
    time=TIME,
)
cutout_CDS.prepare()
[2]:
<Cutout "era5-cds-native">
 x = -4.00 ⟷ 1.50, dx = 0.25
 y = 56.00 ⟷ 62.00, dy = 0.25
 time = 2013-01-01 ⟷ 2013-01-01, dt = h
 module = era5
 prepared_features = ['height', 'wind', 'influx', 'temperature', 'runoff']
[3]:
cutout_EDH = atlite.Cutout(
    path=CACHE / "era5-edh-native.nc",
    module="era5-edh",
    bounds=BOUNDS,
    time=TIME,
)
cutout_EDH.prepare(tmpdir=CACHE)
[3]:
<Cutout "era5-edh-native">
 x = -4.00 ⟷ 1.50, dx = 0.25
 y = 56.00 ⟷ 62.00, dy = 0.25
 time = 2013-01-01 ⟷ 2013-01-01, dt = h
 module = era5-edh
 prepared_features = ['height', 'wind', 'influx', 'temperature', 'runoff']

As you can see, the API for both datasets is identical. The only change needed is replacing module="era5" with module="era5-edh". EDH provides the same atlite-facing functionality, except that it only works at 0.25x0.25 deg resolution. Now let’s investigate if the datasets provide the same data:

[4]:
common_vars = set(cutout_CDS.data.data_vars) & set(cutout_EDH.data.data_vars)

print(
    "Both cutouts have the same features:",
    common_vars == set(cutout_CDS.data.data_vars),
)

rtol = 1.5e-2
try:
    xr.testing.assert_allclose(
        cutout_CDS.data[common_vars], cutout_EDH.data[common_vars], rtol=rtol
    )
    print(f"Data in both sources matches within tolerance ({rtol * 100}%)")
except Exception:
    print("Data in both features is NOT identical")
Both cutouts have the same features: True
Data in both sources matches within tolerance (1.5%)

The two datasets match well enough for atlite use (within 1.5%). The source of error is lossy compression on the EDH side. They use a bitround filter, which only stores 10 bits of each variable. The result is worst for height and temperature variables, as you can see below:

[5]:
diff = abs(cutout_CDS.data[common_vars] - cutout_EDH.data[common_vars])
max_diff = diff.max().compute()
print(max_diff.to_pandas())
wnd_azimuth             4.093647e-04
wnd_shear_exp           3.905668e-04
height                  1.690674e-01
albedo                  8.535162e-04
dewpoint temperature    1.250000e-01
solar_azimuth           2.384101e-07
temperature             1.250000e-01
influx_direct           3.556061e-02
roughness               2.440810e-04
soil temperature        1.248779e-01
influx_diffuse          1.066666e-01
wnd100m                 7.616043e-03
runoff                  0.000000e+00
influx_toa              7.113647e-02
solar_altitude          2.980201e-08
dtype: float64

We can also plot relative differences on a graph:

[6]:
WORST_DIFF_VARS = [
    "height",
    "temperature",
    "soil temperature",
]


def scaled_max_abs_diff(reference, candidate):
    diff = np.abs(candidate - reference)
    scale = float(np.maximum(np.abs(reference), np.abs(candidate)).max(skipna=True))
    reduce_dims = [dim for dim in diff.dims if dim not in {"x", "y"}]
    diff_map = diff.max(dim=reduce_dims, skipna=True) if reduce_dims else diff
    return 100 * diff_map.fillna(0) / scale


diff_maps = [
    scaled_max_abs_diff(
        cutout_CDS.data[var],
        cutout_EDH.data[var],
    ).assign_coords(variable=var)
    for var in WORST_DIFF_VARS
]

relative_max_diff = xr.concat(diff_maps, dim="variable").rename(
    "scaled max absolute difference [%]"
)

grid = relative_max_diff.plot.pcolormesh(
    x="x",
    y="y",
    col="variable",
    col_wrap=3,
    cmap="magma",
    robust=True,
    cbar_kwargs={"label": "scaled max absolute difference [%]"},
)

for ax in grid.axs.flat:
    ax.set_aspect("equal", adjustable="box")
    ax.set_xlabel("longitude")
    ax.set_ylabel("latitude")
../_images/examples_comparing_era5_sources_cds_vs_edh_11_0.png

Once we actually calculate the renewable potentials, all the quantisations average out, resulting in a smaller relative difference. Over larger time and space the differences are negligible. Here is the 99-percentile difference in pv and wind capacity factors between CDS and EDH over Europe in 2013:

output.png

Hopefully that quells any doubts you may have had about using EDH data. And you learned a bit about compression as well!

The conclusion once again: For workflows that already used CDS, and when you require data at resolution that is not a multiple of 0.25/0.25 deg. grid, use CDS. For everything else, EDH is more convenient. And the change from one to another is a single line of code.