Back to blog
Technical deep-dive 6 April 2026 · 7 min read

Weather normalisation: how Open-Meteo stops false alarms on clipping days

Raw string-current comparisons fire false alarms on hot, sunny days. We use Open-Meteo's hourly irradiance and temperature data to weather-normalise our detections. Here's the maths and the gotchas.

Josh Powell
Founder, InspireGreen

There’s a particular kind of false alarm that ruins a Saturday morning. Your phone buzzes. A platform is telling you that String 3 on Site X is at 38% of its peers. You drive forty minutes to the site. You walk the array. You measure everything. Everything is fine.

The thing the platform missed wasn’t a fault. It was the weather.

This post is about why raw string-current comparisons fail in certain conditions, why we use Open-Meteo’s hourly irradiance and temperature data as the comparison baseline, and the handful of gotchas we ran into when we built it.

The naive detection

The simplest string-fault detection compares each string’s current to the median of its siblings on the same MPPT:

relative_drop = (median_siblings - this_string) / median_siblings

If relative_drop > 0.30 for more than two consecutive polling intervals, raise a case. Simple. Catches real faults reliably most of the time.

Then comes a hot July afternoon and the alarm goes off on six sites at once.

What’s actually happening

On a clear summer afternoon at peak irradiance, a typical commercial PV array operates near or at the inverter’s DC clipping limit. The inverter limits total DC input to protect itself; what this means at the string level depends on the inverter’s MPPT algorithm.

Two things happen that confuse a naive comparison:

  1. The inverter no longer tracks each string’s MPP. Strings are running off their true maximum-power-point. Small differences between strings — in irradiance, in cell temperature, in panel age — get amplified because the inverter is no longer adapting to each string individually.
  2. Hot panels behave non-linearly. The voltage temperature coefficient of a crystalline silicon module is around -0.3% per °C. At 65°C cell temperature (which is normal on a 30°C ambient day in full sun), voltages are about 12% below their STC value. A small variation in cell temperature across an array — shading from a parapet, airflow differences between the front and back of a row, a panel with a slightly higher mounting clearance — creates current variations of several per cent that have nothing to do with a fault.

The combined effect: on a clipping-condition afternoon, naive string comparisons can show 20-40% relative drops on perfectly healthy strings. Every operator we’ve spoken to who’s built a string-level detection has hit this and had to back the threshold off until the false-fire rate became tolerable. Which means the threshold is now too lax to catch real faults in less-extreme conditions.

The right answer is to weather-normalise the comparison.

What “weather-normalised” actually means

You compare strings not against each other in raw current terms, but against the production each string should be doing given the current weather conditions.

The inputs we need:

  • Plane-of-array irradiance (W/m²) at the site, in real time
  • Cell temperature, derived from ambient and irradiance
  • Module datasheet parameters: Isc, Voc, temperature coefficients
  • Array orientation: tilt and azimuth, for converting global horizontal irradiance to plane-of-array

For a string of N modules with rated Isc and a known coefficient α (typically around +0.04% per °C for current), the expected string current at a given irradiance G and cell temperature T is approximately:

I_expected = Isc * (G / 1000) * (1 + α * (T - 25))

where G is in W/m² of plane-of-array irradiance and T is cell temperature in °C.

The relative comparison then becomes:

relative_drop = (median_siblings_normalised - this_string_normalised) / median_siblings_normalised

where each string’s “normalised” value is its actual current divided by its expected current. In clipping conditions, every string’s normalised value is well above 1 (because the inverter is artificially limiting output), but the differences between strings collapse — because the irradiance and temperature variations have been factored out.

Why Open-Meteo

The bit you actually need from the weather is hourly plane-of-array irradiance for the specific site location. That’s not something you can get from a typical Met Office forecast feed.

We tested several options. Open-Meteo (open-meteo.com) is what we landed on, for three reasons:

  1. Hourly historical and forecast irradiance at arbitrary lat/lon. The dataset is high-resolution (about 11 km at UK latitudes) and includes direct normal, global horizontal, and diffuse components from which plane-of-array irradiance can be derived.
  2. No API key in our use case. The non-commercial endpoints are free for the request volumes we generate. We pay for the commercial tier on production now that we’re past pilot.
  3. The underlying model is documented and accountable. Open-Meteo’s data comes from ECMWF and from national meteorological models (GFS, etc); they cite their sources and you can reason about the data quality.

We pull hourly forecast and historical irradiance for every site we monitor, refreshed every hour. We cache the data per-site so we’re not hitting Open-Meteo on every detection tick.

The plane-of-array conversion

Open-Meteo gives global horizontal irradiance. The panels are tilted. Converting from one to the other is the bit of the maths most engineers get wrong on the first try.

