// coefficients.ts — chaque grandeur physique UNE fois, avec source et date. // // Règle : le document de recherche l'emporte sur le code quand ils divergent // (SPEC phase 1 §4.1, LOT P1.0 point 5). La table lisible par Jules est // docs/CONSTANTES.md (une ligne par grandeur : ancienne(s) valeur(s) → // retenue, source, date). Le jeu de coefficients d'ORIGINE (valeurs du code // de calculs.trans-former.fr au 28/09/2026) vit dans // fixtures/coefficients-2026-05.json et sert au test de non-régression. // // Les poids d'axes, modulateurs, matrices d'adéquation et tolérances de // algo-config.json ne sont PAS ici : ce sont des heuristiques de conception, // non sourcées, livrées comme données (fixtures/algo-config.json) et injectées. const D_RECHERCHE = '2026-04-28'; const D_LOT = '2026-09-28'; const S_EAU = '1 PROJETS/DEV/Simulateur autonomie V2/01-recherche-eau.md'; const S_ENERGIE = '1 PROJETS/DEV/Simulateur autonomie V2/02-recherche-energie.md'; const S_BRIEF = '1 PROJETS/DEV/Simulateur autonomie V2/99-brief-atis-dev.md'; const S_V1 = 'calculs.trans-former.fr, simulateur autoconstruction V1 (public/simulateurs/autoconstruction/app.js, calcSurfaces)'; /** Table harmonisée (LOT P1.0, 28/09/2026). Référence tant que Jules ne met pas de veto (R-7). */ export const COEFFICIENTS = { captage_rendement: { valeur: 0.8, source: `${S_EAU} §3 « Captable max » et §7 n°6 (NF EN 16941-1, recueil Nény p.16)`, date: D_RECHERCHE, note: 'fourchette 0,75 (zone sèche) à 0,85 (zone humide, gouttières optimisées) ; le code portait 0,7 (scoreurs), 0,8 (taux) et 0,85 (V1 bioclimatique)', }, captage_rendement_par_toiture: { valeur: { tuile: 0.85, ardoise: 0.85, gravillonnee: 0.5, vegetalisee: 0.3 }, source: `${S_EAU} §7 n°6 (ADEME, réf. NF EN 16941-1)`, date: D_RECHERCHE, note: 'à brancher quand le formulaire demande le type de toiture ; non utilisé en v0.1', }, besoin_eau_l_j_pers: { valeur: 150, source: `${S_EAU} §3 « Besoin eau » (INSEE / Eau France 2024) ; ${S_BRIEF} §7.1`, date: D_RECHERCHE, note: '54 750 L/an/personne ; le V1 comptait 46,5 m³/an', }, part_eau_substituable: { valeur: 0.7, source: `${S_EAU} §3 ; ${S_BRIEF} §7.1 (WC 30 + douche 60 + lessive 30 + jardin 25 L/j sur 150)`, date: D_RECHERCHE, note: 'le V1 comptait 32 % (15 m³ sur 46,5)', }, orientation_pv: { valeur: { sud: 1, 'sud-est': 0.95, 'sud-ouest': 0.95, est: 0.8, ouest: 0.8, nord: 0.6 }, source: `${S_ENERGIE} §3.2 « Variantes orientation toiture »`, date: D_RECHERCHE, note: 'source : SE/SO 95 %, E/O 80-85 %, N 60-65 % (bas de fourchette retenu) ; le brief §7.2 prenait les milieux 0,82 et 0,62 ; le code portait 0,93 / 0,78 / 0,40', }, productible_pv_kwh_kwc: { valeur: { par: 'zone_rt', table: { H1a: 1000, H1b: 1050, H1c: 1200, H2a: 1100, H2b: 1200, H2c: 1250, H2d: 1300, H3: 1400 }, defaut: 1100, }, source: `${S_ENERGIE} §3.2 (PVGIS, sud 30°, pertes 14 %)`, date: D_RECHERCHE, note: 'H1a : moyenne Lille 950 / Paris 1 050 ; H2d : interpolé entre H2c et H3 par la MOE, non sourcé (déclaré en avertissement) ; défaut = H2a ; le code portait une table par zone biogéographique allant jusqu\'à 1 550, au-dessus du maximum de la source', }, autoconso_sans_batterie: { valeur: 0.3, source: `${S_ENERGIE} §3.2 ; ${S_BRIEF} §7.2`, date: D_RECHERCHE, note: 'absent du code : la couverture énergie comparait la production brute au besoin', }, autoconso_avec_batterie_10kwh: { valeur: 0.7, source: `${S_ENERGIE} §3.2 ; ${S_BRIEF} §7.2`, date: D_RECHERCHE, note: 'entre 0 et 10 kWh de batterie, le moteur interpole linéairement (choix MOE, non sourcé)', }, besoin_elec_kwh_pers_an: { valeur: 1000, source: `${S_ENERGIE} §3.1 (ADEME 2023, RTE 2024) ; ${S_BRIEF} §7.2`, date: D_RECHERCHE, note: 'fourchette 900 à 1 200 ; hors chauffage électrique, ECS et véhicule', }, dju_defaut_par_classe_rt: { valeur: { H1: 2800, H2: 2200, H3: 1500 }, source: 'heuristique de conception V2 (calibrage.js et algo-config.json), non sourcée', date: '2026-05-01', note: 'repli seulement : la table france_climat porte un DJU par département', }, ml_haie_par_pers: { valeur: 25, source: `${S_BRIEF} §7.3 (correction Jules du 28/04/2026) ; 03-recherche-alimentation.md §5`, date: D_RECHERCHE, note: '50 ml/personne pour un excédent transformable', }, contexte_defaut: { valeur: { zone_rt: 'H2a', zone_biogeo_code: 4, pluviometrie_mm: 800, dju: 2200 }, source: 'heuristique de conception V2 (algo.js deriveContexte, simulation.js), non sourcée', date: '2026-05-01', note: 'appliqué seulement si le département est absent de france_climat ; toujours déclaré en avertissement', }, geometrie_ratio_shab_emprise: { valeur: 0.8, source: 'heuristique MOE (murs extérieurs ~0,3 m, refends, escalier), non sourcée', date: D_LOT, note: 'hypothèse porteuse du P1.2 : à confronter à la surface DPE des adresses A et B (écart visé < 20 %)', }, geometrie_ratio_vitrage_shab: { valeur: 1 / 6, source: `${S_V1} (vit = S/6) ; règle RT 2012 (baies ≥ 1/6 de la SHAB)`, date: '2025-01-01', note: 'la SPEC §4.1 écrit « 15 % » ; 1/6 = 16,7 %', }, geometrie_hauteur_etage_m: { valeur: 2.5, source: 'heuristique MOE (hauteur sous plafond courante), non sourcée', date: D_LOT, }, geometrie_facteur_perimetre: { valeur: 1.3, source: `${S_V1} (perimetre = √(S × 1,3) × 4)`, date: '2025-01-01', }, geometrie_part_mur_mitoyen: { valeur: 0.25, source: 'heuristique MOE (un côté mitoyen = un quart du périmètre), non sourcée', date: D_LOT, }, geometrie_majoration_toiture: { valeur: 1.05, source: `${S_V1} (toiture = S / cos α × 1,05)`, date: '2025-01-01', }, geometrie_pente_defaut_deg: { valeur: 30, source: 'heuristique MOE (pente courante des toitures à deux versants), non sourcée', date: D_LOT, }, }; /** Clés de la table, pour valider un jeu de coefficients injecté (fixture, veto). */ export const CLES_COEFFICIENTS = Object.keys(COEFFICIENTS); /** * Vérifie qu'un objet a la forme d'une table de coefficients (toutes les clés, * chaque entrée avec valeur, source et date). Renvoie la liste des défauts. */ export function verifieTableCoefficients(table) { const erreurs = []; if (!table || typeof table !== 'object') return ['table absente ou non objet']; const t = table; for (const cle of CLES_COEFFICIENTS) { const c = t[cle]; if (!c || typeof c !== 'object') { erreurs.push(`${cle} : absent`); continue; } if (!('valeur' in c)) erreurs.push(`${cle} : valeur absente`); if (typeof c.source !== 'string' || !c.source) erreurs.push(`${cle} : source absente`); if (typeof c.date !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(c.date)) erreurs.push(`${cle} : date absente ou non AAAA-MM-JJ`); } return erreurs; }