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
103 lines
4.4 KiB
JavaScript
103 lines
4.4 KiB
JavaScript
// contexte.ts — moteur `commun.contexte` : code postal → département → zone.
|
|
// UNE seule version (le noyau autonomie l'utilise aussi). Corse 2A/2B et
|
|
// DOM 971-976 corrigés : le code d'origine (algo.js) prenait deux chiffres et
|
|
// retombait sur H2a pour ces départements.
|
|
import { Trace } from './contrat.js';
|
|
import { COEFFICIENTS } from './coefficients.js';
|
|
import { VERSION } from '../version.js';
|
|
/** Zone biogéographique numérique → libellé (V2 calibrage.js, inchangé). */
|
|
export const ZONE_BIOGEO_MAP = {
|
|
1: 'C', // Continental
|
|
2: 'M', // Montagnard
|
|
3: 'OC', // Océanique côtier
|
|
4: 'OC', // Océanique intérieur
|
|
5: 'MED', // Méditerranéen continental
|
|
6: 'MED', // Méditerranéen côtier
|
|
7: 'MED', // Pré-alpin méditerranéen
|
|
8: 'OC', // Semi-océanique
|
|
};
|
|
/** Sous-zone RT (H1a, H2d…) → classe principale. */
|
|
export function zoneRtToClass(zone_rt) {
|
|
if (!zone_rt)
|
|
return 'H2';
|
|
if (zone_rt.startsWith('H1'))
|
|
return 'H1';
|
|
if (zone_rt.startsWith('H2'))
|
|
return 'H2';
|
|
if (zone_rt === 'H3')
|
|
return 'H3';
|
|
return 'H2';
|
|
}
|
|
/**
|
|
* Département depuis un code postal : DOM (97x → trois caractères), Corse
|
|
* (20000-20190 → 2A, 20200-20620 → 2B, autre 20xxx → 2A), sinon deux chiffres.
|
|
* Reprend `deptFromCp` de calculs (scripts/autonomie/data.js), seule version
|
|
* qui gérait ces cas. Renvoie null si le code postal est inexploitable.
|
|
*/
|
|
export function departementDepuisCodePostal(code_postal) {
|
|
const cp = String(code_postal ?? '').trim();
|
|
if (!/^\d{5}$/.test(cp))
|
|
return cp.length >= 2 && /^\d{2}/.test(cp) ? cp.slice(0, 2) : null;
|
|
if (cp.startsWith('97'))
|
|
return cp.slice(0, 3);
|
|
if (cp.startsWith('20')) {
|
|
const n = Number(cp);
|
|
if (n >= 20000 && n <= 20190)
|
|
return '2A';
|
|
if (n >= 20200 && n <= 20620)
|
|
return '2B';
|
|
return '2A';
|
|
}
|
|
return cp.slice(0, 2);
|
|
}
|
|
/**
|
|
* Dérive le contexte climatique. Fonction pure, utilisée par le moteur
|
|
* `commun.contexte` et par le noyau autonomie (`selectionner`).
|
|
*/
|
|
export function deriveContexte(code_postal, climat, coefficients = COEFFICIENTS) {
|
|
const trace = new Trace();
|
|
const departement = departementDepuisCodePostal(code_postal);
|
|
const ligne = departement ? climat.find((c) => c.dept === departement) : undefined;
|
|
if (!ligne) {
|
|
const d = coefficients.contexte_defaut;
|
|
trace.avertit(`contexte par défaut appliqué (${d.valeur.zone_rt}, ${d.valeur.pluviometrie_mm} mm, ${d.valeur.dju} DJU) : ` +
|
|
(departement ? `département ${departement} absent de france_climat` : `code postal « ${code_postal ?? ''} » inexploitable`));
|
|
trace.source('contexte_defaut', d.valeur.zone_rt, d.source, d.date);
|
|
return trace.resultat({
|
|
departement,
|
|
zone_climatique_rt: d.valeur.zone_rt,
|
|
zone_rt_class: zoneRtToClass(d.valeur.zone_rt),
|
|
zone_biogeo: ZONE_BIOGEO_MAP[d.valeur.zone_biogeo_code] ?? 'OC',
|
|
zone_biogeo_code: d.valeur.zone_biogeo_code,
|
|
pluviometrie_mm: d.valeur.pluviometrie_mm,
|
|
dju: d.valeur.dju,
|
|
}, VERSION);
|
|
}
|
|
const d = coefficients.contexte_defaut;
|
|
const zone_biogeo_code = ligne.zone_biogeo ?? d.valeur.zone_biogeo_code;
|
|
if (ligne.zone_biogeo == null)
|
|
trace.avertit(`zone biogéographique absente pour ${departement}, défaut ${d.valeur.zone_biogeo_code}`);
|
|
const pluviometrie_mm = ligne.pluvio_mm_an ?? d.valeur.pluviometrie_mm;
|
|
if (ligne.pluvio_mm_an == null)
|
|
trace.avertit(`pluviométrie absente pour ${departement}, défaut ${d.valeur.pluviometrie_mm} mm`);
|
|
const dju = ligne.dju_base_18 ?? d.valeur.dju;
|
|
if (ligne.dju_base_18 == null)
|
|
trace.avertit(`DJU absent pour ${departement}, défaut ${d.valeur.dju}`);
|
|
trace.source(`france_climat.${departement}`, ligne.zone_rt, String(ligne.source_climat ?? 'france_climat'), String(ligne.source_date ?? ''));
|
|
return trace.resultat({
|
|
departement,
|
|
zone_climatique_rt: ligne.zone_rt,
|
|
zone_rt_class: zoneRtToClass(ligne.zone_rt),
|
|
zone_biogeo: ZONE_BIOGEO_MAP[zone_biogeo_code] ?? 'OC',
|
|
zone_biogeo_code,
|
|
pluviometrie_mm,
|
|
dju,
|
|
}, VERSION);
|
|
}
|
|
export const contexte = {
|
|
id: 'commun.contexte',
|
|
calculer(entrees, donnees) {
|
|
return deriveContexte(entrees.code_postal, donnees.france_climat, donnees.coefficients ?? COEFFICIENTS);
|
|
},
|
|
};
|