Solar plant performance ratio: separate weather from equipment losses
In this article (8 sections)
Related programme: the Data Science course (6 months; ₹1,30,000 (inclusive of GST) · EMI from ₹5,000/month).
Performance ratio (PR) is the energy a solar plant delivered divided by the energy it would have delivered if it converted plane-of-array sunlight at its rated DC capacity: final yield (kWh AC ÷ kWp) over reference yield (kWh/m² of plane-of-array irradiation ÷ 1 kW/m²). Because sunshine sits in the denominator, PR stays roughly level on cloudy days and falls when equipment, the grid or dirt is losing energy. In the synthetic ten days below, a heavily overcast day keeps a PR of 0.793 while a grid outage day drops to 0.517.
What the asset manager is really asking
A monthly generation report says March was 6% below budget. The asset manager for a portfolio of rooftop and ground-mount plants has to answer one thing before anything else: was it the weather, or was it us? Weather shortfalls are nobody's fault and need no action. Equipment, soiling and outage losses are recoverable, and each has a different owner: the O&M contractor, the cleaning crew, the DISCOM or the inverter supplier. Energy alone cannot separate these. PR can, if the inputs are right.
Inverter, meter and irradiance files
| Source | Columns | Watch for |
|---|---|---|
| Energy meter or SCADA | timestamp, e_ac_kwh (export or inverter output), meter_id | Know whether it is inverter output or the export meter after transformer losses |
| Weather station | timestamp, poa_irradiance_w_m2, ghi_w_m2, module_temp_c, ambient_temp_c | POA and GHI are different sensors; labels are often ambiguous |
| Plant master | plant_id, dc_capacity_kwp, ac_capacity_kw, tilt, azimuth, module datasheet | DC and AC capacity are different numbers |
| Event log | start, end, event_type (grid_outage, curtailment, inverter_trip, cleaning) | Frequently kept in a spreadsheet, not SCADA |
Aggregate irradiance to kWh/m² by summing interval readings × interval length in hours, and only after dropping night-time negative offsets that some sensors report.
Definitions
- Final yield Y_f = E_AC ÷ P_DC (kWh ÷ kWp, in hours)
- Reference yield Y_r = H_POA ÷ G_ref, with G_ref = 1 kW/m² (also in hours)
- PR = Y_f ÷ Y_r
- Period PR = Σ E_AC ÷ (P_DC × Σ Y_r), a ratio of sums, not the mean of daily PRs
- Temperature-adjusted PR (optional) = E_AC ÷ (P_DC × Y_r × (1 + γ × (T_cell − 25))), where γ is the power temperature coefficient from your module datasheet
The temperature adjustment removes the expected summer dip so that a hot month is not read as degradation. Contracts and standards define their own correction methods; if your PR guarantee specifies one, use that method and not this simplified version.
Ten days on a synthetic 500 kWp plant
The data is synthetic. 4 March is heavily overcast, 6 March has a 3.5-hour grid outage, the modules gather dust through the first eight days, and they are cleaned on the morning of 9 March.
import pandas as pd
# Synthetic daily data for a hypothetical 500 kWp (DC) rooftop plant
P_DC_KWP = 500.0
G_STC = 1.0 # kW/m², the irradiance at standard test conditions
GAMMA = -0.0035 # per °C power temperature coefficient: take this from your module datasheet
d = pd.DataFrame({
"date": pd.date_range("2026-03-01", periods=10, freq="D"),
"e_ac_kwh": [2380, 2352, 2310, 1190, 2275, 1500, 2236, 2200, 2440, 2425],
"h_poa_kwh_m2": [5.90, 5.88, 5.85, 3.00, 5.82, 5.80, 5.78, 5.75, 5.95, 5.92],
"h_ghi_kwh_m2": [5.30, 5.28, 5.26, 2.80, 5.24, 5.22, 5.20, 5.18, 5.35, 5.33],
"grid_down_hours": [0, 0, 0, 0, 0, 3.5, 0, 0, 0, 0],
"t_cell_avg_c": [42, 43, 43, 33, 44, 44, 45, 45, 43, 44],
"cleaned": [False] * 8 + [True, False],
})
d["ref_yield_h"] = d["h_poa_kwh_m2"] / G_STC
d["final_yield_h"] = d["e_ac_kwh"] / P_DC_KWP
d["pr"] = d["final_yield_h"] / d["ref_yield_h"]
d["pr_ghi_wrong"] = d["final_yield_h"] / (d["h_ghi_kwh_m2"] / G_STC)
d["pr_temp_corr"] = d["e_ac_kwh"] / (P_DC_KWP * d["ref_yield_h"] * (1 + GAMMA * (d["t_cell_avg_c"] - 25)))
cols = ["e_ac_kwh", "h_poa_kwh_m2", "grid_down_hours", "pr", "pr_ghi_wrong", "pr_temp_corr"]
out = d.set_index(d["date"].dt.strftime("%d-%b"))[cols]
print(out.round(3).to_string())
clean_days = d[d["grid_down_hours"] == 0]
print("Period PR, all days:", round(d["e_ac_kwh"].sum() / (P_DC_KWP * d["ref_yield_h"].sum()), 3))
print("Period PR, excluding grid-outage day:",
round(clean_days["e_ac_kwh"].sum() / (P_DC_KWP * clean_days["ref_yield_h"].sum()), 3))Output:
e_ac_kwh h_poa_kwh_m2 grid_down_hours pr pr_ghi_wrong pr_temp_corr
date
01-Mar 2380 5.90 0.0 0.807 0.898 0.858
02-Mar 2352 5.88 0.0 0.800 0.891 0.854
03-Mar 2310 5.85 0.0 0.790 0.878 0.843
04-Mar 1190 3.00 0.0 0.793 0.850 0.816
05-Mar 2275 5.82 0.0 0.782 0.868 0.837
06-Mar 1500 5.80 3.5 0.517 0.575 0.554
07-Mar 2236 5.78 0.0 0.774 0.860 0.832
08-Mar 2200 5.75 0.0 0.765 0.849 0.823
09-Mar 2440 5.95 0.0 0.820 0.912 0.875
10-Mar 2425 5.92 0.0 0.819 0.910 0.878
Period PR, all days: 0.766
Period PR, excluding grid-outage day: 0.795Three stories in one column
The cloudy day is not a problem. Energy on 4 March halved to 1,190 kWh, yet PR was 0.793, in line with its neighbours. A report that ranks days by energy would flag it; PR correctly does not.
The outage day is. On 6 March irradiance was normal and PR fell to 0.517. The event log explains it: 3.5 hours without the grid. That loss belongs to the grid, not the plant, and for a contractor's PR guarantee it is usually excluded under the contract's own rules. Here, excluding it moves the period PR from 0.766 to 0.795. Report both, and say which one the contract uses.
Soiling hides in a slow slide. From 1 to 8 March PR drifts from 0.807 to 0.765 with no event at all. After cleaning on 9 March it jumps to 0.820. That step size, divided by the days since the last clean, is your soiling rate, and it is the input to a cleaning-frequency decision that weighs water and labour cost against recovered energy.
GHI, AC capacity and other PR inflators
- GHI in place of POA. Horizontal irradiance is lower than tilted irradiance for a south-facing tilted array, so dividing by GHI inflates PR. In this synthetic data it adds about 0.09 on every day, turning an ordinary plant into an apparently excellent one.
- AC capacity in the denominator. Plants are often built with more DC than AC capacity. Using kW AC in place of kWp DC inflates PR. Always state which capacity you used.
- A dirty or drifting pyranometer. If the sensor gathers dust like the modules, both numerator and denominator fall together and PR hides the soiling. A sensor cleaning log, or a comparison with a nearby reference or satellite estimate, catches this.
- Curtailment and export limits. When the DISCOM or a zero-export controller limits output, the energy is lost by instruction, not by fault. Flag those intervals from the event log.
- Averaging daily PRs. The mean of daily ratios weights a cloudy day the same as a sunny one. Use the ratio of sums for any period figure.
- Temperature effects read as degradation. Summer PR is normally lower. Compare the same month year on year, or use the temperature-adjusted series with the datasheet coefficient.
Presenting to the asset manager
Lead with a loss waterfall, not a table: budget energy, then the weather effect (from irradiance versus the budget's irradiance), then grid outage and curtailment, then soiling, then residual equipment loss, ending at actual energy. Put the daily PR chart underneath with event markers for outages and cleanings. The one sentence that matters is who owns each loss. A shortfall that is mostly weather needs no action; a residual equipment loss that grows month after month needs an inverter or string-level investigation, and that is where string current data comes in.
Building the skill
Normalising an outcome by the input that nobody controls, and then attributing the remainder, is a modelling habit that the Data Science programme builds on real time-series data. The same idea of a physical measurement checked against a book figure appears in fuel station wet-stock reconciliation. If you are coming from an energy O&M role, the free skill check will map what your current work already shows.
Frequently asked questions
What is the performance ratio of a solar plant?
Performance ratio is final yield divided by reference yield. Final yield is AC energy delivered divided by the plant's rated DC capacity in kWp. Reference yield is plane-of-array irradiation in kWh per square metre divided by the 1 kW per square metre reference irradiance. Because weather appears in the denominator, a well-run plant keeps a similar ratio on sunny and cloudy days.
Why does performance ratio fall on a day with no clouds?
Common causes are grid outages or curtailment that stop export, inverter trips, dirty modules, and a pyranometer that is itself dirty or misaligned. Look at the day's hourly profile and the event log before blaming the modules.
Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.
This article is part of our Data Science programme — 6 months. From data foundations to machine learning, deep learning and deployment.
Explore Data Science