The goal of PV system analytics is to concisely inform about the performance of an operating asset. This is done by evaluating performance through the detection and quantification of losses from operational PV system data. Depending on the customer type, performance is evaluated more from a financial perspective or more from a PV-system-performance perspective: do I want to know what the primary factors are that drive my measurable underperformance, or do I additionally want to know which hidden underperformance root-causes are overshadowed by primary loss factors such as curtailment?
This document describes how Univers's Performance+ answers both questions from the same input data and the same status-detection layer, producing two complementary views:
- An actual (operational) loss breakdown, derived from a gap analysis between expected and measured production. This is the financial view: it tells the asset owner where the measurable energy - and therefore revenue - was lost, and it reconciles exactly against the observed production gap (e.g. curtailment that is later compensated).
- A potential ("what-if") loss breakdown, focusing on DC-side mechanisms. This is the actionable O&M view: if we did not have a down inverter or a heavily curtailed site, which DC losses (soiling, module thermal, tracker, under-performing or down DC strings, inverter efficiency) would be predominant and impact performance? Out of this view, corrective maintenance activities can be prioritised.
Chapter 1 outlines the high-level workflow. Chapter 2 describes the actual loss breakdown (the priority-based gap allocation). Chapter 3 describes the potential loss breakdown (the gap-scaled "what-if" distribution). Chapter 4 covers site aggregation and the total-loss summary.
Note on units and resolution. All losses are computed per inverter at the native sub-daily resolution (e.g. 5 min) as instantaneous power. Per-inverter outputs are in kW / kWh; site-aggregated outputs are reported in MW / MWh. Every loss series is also aggregated to daily energy for reporting.
1. High-level workflow
Figure 1 shows the workflow from raw input data to the operational and potential loss breakdown split. The main steps are data collection, data preparation, data analysis, status detection of the underlying underperformances, loss assignment, and distribution into the individual loss categories. The result is a concise loss breakdown view showing actual losses (which can be compared to budget losses) as well as potential losses.
The two breakdowns share the same upstream layer:
- Consolidated signals (
InverterDataConsolidation) provide measured AC/DC power and several modelled expected-power permutations, including the expected AC power with degradation (p_ac_expected) used as the reference for the gap. - Consolidated status flags (
InverterStatusConsolidation) provide theac_status(primary),ac_status_secondary, anddc_statuscolumns that drive the masking and priority logic. - Per-inverter parameters (AC/DC nameplate capacity, EU efficiency, thermal coefficient gamma, kVA-temperature curve, daily soiling rate) are read from the inverter object attributes.
Figure 1: Flowchart loss breakdown
The reference for both breakdowns is the gap:
gap = (p_ac_expected - active_power) clipped to >= 0
The actual breakdown assigns named losses until this gap is closed; the potential breakdown distributes this same gap proportionally across the DC mechanisms.
2. Actual loss breakdown (gap analysis)
Actual losses are derived from a priority-ordered gap analysis. Losses are assigned based on their priority, the remaining gap is recalculated after every assignment, and once the gap is closed no further losses are assigned. This guarantees that the sum of all assigned losses reconciles exactly to the observed gap.
The allocation runs in five steps. Steps 1-3 cover the AC side and are mutually exclusive per timestamp: only the highest-priority AC condition that fires consumes the gap (down inverter -> clipping/MaxKVA -> reduced power), and within whichever step fires the gap is attributed to exactly one loss category. Step 4 then handles the DC side against whatever gap was not claimed by Steps 1-3: here multiple mechanisms (shading, branch losses, soiling, thermal, efficiency, tracker) can stack additively, each capped at the remaining gap in strict priority order, and all are gated to normal-operation timestamps. Step 5 writes any gap that survived all explicit categories to loss_residual.
2.1 AC-side cascade (Steps 1-3)
| Step | Loss category | Condition / status |
|---|---|---|
| 1 | loss_curtailment, loss_inv_thermal, loss_far_shading, loss_snow, loss_down |
Down inverter (zero/minimum output). A down timestamp is detected via two paths: (a) explicit INV_DOWN primary status always counts; (b) a power-based heuristic fires when output is very low (below THR_DOWN_ACTIVE_POWER | ac_capacity) or far below expected (ratio below THR_DOWN_RATIO_POWER). The heuristic is gated to NORMAL primary status only - this prevents it from firing at timestamps already owned by INV_POWER_LIMIT, SITE_CLIPPING, GRID_CURTAILMENT, etc. (which would double-count the gap into both LOSS_DOWN and the primary-status loss column). The heuristic additionally defers when dc_status is a tracker deviation or shading (TRACKER_WIND, TRACKER_NON_WIND, NEAR_SHADING, FAR_SHADING) so a stowed tracker or shaded array at dawn/dusk is not mis-labelled as a down inverter. Each down timestamp is attributed to exactly one mutually-exclusive root cause. |
| 2 | loss_inv_clip_operational, loss_maxkva_operational |
Operational losses at upper AC limits - gap-bounded expected - active on the inverter-clipping and MaxKVA masks. loss_maxkva_operational is excluded when ac_status_pri is INV_POWER_LIMIT or INV_CLIPPING: those statuses already claim the full expected - active gap in Step 3, so including it here too would double-count into both LOSS_POWER_LIMIT and LOSS_MAXKVA_OPERATIONAL. Their unbounded "design headroom" twins (loss_inv_clip, loss_maxkva) are computed alongside but reported as potential losses (see Sect. 3) - they sit on the DC-side of the cap, not in the operational reconciliation budget. |
| 3 | loss_site_clip, loss_curtailment, loss_power_limit, loss_reactive, loss_inv_thermal |
Reduced active power - site grid-export limit, grid-operator curtailment, firmware/remote power limit, reactive-power P-Q constraint, and inverter thermal derating. |
Why design and operational caps are reported separately. A clipping or MaxKVA event simultaneously produces two physically different losses: the operational drop of measured AC below post-cap expected AC, and the design DC headroom that the cap discards regardless of plant health. Mixing them in one stack made the AC total spike well past expected - active and broke the reconciliation invariant. The split keeps the operational total tight (Sigma AC operational + Sigma DC <= expected - active) while preserving the design figure for sizing and waterfall reviews.
2.2 DC-side allocation (Step 4)
Before any DC category is assigned, the AC losses already attributed in Steps 1-3 are subtracted so that the DC step only ever sees the un-attributed gap:
remaining_gap = (p_ac_expected - active_power - already_allocated_ac) clipped to >= 0
already_allocated_ac sums the operational AC columns (the _OPERATIONAL clip / MaxKVA twins, not the design ones), so the DC budget is consistent with the operational reconciliation invariant.
DC losses are then consumed from remaining_gap in strict priority order. Each category is capped at the remaining gap (clip(0, remaining_gap)), and the gap is reduced after every assignment:
- Near shading - highest DC priority, takes its ratio-based share of the gap
(1 - active / p_dc_expected_tp_no_loss) | p_ac_expected, so a partially shaded inverter still passes the remainder on. The transposed no-loss expected DC is used as a stable denominator unaffected by a shaded sensor. - Structural losses -
loss_dc_input_down(full rated capacity of an absent branch) thenloss_dc_input_underperform(capacity x (1 - relative yield) of a partially-producing branch). These are gated out at near-shading timestamps to avoid double-counting dawn/dusk geometry, and underperform is additionally gated against reactive/MaxKVA/thermal secondary statuses. - Continuous physics losses -
loss_soiling,loss_module_thermal,loss_inv_efficiencybook their physically-modelled magnitudes next. OnNORMALtimestamps these mechanisms stack multiplicatively with any tracker deviation, so they must claim before tracker so a tracker stow does not absorb soiling/thermal/efficiency that would have been lost regardless. - Tracker - residual absorber -
loss_trackeris allocated last and absorbs the full remaining gap on every tracker-flagged timestamp (hard-capped atremaining_gap, so the reconciliation invariant is preserved). Tracker is allowed to fire underNORMALand under the inverter-side AC caps (INV_CLIPPING,INV_POWER_LIMIT): once the operational cap loss has been booked, any remaining gap on a tracker-flagged timestamp is by definition the stow's drop below the cap and physically belongs to tracker - not to residual.
All other DC categories are gated to the NORMAL primary status - they describe why a normally-operating inverter still under-delivered. Tracker is the single exception, by design.
2.3 Residual (Step 5)
Whatever gap remains at NORMAL timestamps after all explicit categories is written to loss_residual. With complete status assignment and all DC categories populated this is approximately zero; a non-zero residual flags a loss mechanism that is missing or not yet detected, which makes it a useful self-diagnostic of the breakdown's completeness.
2.4 Per-mechanism loss models
The raw (ungated) DC loss series fed into the cascade are computed from physical relationships:
| Mechanism | Model |
|---|---|
| Tracker | power | (1 - G_eff / G_transposed), clipped to >= 0. The ratio of measured POA to transposed (ideal-tracking) POA quantifies the non-optimal tracker position; missing/zero transposed irradiance yields no loss. The model is informational only in the operational breakdown - Step 4's tracker step absorbs the full remaining gap on flagged timestamps regardless of the model value, because the sensor-ratio model is blind whenever the POA sensor is not mounted on the stuck tracker. |
| Soiling | per-interval derating from the daily soiling rate, or the measured soiling-loss series (from InverterSoilingLoss) when available, which overrides the rate-based value. |
| Module thermal | exact temperature-derate inversion: power / (1 + gamma | max(T_cell - T_ref, 0)) - power, clipped to >= 0 (gamma < 0). Represents the power lost to heat above the reference cell temperature. A separate informational column loss_module_thermal_theoretical is computed structurally against the expected curve (p_ac_expected / (1 + gamma|delta T) - p_ac_expected) for visualisation only - it is not part of the operational reconciliation, since module thermal derating is already embedded in p_ac_expected via the thermal factor. |
| DC branch down / underperform | capacity-weighted shares per inverter, broadcast from the daily relative DC-yield (DCHealth) onto the sub-daily index. A branch below the down threshold contributes its full rated capacity; a branch between the down and underperform thresholds contributes cap | (1 - relative_yield). |
| Inverter efficiency | power | (eta_expected - eta_actual), clipped to >= 0, using EU efficiency as the expected scalar. |
Eight intermediate expected-power stage columns (p_ac_exp1 ... p_ac_exp8) are written for traceability - each stage is the previous expected power minus the loss attributed at that step.
3. Potential loss breakdown ("what-if")
The potential breakdown answers the second, more actionable question. Focusing on DC losses (soiling, module thermal, tracker, under-performing or down DC strings, and inverter efficiency), each mechanism is computed against the expected power rather than the measured power, representing the full theoretical impact of that mechanism - i.e. the loss it would cause if it were the only thing limiting the plant. Alongside these, the design upper-cap headrooms (loss_inv_clip, loss_maxkva - the un-capped DC potential vs post-cap expected AC) are reported as part of the same potential family, since they describe the energy the design intentionally discards rather than energy lost to operational issues.
These full-impact potentials are then reconciled with reality through a gap-scaling factor, so that the sum of realised potential losses never exceeds the actual observed gap. The procedure is:
- Compute each mechanism against
p_ac_expected(not measured power), then apply masks: - soiling, module thermal, and DC branch losses are present wherever no down-loss is allocated (mask_not_down); - tracker is present only wheredc_statusflags a tracker condition; - inverter efficiency is present only atNORMALAC primary status (to avoid inflating potential during curtailment or clipping). - Tracker gets a hybrid magnitude:
- on
NORMALtimestamps the tracker potential is the gap residual after structural DC + concurrent continuous models (soiling, thermal, inverter efficiency) - this is the same residual-absorbing logic the operational breakdown uses, applied to the potential gap; - on non-NORMAL(curtailed / clipped) timestamps the gap is AC-contaminated, so the sensor-ratio modelpower | (1 - G_eff / G_transposed)is used instead. - Structural DC losses consume the gap first (hard cap). Because a down or underperforming branch is a known fact,
loss_dc_input_down_potentialandloss_dc_input_underperform_potentialtake priority and are clipped against the gap before anything else. - Continuous losses share the remaining gap proportionally. A single scaling factor
f = min(1, remaining_gap / (soiling + module_thermal + tracker + inv_efficiency))
scales the four continuous potentials together, so their realised sum never exceeds the remaining gap while preserving the relative magnitude of each mechanism.
The result is the "best of both worlds": the structural certainty of the gap analysis is preserved, while the proportional distribution reveals which DC mechanisms dominate the recoverable loss - even when they are partly overshadowed by a larger primary loss such as curtailment or a down inverter. This is exactly the signal an O&M team needs to prioritise cleaning, tracker maintenance, or string repairs.
4. Site aggregation and total loss
SiteAggregationLossBreakdown sums every per-inverter loss column across the fleet at each timestamp, re-keys the result to the site object, and converts kW -> MW (and the daily resample to MWh). It produces both the actual and the potential breakdown at sub-daily and daily resolution.
TotalLossBreakdown then compresses each breakdown into compact totals:
total_loss_ac- sum of all AC-side categories,total_loss_dc- sum of all DC / module-level categories,total_loss-total_loss_ac + total_loss_dc,loss_factor-total_loss / p_ac_exp1, clipped to[0, 1].
For daily values, the energy columns are summed and loss_factor is recomputed from the daily totals so it reflects the true daily loss fraction rather than a naive average of per-interval ratios. Because loss_factor is dimensionless, it is unaffected by the kW-vs-MW scale and is directly comparable across inverters and sites.
These two reconciled views - the actual loss breakdown (compared against budget losses) and the potential DC loss breakdown - give the customer both the financial accounting of measurable losses and the actionable, what-if prioritisation of hidden DC root-causes from a single, consistent analytics pipeline.