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
115 lines
5.7 KiB
JavaScript
115 lines
5.7 KiB
JavaScript
// integrateur.ts — moteur `autonomie.integrateur` : contexte + pondération des
|
|
// axes + allocation du budget + agrégation.
|
|
//
|
|
// Deux modes :
|
|
// - 'global' (défaut) : le pipeline complet d'origine (`selectionner`), avec
|
|
// couplages et réajustement GLOBAUX entre axes — celui de calculs ;
|
|
// - 'par_axe' : chaque axe est calculé par son moteur d'axe sur son budget,
|
|
// puis les sorties sont additionnées — celui du menu de la plateforme.
|
|
// Le mode « aménagements cochés » (recalcul sans relancer l'allocation) est
|
|
// P1.5 : une liste transmise est ignorée et signalée.
|
|
import { Trace } from '../commun/contrat.js';
|
|
import { COEFFICIENTS } from '../commun/coefficients.js';
|
|
import { deriveContexte } from '../commun/contexte.js';
|
|
import { VERSION } from '../version.js';
|
|
import { calculeAxe, enregistreLectures, traceSelection } from './axe.js';
|
|
import { selectionner } from './noyau/algo.js';
|
|
import { alloueBudget } from './noyau/alloue-budget.js';
|
|
import { configPourAxes } from './noyau/axes-actifs.js';
|
|
import { pondereAxes } from './noyau/pondere-axes.js';
|
|
import { AXES } from './noyau/types.js';
|
|
import { aCaptage, batterieSelectionnee, kwcSelectionnes, mlHaiesSelectionnes, tauxAlim, tauxEau, tauxEnergie } from './taux.js';
|
|
/** Statut agrégé : BUDGET_INSUFFISANT l'emporte, puis AJOUTER_OPTIONNEL, sinon OK. */
|
|
export function agregeStatuts(statuts) {
|
|
if (statuts.includes('BUDGET_INSUFFISANT'))
|
|
return 'BUDGET_INSUFFISANT';
|
|
if (statuts.includes('AJOUTER_OPTIONNEL'))
|
|
return 'AJOUTER_OPTIONNEL';
|
|
return 'OK';
|
|
}
|
|
export function calculeIntegrateur(entrees, donnees) {
|
|
const trace = new Trace();
|
|
const mode = entrees.mode ?? 'global';
|
|
const coefficients_base = donnees.coefficients ?? COEFFICIENTS;
|
|
const { table: coefficients, lues } = enregistreLectures(coefficients_base);
|
|
const config = configPourAxes(donnees.algo_config, entrees.axes_actifs);
|
|
const inputs = { ...entrees.autodiag, budget_eur: entrees.budget_total };
|
|
if (entrees.amenagements_coches && entrees.amenagements_coches.length) {
|
|
trace.avertit('aménagements cochés ignorés : le mode « coché » arrive en P1.5');
|
|
}
|
|
const ctx = deriveContexte(inputs.code_postal, donnees.france_climat, coefficients);
|
|
trace.absorbe(ctx);
|
|
const contexte = ctx.sorties;
|
|
const poids = pondereAxes(inputs, contexte, config, false);
|
|
const budgets = alloueBudget(entrees.budget_total, poids);
|
|
const axes = {};
|
|
const taux_couverture = {};
|
|
let total = 0;
|
|
let status;
|
|
let message_status;
|
|
let couplages = [];
|
|
if (mode === 'par_axe') {
|
|
const statuts = [];
|
|
for (const axe of AXES) {
|
|
const actif = !entrees.axes_actifs || entrees.axes_actifs[axe] !== false;
|
|
if (!actif || budgets[axe] <= 0) {
|
|
axes[axe] = { postes: [], typologies: [], sous_total_eur_ht: 0, budget_alloue: budgets[axe], status: 'OK', taux_couverture: null, taux_detail: null };
|
|
taux_couverture[axe] = null;
|
|
continue;
|
|
}
|
|
const r = calculeAxe(axe, { autodiag: entrees.autodiag, budget_axe: budgets[axe], contexte }, { ...donnees, coefficients: coefficients_base });
|
|
trace.absorbe(r);
|
|
const s = r.sorties;
|
|
axes[axe] = { postes: s.postes, typologies: s.typologies, sous_total_eur_ht: s.sous_total_eur_ht, budget_alloue: s.budget_alloue, status: s.status, taux_couverture: s.taux_couverture, taux_detail: s.taux_detail };
|
|
taux_couverture[axe] = s.taux_couverture;
|
|
total += s.sous_total_eur_ht;
|
|
statuts.push(s.status);
|
|
couplages = couplages.concat(s.couplages);
|
|
}
|
|
status = agregeStatuts(statuts);
|
|
}
|
|
else {
|
|
const r = selectionner(inputs, donnees.options, donnees.postes, donnees.france_climat, { config, coefficients }, false);
|
|
const tous = [];
|
|
for (const axe of AXES) {
|
|
const a = r.axes[axe];
|
|
const postes = a?.postes ?? [];
|
|
tous.push(...postes);
|
|
let t;
|
|
if (axe === 'eau')
|
|
t = tauxEau({ surface_toit_m2: inputs.surface_toit_m2, nb_personnes: inputs.nb_personnes, pluviometrie_mm: contexte.pluviometrie_mm, a_captage: aCaptage(postes) }, coefficients, trace);
|
|
else if (axe === 'energie')
|
|
t = tauxEnergie({ kwc: kwcSelectionnes(postes), batterie_kwh: batterieSelectionnee(postes), orientation_toit: inputs.orientation_toit, nb_personnes: inputs.nb_personnes, contexte }, coefficients, trace);
|
|
else
|
|
t = tauxAlim({ ml: mlHaiesSelectionnes(postes), nb_personnes: inputs.nb_personnes }, coefficients, trace);
|
|
axes[axe] = { postes, typologies: postes.map((p) => p.typologie_choisie), sous_total_eur_ht: a?.sous_total_eur_ht ?? 0, budget_alloue: a?.budget_alloue ?? budgets[axe], status: r.status, taux_couverture: t.taux, taux_detail: t.detail };
|
|
taux_couverture[axe] = t.taux;
|
|
}
|
|
total = r.total_eur_ht;
|
|
status = r.status;
|
|
message_status = r.message_status;
|
|
couplages = r.couplages;
|
|
traceSelection(trace, coefficients_base, lues, tous, donnees.options, couplages);
|
|
}
|
|
const sorties = {
|
|
mode,
|
|
contexte,
|
|
poids,
|
|
budgets,
|
|
axes,
|
|
total_eur_ht: total,
|
|
budget_cible: entrees.budget_total,
|
|
ecart_budget_pct: entrees.budget_total > 0 ? +(((total - entrees.budget_total) / entrees.budget_total) * 100).toFixed(2) : 0,
|
|
status,
|
|
message_status,
|
|
couplages,
|
|
taux_couverture,
|
|
};
|
|
return trace.resultat(sorties, VERSION);
|
|
}
|
|
export const integrateur = {
|
|
id: 'autonomie.integrateur',
|
|
calculer: calculeIntegrateur,
|
|
};
|
|
export default integrateur;
|