For a fixed-tilt rooftop with tilt β and azimuth γ (where γ = 180° for south-facing in the northern hemisphere), the plane-of-array irradiance at time t involves:

  • Direct beam component, transformed by the sun’s angle of incidence onto the tilted plane
  • Sky diffuse component, transformed by an isotropic or anisotropic sky model
  • Ground-reflected component, depending on ground albedo (usually assumed ~0.2 for grass, ~0.15 for asphalt)

The standard reference is the Hay-Davies model implemented in pvlib, which is what we use. We don’t reinvent it; pvlib’s been validated against a lot of field data and there’s no value in writing your own irradiance decomposition.

A worked example: on a south-facing 10° tilt rooftop in Cardiff, on a clear June afternoon at 13:00 local with a global horizontal irradiance of 850 W/m², the plane-of-array figure works out to around 880 W/m². On the same roof in December at 11:00 with GHI of 200 W/m², plane-of-array is around 290 W/m² because the low sun angle aligns better with the slight south tilt.

If you skip the transform and use GHI directly, you’ll be wrong by 5-30% depending on time of year and tilt. That’s enough to make the normalisation useless.

Cell temperature: the other gotcha

Cell temperature is rarely measured directly on a commercial install. You have to derive it.

The simplest derivation is the NOCT model:

T_cell = T_ambient + (NOCT - 20) * (G / 800)

where NOCT is the Nominal Operating Cell Temperature from the module datasheet (typically 45 °C ± 2 °C for crystalline silicon modules).

Open-Meteo gives us ambient temperature; the module datasheet gives us NOCT; the irradiance figure we’ve already calculated gives us G. The model isn’t perfect — it doesn’t account for wind speed properly, and roof-mounted modules with limited rear ventilation run hotter than free-standing modules of the same datasheet rating — but it’s close enough for the comparison to be useful.

For sites where we have wind speed data (Open-Meteo provides it) and where the install detail is documented, we use the Sandia thermal model which incorporates wind speed and mounting configuration. The difference between the two models is meaningful in marginal cases.

The clipping question

Even after normalisation, clipping conditions are a special case. When the inverter is at its DC limit, the relationship between irradiance and string current breaks down — the strings aren’t being operated at their MPP.

Our rule: if the inverter is at or above 95% of its rated DC input for two or more consecutive polling intervals, we suppress the relative-drop detection on that inverter for the duration of the clipping condition. We log the suppression in the audit trail so it’s clear what happened.

This means we will not detect a string fault that occurs during clipping. The trade-off is that we don’t fire false alarms during clipping either, and the same string will almost certainly show the fault as soon as clipping ends (typically within an hour, as irradiance drops below the clip threshold). The false-positive cost of catching the fault during clipping is too high relative to the marginal latency gain.

The bit nobody warned us about

The first time we deployed weather normalisation, we discovered that Open-Meteo’s hourly data is reported at the start of the hour, but the inverter polling happens every 5 or 15 minutes. Naive matching (“which hourly irradiance figure applies to this 5-minute telemetry sample?”) produced step changes in the normalised current every hour that confused our detection thresholds.

The fix is to interpolate between adjacent hourly figures for the actual sample time. We linearly interpolate irradiance and temperature between the surrounding hours. The interpolation is cheap to compute and removes the step-change artefact entirely.

A subtler issue: Open-Meteo’s “current hour” data is sometimes a forecast (made earlier that day) rather than an observation. The forecast accuracy is good but not perfect. For real-time detection we use the forecast; for historical analysis (monthly reports, PR calculations) we use the post-hoc observation data, which Open-Meteo re-publishes once the hour is past.

The result

After deploying weather-normalised string detection in mid-2025, our false-positive rate on the relative-drop detection dropped from roughly one false fire per site per month to under one per site per quarter. The detection threshold could be tightened from 30% to 20%, which catches a wider range of real faults than the looser threshold did.

The remaining false fires are almost all from one of two causes: brief shading events not captured in the irradiance data (a passing cloud, a piece of equipment on the roof casting a momentary shadow) or genuine measurement noise from a flaky inverter that we’ve since flagged for replacement. Both are manageable.

What this looks like to the operator

You don’t see any of this. A case opens for “String 3 on Site X, 24% below MPPT peers, weather-normalised, persistent across 4 polling intervals.” The case has the data points attached: the actual current, the expected current, the irradiance, the cell temperature, the clipping status of the inverter. An engineer reviews the case, decides whether to dispatch, and acts.

The point of all the maths is that they don’t have to think about whether it was a hot day. The platform did.


Open-Meteo weather-normalisation runs on every SolarFleet site by default. See how our string-level fault detection works or start free with 2 sites.

#weather-normalisation #open-meteo #irradiance #fault-detection #clipping
Josh Powell
Founder, InspireGreen

Josh runs InspireGreen, a solar installer based in Cardiff, and builds SolarFleet — the O&M platform we use to monitor our own sites. Most posts here come straight out of the work: a case we dealt with, a feature we shipped, or a thing we wish we'd known earlier.