Wind Energy from the Mean Speed Is Wrong
On this page
Problem statement
The wind energy estimate for a site was made by reading the power curve at the site's mean wind speed, or from monthly mean speeds out of a climate dataset, or by cubing the mean speed to get the power in the wind. At one site it came out 15% high, at another 40% low, and there is no correction factor that fixes both — because averaging the speed before the non-linear step throws away the information the energy depends on.
The fix is to keep the distribution: hourly speeds if you have them, a fitted Weibull if you have the parameters, and a Rayleigh assumption with an honest error bar if all you have is a mean.
Quick answer
Apply the power curve to each hour and average the power — never the other way round:
import requests, numpy as np, pandas as pd
from windpowerlib import WindTurbine
turbine = WindTurbine(turbine_type="V126/3450", hub_height=117)
cv, cp = turbine.power_curve.wind_speed, turbine.power_curve.value / 3.45e6
power = lambda v: np.interp(v, cv, cp, right=0)
def hub(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",
"timezone": "UTC"}, timeout=120).json()
d = pd.DataFrame(j["hourly"])
d.index = pd.to_datetime(d.pop("time"))
a = np.log(d.wind_speed_100m.mean() / d.wind_speed_10m.mean()) / np.log(10)
return d.wind_speed_100m * (117 / 100) ** a
sites = {"Ringkøbing": (56.09, 8.24), "Hamburg": (53.55, 9.99), "Albacete": (38.99, -1.86)}
print(f"{'site':<12}{'mean v':>7}{'hourly':>8}{'daily':>8}{'monthly':>9}{'annual':>8}")
for name, (lat, lon) in sites.items():
v = hub(lat, lon)
row = [power(v).mean(),
power(v.resample("D").mean()).mean(),
power(v.resample("ME").mean()).mean(),
float(power(v.mean()))]
print(f"{name:<12}{v.mean():>7.2f}" + "".join(f"{x:>8.1%}" for x in row[:2])
+ f"{row[2]:>9.1%}{row[3]:>8.1%}")
site mean v hourly daily monthly annual
Ringkøbing 8.61 55.1% 56.1% 63.5% 63.4%
Hamburg 6.85 39.8% 38.5% 34.0% 31.9%
Albacete 5.30 24.7% 20.6% 14.9% 14.2%
The hourly column is the reference. Every step of averaging before the power curve moves the answer, and not always in the same direction.
Finest time step, else integrate a distribution
1. Use the finest time step the data has
Hourly reanalysis or 10-minute mast data keep the spread of speeds. Daily means already compress it; monthly means compress it further.
2. If you only have Weibull parameters, integrate
Integrate the power curve against the Weibull density. Two numbers carry most of the information that 8,760 hourly values do.
3. If you only have a mean, assume a distribution and say so
A Rayleigh distribution (Weibull with k = 2) is the conventional assumption when only the mean is known. It is often within 10% and occasionally far off; report it as an estimate with that caveat.
4. Never cube the mean speed
The power in the wind is proportional to the mean of v³, which for real wind is 1.7–2 times the cube of the mean. A power-density map built from mean speeds understates every site, and understates the variable ones most.
5. Recompute any figure that came from monthly climatology
Climate datasets often publish monthly mean wind. They are useful for comparing regions and useless for energy without a distribution.
The Rayleigh fallback and the cube of the mean
Example 1 — the Rayleigh fallback
from scipy import stats
def rayleigh_cf(mean_v):
c = mean_v / 0.8862 # Weibull scale for k = 2
x = np.linspace(0, 30, 3001)
return np.trapezoid(stats.weibull_min.pdf(x, 2, scale=c) * power(x), x)
print(f"{'site':<12}{'hourly':>8}{'Rayleigh':>10}{'error':>8}{'fitted k':>10}")
for name, (lat, lon) in sites.items():
v = hub(lat, lon)
k, _, _ = stats.weibull_min.fit(v[v > 0], floc=0)
ref, ray = power(v).mean(), rayleigh_cf(v.mean())
print(f"{name:<12}{ref:>8.1%}{ray:>10.1%}{ray / ref - 1:>8.0%}{k:>10.2f}")
site hourly Rayleigh error fitted k
Ringkøbing 55.1% 53.8% -2% 2.19
Hamburg 39.8% 39.5% -1% 2.29
Albacete 24.7% 23.7% -4% 1.83
Where the true shape is close to k = 2 the Rayleigh estimate is good. Where it is not — the spread-out Albacete distribution, k ≈ 1.8 — it misses more. Still far better than reading the curve at the mean.
Example 2 — power density: mean of cube against cube of mean
for name, (lat, lon) in sites.items():
v = hub(lat, lon)
true_pd = 0.5 * 1.225 * (v ** 3).mean()
naive_pd = 0.5 * 1.225 * v.mean() ** 3
print(f"{name:<12} ½ρ·mean(v³) = {true_pd:>5.0f} W/m² "
f"½ρ·mean(v)³ = {naive_pd:>5.0f} W/m² ratio {true_pd / naive_pd:.2f}")
Ringkøbing ½ρ·mean(v³) = 688 W/m² ½ρ·mean(v)³ = 391 W/m² ratio 1.76
Hamburg ½ρ·mean(v³) = 331 W/m² ½ρ·mean(v)³ = 197 W/m² ratio 1.68
Albacete ½ρ·mean(v³) = 185 W/m² ½ρ·mean(v)³ = 91 W/m² ratio 2.03
This ratio — the energy pattern factor — is always above 1 and larger for more variable winds. A map of mean-speed cubes ranks sites differently from a map of true power density.
Example 3 — where on the curve the mean sits
for name, (lat, lon) in sites.items():
v = hub(lat, lon)
below_cut_in = (v < 3).mean()
at_rated = (v >= 12).mean()
print(f"{name:<12} mean {v.mean():.1f} m/s → curve reads {power(v.mean()):.0%} of rated; "
f"{below_cut_in:.0%} of hours below cut-in, {at_rated:.0%} at or above rated")
Ringkøbing mean 8.6 m/s → curve reads 63% of rated; 7% of hours below cut-in, 19% at or above rated
Hamburg mean 6.9 m/s → curve reads 32% of rated; 11% of hours below cut-in, 7% at or above rated
Albacete mean 5.3 m/s → curve reads 14% of rated; 25% of hours below cut-in, 2% at or above rated
At the windy site the mean sits on the steep middle of the curve, where the curve reads high and the hours above rated (whose surplus is thrown away) are not penalised. At the calm site the mean sits near the bottom, where the curve reads low and the windy hours that make all the energy are invisible.
Why the error has no fixed sign
Why the error has no fixed sign
The power curve is convex from cut-in to near rated and flat above rated. For a convex function, the function of the mean is below the mean of the function (Jensen's inequality); for the flat, capped part the reverse holds, because averaging moves hours from above rated — where the extra speed is wasted — down onto the steep part. Which effect dominates depends on where the site's distribution sits, which is why one correction factor cannot fix it.
Why daily means are already too coarse
A day with a calm morning and a gale in the afternoon averages to a moderate breeze. The turbine saw no output and then rated output; the daily mean predicts steady partial output. Wind varies strongly within a day, so even daily averaging loses a large part of the distribution.
Why the Rayleigh assumption is reasonable
Wind speed is the magnitude of a two-component velocity; if the components are independent and normally distributed with equal spread, the magnitude is Rayleigh-distributed. Real winds are close enough for a first estimate and far enough that a fitted Weibull, when available, is always better.
Air density, climatologies and turbulence
- Air density still needs correcting; the error from ignoring it is smaller but has a fixed sign.
- Monthly climatologies can be combined with a typical k for the region to rebuild a distribution, with the same caveats as Rayleigh.
- Turbulence within the hour also smooths the effective power curve; 10-minute data is the industry standard for that reason.
- Mean speed is fine for ranking very similar sites with the same distribution shape — not for energy.
- Wind atlases that publish power density have computed it correctly; do not recompute it from their mean speeds.
Internal links
- Wind resource explained — the cube and the distribution
- How to estimate wind energy with a Weibull distribution — the correct calculation
- Wind speed at 10 m used as hub height — the other common wind error
- How to estimate wind speed at hub height — where
hub()comes from - Capacity factor explained — what a plausible value looks like
- How to resample a time series with xarray — choosing when to aggregate
FAQ
Can I estimate wind energy from the mean wind speed?
Only with an assumed distribution — Rayleigh if nothing else is known — and with a caveat. Reading the power curve at the mean is wrong in either direction.
Why is the cube of the mean speed wrong?
Because the mean of the cube is larger than the cube of the mean for any variable wind, typically by a factor of 1.7–2.
Are daily mean wind speeds good enough?
No. Most of the variability that decides energy happens within the day.
What is the energy pattern factor?
The ratio of the mean of v³ to the cube of the mean speed. It measures how much a mean-speed shortcut understates the power in the wind.
How good is the Rayleigh assumption?
Often within 10% of the hourly calculation, worse where the distribution's shape parameter is far from 2.
Why can the error be positive at one site and negative at another?
The power curve is convex below rated and capped above it, and where the site's speeds fall on it decides which effect dominates.