Skip to content

PV twin predicts non-zero PV at night (Model.Predict returns Beta[0]) #133

Description

@frahlg

Symptom

Operator report (2026-04-19): planner shows PV generation of −1.1 kW at Mon 02:00 local — physically impossible. Screenshot in PR description.

Root cause

`go/internal/pvmodel/model.go:109-148` `(Model).Predict`:

Feature vector has an intercept term:

```go
x = [ 1.0, // ← always 1
clearSkyW,
clearSkyW * cloudFactor,
clearSkyW * sin(h), ... ]
```

At night `clearSkyW = 0`, so every feature except `x[0]` is zero and:

```
learned = Beta[0] * 1 + 0 + 0 + ... = Beta[0]
prior = 0
y = trust * Beta[0] (trust = 1 after 50 samples)
```

RLS trains only in daylight (Update gates on `clearSkyW >= 50`), but nothing pins `Beta[0]` to 0. Whenever the daytime residual is minimized by a non-zero intercept + lower `Beta[2]` coefficient, the model picks that — and the intercept leaks into night predictions.

The planner's `selectPlannerPVW` (mpc/service.go:832) makes it worse at night specifically: when `forecastPVW < 200 W` (which is always true at night — met.no returns 0) it falls back unreserved to the twin's prediction, so the 1.1 kW phantom generation becomes the MPC's sole PV input.

Fix (this issue — narrow)

Hard gate at the top of `Model.Predict`: when `clearSkyW < 50` return 0. Mirrors the Update-side gate (same threshold, same physical justification: no sun → no PV).

Follow-up (separate issue)

The intercept still drifts during the day and biases daytime predictions. See #TBD.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions