← All posts

Understanding P50/P90 Exceedance Probabilities

yield-assessment
offshore-wind
data-analysis
Monte Carlo simulation of AEP uncertainty to derive P50 and P90 estimates.
Author

Anemona Team

Published

January 15, 2026

Introduction

P50 is the AEP exceeded 50% of years. P90 is exceeded 90% of years — i.e. a pessimistic but bankable estimate. We derive these by propagating uncertainty sources through a Monte Carlo simulation.

Code
import numpy as np
import matplotlib.pyplot as plt

rng = np.random.default_rng(0)
n_simulations = 10_000
p50_aep = 200.0  # GWh/yr (central estimate)
Code
# Uncertainty sources (all log-normal, combined in quadrature)
sigma_wind     = 0.06  # 6% wind resource uncertainty
sigma_wake     = 0.03  # 3% wake model uncertainty
sigma_avail    = 0.02  # 2% availability uncertainty
sigma_total    = np.sqrt(sigma_wind**2 + sigma_wake**2 + sigma_avail**2)

aep_samples = p50_aep * np.exp(rng.normal(0, sigma_total, n_simulations))
Code
p50 = np.percentile(aep_samples, 50)
p90 = np.percentile(aep_samples, 10)  # exceeded 90% of the time

print(f'P50 AEP: {p50:.1f} GWh/yr')
print(f'P90 AEP: {p90:.1f} GWh/yr')
print(f'P90/P50 ratio: {p90/p50:.3f}')

fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(aep_samples, bins=60, density=True, alpha=0.6)
ax.axvline(p50, color='#a43e85', lw=2, label=f'P50 = {p50:.0f} GWh')
ax.axvline(p90, color='#edbe4f', lw=2, label=f'P90 = {p90:.0f} GWh')
ax.set_xlabel('AEP (GWh/yr)')
ax.legend()
plt.tight_layout()

Conclusion

With combined uncertainty σ ≈ 7%, the P90/P50 ratio is roughly 0.91. Lenders typically require P90 AEP for debt sizing — understanding this ratio is critical for project financing.