Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
8a14ac0
initial attemtp at opendap access
mrmorawski May 8, 2026
2732e4f
smarter CE plus semaphore to limit no of simultaneous connections
mrmorawski May 8, 2026
9c17e13
add caching
mrmorawski May 8, 2026
336df02
add deps for ncar
mrmorawski May 8, 2026
e029c46
zarr instead of netcdf for cahing to improve parallelism, tests to co…
mrmorawski May 8, 2026
e1d0dcd
test setup simplified, minor fix to downloaded coords
mrmorawski May 8, 2026
21488ef
first pass at spatial subsetting
mrmorawski May 8, 2026
e7ce2d6
processing for solar
mrmorawski May 8, 2026
86f734c
first attempt at regrid
mrmorawski May 8, 2026
66ca5da
regridding fixed
mrmorawski May 8, 2026
016b6a6
processing for remaining values implemented, passes tests at native r…
mrmorawski May 8, 2026
1c9a7b4
merged tests for era5_ncar
mrmorawski May 8, 2026
5f1e7ff
precomputed time axis to save on fetches, parallel building, skip int…
mrmorawski May 8, 2026
5ddb2eb
rate limit downloads through a custom dask pool instead of semaphore
mrmorawski May 8, 2026
e4cc402
todos after code review
mrmorawski May 8, 2026
4050e3f
style updated for consistency with era5.py, type annotations
mrmorawski May 8, 2026
96031ed
added units to missing vars
mrmorawski May 8, 2026
294bc87
invariant import handling simplified@
mrmorawski May 8, 2026
29cdb9e
per-variable tolerances for weird resolution cutout
mrmorawski May 8, 2026
540685d
numpy style docstrings
mrmorawski May 8, 2026
c2347b2
add time sampling tests
mrmorawski May 9, 2026
df033d6
first pass at era5_ncar doc
mrmorawski May 9, 2026
ada0936
finished docs
mrmorawski May 10, 2026
c55a890
suppress pydap log, add regridding warning
mrmorawski May 10, 2026
b5d5010
updated comments + reorganised function - update incomplete
mrmorawski May 10, 2026
ff639ec
use circular azimuth error in tests, better zero meridian handling, c…
mrmorawski May 10, 2026
7e29bca
fix documentation typos
mrmorawski May 10, 2026
a3d1e01
clearer comment structure, fix for edge case when retrieving data aro…
mrmorawski May 10, 2026
ed5a2c7
Merge branch 'PyPSA:master' into era5-ncar-thredds
mrmorawski May 10, 2026
fce7552
add release note
mrmorawski May 10, 2026
639a749
move release note to correct place
mrmorawski May 10, 2026
bb0c78f
clean up comments in era5_ncar tests
mrmorawski May 11, 2026
4538c74
consistent formatting of log output in era5_ncar, suppress known warn…
mrmorawski May 11, 2026
8bec53c
clean up doc notebook output
mrmorawski May 12, 2026
e88f915
more efficient zarr cache chunking, minor refactoring of logging setup
mrmorawski May 12, 2026
f0e286b
add all atlite vars downloaded from edh plus tests
mrmorawski May 15, 2026
d70e5c7
update docs for EDH model
mrmorawski May 15, 2026
81cf6a0
customizable chunk size support, cast all vars to float32 to save on …
mrmorawski May 16, 2026
342075d
udpate test tols for compatibility with float32 cast
mrmorawski May 16, 2026
ff4ecd3
updated documentation in notebooks
mrmorawski May 17, 2026
41c7ad8
clearer comments
mrmorawski May 17, 2026
ec9085f
retries on failed chunk downloads, minor refactors
mrmorawski May 19, 2026
08fe6aa
docs improvements
mrmorawski May 21, 2026
ce942d4
fix typo in test docstring
mrmorawski May 22, 2026
dad4f80
replace fsspecstore with obstore for more robust connection
mrmorawski May 27, 2026
dc3ad56
add EDH key to test CI
mrmorawski May 27, 2026
ebcec45
bump python version to support zarr v3
mrmorawski May 27, 2026
2a91591
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 27, 2026
8c63e85
update classifiers in pyproject to remove 3.10
mrmorawski May 27, 2026
8f63f2e
remove unnecessary tempdir from test on edh
mrmorawski May 27, 2026
5f4330a
performance optimisation to reduce ram usage. more sensible chunk siz…
mrmorawski May 29, 2026
f93ec07
rechunk for memory use optimisation, add example on using distributed…
mrmorawski Jun 5, 2026
dfafc7e
fix example doc to render properly with pandoc
mrmorawski Jun 5, 2026
3748f98
fix CI runners getting bounced from EDH due to multiple machines atte…
mrmorawski Jun 5, 2026
e681cce
fix type errors in era5-edh
mrmorawski Jun 5, 2026
606e41a
merge changes from main post 966380, resolve small merge conflicts (i…
mrmorawski Jun 10, 2026
363eb15
fix types and docstrings to match new ruff rules
mrmorawski Jun 10, 2026
88b2deb
move era5-edh to an upcoming release in release notes
mrmorawski Jun 10, 2026
b348a06
refactor: reduce test retry timeout to fail faster in case of edh ser…
mrmorawski Jul 9, 2026
1f33d61
chore: remove typing_extensions dependency post upgrade to python 3.11
mrmorawski Jul 9, 2026
6d3bf15
docs: fix typos in example and installation instructions
mrmorawski Jul 9, 2026
a03afea
Merge branch 'master' into era5-ncar-thredds
mrmorawski Jul 9, 2026
bd16460
refactor: restore long edh reach retry times to deal with EDH timing …
mrmorawski Jul 9, 2026
5b89211
chore: merge precommit.ci changes from pypsa main
mrmorawski Jul 9, 2026
073a3ad
refactor(era5-edh): share wind/influx processing with era5, add offli…
FabianHofmann Jul 17, 2026
d050851
Merge branch 'master' into era5-ncar-thredds
FabianHofmann Jul 17, 2026
3f45cc4
docs: add era5-edh usage note to release notes
FabianHofmann Jul 17, 2026
5b6860a
refactor: edh reachability test retries on 429s
mrmorawski Jul 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
MPLBACKEND: Agg # https://github.com/orgs/community/discussions/26434
CDSAPI_URL: ${{ vars.CDSAPI_URL }}
CDSAPI_KEY: ${{ secrets.CDSAPI_TOKEN }}
EARTHDATAHUB_API_KEY: ${{ secrets.EARTHDATAHUB_API_KEY}}

steps:
- name: Setup cache and secrets (Linux & MacOS)
Expand Down
6 changes: 6 additions & 0 deletions RELEASE_NOTES.rst
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ Upcoming Release
buffer that computes a geometrically accurate Euclidean buffer, correct in
diagonal directions.

* Add an Earth Data Hub (EDH) source for ERA5 as a new dataset module ``era5-edh``,
which serves data without the CDS processing queue. Configure a DestinE API key
(see :doc:`installation`) and pass ``module="era5-edh"`` when creating a cutout.
Only the native 0.25°×0.25° grid is supported; use ``module="era5"`` (CDS) for
other resolutions.

**Bug fixes**

* Fix ``Cutout.line_rating`` passing line azimuth in radians while
Expand Down
9 changes: 7 additions & 2 deletions atlite/datasets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

"""atlite datasets."""

from atlite.datasets import era5, gebco, sarah
from atlite.datasets import era5, era5_edh, gebco, sarah

modules = {"era5": era5, "sarah": sarah, "gebco": gebco}
modules = {
"era5": era5,
"era5-edh": era5_edh,
"sarah": sarah,
"gebco": gebco,
}
116 changes: 77 additions & 39 deletions atlite/datasets/era5.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import pandas as pd
import xarray as xr
from dask import compute, delayed
from dask.array import arctan2, sqrt
from numpy import atleast_1d

from atlite.gis import maybe_swap_spatial_dims
Expand Down Expand Up @@ -129,6 +128,40 @@ def _rename_and_clean_coords(ds: xr.Dataset, add_lon_lat: bool = True) -> xr.Dat
return ds.drop_vars(["expver", "number"], errors="ignore")


def _process_wind(ds: xr.Dataset, single_precision: bool = False) -> xr.Dataset:
"""
Derive wind speed, shear exponent, azimuth and roughness from raw components.

Shared by the CDS (:mod:`atlite.datasets.era5`) and EDH
(:mod:`atlite.datasets.era5_edh`) backends. Operates on a dataset carrying
the raw ``u10``/``v10``/``u100``/``v100``/``fsr`` variables.
``single_precision`` casts the float64-promoted shear and azimuth back to
float32 (used by EDH to halve on-disk size).

Returns
-------
xr.Dataset
Dataset with variables: wnd100m, wnd_shear_exp, wnd_azimuth, roughness.
"""
for h in (10, 100):
units = ds[f"u{h}"].attrs.get("units", "m s**-1")
ds[f"wnd{h}m"] = np.sqrt(ds[f"u{h}"] ** 2 + ds[f"v{h}"] ** 2).assign_attrs(
units=units, long_name=f"{h} metre wind speed"
)
shear = (np.log(ds["wnd10m"] / ds["wnd100m"]) / np.log(10 / 100)).assign_attrs(
units="", long_name="wind shear exponent"
)
ds["wnd_shear_exp"] = shear.astype(np.float32) if single_precision else shear

# span the whole circle: 0 is north, π/2 is east, -π is south, 3π/2 is west
azimuth = np.arctan2(ds["u100"], ds["v100"])
azimuth = azimuth.where(azimuth >= 0, azimuth + 2 * np.pi)
ds["wnd_azimuth"] = azimuth.astype(np.float32) if single_precision else azimuth

ds = ds.drop_vars(["u100", "v100", "u10", "v10", "wnd10m"])
return ds.rename({"fsr": "roughness"})


def get_data_wind(retrieval_params: dict[str, Any]) -> xr.Dataset:
"""
Retrieve and compute wind speed variables from ERA5.
Expand Down Expand Up @@ -157,21 +190,7 @@ def get_data_wind(retrieval_params: dict[str, Any]) -> xr.Dataset:
**retrieval_params,
)
ds = _rename_and_clean_coords(ds)

for h in [10, 100]:
ds[f"wnd{h}m"] = sqrt(ds[f"u{h}"] ** 2 + ds[f"v{h}"] ** 2).assign_attrs(
units=ds[f"u{h}"].attrs["units"], long_name=f"{h} metre wind speed"
)
ds["wnd_shear_exp"] = (
np.log(ds["wnd10m"] / ds["wnd100m"]) / np.log(10 / 100)
).assign_attrs(units="", long_name="wind shear exponent")

# span the whole circle: 0 is north, π/2 is east, -π is south, 3π/2 is west
azimuth = arctan2(ds["u100"], ds["v100"])
ds["wnd_azimuth"] = azimuth.where(azimuth >= 0, azimuth + 2 * np.pi)

ds = ds.drop_vars(["u100", "v100", "u10", "v10", "wnd10m"])
return ds.rename({"fsr": "roughness"})
return _process_wind(ds)


def sanitize_wind(ds: xr.Dataset) -> xr.Dataset:
Expand All @@ -192,36 +211,22 @@ def sanitize_wind(ds: xr.Dataset) -> xr.Dataset:
return ds


def get_data_influx(retrieval_params: dict[str, Any]) -> xr.Dataset:
def _process_influx(ds: xr.Dataset, single_precision: bool = False) -> xr.Dataset:
"""
Retrieve and compute solar radiation variables from ERA5.
Derive influx variables and solar position from raw radiation fields.

Downloads radiation components, converts from J/m² to W/m², computes
albedo, diffuse radiation, and solar position (altitude/azimuth).

Parameters
----------
retrieval_params : dict[str, Any]
CDS API retrieval parameters including area, time, and format.
Shared by the CDS (:mod:`atlite.datasets.era5`) and EDH
(:mod:`atlite.datasets.era5_edh`) backends. Operates on a dataset carrying
the raw ``ssrd``/``ssr``/``fdir``/``tisr`` variables. ``single_precision``
casts the solar-position fields back to float32 (used by EDH to halve
on-disk size).

Returns
-------
xr.Dataset
Dataset with variables: influx_toa, influx_direct, influx_diffuse,
albedo, solar_altitude, solar_azimuth.
"""
ds = retrieve_data(
variable=[
"surface_net_solar_radiation",
"surface_solar_radiation_downwards",
"toa_incident_solar_radiation",
"total_sky_direct_solar_radiation_at_surface",
],
**retrieval_params,
)

ds = _rename_and_clean_coords(ds)

ds = ds.rename({"fdir": "influx_direct", "tisr": "influx_toa"})
ds["albedo"] = (
((ds["ssrd"] - ds["ssr"]) / ds["ssrd"].where(ds["ssrd"] != 0))
Expand All @@ -245,13 +250,46 @@ def get_data_influx(retrieval_params: dict[str, Any]) -> xr.Dataset:
# Suppress DeprecationWarning from new SolarPosition calculation (#199)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
time_shift = pd.to_timedelta("-30 minutes")
sp = SolarPosition(ds, time_shift=time_shift)
sp = SolarPosition(ds, time_shift=pd.to_timedelta("-30 minutes"))
sp = sp.rename({v: f"solar_{v}" for v in sp.data_vars})
if single_precision:
sp = sp.astype(np.float32)

return xr.merge([ds, sp])


def get_data_influx(retrieval_params: dict[str, Any]) -> xr.Dataset:
"""
Retrieve and compute solar radiation variables from ERA5.

Downloads radiation components, converts from J/m² to W/m², computes
albedo, diffuse radiation, and solar position (altitude/azimuth).

Parameters
----------
retrieval_params : dict[str, Any]
CDS API retrieval parameters including area, time, and format.

Returns
-------
xr.Dataset
Dataset with variables: influx_toa, influx_direct, influx_diffuse,
albedo, solar_altitude, solar_azimuth.
"""
ds = retrieve_data(
variable=[
"surface_net_solar_radiation",
"surface_solar_radiation_downwards",
"toa_incident_solar_radiation",
"total_sky_direct_solar_radiation_at_surface",
],
**retrieval_params,
)

ds = _rename_and_clean_coords(ds)
return _process_influx(ds)


def sanitize_influx(ds: xr.Dataset) -> xr.Dataset:
"""
Clip negative radiation values to zero.
Expand Down
Loading
Loading