Rest the meter just below zero, not at zero
The deadband is a one-way ratchet: any resting point inside it holds indefinitely. Import and export are separate registers on the meter, so a loop resting at +14 W bills 0.34 kWh/day while behaving perfectly. target_grid_w (default -10 W) moves that residue onto the export register. Worst billed rest point drops from 15 W to under 5 W. Behaviour is unchanged at target_grid_w: 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1b85eb41ad
commit
5184a2cfc2
@@ -28,6 +28,14 @@ class Tuning:
|
||||
step_w: int = 10
|
||||
saturation_w: float = 500.0
|
||||
saturation_cycles: int = 3
|
||||
# What the meter should rest at, in W. Negative = a slight export.
|
||||
# ⚠️ The deadband is a one-way ratchet: any resting point inside it holds
|
||||
# forever, and the meter's IMPORT register counts every positive one with
|
||||
# no export to cancel it. Resting at 0 W therefore leaks ~deadband/2 W of
|
||||
# billed import all day (15 W deadband ≈ 0.2-0.35 kWh). Biasing the rest
|
||||
# point below zero moves that leak into the export register, which is not
|
||||
# billed. Cost is ~|bias| W of given-away export; keep it small.
|
||||
target_grid_w: float = 0.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -74,11 +82,12 @@ def compute(
|
||||
# recognisable resting state, which is worth more than it looks - "flat for
|
||||
# 70 s" is how a healthy loop is recognised at a glance, and a command
|
||||
# frozen where it should not be is how two real bugs were caught.
|
||||
if abs(grid_w) < tuning.deadband_w:
|
||||
error = grid_w - tuning.target_grid_w
|
||||
if abs(error) < tuning.deadband_w:
|
||||
want = prev_w
|
||||
reason = "deadband"
|
||||
else:
|
||||
want = prev_w + tuning.gain * grid_w
|
||||
want = prev_w + tuning.gain * error
|
||||
|
||||
# ⚠️ Maintenance shaping (charge-only, cheap-window floor) used to live
|
||||
# here. It now belongs to arbiter.py as limit claims, so that precedence
|
||||
|
||||
@@ -66,6 +66,7 @@ class Controller:
|
||||
max_w=float(opts.get("max_w", 2000)),
|
||||
slew_w=float(opts.get("slew_w", 1000)),
|
||||
deadband_w=float(opts.get("deadband_w", 15)),
|
||||
target_grid_w=float(opts.get("target_grid_w", 0)),
|
||||
step_w=int(opts.get("step_w", 10)),
|
||||
saturation_w=float(opts.get("saturation_w", 500)),
|
||||
saturation_cycles=int(opts.get("saturation_cycles", 3)),
|
||||
|
||||
Reference in New Issue
Block a user