fix(LM-1004): le recrédit appartient à la passe qui dépense du surplus
La règle disait « attribué à la PREMIÈRE passe où la charge apparaît », et le code l'appliquait fidèlement. Mais la passe éco n'est pas bornée par le surplus : elle l'est par l'autorisation de soutirage, et ne touche jamais remainingSurplusW. Lui attribuer le recrédit revenait donc à ne le créditer NULLE PART. Conséquence : une charge servie en éco ne récupérait jamais sa propre consommation, et son tirage déprimait le budget dont elle avait besoin pour recevoir du surplus par-dessus. Mesuré sur .75 : borne à 3 899 W, surplus publié −3 148 W ; sans elle il valait +700 W. Elle se privait exactement du surplus qu'elle pouvait recevoir, et « minimum + surplus » ne servait jamais que le minimum. LE DÉFAUT PRÉEXISTAIT au lot d'aujourd'hui — le chauffe-eau servi en éco l'avait déjà. Il était invisible parce que son compteur affiche 0 W : une charge qui ne mesure rien ne peut pas se priver de quoi que ce soit. Il a fallu une charge qui mesure, et qui tire quatre kilowatts, pour qu'il se voie. C'est une septième forme de l'homogénéité du banc, et la première où c'est l'ABSENCE de mesure qui masque et non l'uniformité. Le paramètre recreditDejaAttribue et l'ensemble recreditAttribue sont retirés plutôt que laissés inertes : un mécanisme vestigial se relit comme une garde. Suites : loadmodel 26/26, simulation 67/67, charging 48/48, spotmarket 32/32.
This commit is contained in:
parent
049eaeeaa1
commit
421e0452cc
@ -246,7 +246,6 @@ Plan RuleBasedScheduler::getPlan(const SurplusContext &ctx)
|
||||
// donc après un éco de rang 3 — c'est le sens même des deux passes, et le publier sous forme
|
||||
// de niveaux est ce qui rend cet ordre lisible sans qu'un client ait à le redériver.
|
||||
QHash<QString, LoadAction::Level> niveauxEco;
|
||||
QSet<QString> recreditAttribue;
|
||||
|
||||
for (const LoadContext &lc : nonEvLoads) {
|
||||
const EcoProgress avancement = m_arbitrator->ecoProgress(lc, ctx.timestamp);
|
||||
@ -358,10 +357,6 @@ Plan RuleBasedScheduler::getPlan(const SurplusContext &ctx)
|
||||
eco.reason = { DecisionCode::EcoFloorGrid, motif };
|
||||
niveauxEco.insert(lc.id, eco);
|
||||
|
||||
// LM-1004 — le recrédit est attribué à la PREMIÈRE passe où la charge apparaît, et le
|
||||
// champ le rend vérifiable plutôt que discipliné : la discipline ne se teste pas.
|
||||
recreditAttribue.insert(lc.id);
|
||||
|
||||
draw.committedW += achatW;
|
||||
if (margeRestante.borne)
|
||||
margeRestante.watts -= achatW;
|
||||
@ -373,8 +368,8 @@ Plan RuleBasedScheduler::getPlan(const SurplusContext &ctx)
|
||||
slot.actions.append(buildSgReadyStateAction(lc, remainingSurplusW, compta));
|
||||
else
|
||||
slot.actions.append(emitSetpointAction(
|
||||
decideSetpoint(lc, remainingSurplusW, margeRestante, compta, draw,
|
||||
recreditAttribue.contains(lc.id)))); // etmvariableload + relay-router + evcharger
|
||||
decideSetpoint(lc, remainingSurplusW, margeRestante, compta, draw)));
|
||||
// etmvariableload + relay-router + evcharger
|
||||
|
||||
// Le décrément vaut pour TOUTES les charges servies, bornes et PAC comprises : elles
|
||||
// consomment la même autorisation, qu'elles aient été bornées par elle ou non. Ne
|
||||
@ -515,8 +510,7 @@ DecisionReason RuleBasedScheduler::choisirMotif(const MotifEntrees &e) const
|
||||
RuleBasedScheduler::SetpointTarget RuleBasedScheduler::decideSetpoint(const LoadContext &lc,
|
||||
double &remainingSurplusW,
|
||||
DrawMargin &margeRestante,
|
||||
PlanBudget &budget, PlanDraw &draw,
|
||||
bool recreditDejaAttribue) const
|
||||
PlanBudget &budget, PlanDraw &draw) const
|
||||
{
|
||||
// Recrédit anti-clignotement (Correction B / contrat rév. 2 §5) : la conso de DÉBUT de cycle
|
||||
// est déjà soustraite de l'export mesuré → on la rend au budget local avant d'arrondir.
|
||||
@ -539,8 +533,22 @@ RuleBasedScheduler::SetpointTarget RuleBasedScheduler::decideSetpoint(const Load
|
||||
// rembourser, et les watts prêtés sont réalloués aux suivantes pendant que la charge
|
||||
// continue de les tirer. L'identité comptable TIENT malgré tout : aucun contrôle de
|
||||
// cohérence ne peut l'attraper, c'est l'hypothèse qui est fausse, pas l'arithmétique.
|
||||
// LM-1004 CORRIGÉE (2026-09-07) — le recrédit appartient à la passe qui DÉPENSE DU SURPLUS.
|
||||
//
|
||||
// La règle disait « à la première passe où la charge apparaît ». Or la passe éco ne dépense
|
||||
// pas de surplus : elle est bornée par l'autorisation de soutirage, et ne touche jamais
|
||||
// `remainingSurplusW`. Lui « attribuer » le recrédit revenait donc à ne le créditer NULLE
|
||||
// PART — une charge servie en éco ne récupérait jamais sa propre consommation, et son
|
||||
// tirage déprimait le budget dont elle avait besoin pour recevoir du surplus par-dessus.
|
||||
//
|
||||
// Mesuré sur .75 le 2026-09-07 : borne à 3 899 W, surplus publié −3 148 W. Sans la borne,
|
||||
// il valait +700 W. Elle se privait exactement du surplus qu'elle pouvait recevoir, et le
|
||||
// mode « minimum + surplus » ne servait jamais que le minimum.
|
||||
//
|
||||
// Le défaut préexistait pour le chauffe-eau servi en éco ; il était invisible parce que son
|
||||
// compteur affiche 0 W — une charge qui ne mesure rien ne peut pas se priver de rien.
|
||||
const bool refuse = lc.telemetry.commandRefused;
|
||||
const double recreditW = (m_reserveActive || recreditDejaAttribue || refuse)
|
||||
const double recreditW = (m_reserveActive || refuse)
|
||||
? 0.0 : lc.telemetry.currentPowerW;
|
||||
const double budgetW = remainingSurplusW + recreditW;
|
||||
budget.recreditedW += recreditW;
|
||||
|
||||
@ -83,11 +83,6 @@ private:
|
||||
*
|
||||
* \param lc Charge \c etmvariableload du SurplusContext.
|
||||
* \param[in,out] remainingSurplusW Budget de surplus restant (W) ; mis à jour pour la suite.
|
||||
* \param recreditDejaAttribue Vrai si la passe ÉCO a déjà crédité cette charge. Le recrédit
|
||||
* anti-clignotement s'attribue à la PREMIÈRE passe où la charge apparaît et une seule fois
|
||||
* (\rule{LM-1004}) — le recréditer deux fois rendrait au budget de surplus une puissance
|
||||
* que l'éco a financée au réseau. Champ EXPLICITE plutôt que discipline : la discipline ne
|
||||
* se teste pas.
|
||||
* \param[in,out] margeRestante Autorisation de SOUTIRAGE restante (W), consommée le long
|
||||
* de la cascade au même titre que le surplus. \c borne à faux = aucun plafond en vigueur,
|
||||
* ce qui n'est PAS une marge nulle : sans plafond la cascade n'est pas bornée, à marge
|
||||
@ -166,8 +161,7 @@ private:
|
||||
*/
|
||||
SetpointTarget decideSetpoint(const LoadContext &lc, double &remainingSurplusW,
|
||||
DrawMargin &margeRestante,
|
||||
PlanBudget &budget, PlanDraw &draw,
|
||||
bool recreditDejaAttribue = false) const;
|
||||
PlanBudget &budget, PlanDraw &draw) const;
|
||||
|
||||
/*!
|
||||
* \brief Forme la commande à partir d'une cible décidée — et forme le motif.
|
||||
|
||||
@ -558,10 +558,26 @@ C'est la règle qui empêche le défaut le plus probable de cette conception : u
|
||||
en passe 1 puis recommandé en passe 2, donc deux commutations dans le même cycle — du
|
||||
court-cycling fabriqué par l'arbitre lui-même.
|
||||
|
||||
**LM-1004 — Le recrédit anti-clignotement s'attribue UNE fois.** La correction B rend à chaque
|
||||
charge sa consommation de début de cycle avant d'arrondir. Avec deux passes, ce crédit doit
|
||||
être attribué à la **première** passe où la charge apparaît, jamais aux deux : compté deux
|
||||
fois, il fabriquerait du budget qui n'existe pas.
|
||||
**LM-1004 — Le recrédit anti-clignotement appartient à la passe qui DÉPENSE DU SURPLUS.** La
|
||||
correction B rend à chaque charge sa consommation de début de cycle avant d'arrondir. Avec deux
|
||||
passes, ce crédit ne doit être compté qu'une fois : compté deux fois, il fabriquerait du budget
|
||||
qui n'existe pas.
|
||||
|
||||
> **Formulation corrigée le 2026-09-07.** La règle disait « à la **première** passe où la charge
|
||||
> apparaît ». C'était faux, et le code l'appliquait fidèlement : la passe éco n'est pas bornée
|
||||
> par le surplus mais par l'**autorisation de soutirage**, et ne touche jamais
|
||||
> `remainingSurplusW`. Lui « attribuer » le recrédit revenait donc à ne le créditer **nulle
|
||||
> part** — une charge servie en éco ne récupérait jamais sa propre consommation.
|
||||
>
|
||||
> **Conséquence mesurée** : borne à 3 899 W, surplus publié **−3 148 W** ; sans elle il valait
|
||||
> **+700 W**. Elle se privait exactement du surplus qu'elle pouvait recevoir, et « minimum +
|
||||
> surplus » ne servait jamais que le minimum.
|
||||
>
|
||||
> Le défaut existait depuis le §10 pour le chauffe-eau servi en éco. Il était **invisible parce
|
||||
> que son compteur affiche 0 W** : une charge qui ne mesure rien ne peut pas se priver de quoi
|
||||
> que ce soit. Il a fallu une charge qui mesure — et qui tire quatre kilowatts — pour qu'il se
|
||||
> voie. Septième forme de l'homogénéité du banc, et la première où c'est l'ABSENCE de mesure
|
||||
> qui masque, non l'uniformité.
|
||||
|
||||
**LM-1005 — La frontière que ce modèle franchit, et il faut la nommer.**
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user