UX-77: Home's roll shares the peak room by room left to full, as control does
The cheap-run roll (cheaproll._Charge) shared the peak room by rating, control shares it by room left to full (packshare.weights). With unequal levels one battery's drawn block ended too early (rig F-1). The roll now uses packshare.weights over the simulated levels, each battery capped at its rating, a full one dropping out. test_ux77 gains two unequal-level walks and a by-rating mutant; its walks model control's split by room too. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
47c89a13d0
commit
6d2b9b5335
@@ -10,8 +10,10 @@ that goes on until the batteries are at their level or the cheap hours end.
|
||||
(`modetrade.energy()`, `priceruns.plan()`), until nothing is short or the next
|
||||
run does not follow on. `for_today()` reads the batteries at the level they
|
||||
will have by then: the charge power is the peak limit's room (peak aim minus
|
||||
the house, as `dayplan` counts it), shared by rating. Where that room is not
|
||||
known (no house profile or no aim), the rating is used.
|
||||
the house, as `dayplan` counts it), shared as control shares it: by room
|
||||
left to full (`packshare.weights()`), read again as the levels move, so a
|
||||
battery that fills drops out and its share goes to the others. Where that
|
||||
room is not known (no house profile or no aim), the rating is used.
|
||||
DECISIONS/UX-54-forecast-tab.md has the record.
|
||||
"""
|
||||
|
||||
@@ -20,7 +22,7 @@ import math
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from . import arbitrage, dayplan, modetrade, priceruns
|
||||
from . import arbitrage, dayplan, modetrade, packshare, priceruns
|
||||
from .dayplan import EFF
|
||||
from .price import required_spread, uncertainty_of
|
||||
|
||||
@@ -85,7 +87,7 @@ def _ceil_slot(t):
|
||||
class _Charge:
|
||||
"""The batteries' levels followed from `t0` while the chargers charge at
|
||||
the room the peak limit leaves: `min(sum of ratings, aim - house)`, shared
|
||||
by rating, each stopping at `modetrade.charge_to()`."""
|
||||
by room left to full as control does (`_shares()`), each stopping at `modetrade.charge_to()`."""
|
||||
|
||||
def __init__(self, packs, t0, ceiling_pct, targets, aim_w, house):
|
||||
self.packs = {p.name: p for p in packs}
|
||||
@@ -115,6 +117,34 @@ class _Charge:
|
||||
self._charge(s, e, hour)
|
||||
self.t = nxt
|
||||
|
||||
def _shares(self, ch, w):
|
||||
"""Watts per charger: `w` shared by `packshare.weights()` over all
|
||||
packs (room left to full, as control passes it), no charger above its
|
||||
rating, the surplus going to the others. By rating where the room left
|
||||
is not known (no capacity)."""
|
||||
wt = packshare.weights(list(self.packs.values()), -1.0)
|
||||
if wt is None:
|
||||
wt = {p.name: abs(p.rating_w) for p in ch}
|
||||
wt = {p.name: wt.get(p.name, 0.0) for p in ch}
|
||||
cap = {p.name: abs(p.rating_w) for p in ch}
|
||||
out, open_, left = {}, set(wt), float(w)
|
||||
while open_:
|
||||
tot = sum(wt[n] for n in open_)
|
||||
if tot <= 0.0:
|
||||
for n in open_:
|
||||
out[n] = 0.0
|
||||
break
|
||||
over = [n for n in open_ if left * wt[n] / tot > cap[n] + 1e-9]
|
||||
if not over:
|
||||
for n in open_:
|
||||
out[n] = left * wt[n] / tot
|
||||
break
|
||||
for n in over:
|
||||
out[n] = cap[n]
|
||||
left -= cap[n]
|
||||
open_.discard(n)
|
||||
return out
|
||||
|
||||
def _charge(self, s, e, hour):
|
||||
ch = modetrade.chargers(list(self.packs.values()), self.ceil,
|
||||
targets=self.targets)
|
||||
@@ -123,12 +153,13 @@ class _Charge:
|
||||
return
|
||||
w = self._room_w(hour, total)
|
||||
hrs = (e - s).total_seconds() / 3600.0
|
||||
share = self._shares(ch, w)
|
||||
for p in ch:
|
||||
self.stops.setdefault(p.name, None)
|
||||
if not p.capacity_kwh:
|
||||
continue
|
||||
to = modetrade.charge_to(p, self.ceil, self.targets)
|
||||
kwh = w * abs(p.rating_w) / total * hrs / 1000.0 * EFF
|
||||
kwh = share[p.name] * hrs / 1000.0 * EFF
|
||||
room = (to - p.soc) / 100.0 * float(p.capacity_kwh)
|
||||
if kwh <= 0.0:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user