Extraction du cœur autonomie de calculs.trans-former.fr (12 modules, sans singleton, couplages sans mutation, console derrière debug), découpage en trois moteurs d'axe + intégrateur (mode global = calculs, mode par_axe = plateforme), moteurs commun.contexte (Corse 2A/2B, DOM 97x corrigés) et commun.geometrie, table des coefficients harmonisés (docs/CONSTANTES.md pour Jules, R-7), validation structurelle des données (schemas/), tests hors ligne : 28 assertions d'origine + épingle générée depuis le code d'astro-pro + écarts d'harmonisation figés + contrat des moteurs. dist/ committé. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UBHYPdeQYy2m1GVJp6b12
92 lines
4.3 KiB
JavaScript
92 lines
4.3 KiB
JavaScript
// pondere-axes.ts — Étape 1 : pondération des axes selon contexte
|
|
// Spec: V2-algo-spec.md §Étape 1 — origine calculs/src/lib/autonomie/pondere-axes.js.
|
|
// Correction LOT P1.0 : `config` en paramètre (plus de getConfig()).
|
|
export function pondereAxes(inputs, contexte_derived, config, debug = false) {
|
|
const base = config.poids_axes_base ?? { eau: 40, energie: 40, alim: 20 };
|
|
const mod = (config.modulateurs ?? {});
|
|
const poids = { eau: base.eau, energie: base.energie, alim: base.alim };
|
|
const log = debug ? [] : null;
|
|
// SI surface_toit < seuil → poids_energie -= 15, redistribuer eau+5 alim+10
|
|
const toit_seuil = mod.toit_petit_seuil_m2 ?? 30;
|
|
if ((inputs.surface_toit_m2 ?? 0) < toit_seuil) {
|
|
const delta = mod.toit_petit_delta ?? { energie: -15, eau: 5, alim: 10 };
|
|
poids.energie += delta.energie ?? 0;
|
|
poids.eau += delta.eau ?? 0;
|
|
poids.alim += delta.alim ?? 0;
|
|
if (log)
|
|
log.push(`toit < ${toit_seuil}m² → Δenergie${delta.energie}, Δeau+${delta.eau}, Δalim+${delta.alim}`);
|
|
}
|
|
// SI surface_toit > 100 m² → poids_energie += 5
|
|
if ((inputs.surface_toit_m2 ?? 0) > 100) {
|
|
poids.energie += 5;
|
|
if (log)
|
|
log.push('toit > 100m² → Δenergie+5');
|
|
}
|
|
// SI nb_personnes >= 5 → poids_eau += 10, poids_alim += 5, poids_energie -= 15
|
|
const famille_seuil = mod.famille_grande_seuil ?? 5;
|
|
if ((inputs.nb_personnes ?? 0) >= famille_seuil) {
|
|
const delta = mod.famille_grande_delta ?? { eau: 10, alim: 5, energie: -15 };
|
|
poids.eau += delta.eau ?? 0;
|
|
poids.alim += delta.alim ?? 0;
|
|
poids.energie += delta.energie ?? 0;
|
|
if (log)
|
|
log.push(`nb_pers >= ${famille_seuil} → Δeau+${delta.eau}, Δalim+${delta.alim}, Δenergie${delta.energie}`);
|
|
}
|
|
// SI zone_biogeo == 'MED' → poids_eau += 10
|
|
if (contexte_derived.zone_biogeo === 'MED') {
|
|
const delta = mod.biogeo_med_delta ?? { eau: 10 };
|
|
poids.eau += delta.eau ?? 0;
|
|
if (log)
|
|
log.push(`zone MED → Δeau+${delta.eau}`);
|
|
}
|
|
// SI pluviometrie_mm < 600 → poids_eau += 5
|
|
const pluvio_seuil = mod.pluvio_basse_seuil_mm ?? 600;
|
|
if ((contexte_derived.pluviometrie_mm ?? 800) < pluvio_seuil) {
|
|
const delta = mod.pluvio_basse_delta ?? { eau: 5 };
|
|
poids.eau += delta.eau ?? 0;
|
|
if (log)
|
|
log.push(`pluvio < ${pluvio_seuil}mm → Δeau+${delta.eau}`);
|
|
}
|
|
// SI surface_parcelle < 300 m² → poids_alim = 0
|
|
const parcelle_petite = mod.parcelle_petite_seuil_m2 ?? 300;
|
|
if ((inputs.surface_parcelle_m2 ?? 0) < parcelle_petite) {
|
|
poids.alim = 0;
|
|
if (log)
|
|
log.push(`parcelle < ${parcelle_petite}m² → alim=0`);
|
|
}
|
|
// SI surface_parcelle >= 1000 m² ET temps_dispo == 3 → poids_alim += 10
|
|
const parcelle_grande = mod.parcelle_grande_seuil_m2 ?? 1000;
|
|
if ((inputs.surface_parcelle_m2 ?? 0) >= parcelle_grande && inputs.temps_dispo === 3) {
|
|
const delta = mod.parcelle_grande_temps3_delta ?? { alim: 10 };
|
|
poids.alim += delta.alim ?? 0;
|
|
if (log)
|
|
log.push(`parcelle >= ${parcelle_grande}m² + temps=3 → Δalim+${delta.alim}`);
|
|
}
|
|
// Axes explicitement désactivés par l'utilisateur (V3 toggles d'axes) :
|
|
// marqueur posé par axes-actifs.ts (configPourAxes). Les modulations
|
|
// hardcodées ci-dessus (ex : toit > 100 m² -> energie +5) ne doivent pas
|
|
// réactiver un axe éteint. Absent = comportement historique inchangé.
|
|
const desactives = config._axes_desactives || [];
|
|
for (const axe of desactives) {
|
|
if (axe in poids && poids[axe] !== 0) {
|
|
poids[axe] = 0;
|
|
if (log)
|
|
log.push('axe ' + axe + ' désactivé (toggle UI) -> poids 0');
|
|
}
|
|
}
|
|
// Normalisation : somme = 100
|
|
const total = poids.eau + poids.energie + poids.alim;
|
|
if (total !== 100 && total > 0) {
|
|
poids.eau = Math.round((poids.eau / total) * 100);
|
|
poids.energie = Math.round((poids.energie / total) * 100);
|
|
poids.alim = 100 - poids.eau - poids.energie; // garantie somme exacte
|
|
}
|
|
// Garde-fou : pas de poids négatif
|
|
poids.eau = Math.max(0, poids.eau);
|
|
poids.energie = Math.max(0, poids.energie);
|
|
poids.alim = Math.max(0, poids.alim);
|
|
if (debug)
|
|
return { poids, log: log ?? [] };
|
|
return poids;
|
|
}
|