Skip to content

Fix 10%/90%/CL calibrated PV values being ~2× too low with Open-Meteo#3827

Merged
springfall2008 merged 5 commits intomainfrom
copilot/fix-calibrated-pv-values
Apr 25, 2026
Merged

Fix 10%/90%/CL calibrated PV values being ~2× too low with Open-Meteo#3827
springfall2008 merged 5 commits intomainfrom
copilot/fix-calibrated-pv-values

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 24, 2026

When using Open-Meteo (60-min forecast slots) with the default 30-min plan interval, pv_calibration() annotated each forecast entry with only the calibrated value from the first 30-min plan slot, silently discarding the second half of each 60-min window.

Root cause

In the annotation loop, the lookup was a direct dict get for a single slot:

slot = int(minutes_since_midnight / self.plan_interval_minutes) * self.plan_interval_minutes
calibrated = pv_estimateCL.get(slot, None)          # only slot 600, never 630
entry["pv_estimateCL"] = calibrated * divide_by     # → ~50% of correct kWh

For a 60-min entry at minute 600, pv_estimateCL[600] covers only minutes 600–629. pv_estimateCL[630] (minutes 630–659) was never included.

Changes

  • solcast.pypv_calibration(): Added period parameter (defaults to plan_interval_minutes for backward compatibility). Computes slots_per_period = round(period / plan_interval_minutes) and sums all plan slots within each forecast entry's duration before storing the annotated value. Updated fetch_pv_forecast() to pass period through.

  • tests/test_solcast.py: Added test_pv_calibration_60min_period — exercises pv_calibration() with 60-min forecast entries and 30-min plan slots, asserting the annotated pv_estimateCL matches the full-period kWh (not the half-period value the bug produced).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.octopus.energy
    • Triggering command: /home/REDACTED/work/batpred/batpred/coverage/venv/bin/python3 python3 ../apps/predbat/unit_test.py --quick (dns block)
  • gitlab.com
    • Triggering command: /usr/lib/git-core/git-remote-https /usr/lib/git-core/git-remote-https origin REDACTED (dns block)
  • https://api.github.com/repos/springfall2008/batpred/contents/apps/predbat
    • Triggering command: /home/REDACTED/work/batpred/batpred/coverage/venv/bin/python3 python3 ../apps/predbat/unit_test.py --test solcast (http block)
    • Triggering command: /home/REDACTED/work/batpred/batpred/coverage/venv/bin/python3 python3 ../apps/predbat/unit_test.py --quick (http block)

If you need me to access, download, or install something from one of these locations, you can either:

…Meteo)

Agent-Logs-Url: https://github.com/springfall2008/batpred/sessions/175ef50b-045f-4533-89cd-dbafb11a7598

Co-authored-by: springfall2008 <48591903+springfall2008@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix calibrated PV values out by a factor of 2 using open-meteo Fix 10%/90%/CL calibrated PV values being ~2× too low with Open-Meteo Apr 24, 2026
Copilot AI requested a review from springfall2008 April 24, 2026 08:11
@springfall2008
Copy link
Copy Markdown
Owner

@copilot this doesn't make sense, the data should all be normalised before the calibration runs. Is this working around a bug upstream?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot this doesn't make sense, the data should all be normalised before the calibration runs. Is this working around a bug upstream?

The per-minute data (pv_forecast_minute) is correctly normalised before calibration runs — minute_data() with divide_by=60, spreading=60 (for Open-Meteo's 60-min entries) produces the right kWh/min values and calibration uses those correctly.

The bug is only in the write-back step: after calibration computes pv_estimateCL at 30-min plan-slot granularity, the loop that annotates the original pv_forecast_data entries only looked up the plan-slot at the entry's start time. For a 60-min Open-Meteo entry at 10:00, that means pv_estimateCL[600] (covering 10:00–10:30 = 1.0 kWh) was stored but pv_estimateCL[630] (10:30–11:00 = 1.0 kWh) was silently dropped — giving half the correct kWh.

This is not working around an upstream bug. The Open-Meteo data is correct (60-min kWh from trapezoidal integration of hourly irradiance), and the per-minute normalisation is correct. The only issue was the annotation loop not summing all plan slots that span each forecast entry's period.

An alternative "normalise upstream" approach would split the 60-min entries into two 30-min entries before calling pv_calibration(). That would also work, but it:

  • Changes the detailedForecast attribute to show 30-min slots for Open-Meteo (potentially confusing since Open-Meteo only has hourly data)
  • Requires equal-halving (constant power assumption within the hour) as an approximation
  • Moves complexity into fetch_pv_forecast()

Happy to implement either approach — let me know if you'd prefer the upstream split.

@springfall2008 springfall2008 marked this pull request as ready for review April 25, 2026 09:02
Copilot AI review requested due to automatic review settings April 25, 2026 09:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes PV calibration annotation for coarser forecast resolutions (notably Open-Meteo hourly data) so calibrated PV values reflect the full forecast-entry duration rather than only the first plan slot.

Changes:

  • Extend SolarAPI.pv_calibration() to accept a period and sum all plan-interval slots within each forecast entry when annotating pv_estimateCL/pv_estimate10/pv_estimate90.
  • Update fetch_pv_forecast() to pass the detected forecast period through to pv_calibration().
  • Add a regression test covering 60-minute forecast entries with 30-minute plan slots.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
apps/predbat/solcast.py Accumulates calibrated values across multiple plan slots per forecast entry and threads the detected forecast period into calibration.
apps/predbat/tests/test_solcast.py Adds a regression test for 60-min forecast periods to prevent the “half-period” calibration annotation bug.

Comment thread apps/predbat/solcast.py Outdated
Comment on lines +2509 to +2516
for entry in adj_data:
cl = entry.get("pv_estimateCL")
e10 = entry.get("pv_estimate10")
e90 = entry.get("pv_estimate90")

if cl is None or cl == 0:
continue

springfall2008 and others added 3 commits April 25, 2026 10:43
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@springfall2008 springfall2008 merged commit 73e1034 into main Apr 25, 2026
1 check passed
@springfall2008 springfall2008 deleted the copilot/fix-calibrated-pv-values branch April 25, 2026 10:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10%, 90%, and Calibrated PV values are out by a factor of 2 when using open-meteo

3 participants