Code: https://github.com/quochung-cyou/solar_radiation_prediction_tahmo
Task: Predict incoming shortwave solar radiation (W/m²) at 15-minute intervals for the missing even months of year 1 at each TAHMO station.
Final Score: Abs MBE (3.640988734), Abs RMSE (61.55162364) (0.357163087)


Problem Summary
The task is to reconstruct incoming shortwave radiation during the first year after sensor installation, using the period when sensors are still assumed to be well-calibrated. The training set contains radiation for odd months only; the target months to fill are the even months. Temperature, relative humidity, and precipitation are available for all months.
- Evaluation: score = 0.5 × (1 − |MBE| / 5.89) + 0.5 × (1 − RMSE / 92.26), where higher is better (max 1.0). The weighted error component is 0.5 × |MBE| / 5.89 + 0.5 × RMSE / 92.26.
- Train: 642,175 rows across 40 stations, odd months only
- Test: 683,353 rows across the same 40 stations, even months only
- Timestamps: UTC, 15-minute resolution
Data Used
Provided data
Train.csv/Test.csv: timestamp, station, temperature, relative humidity, precipitation, latitude, longitude, elevation, installation height, and target radiation (train only).
External data (operationally / freely available)
- ERA5 reanalysis via Open-Meteo: cloud cover, shortwave radiation, direct/diffuse radiation, precipitation, dewpoint, surface pressure, wind.
- NASA POWER: all-sky/clear-sky GHI/DNI/DIFF, cloud amount, AOD, precipitation.
- CAMS Solar Radiation (15-minute, point API): all-sky and clear-sky GHI, DNI, DHI, TOA irradiation, reliability.
- CAMS EAC4: aerosol optical depth (total, dust, organic, black carbon, sulfate, sea salt) and total column water vapour.
All external sources were merged per-station by nearest timestamp with appropriate tolerances (15–180 min). CAMS Solar radiation values were converted from W·h/m² to W/m² by multiplying by 4 to match the 15-minute interval target.
Approach
Core idea
Treat the problem as temporal interpolation / gap-filling, not time-series forecasting. Because we already have concurrent weather for the even-month timestamps, the model learns a cross-sectional mapping from weather + solar geometry + satellite data to surface radiation.
Pipeline
- Solar geometry & clear-sky physics via
pvlib(Ineichen-Perezmodel): solar zenith, elevation, azimuth, cos(zenith), extraterrestrial radiation, airmass, Linke turbidity, clear-sky GHI/DNI/DHI. - Meteorological features: Hargreaves radiation estimate, 24-hour temperature range, dew point, dew point depression, vapour pressure deficit, precipitable water estimate, rain flags, recent precipitation.
- Temporal features: cyclical hour, day-of-year, month, plus raw hour/minute/dow/year.
- Lag/rolling features on temperature, humidity, and precipitation (1, 2, 4, 8 step lags; 4, 8, 16, 96-step rolling means/stds; first differences), computed per station and per contiguous block to avoid month-boundary leakage.
- Adjacent-month climatology: mean/std radiation per station-hour from the two nearest odd months.
- External satellite features: 41 columns from ERA5, NASA POWER, CAMS Solar, and CAMS EAC4.
- Model ensemble: three gradient-boosted decision trees trained with per-station, leave-one-odd-month-out CV:
- LightGBM per-station (one model per station, learns local microclimate).
- LightGBM global (all stations together, learns universal physics).
- XGBoost global (algorithmic diversity).
- Adaptive ensemble: per-station weights are set by inverse OOF RMSE of the three models.
- Physical post-processing: force nighttime predictions to 0 (zenith ≥ 90°), clip to non-negative, cap at
ETR × 1.1andclearsky_ghi × 1.3.
Model Details
| Component | Details |
|---|---|
| Primary learners | LightGBM (per-station + global), XGBoost (global) |
| CV strategy | Leave-one-odd-month-out: 6 folds, each holding out one odd month |
| Per-station LGB | num_leaves=63, lr=0.05, feature_fraction=0.7, bagging_fraction=0.8, reg α/λ |
| Global LGB | num_leaves=127, larger capacity, same regularization |
| Global XGB | max_depth=7, hist tree method, min_child_weight=50 |
| Ensemble | Inverse-RMSE weighted blend of the three models per station |
| Final features | ~121 features |
What Worked
- External satellite data dominated the signal. Adding CAMS Solar + ERA5 + NASA POWER + EAC4 dropped validation RMSE from ~71.9 to ~62.1.
- Clear-sky physics features.
clearsky_ghi,cos(zenith), andetr_horizontalgive the model a strong physical upper bound and remove the need to learn solar geometry from scratch. - Hard nighttime mask. Setting radiation to 0 when the sun is below the horizon is a free physical constraint and removes ~50% of potential errors.
- Per-station models + global ensemble. Per-station models helped on stations with local data-quality issues; the global model provided robustness.
- Adjacent-month climatology. Using radiation statistics from the nearest odd months provided a strong baseline for the missing even months.
Intuition
- Solar radiation is mostly geometry.
cos(zenith)explains the majority of GHI variance. Give the model the correct solar geometry and let it focus on the residual (clouds, aerosols, humidity). - Clear-sky is a strong prior. The clear-sky model provides a physically grounded upper bound. Predicting raw GHI with clear-sky as a feature is more robust than dividing by it (avoids twilight division-by-zero and propagates less model error).
- Concurrent weather matters more than history. Because we already know temperature/humidity/precipitation at the target timestamp, forecasting models are unnecessary. Lag features are still useful, but the strongest signal is the instantaneous weather + satellite state.
Files
notebook.ipynb— full training, inference, and submission pipeline.Train.csv/Test.csv— provided data.SampleSubmission.csv— submission format.
How to Run
The final submission CSV has columns ID, TargetMBE, TargetRMSE with identical values per row.
Install dependencies: pip install pvlib tqdm matplotlib tabulate catboost xarray netCDF4 cdsapi requests lightgbm xgboost
Download external data using the cells in notebook.ipynb (ERA5 via Open-Meteo, NASA POWER, CAMS Solar via CDS API, CAMS EAC4).
Run the notebook sequentially from top to bottom. It computes features, trains the three-model ensemble, applies physical post-processing, and writes the submission file.