How to Estimate Wind Speed at Hub Height in Python
On this page
Problem statement
Weather stations measure wind at 10 m. Modern turbines put their hubs at 100–170 m, where the wind is faster and steadier, and the energy calculation depends on the cube of the speed at that height. Moving a speed from 10 m to the hub is therefore the most consequential single step in a wind resource estimate, and it is usually done with one number — the shear exponent — copied from a textbook.
When a dataset offers two heights, the exponent can be measured instead. ERA5 via Open-Meteo gives 10 m and 100 m for every hour at every grid cell, which is enough to measure shear per site, per hour of day and per season, and to test how wrong the textbook value is.
Quick answer
The power law moves a speed between heights:
v(h) = v(h_ref) · (h / h_ref) ** α
Measure α from two heights, then apply it from the higher one — the shorter the extrapolation, the smaller the error:
import requests, numpy as np, pandas as pd
def wind(lat, lon):
j = requests.get("https://archive-api.open-meteo.com/v1/archive", params={
"latitude": lat, "longitude": lon,
"start_date": "2023-01-01", "end_date": "2023-12-31",
"hourly": "wind_speed_10m,wind_speed_100m",
"wind_speed_unit": "ms", # the default is km/h
"timezone": "UTC"}, timeout=120).json()
d = pd.DataFrame(j["hourly"])
d.index = pd.to_datetime(d.pop("time"))
return d.rename(columns={"wind_speed_10m": "v10", "wind_speed_100m": "v100"})
d = wind(53.55, 9.99) # Hamburg
alpha = np.log(d.v100.mean() / d.v10.mean()) / np.log(100 / 10)
hub = 117
d["v_hub"] = d.v100 * (hub / 100) ** alpha
print(f"mean speed: 10 m {d.v10.mean():.2f}, 100 m {d.v100.mean():.2f} m/s")
print(f"shear exponent from the two means: α = {alpha:.3f}")
print(f"at {hub} m: {d.v_hub.mean():.2f} m/s")
mean speed: 10 m 3.94, 100 m 6.61 m/s
shear exponent from the two means: α = 0.225
at 117 m: 6.85 m/s
The measured α of 0.22 is 57% higher than the textbook 1/7, and that matters when extrapolating from 10 m — much less when starting from 100 m.
Metres per second, a measured exponent, the nearest height
1. Request speeds in metres per second
Open-Meteo returns km/h unless told otherwise. Hamburg's 100 m mean arrives as 23.8, which would be an extraordinary site in m/s; it is 6.6 m/s.
2. Measure α from two heights where you can
Compute it from the ratio of means for a site-level value, or hour by hour for a distribution. Drop hours where either speed is below about 1 m/s; the logarithm of a ratio of two small numbers is noise.
3. Extrapolate from the nearest height
From 100 m to 117 m, an error of 0.05 in α changes the speed by less than 1%. From 10 m to 117 m, the same error changes it by 13%.
4. Let α vary by hour if the hours matter
Shear is low on sunny afternoons and high on clear nights. If the analysis needs the diurnal pattern of output — for matching demand, for example — use an hourly α, not an annual one.
5. Validate the method on the heights you have
Extrapolate 10 m to 100 m with the method you intend to use, and compare with the modelled 100 m speeds. The error there bounds the error you should expect at hub height.
How wrong the one-seventh rule is in Hamburg
Example 1 — how wrong is 1/7?
sites = {"Ringkøbing": (56.09, 8.24), "Hamburg": (53.55, 9.99), "Albacete": (38.99, -1.86)}
print(f"{'site':<12}{'α':>7}{'v100 modelled':>15}{'10 m × 1/7':>12}{'error':>8}{'error in v³':>13}")
for name, (lat, lon) in sites.items():
d = wind(lat, lon)
a = np.log(d.v100.mean() / d.v10.mean()) / np.log(10)
guess = d.v10 * 10 ** (1 / 7)
err_v = guess.mean() / d.v100.mean() - 1
err_p = (guess ** 3).mean() / (d.v100 ** 3).mean() - 1
print(f"{name:<12}{a:>7.3f}{d.v100.mean():>15.2f}{guess.mean():>12.2f}"
f"{err_v:>8.0%}{err_p:>13.0%}")
site α v100 modelled 10 m × 1/7 error error in v³
Ringkøbing 0.183 8.37 7.62 -9% -20%
Hamburg 0.225 6.61 5.48 -17% -38%
Albacete 0.220 5.12 4.28 -16% -36%
The textbook exponent underestimates the 100 m speed at every site, by 9–17%, and the error in v³ — the energy in the wind — is 2.2 times larger: 20–38%.
Example 2 — shear by hour of day and by season
d = wind(53.55, 9.99)
ok = (d.v10 > 1) & (d.v100 > 1)
a = np.log(d.v100[ok] / d.v10[ok]) / np.log(10)
table = a.groupby([a.index.month.map(lambda m: "summer" if m in (5, 6, 7, 8) else
"winter" if m in (11, 12, 1, 2) else "other"),
a.index.hour]).median().unstack(0)
print(table.loc[[0, 3, 6, 9, 12, 15, 18, 21], ["winter", "summer"]].round(3).to_string())
time winter summer
time
0 0.248 0.282
3 0.243 0.290
6 0.241 0.193
9 0.215 0.126
12 0.167 0.122
15 0.229 0.134
18 0.257 0.213
21 0.253 0.283
In summer the diurnal cycle is strong: α falls from 0.29 at night to 0.12 at midday, when the sun mixes the lower atmosphere. In winter the sun is too weak to do much: the dip reaches only 0.17 around noon and shear is back near 0.25 by evening.
Example 3 — the log law and the roughness length
# v(h) ∝ ln(h / z0): solve for z0 from the two heights, then extrapolate.
d = wind(53.55, 9.99)
r = d.v100.mean() / d.v10.mean()
z0 = np.exp((np.log(100) - r * np.log(10)) / (1 - r))
v_log = d.v100.mean() * np.log(hub / z0) / np.log(100 / z0)
v_pow = d.v100.mean() * (hub / 100) ** alpha
print(f"implied roughness length z0 = {z0:.2f} m")
print(f"at {hub} m: log law {v_log:.2f} m/s, power law {v_pow:.2f} m/s")
implied roughness length z0 = 0.33 m
at 117 m: log law 6.80 m/s, power law 6.85 m/s
The two laws agree closely over a short extrapolation. The implied roughness length is also a sanity check on the data: 0.1–0.5 m is farmland with hedges and scattered buildings; 1 m or more is forest or town.
Why shear exists, and averaging exponents
Why shear exists
The ground slows the air next to it, and the effect weakens with height. Rough ground — forest, towns, hedgerows — slows it more and deeper, which is why α is larger over land than over water. Stability adds a second effect: when the ground is colder than the air above it, vertical mixing stops and the layers decouple.
Why reanalysis heights are a gift
ERA5's 100 m wind is a model level, not a measurement, but it is a model that knows the stability and the surface roughness hour by hour. Starting from it removes most of the extrapolation. It does not remove the grid-cell smoothing: a ridge inside a 30 km cell is windier than the cell.
Why a mean α is not the α of the means
α computed hour by hour and averaged is not the same as α computed from the two mean speeds, because the relationship is non-linear. For energy work, apply α to each hour, or use the one derived from the means as a single scalar — but say which.
Forests, complex terrain and low-level jets
- Forest sites need a displacement height: the effective ground is two thirds of the canopy height up.
- Complex terrain: speed-up over hills breaks the power law; use a flow model or a mast.
- Low-level jets at night can make the wind at 150 m faster than a smooth profile predicts — a benefit the power law misses.
- Offshore: α is typically 0.08–0.12; 1/7 overestimates offshore shear.
- Measurement masts with three or more heights allow a fitted profile rather than a two-point exponent.
Internal links
- Wind resource explained — the cube, the distribution and the shear
- How to estimate wind energy with a Weibull distribution — the next step with the hub-height speeds
- Wind speed at 10 m used as hub height — the fix guide
- Capacity factor explained — turning speeds into a yield
- How to read GRIB weather data in Python — ERA5 directly, for many points
- NetCDF and gridded data explained — what a model level is
FAQ
How do I convert wind speed from 10 m to hub height?
Use the power law with a shear exponent measured at the site, preferably starting from the nearest measured height.
What shear exponent should I use?
Measure it. Over land it is usually 0.15–0.30; the textbook 1/7 (0.143) suits open, flat, neutral conditions.
Is the log law better than the power law?
It is more physical and needs a roughness length. Over short extrapolations the two agree closely.
Does Open-Meteo provide wind at 100 m?
Yes, in the historical archive (from ERA5). Request wind_speed_unit=ms or the values come in km/h.
Why does shear change during the day?
Sunshine heats the ground and mixes the air vertically in the afternoon; at night the ground cools and the layers decouple.
How far can I extrapolate?
As little as possible. The error grows with the logarithm of the height ratio times the error in α.