Document the stabilization pass
README: window unknown states, what configuration changes do to the learned model, inputs_changed tolerances, coefficient confidence, model_changes, and Model Error vs Forecast Error. docs/MODEL.md: configuration-change table, confidence semantics, both validation modes. CHANGELOG gains an Unreleased section (version unchanged); AUDIT and ROADMAP record findings, evidence and remaining risks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8eef04c385
commit
35370958fc
5 files changed
+340
-58
No files matched your search
+64
-11
@@ -93,9 +93,16 @@ measure the input side or lower the heat fraction accordingly.
|
||||
|
||||
Present when a zone has an enabled window. Learned separately, from the part of
|
||||
the measured change the closed-window layers cannot explain, only while a window is
|
||||
open, its state is unknown, or the zone is recovering. During those periods the
|
||||
base and extended layers do **not** learn, so window events cannot inflate `loss`
|
||||
or shift `bias`.
|
||||
open or the zone is recovering, and only while every window state in the zone is
|
||||
known. While a window is open, possibly open (its sensor is unknown and it was last
|
||||
seen open) or the zone is recovering, the base and extended layers do **not**
|
||||
learn, so window events cannot inflate `loss` or shift `bias`.
|
||||
|
||||
An unknown window state is never read as closed. A window last seen open stays
|
||||
assumed open in forecasts; one last seen closed contributes no ventilation term.
|
||||
If a window closes while its sensor was unknown, the event's duration is reported
|
||||
as a range and the recovery term is not learned from it, because the closing time
|
||||
is not known.
|
||||
|
||||
`vent_wind_dir` is bounded by `vent_wind` (|vent_wind_dir| ≤ vent_wind), so the
|
||||
combined wind term never goes negative: a leeward window still ventilates.
|
||||
@@ -128,11 +135,20 @@ outlier rejection above 0.8 °C per 5 minutes.
|
||||
Each coefficient reports:
|
||||
|
||||
* `confidence`: the share of its information that comes from data rather than the
|
||||
prior (0 = prior only);
|
||||
prior (0 = prior only), from its posterior variance. It is not a sample count:
|
||||
it grows only while the data varies the coefficient's input independently of
|
||||
the others, and with forgetting it falls again during long periods without such
|
||||
variation. The base layer reports it too (`loss`, `heat`, `bias`);
|
||||
* `std`: its standard error, using a running estimate of the unexplained noise;
|
||||
* `samples`: how many accepted samples actually exercised it;
|
||||
* `identified`: at least 30 exercising samples and confidence ≥ 0.8.
|
||||
|
||||
The base layer has no per-coefficient sample counts or standard errors; its
|
||||
`confidence` alone shows identification. In the tests, two weeks of samples with
|
||||
the relay always off and a steady indoor-outdoor difference leave `heat` at 0 and
|
||||
`loss` and `bias` below 0.8, while four days with the relay cycling and the
|
||||
weather changing bring all three above 0.8.
|
||||
|
||||
A coefficient can move without being identified. Example from the tests: two rooms
|
||||
whose temperature difference stays fairly steady cannot separate exchange between
|
||||
them from each room's bias; the learner finds a positive coupling but correctly
|
||||
@@ -154,6 +170,27 @@ A zone learns from the interval between two polls only when:
|
||||
No experiments are run and the boiler is never switched to gather data: everything
|
||||
is learned passively.
|
||||
|
||||
## Configuration changes
|
||||
|
||||
Every learned component stores a fingerprint of the configuration it was learned
|
||||
under (`custom_components/heating_mpc/compatibility.py`). At startup, components
|
||||
whose fingerprint changed are reset; the rest are kept.
|
||||
|
||||
| Change | Effect |
|
||||
|---|---|
|
||||
| A zone's sensors, or the ratio of their weights | Zone recalibrated: coefficients kept as a starting point; uncertainty, counts and accuracy statistics reset. Its extended and ventilation layers start over too (their loss, heat and bias values kept as a starting point) |
|
||||
| Scaling all of a zone's weights equally | Nothing (the normalized measurement is unchanged) |
|
||||
| A heat source's sensor, heat share, baseline, response time or "included in" | `internal` of its zone; the source's baseline samples if its effective power changed |
|
||||
| A neighbour's sensors or weights | `couple:<neighbour>` |
|
||||
| A window's sensor, polarity or opening factor | that zone's ventilation terms; `vent_cross` of the other zones |
|
||||
| A window's facing direction | `vent_wind_dir` |
|
||||
| Recovery time constant | `recovery` |
|
||||
| Comfort weight, target offset, names | Nothing (not physical) |
|
||||
|
||||
Models saved before fingerprints existed (0.4.0) are assumed to match the
|
||||
configuration they are loaded with, and are fingerprinted at once. Every reset is
|
||||
logged in `model_changes` with its reason.
|
||||
|
||||
## Forecasts
|
||||
|
||||
Forecasts simulate all zones together, step by step, over the MPC horizon (60–180
|
||||
@@ -177,18 +214,34 @@ they describe what would happen under the MPC's proposed plan.
|
||||
|
||||
## Validation
|
||||
|
||||
The Forecast Error sensors score forecasts that are valid for comparison with
|
||||
measured temperatures. At every poll the model parameters are frozen; 15, 30, 60
|
||||
and 120 minutes later all zones are replayed from that poll using the relay,
|
||||
electrical heat and window history that actually followed (outdoor temperature held
|
||||
at its start value), and compared with the readings. The base layer and a
|
||||
no-change forecast are scored on exactly the same spans:
|
||||
Two separate measurements, because they answer different questions.
|
||||
|
||||
* `mae`, `bias`: the full model's error;
|
||||
**Model accuracy (Model Error sensors)** scores the model, not a forecast. At
|
||||
every poll the model parameters are frozen; 15, 30, 60 and 120 minutes later all
|
||||
zones are replayed from that poll using the relay, electrical heat and window
|
||||
history that actually followed (outdoor temperature held at its start value),
|
||||
and compared with the readings. The base layer and a no-change forecast are
|
||||
scored on exactly the same spans:
|
||||
|
||||
* `mae`, `rmse`, `bias`: the full model's error;
|
||||
* `persistence_mae`, `skill`: against assuming no change;
|
||||
* `base_model_mae`, `improvement_vs_base`: against the base layer alone. A feature
|
||||
that does not improve this should be switched off (for example the gust term).
|
||||
|
||||
**Forecast accuracy (Forecast Error sensor)** scores forecasts as published: the
|
||||
start readings, trajectory, plan, model revision and assumptions of the moment
|
||||
they were made, nothing replaced with hindsight. Because the plan is the MPC's
|
||||
proposal while Generic Thermostat controls the relay, a forecast is scored at a
|
||||
horizon only if the relay was in the planned state for at least 12 minutes of
|
||||
every 15-minute block up to it; otherwise it is counted as `counterfactual` (or
|
||||
`unverifiable` when the relay history is incomplete) and never as an error.
|
||||
|
||||
Both report how much evidence there is. Forecasts start every 5 minutes, so at
|
||||
horizon h consecutive forecasts share most of their span, and all zones share one
|
||||
boiler: `effective_samples = samples × 5 / h / zones`. Below 5 the quality is
|
||||
`insufficient` and the sensor's state stays unknown; below 30 it is
|
||||
`indicative`.
|
||||
|
||||
Results on synthetic apartments (the tests; these check the method, not real-world
|
||||
accuracy):
|
||||
|
||||
|
||||
Reference in new issue
Block a user