Repository navigation
Conversation
Core computed household load three ways: history balanced per-source energy per interval, the Energyplan tracker kept its own quarter-hour accumulator, and the built-in load model learned from one instant reading per minute. The models learned a different quantity from the Home use shown in History. Add state.HouseholdIntervals, which returns closed, valid intervals from the same source energy as daily totals. The built-in model trains once per closed quarter on its mean, and the tracker takes its load labels from the same quarters. PV keeps its accumulator for the curtailment flag and waits until history decides the quarter, so each immutable observation is written once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Short source gaps may become estimated history once #1753 lands, and then a training label. Keep these tests about gaps that stay unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
History may show Home use across a bridged or estimated gap, but a load model must not learn from it. Leave out intervals with EstimatedLoad or BalanceMismatch as well as a missing source. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 2026
frahlg
added a commit
that referenced
this pull request
Oct 19, 2026
Load models train only on fully measured intervals (#1754). Marking every dark PV snapshot as an estimate would drop each night with a sleeping inverter from training. Past civil dusk (zenith over 96°) no panel produces, so an unknown PV source now reads as a known 0 W without EstimatedLoad: the night counts in Home use and in training. Between sunset and civil dusk diffuse light remains, and the 0 W stays a marked estimate. No new bit records the physical zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frahlg
added a commit
that referenced
this pull request
Oct 19, 2026
Load models train only on fully measured intervals (#1754). Marking every dark PV snapshot as an estimate would drop each night with a sleeping inverter from training. Past civil dusk (zenith over 96°) no panel produces, so an unknown PV source now reads as a known 0 W without EstimatedLoad: the night counts in Home use and in training. Between sunset and civil dusk diffuse light remains, and the 0 W stays a marked estimate. No new bit records the physical zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frahlg
added a commit
that referenced
this pull request
Oct 19, 2026
Load models train only on fully measured intervals (#1754). Marking every dark PV snapshot as an estimate would drop each night with a sleeping inverter from training. Past civil dusk (zenith over 96°) no panel produces, so an unknown PV source now reads as a known 0 W without EstimatedLoad: the night counts in Home use and in training. Between sunset and civil dusk diffuse light remains, and the 0 W stays a marked estimate. No new bit records the physical zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Core worked out household load three ways:
history_source_energy)ForecastAccumulator: 15-minute trapezoid means of aligned instant readings, any invalid instant drops the quarterloadmodel,mpc.Load)HouseholdWreading per minuteSo the models learned a different quantity from the Home use people see, and the built-in model learned from single instant readings. Fredrik asked for one common basis.
Two layers
Fredrik set the direction on 19 October: Home use has two layers.
The contract between them is the
history_qualitymask. Anything the display layer bridges or estimates carriesEstimatedLoad; training leaves out every interval withMissingPower,BalanceMismatchorEstimatedLoad. PV at 0 W while the sun is well below the horizon is physics, not an estimate, so a PV inverter asleep all night must not remove the night from training (being settled in #1755).Change
state.HouseholdIntervalsreturns closed, fully measured intervals from the same source energy as daily totals. An interval is closed once committed source progress passes its end, so a returned value never changes. Intervals with a missing source, a recording gap, an estimate or a negative balance are left out. It also returns where the closed range ends, so callers move past intervals left out.loadmodel.Servicetrains once per closed quarter-hour on its mean instead of one instant reading per minute. The model already averages within a day per weekly hour, so the hourly means keep their meaning. Cold-day counters keep the minute units used by stored models: each new quarter adds 15 minute units to the count and both sums. This preserves partial-day evidence and the six-hour bound without resetting learning. It persists after each trained quarter.Quality: source_interval_balance_v1) and drops its household accumulator. PV keeps its accumulator, since PV labels need the curtailment flag. Observations are immutable per quarter, so PV waits until history decides the quarter (at most 2 minutes), and each quarter is archived once.Model state, feature hash and learning revisions are unchanged, so no site loses learning.
This follows docs/roadmap.md: household Load history balances source energy over the same interval, and every use is checked against the same stored evidence.
Relation to other work
Evidence
go test ./internal/state ./internal/loadmodel ./cmd/ftw ./internal/api ./internal/telemetry: all pass.TestHouseholdIntervalsMatchDailyTotal: the sum of training intervals equalsDailyEnergy.LoadWh.TestHouseholdIntervalsLeaveOutIncompleteEvidence: missing EV, a 3-minute recording gap and a negative balance give no label; neighbours keep theirs.TestHouseholdIntervalsWaitForCommittedProgress: an open quarter is never returned.TestTrainsOnceOnEachClosedValidHouseholdInterval,TestLearningCutoffSkipsIntervalsStartedBeforeIt,TestIntervalReadInFlightIsDiscardedfor the built-in model.TestForecastPVWaitsForTheHouseholdDecision, reworkedTestForecastConfigChangeBreaksIntervalandTestForecastIndependentPVIntervalsAndCurtailmentfor the tracker. Readings carry a wrong instant house value; labels must come from history.loadmodel; history now owns those validity rules and the state tests cover them.TestRestoredMinuteHeatingBoundsKeepLearning: restores both learned profiles and the active profile unchanged from minute-based state, persists and reloads them after a new quarter, rejects 30 old minutes as a full cold day, and accepts 345 old minutes plus one new quarter as exactly six hours. It fails before the fix and passes after it.go test -race ./internal/loadmodel: passed.make verify: passed (vet, tests, build).TestHouseholdIntervalsLeaveOutEstimatedMinutes: an estimated minute keeps its quarter out of training while daily Home use still shows it.Watch after the beta:
loadmodelsample counts grow per quarter, and the Energyplan learning status stays healthy.🤖 Generated with Claude Code