Skip to content

fix(prediction): stop predict_pv_power scaling with plan_interval_minutes - #4374

Merged
springfall2008 merged 1 commit into
springfall2008:mainfrom
chalfontchubby:fix/predict-pv-power-plan-interval-scaling
Jul 29, 2026
Merged

fix(prediction): stop predict_pv_power scaling with plan_interval_minutes#4374
springfall2008 merged 1 commit into
springfall2008:mainfrom
chalfontchubby:fix/predict-pv-power-plan-interval-scaling

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Summary

Root-cause fix for issue #4361 (reported as "LoadML Solar Prediction differs from PV7 Solar Forecast" - the actual bug is unrelated to LoadML or Open-Meteo vs Solcast).

predict_pv_power (prediction.py) - which feeds pv_power_best, the source of the "PV Power (Predicted)" trace on the LoadMLPower chart - converts two summed PREDICT_STEP (5-minute) PV energy chunks into an instantaneous kW reading. The correct scaling constant is 60/(2*step), derived purely from the simulation's fixed 5-minute step - it has nothing to do with plan_interval_minutes (the user-configurable plan slot width).

Git blame confirms the regression: the original code (#1193) correctly hardcoded 30/step (60/(2*step) with step=5). PR #2865 ("Make default 30 minute timeslot configurable with plan_interval_minutes", merged 2025-11-07) swapped that constant for self.plan_interval_minutes, on the reasonable-looking but incorrect assumption that the 30 was tied to the plan-interval default rather than being an independent constant. Since then, anyone using the 30-minute default has been unaffected (the two formulas coincide), but anyone on a shorter plan_interval_minutes has seen the reported PV power scaled down by plan_interval_minutes/30 - exactly half for 15-minute slots (as in #4361), a third for 10-minute.

Verified empirically with a flat 4kW PV input at three plan intervals, before and after:

plan_interval_minutes before after
30 4.0 kW 4.0 kW
15 2.0 kW 4.0 kW
10 1.333 kW 4.0 kW

Change

One-line fix: self.plan_interval_minutes / step60 / (2 * step), matching the pattern already used correctly by the sibling predict_battery_power/predict_grid_power/predict_load_power lines a few lines above (all use 60/step directly on a single un-summed chunk; predict_pv_power sums two chunks first, hence the extra factor of 2).

Test plan

  • New regression test test_predict_pv_power_matches_input_regardless_of_plan_interval (tests/test_predict_pv_power.py): runs a real run_prediction(save="best") with a known flat PV forecast at plan_interval_minutes = 30/15/10 and asserts predict_pv_power always reports the true kW value. Verified it fails without the fix (reproduces the exact 2.0kW/1.333kW figures above) and passes with it.
  • unit_test.py --test predict_pv_power and --quick - all pass.
  • pre-commit (ruff, black, cspell) clean.

Notes

  • This only affects the diagnostic predict_pv_power display path (if debug_enable or save:), gated well away from the optimizer's hot loop and the C++ prediction kernel - no plan/optimization behaviour changes, only what gets shown on the LoadMLPower chart.
  • Byte-identical output for anyone on the 30-minute default plan_interval_minutes.

🤖 Generated with Claude Code

…utes

predict_pv_power (which feeds pv_power_best, the source of the "PV Power
(Predicted)" trace on the LoadMLPower chart) converts two summed
PREDICT_STEP energy chunks into an instantaneous kW reading using
60/(2*step) - a constant derived from the simulation's fixed 5-minute
step, unrelated to plan_interval_minutes. PR springfall2008#2865 (2025-11-07)
accidentally swapped that constant for self.plan_interval_minutes,
which happens to match the default (30) but scales the reported PV
power by plan_interval_minutes/30 for anyone using a shorter plan
interval - exactly half for 15-minute slots, a third for 10-minute.

Fixes springfall2008#4361.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a regression in the debug/“best” prediction output path where predict_pv_power (used for the “PV Power (Predicted)” trace) was incorrectly scaled by plan_interval_minutes, causing PV power to be under-reported for non-30-minute plan intervals.

Changes:

  • Correct predict_pv_power scaling to use a constant derived from the fixed prediction step (60 / (2 * step)) rather than plan_interval_minutes.
  • Add a regression test that runs a real run_prediction(save="best") with flat PV input across multiple plan_interval_minutes values and asserts the reported kW matches the input.
  • Register the new test module in the custom unit_test.py registry.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
apps/predbat/prediction.py Fixes PV power scaling to be independent of plan_interval_minutes in the debug/save output path.
apps/predbat/tests/test_predict_pv_power.py Adds a regression test covering PV power scaling across 30/15/10 minute plan intervals.
apps/predbat/unit_test.py Wires the new regression test into the test runner registry.

Comment on lines +80 to +88
finally:
my_predbat.plan_interval_minutes = original_plan_interval_minutes
my_predbat.forecast_minutes = original_forecast_minutes
my_predbat.end_record = original_end_record
my_predbat.pv_forecast_minute = original_pv_forecast_minute
my_predbat.pv_forecast_minute10 = original_pv_forecast_minute10
my_predbat.load_minutes_step = original_load_minutes_step
my_predbat.load_minutes_step10 = original_load_minutes_step10
my_predbat.prediction = original_prediction
@springfall2008
springfall2008 merged commit b03d114 into springfall2008:main Jul 29, 2026
2 checks passed
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.

3 participants