Forecasting solar and wind output
Forecasting how much power a solar or wind farm will generate is really a weather forecasting problem, because the fuel — sunlight and wind — cannot be stored or turned on demand.
- 9 min read
- 3 reading levels
- Published
Read these first
On this page 6
One lesson, three depths. Pick the one that fits you today — you can switch any time.
Beginner — No maths. Plain English.
Forecasting solar and wind power is really a weather forecast, translated into megawatts.
You already do this without a machine. You glance at the sky before hanging laundry out to dry, and judge whether today's sun and breeze will get it dry by evening. A cloudy, still day means wet clothes at sunset. A sunny, breezy one means dry clothes by lunch. Forecasting a solar farm's output, or a wind farm's, is the same judgement, made precisely instead of by eye.
Why it exists
A coal or gas power plant can be told to produce more or less electricity, on demand. A solar panel cannot. It produces exactly what the available sunlight allows, and nothing more, regardless of how much electricity is needed at that moment. Wind turbines are the same, tied to however much wind happens to be blowing.
Grid operators need to know, hours or a day ahead, roughly how much solar and wind power will be available. That lets them plan everything else around it — how much other generation to have ready, and how much to hold in batteries. Getting this forecast wrong forces expensive last-minute backup power, or wastes clean energy that could have been used.
How it works
weather forecast (cloud cover, wind speed, temperature)
|
v
[ model: weather conditions -> expected power output ]
|
v
predicted generation, in megawatts, for the next hours or day
|
v
feeds directly into grid scheduling decisionsThe relationship between weather and output is physically well understood. More sun means more solar power, up to a panel's rated capacity. More wind means more turbine power, up to a point — after which turbines are deliberately throttled to avoid damage. Machine learning earns its place by learning the messier, site-specific details. A shadow that falls at a particular hour. How a specific hillside affects local wind speed.
Where you have already seen it
Grid operators, including India's Grid-India (formerly POSOCO), forecast expected solar and wind generation as a routine part of daily grid scheduling. Rooftop solar apps that estimate "your panels will generate about this many units today" run a smaller version of the same forecast.
An honest note
A forecast error here has a real financial cost, not only an inconvenience. Under-forecasting means paying for backup generation that turns out not to be needed. Over-forecasting means the grid is short of power it was counting on. This is exactly why grid operators keep a margin of reserve capacity, sized around how reliable their forecasts have historically been. The forecast is one input into a system built to tolerate it being imperfect, not a number treated as certain.
Remember this
- Solar and wind power cannot be turned up on demand; forecasting them is forecasting the weather that fuels them.
- A good forecast lets grid operators plan backup power and storage in advance, instead of reacting at the last minute.
- Forecast error carries a real financial and operational cost, which is why grids are designed with reserve margins, not built to assume a perfect forecast.
What to learn next
- Electricity load forecasting — forecasting the demand side, to match against this supply-side forecast.
- Battery dispatch and grid decisions — what happens with a forecast once it exists.
- What is time series data? — the general structure of the hourly generation data this problem is built on.
Developer — Code and libraries.
Setup
pip install numpy scikit-learnMinimal runnable code
A simplified solar farm: output depends on time of day (a clear physical curve) and cloud cover (which the naive forecast below ignores entirely).
import numpy as np
from sklearn.ensemble import RandomForestRegressor
rng = np.random.default_rng(0)
CAPACITY_MW = 100.0
def solar_elevation(hour):
return np.clip(np.sin(np.pi * (hour - 6) / 12), 0, None) # zero at night, peaks at noon
def true_output(hour, cloud_cover):
clear_sky = CAPACITY_MW * solar_elevation(hour)
return clear_sky * (1 - 0.85 * cloud_cover) # heavy cloud blocks most output
# Training data: 200 days, hourly, with a different cloud pattern each day.
hours = np.tile(np.arange(24), 200)
cloud_cover = np.repeat(rng.beta(2, 3, size=200), 24)
power = np.clip(true_output(hours, cloud_cover) + rng.normal(0, 2.0, size=len(hours)), 0, CAPACITY_MW)
model = RandomForestRegressor(n_estimators=100, random_state=0)
model.fit(np.stack([hours, cloud_cover], axis=1), power)
# A new test day: partly cloudy.
test_hours = np.arange(24)
test_cloud = np.full(24, 0.4)
actual = true_output(test_hours, test_cloud)
predicted = model.predict(np.stack([test_hours, test_cloud], axis=1))
clear_sky_only = CAPACITY_MW * solar_elevation(test_hours) # naive: ignores clouds entirely
def rmse(a, b):
return np.sqrt(np.mean((a - b) ** 2))
print(f"naive 'ignore clouds' forecast RMSE: {rmse(clear_sky_only, actual):.2f} MW")
print(f"weather-aware model forecast RMSE: {rmse(predicted, actual):.2f} MW")
print(f"actual output at 12:00: {actual[12]:.1f} MW")
print(f"naive forecast at 12:00: {clear_sky_only[12]:.1f} MW")
print(f"model forecast at 12:00: {predicted[12]:.1f} MW")naive 'ignore clouds' forecast RMSE: 17.00 MW weather-aware model forecast RMSE: 1.29 MW actual output at 12:00: 66.0 MW naive forecast at 12:00: 100.0 MW model forecast at 12:00: 66.8 MW
What actually happened
The naive forecast — clear-sky output, ignoring cloud cover completely — overshoots badly at midday. It predicts full 100MW capacity, when the real output, dimmed by cloud, is only 66MW. The weather-aware model, trained on both time of day and cloud cover, gets within a megawatt of the true value.
RandomForestRegressorwas chosen because the true relationship has a hard zero at night, and a smooth curve during the day. Tree-based models represent that shape naturally, without needing to be told it in advance.- The
0.85cloud-blocking factor and theCAPACITY_MWlimit are invented for this example. But the underlying pattern is real physics, not an approximation specific to this toy: cloud cover sharply reduces solar output, and night produces exactly zero.
Common mistakes
Forecasting average daily output only, and discarding the hourly shape. Grid operators need to know when power will be available, not only how much across a whole day. A cloudy morning followed by a clear afternoon needs a very different response than the reverse.
Ignoring turbine cut-out speed for wind forecasting. Wind turbines are deliberately shut down above a certain wind speed, to prevent mechanical damage. So "more wind" does not mean "more power" without limit. A model trained only on moderate wind speeds can badly misforecast a storm.
Treating forecast accuracy as constant across horizons. A forecast for the next hour is far more reliable than one for six hours out. Reporting a single accuracy number across all horizons hides this, and can mislead a grid operator about how much to trust a same-day-ahead number specifically.
Try it yourself
Add a third feature, hour_ahead — how many hours in advance the forecast is being made. Make the injected noise grow with it (rng.normal(0, 2.0 + 0.3 * hour_ahead, ...)), the way real forecast uncertainty grows with lead time. Retrain, and compare accuracy at a 1-hour horizon versus a 12-hour horizon.
What to learn next
- Electricity load forecasting — the matching demand-side forecast this one gets compared against.
- Battery dispatch and grid decisions — how a forecast like this actually gets used operationally.
- Multivariate forecasting — forecasting several related signals — solar, wind, demand — together.
Researcher — Mathematics and papers.
Forecast horizons and methods
Renewable generation forecasting spans several distinct horizons, each dominated by a different method:
- Intra-hour (minutes ahead). Dominated by persistence and sky-camera or satellite-image-based nowcasting for solar, tracking cloud motion directly rather than relying on a numerical weather model, which updates too infrequently for this timescale.
- Intra-day to day-ahead (hours to ~48 hours). Dominated by numerical weather prediction (NWP) output, post-processed by a statistical or machine-learned model to convert weather variables into power output — the setting the developer example approximates.
- Medium-range (days ahead). Increasingly informed by the machine-learned weather models covered in machine-learned weather forecasting, given their speed advantage for running large forecast ensembles.
The power curve
For wind, the conversion from wind speed to power is a nonlinear power curve, specific to each turbine model:
P(v) = 0 for v < v_cut-in
P(v) = P_rated * g(v) for v_cut-in <= v < v_rated
P(v) = P_rated for v_rated <= v < v_cut-out
P(v) = 0 for v >= v_cut-outv_cut-in is the minimum wind speed for any generation, v_rated is the speed at which the turbine reaches its maximum rated power P_rated, and v_cut-out is the safety shutdown threshold. g(v) is typically a cubic relationship near cut-in, reflecting that wind power scales with the cube of wind speed, flattening as the turbine's control system limits output approaching v_rated.
Spatial and ensemble methods
Modern operational systems increasingly forecast an entire fleet of distributed generators jointly, rather than site by site. This exploits spatial correlation: cloud systems and wind fronts move coherently across a region, so nearby sites' forecast errors are correlated. That matters for aggregate grid-level uncertainty, not only individual-site accuracy. Ensemble NWP (multiple slightly perturbed physics simulations) provides a distribution over possible outcomes rather than one number, feeding directly into probabilistic generation forecasts used for reserve-margin planning.
Evaluation
Standard metrics include normalised RMSE (RMSE divided by installed capacity, enabling comparison across differently sized sites) and the pinball loss for probabilistic forecasts, which properly scores a predicted quantile rather than only a point estimate:
pinball_tau(y, y_hat) = max( tau * (y - y_hat), (tau - 1) * (y - y_hat) )for a target quantile tau — used because grid reserve planning cares about the tails of the output distribution (a very low-generation scenario), not only the expected value.
Current state
Machine-learned post-processing of NWP output is now standard operational practice at most grid operators with significant renewable penetration, typically as a correction layer on top of physics-based weather forecasts rather than a replacement for them. Forecast accuracy continues to improve with satellite nowcasting and higher-resolution NWP, though intra-hour solar forecasting during rapidly changing cloud conditions remains a documented weak point across the field.
Key references
- Sweeney, C. et al. (2020). The Future of Forecasting for Renewable Energy. WIREs Climate Change.
- Pinson, P. (2013). Wind Energy: Forecasting Challenges for Its Operational Management. Statistical Science.
- Hong, T. et al. (2016). Probabilistic Energy Forecasting: Global Energy Forecasting Competition 2014 and Beyond. International Journal of Forecasting.
What to learn next
- Electricity load forecasting — the demand-side counterpart evaluated against the same grid.
- Battery dispatch and grid decisions — how forecast uncertainty feeds into operational decisions.
- Prediction intervals for regression — the general theory behind pinball loss and quantile forecasting.