Fixing ERA5 Temperatures in Kelvin and Precipitation in Metres

On this page

Problem statement

ERA5 data straight from the archive looks wrong at first sight: summer temperatures of 300, rainfall totals of 0.003, and daily sums that are many times too large. Nothing is wrong. ERA5 uses SI units, kelvin for temperature and metres of water for precipitation, and its precipitation is an accumulation over a period, not a rate. Converting temperature is one subtraction. Converting precipitation needs care, because summing hourly values is right in one ERA5 product and badly wrong in another.

This page reads temperature and precipitation for Washington, D.C. from the ERA5 Zarr store on Google Cloud. It converts temperature and dew point from kelvin, turns precipitation from metres to millimetres, and checks the daily total against the Open-Meteo archive's ERA5 series and the station at Reagan National Airport.

Quick answer

Subtract 273.15 from temperatures and multiply precipitation by 1,000:

t = ds["2m_temperature"].sel(time=HOURS, latitude=38.75, longitude=283.0, method="nearest").load()
tp = ds["total_precipitation"].sel(time=HOURS, latitude=38.75, longitude=283.0, method="nearest").load()
print(f"units in the store: temperature {ds['2m_temperature'].attrs.get('units')}, precipitation {ds['total_precipitation'].attrs.get('units')}")
print(f"raw: temperature {float(t.max()):.2f}, precipitation {float(tp.sum()):.5f}; converted: {float(t.max()) - 273.15:.1f} °C, {float(tp.sum()) * 1000:.1f} mm over the {len(HOURS)} hours")
units in the store: temperature K, precipitation m
raw: temperature 311.46, precipitation 0.00000; converted: 38.3 °C, 0.0 mm over the 4 hours

The store labels temperature in K and precipitation in m. The afternoon peak of 311.46 K is 38.3 °C. Precipitation over those hours was zero, which is easy to misread as missing when the raw values are printed in metres.

Chart of ERA5 hourly precipitation at Washington through the local day of 9 January 2024, peaking near 9 mm in an hour, with the running total reaching 50.3 mm against a station total of 56.9 mm. Chart of ERA5 hourly precipitation at Washington through the local day of 9 January 2024, peaking near 9 mm in an hour, with the running total reaching 50.3 mm against a station total of 56.9 mm.
24 hourly values make the day.

Dew point and humidity, accumulations, the daily-mean trap

1. Dew point and humidity

Dew point is also in kelvin. Relative humidity follows from temperature and dew point with the Magnus formula:

td = ds["2m_dewpoint_temperature"].sel(time=HOURS, latitude=38.75, longitude=283.0, method="nearest").load() - 273.15
tc = t - 273.15
rh = 100 * np.exp(17.625 * td / (243.04 + td)) / np.exp(17.625 * tc / (243.04 + tc))
print(pd.DataFrame({"temp °C": tc.to_series(), "dew point °C": td.to_series(), "RH %": rh.to_series()}).astype(float).round(1).to_string())
                     temp °C  dew point °C  RH %
time                                            
2024-07-16 12:00:00     27.3          22.5  74.9
2024-07-16 15:00:00     33.9          22.4  51.2
2024-07-16 18:00:00     37.2          20.3  37.6
2024-07-16 21:00:00     38.3          19.2  32.9

Through the afternoon of 16 July the temperature rose from 27.3 to 38.3 °C, while the dew point fell from 22.5 to 19.2 °C. Relative humidity dropped from 74.9% to 32.9%.

ERA5 units and their conversions. ERA5 unitsvariableERA5 unitconvert2 m temperature, dew pointK- 273.15total precipitationm per hour (accumulated)x 1,000 for mmwind componentsm/sx 3.6 for km/hsurface pressurePa/ 100 for hParadiation: J/m² accumulated; / 3,600 for W/m²Hourly ERA5, ARCO store spatialworkflow.io
ERA5 units.

2. What "accumulated" means here

In this hourly store, each total_precipitation value is the amount that fell in the hour ending at its time step, so a day's total is the sum of 24 hours. Take 9 January 2024, the station's wettest day of the year, and compare with the Open-Meteo archive's ERA5 daily total and the station:

day = slice("2024-01-09T06:00", "2024-01-10T05:00")                   # hours ending 06:00-05:00 UTC: local 9 January (EST)
tp_day = float(ds["total_precipitation"].sel(latitude=38.75, longitude=283.0, method="nearest").sel(time=day).sum()) * 1000
om = om_daily("2024-01-09", "precipitation_sum")
sta = G.loc["2024-01-09", "PRCP"]
print(f"9 January 2024 precipitation: ERA5 store, 24 hourly values summed {tp_day:.1f} mm; Open-Meteo ERA5 daily {om:.1f} mm; station {sta:.1f} mm")
9 January 2024 precipitation: ERA5 store, 24 hourly values summed 50.3 mm; Open-Meteo ERA5 daily 49.7 mm; station 56.9 mm

Summing the 24 hourly values of 9 January gives 50.3 mm, matching Open-Meteo's ERA5 daily total of 49.7 mm, while the station measured 56.9 mm. The units and the summing are right. What remains is the difference between a model cell and a gauge.

3. The daily-mean trap

In the daily ERA5 products on the Climate Data Store, precipitation is a daily mean of hourly accumulations, or an accumulation from the forecast start. Show what multiplying an hourly mean by the wrong count does:

hourly_mm = ds["total_precipitation"].sel(latitude=38.75, longitude=283.0, method="nearest").sel(time=day).load() * 1000
mean_rate = float(hourly_mm.mean())
print(f"mean hourly amount {mean_rate:.2f} mm; x 24 = {mean_rate * 24:.1f} mm (the day's total); summing daily means of a month without x 24 would understate it 24-fold")
mean hourly amount 2.10 mm; x 24 = 50.3 mm (the day's total); summing daily means of a month without x 24 would understate it 24-fold

The day averaged 2.10 mm an hour. Multiplied by 24 that is the day's 50.3 mm, but a product that stores this mean, if summed as though it were a total, would understate the rain 24-fold.

273 or 273.15

The data used on this page

ERA5 hourly single-level data (Copernicus Climate Change Service) from Google's public ARCO-ERA5 Zarr store; the Open-Meteo archive ERA5 daily series; and the GHCN-Daily record for Reagan National Airport (USW00013743).

import hashlib
import json
from pathlib import Path

import numpy as np
import pandas as pd
import requests
import xarray as xr

Path("data").mkdir(exist_ok=True)
ds = xr.open_zarr("gs://gcp-public-data-arco-era5/ar/full_37-1h-0p25deg-chunk-1.zarr-v3", chunks=None, storage_options={"token": "anon"})
HOURS = pd.date_range("2024-07-16T12:00", "2024-07-16T23:00", freq="3h")

def om_daily(day, var):
    params = {"latitude": 38.8472, "longitude": -77.0345, "start_date": day, "end_date": day, "daily": var, "models": "era5", "timezone": "America/New_York"}
    f = Path("data/om_" + hashlib.md5(json.dumps(params, sort_keys=True).encode()).hexdigest() + ".json")
    if not f.exists():
        r = requests.get("https://archive-api.open-meteo.com/v1/archive", params=params, timeout=300); r.raise_for_status(); f.write_text(r.text)
    return json.loads(f.read_text())["daily"][var][0]

CSV = Path("data/USW00013743.csv")
if not CSV.exists():
    r = requests.get("https://www.ncei.noaa.gov/data/global-historical-climatology-network-daily/access/USW00013743.csv", timeout=600); r.raise_for_status(); CSV.write_bytes(r.content)
G = pd.read_csv(CSV, usecols=["DATE", "PRCP"], parse_dates=["DATE"]).set_index("DATE") / 10
print(f"{len(HOURS)} time steps selected")
4 time steps selected

Four 3-hourly steps on 16 July 2024 are selected.

Example 1: 273 or 273.15

The difference between subtracting 273 and 273.15 is small but systematic:

mean_k = float(t.mean())
print(f"mean of the selected hours: {mean_k - 273.15:.2f} °C with 273.15, {mean_k - 273:.2f} °C with 273; error {0.15:.2f} °C in every value")
mean of the selected hours: 34.18 °C with 273.15, 34.33 °C with 273; error 0.15 °C in every value

Subtracting 273 instead of 273.15 makes every value 0.15 °C too warm, 34.33 instead of 34.18 °C here: small, but systematic, and enough to bias a trend comparison.

Precipitation on 9 January 2024 at Washington from three sources. 9 January 2024 precipitationERA5 store, 24 hours summed50.3 mmOpen-Meteo ERA5 daily49.7 mmstation gauge56.9 mm020 mm40 mm60 mmUnits right; the gap is cell vs gauge spatialworkflow.io
Three sources, one day.

SI units and accumulated variables

Instantaneous and accumulated variables in ERA5. two kinds of variableaccumulatedprecipitation, radiationsum hours within the dayinstantaneoustemperature, wind, pressurevalue at the time stepDaily products use other conventions spatialworkflow.io
Two kinds of variable.

ERA5 follows the conventions of the ECMWF model that produces it: SI units throughout, and variables that are either instantaneous or accumulated. Temperature, dew point, wind and pressure are instantaneous, the state at the time step, in K, m/s and Pa. Precipitation, radiation and evaporation are accumulated, a total over a period, in metres of water for precipitation and J/m² for radiation. In the hourly ERA5 data, including Google's ARCO store, a precipitation value is the total for the hour ending at that time, so 24 consecutive values sum to a day. Daily statistics products and ERA5-Land use other conventions, such as daily means or accumulation from 00 UTC, and those need different handling.

Conversion is mechanical once the convention is known. Subtract 273.15 for °C, multiply metres by 1,000 for millimetres, and sum hourly accumulations within the local day, which in Washington starts at 04:00 or 05:00 UTC. ERA5 precipitation is a model product, so a day's total at a point can differ widely from a gauge even when the units are right. Check the units against a reference before trusting the magnitude.

Negative precipitation, radiation and ERA5-Land

ERA5 unit problems and fixes. unit trapsSymptomFixtemperatures near 300subtract 273.15rain of 0.003metres: x 1,000daily rain 24x too smallmean vs total confusedevery value 0.15 °C warm273.15, not 2739 January 2024: 2.10 mm an hour on average spatialworkflow.io
Unit traps.
  • Negative precipitation: tiny negative values can appear from data packing; clip at zero.
  • Radiation: divide accumulated J/m² by 3,600 for an hourly mean in W/m².
  • ERA5-Land: its accumulations run from 00 UTC through the day; take differences between steps to get hourly amounts.
  • GRIB from the CDS: the same units apply; cfgrib keeps the units attribute, so check it.

FAQ

How do I convert ERA5 temperature to Celsius?

Subtract 273.15 from the kelvin values.

What units is ERA5 precipitation in?

Metres of water accumulated over the period; multiply by 1,000 for millimetres.

How do I get daily precipitation from hourly ERA5?

Sum the 24 hourly values within the day you want, in local time if the day should be local.

Why is my ERA5 rainfall far too small or too large?

Usually metres not converted to millimetres, or daily means and accumulations confused between ERA5 products.