LOT P1.0 : paquet @transformer/moteurs v0.1.0

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
This commit is contained in:
Jules
2026-09-28 17:06:49 +02:00
co-authored by Claude Fable 5.1
commit 69809df5d1
123 changed files with 13225 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
// geometrie.ts — moteur `commun.geometrie` : emprise, niveaux, hauteur, pente,
// mitoyenneté → surfaces de parois, SHAB estimée, volume. Reprend les formules
// de `calcSurfaces` du simulateur autoconstruction V1 (calculs), sans le DOM,
// avec les coefficients sortis dans coefficients.ts.
import { Trace } from './contrat.js';
import { COEFFICIENTS } from './coefficients.js';
import { arrondi2 } from './argent.js';
import { VERSION } from '../version.js';
export function calculeGeometrie(entrees, coefficients = COEFFICIENTS) {
const trace = new Trace();
const c = coefficients;
const emprise = Math.max(0, entrees.emprise_m2 || 0);
const niveaux = Math.max(1, Math.round(entrees.niveaux ?? 1));
if (entrees.niveaux == null)
trace.avertit('nombre de niveaux inconnu : 1 niveau supposé');
let hauteur = entrees.hauteur_m ?? 0;
if (!(hauteur > 0)) {
hauteur = niveaux * c.geometrie_hauteur_etage_m.valeur;
trace.avertit(`hauteur inconnue : ${niveaux} × ${c.geometrie_hauteur_etage_m.valeur} m supposés`);
trace.source('geometrie_hauteur_etage_m', c.geometrie_hauteur_etage_m.valeur, c.geometrie_hauteur_etage_m.source, c.geometrie_hauteur_etage_m.date);
}
const hauteur_etage = hauteur / niveaux;
let pente = entrees.pente_toit_deg;
if (pente == null || !(pente >= 0) || pente >= 90) {
pente = c.geometrie_pente_defaut_deg.valeur;
trace.avertit(`pente de toit inconnue : ${pente}° supposés`);
trace.source('geometrie_pente_defaut_deg', pente, c.geometrie_pente_defaut_deg.source, c.geometrie_pente_defaut_deg.date);
}
const mitoyen = Math.min(2, Math.max(0, Math.round(entrees.mitoyennete ?? 0)));
// SHAB
let shab;
let shab_estimee;
if (entrees.surface_habitable_m2 && entrees.surface_habitable_m2 > 0) {
shab = entrees.surface_habitable_m2;
shab_estimee = false;
}
else {
shab = emprise * niveaux * c.geometrie_ratio_shab_emprise.valeur;
shab_estimee = true;
trace.avertit('surface habitable estimée depuis l\'emprise (aucune surface connue)');
trace.source('geometrie_ratio_shab_emprise', c.geometrie_ratio_shab_emprise.valeur, c.geometrie_ratio_shab_emprise.source, c.geometrie_ratio_shab_emprise.date);
}
// Périmètre (V1 : √(S × 1,3) × 4) et part exposée
const perimetre = 4 * Math.sqrt(emprise * c.geometrie_facteur_perimetre.valeur);
trace.source('geometrie_facteur_perimetre', c.geometrie_facteur_perimetre.valeur, c.geometrie_facteur_perimetre.source, c.geometrie_facteur_perimetre.date);
const perimetre_expose = perimetre * (1 - c.geometrie_part_mur_mitoyen.valeur * mitoyen);
if (mitoyen > 0)
trace.source('geometrie_part_mur_mitoyen', c.geometrie_part_mur_mitoyen.valeur, c.geometrie_part_mur_mitoyen.source, c.geometrie_part_mur_mitoyen.date);
// Parois
const murs_exterieurs = perimetre_expose * hauteur;
const vitrage = shab * c.geometrie_ratio_vitrage_shab.valeur;
trace.source('geometrie_ratio_vitrage_shab', arrondi2(c.geometrie_ratio_vitrage_shab.valeur), c.geometrie_ratio_vitrage_shab.source, c.geometrie_ratio_vitrage_shab.date);
const murs_opaques = Math.max(0, murs_exterieurs - vitrage);
const rad = (pente * Math.PI) / 180;
const toiture = (emprise / Math.cos(rad)) * c.geometrie_majoration_toiture.valeur;
trace.source('geometrie_majoration_toiture', c.geometrie_majoration_toiture.valeur, c.geometrie_majoration_toiture.source, c.geometrie_majoration_toiture.date);
const volume = shab * hauteur_etage;
const sorties = {
shab_m2: arrondi2(shab),
shab_estimee,
perimetre_m: arrondi2(perimetre),
perimetre_expose_m: arrondi2(perimetre_expose),
murs_exterieurs_m2: arrondi2(murs_exterieurs),
murs_opaques_m2: arrondi2(murs_opaques),
vitrage_m2: arrondi2(vitrage),
toiture_m2: arrondi2(toiture),
plancher_bas_m2: arrondi2(emprise),
volume_m3: arrondi2(volume),
hauteur_m: arrondi2(hauteur),
hauteur_etage_m: arrondi2(hauteur_etage),
};
return trace.resultat(sorties, VERSION);
}
export const geometrie = {
id: 'commun.geometrie',
calculer(entrees, donnees) {
return calculeGeometrie(entrees, donnees?.coefficients ?? COEFFICIENTS);
},
};