Chantier etape 4 : le bilan, et description materielle consignee
BALANCE.js ne pilote rien : il publie les faits qui remplacent les seuils fixes — heure de croisement, energie a couvrir, SOC requis (remplacant de grid_soc_off), energie reseau strictement necessaire, surplus disponible pour le vehicule, indice de confiance. Sans une de ses entrees il se declare inutilisable et les controleurs gardent leurs regles. Deux niveaux de prudence. Le premier jet n'utilisait que le profil p80 et exigeait 48 % de SOC pour franchir la matinee du 24/08, soit plus cher que le seuil fixe de 30 % qu'il remplace. Corrige : la decision reseau, reevaluee a chaque cycle et reversible, utilise le profil typique p50 ; le prudent p80 sert a la planification. La marge de securite de 10 % ajoutee au depart etait une constante arbitraire et un double comptage : mesure faite, p80(somme) egale somme(p80) sur toutes les fenetres, les heures de consommation etant fortement correlees. Marge supprimee. Rejeu fige de la matinee du 24/08 (SOC 21 %, capacite 15 kWh, production reellement mesuree) : croisement a 08 h, 0,82 kWh a couvrir contre 0,80 calcule a la main, SOC requis 27,5 %, une session reseau de 0,97 kWh. Suite a 21 scenarios. MANUEL_COMPLET section 0 : description materielle complete relevee dans VRM et completee par le proprietaire — 2 MultiPlus-II 48/5000 en parallele, 53 panneaux sur trois orientations (10 est/sud-est, 30 sud, 10 ouest, 3 sur un petit MPPT), parc Pylontech, borne 32 A. Explique pourquoi aucun modele solaire mono-plan ne peut representer la production, et pourquoi Forecast.Solar mal configure se trompe de repartition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
116
tests/energy-balance/scenarios-balance.mjs
Normal file
116
tests/energy-balance/scenarios-balance.mjs
Normal file
@@ -0,0 +1,116 @@
|
||||
// Scénarios du nœud BALANCE.
|
||||
// Le scénario central est un REJEU de la matinée du 24/08/2026 : la production
|
||||
// horaire est celle réellement mesurée ce jour-là (heures 06-09 h non
|
||||
// censurées, donc fiables), le SOC est celui relevé à 06:43, et la capacité
|
||||
// est celle du parc réel (7 modules actifs, ~15 kWh).
|
||||
// C'est la situation qui a déclenché tout le chantier : le seuil fixe de 30 %
|
||||
// et le yoyo EDF de 2 à 4 minutes.
|
||||
|
||||
const P50 = [0.70,0.70,0.70,0.70,0.60,0.70,0.70,0.80,0.90,1.40,1.40,2.00,
|
||||
2.40,1.90,2.10,1.90,1.80,2.00,1.50,1.70,1.60,1.30,1.30,1.10];
|
||||
const P80 = [1.00,0.70,0.70,0.70,0.70,0.80,1.30,1.80,1.70,2.00,2.50,3.10,
|
||||
3.50,2.60,2.50,2.30,2.20,2.40,2.30,1.90,1.90,1.60,1.40,1.20];
|
||||
|
||||
// Production horaire réellement mesurée le 24/08/2026.
|
||||
const PROD_24_08 = {
|
||||
6: 0.03, 7: 0.65, 8: 2.70, 9: 5.62, 10: 7.17, 11: 9.33,
|
||||
12: 4.37, 13: 2.10, 14: 1.20, 15: 0.90, 16: 0.70, 17: 0.50,
|
||||
18: 0.30, 19: 0.10, 20: 0.00, 21: 0.00, 22: 0.00, 23: 0.00,
|
||||
};
|
||||
|
||||
function horizonFrom(startIso, hours, production) {
|
||||
const h0 = Math.floor(Date.parse(startIso) / 3600000) * 3600000;
|
||||
return Array.from({ length: hours }, (_, i) => {
|
||||
const ms = h0 + i * 3600000;
|
||||
// parseInt et non Number : en fr-FR le format renvoie « 06 h ».
|
||||
const hour = Number.parseInt(new Intl.DateTimeFormat('fr-FR', {
|
||||
timeZone: 'Europe/Paris', hour: '2-digit', hour12: false,
|
||||
}).format(new Date(ms)), 10);
|
||||
return {
|
||||
startMs: ms,
|
||||
hour,
|
||||
expectedKwh: production[hour] ?? 0,
|
||||
shapeQuality: hour >= 12 && hour <= 19 ? 'minoree' : 'fiable',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const balanceScenarios = [
|
||||
{
|
||||
node: 'BALANCE',
|
||||
id: 'bilan-rejeu-24-08-matin',
|
||||
label: 'Rejeu réel du 24/08 06:43 : croisement à 08 h, 0,82 kWh à couvrir, SOC requis 27,5 % au lieu du seuil fixe de 30 %',
|
||||
snapshot: {
|
||||
nowMs: Date.parse('2026-08-24T06:43:00+02:00'),
|
||||
consumption_p50: P50,
|
||||
consumption_p80: P80,
|
||||
horizon: horizonFrom('2026-08-24T06:43:00+02:00', 18, PROD_24_08),
|
||||
soc_percent: 21,
|
||||
usable_capacity_kwh: 15,
|
||||
soc_floor_percent: 22,
|
||||
},
|
||||
expect: {
|
||||
usable: true,
|
||||
crossingHour: 8,
|
||||
energyToCoverKwh: 0.82,
|
||||
requiredSocPct: 27.5,
|
||||
gridNeeded: true,
|
||||
prudentRequiredSocPct: 38.1,
|
||||
deficitTrustworthy: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: 'BALANCE',
|
||||
id: 'bilan-soc-suffisant-pas-de-reseau',
|
||||
label: 'Même matinée avec un SOC de 45 % : aucun réseau nécessaire',
|
||||
snapshot: {
|
||||
nowMs: Date.parse('2026-08-24T06:43:00+02:00'),
|
||||
consumption_p50: P50,
|
||||
consumption_p80: P80,
|
||||
horizon: horizonFrom('2026-08-24T06:43:00+02:00', 18, PROD_24_08),
|
||||
soc_percent: 45,
|
||||
usable_capacity_kwh: 15,
|
||||
soc_floor_percent: 22,
|
||||
},
|
||||
expect: {
|
||||
usable: true,
|
||||
gridNeeded: false,
|
||||
requiredSocPct: 27.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: 'BALANCE',
|
||||
id: 'bilan-nuit-sans-croisement',
|
||||
label: 'Nuit sans production sur tout l\'horizon : le déficit complet est annoncé, pas masqué',
|
||||
snapshot: {
|
||||
nowMs: Date.parse('2026-08-24T22:00:00+02:00'),
|
||||
consumption_p50: P50,
|
||||
consumption_p80: P80,
|
||||
horizon: horizonFrom('2026-08-24T22:00:00+02:00', 6, {}),
|
||||
soc_percent: 60,
|
||||
usable_capacity_kwh: 15,
|
||||
soc_floor_percent: 22,
|
||||
},
|
||||
expect: {
|
||||
usable: true,
|
||||
crossingFound: false,
|
||||
gridNeeded: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: 'BALANCE',
|
||||
id: 'bilan-entrees-manquantes',
|
||||
label: 'Capacité batterie absente : le bilan se déclare inutilisable, les contrôleurs gardent leurs règles',
|
||||
snapshot: {
|
||||
nowMs: Date.parse('2026-08-24T06:43:00+02:00'),
|
||||
consumption_p50: P50,
|
||||
consumption_p80: P80,
|
||||
horizon: horizonFrom('2026-08-24T06:43:00+02:00', 6, PROD_24_08),
|
||||
soc_percent: 21,
|
||||
},
|
||||
expect: {
|
||||
usable: false,
|
||||
missing: 'usable_capacity_kwh',
|
||||
},
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user