@open3cl/engine 1.8.65 → 1.8.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/3.2.1_mur.js +112 -2
- package/3.2.1_mur.spec.js +272 -2
- package/README.md +21 -21
- package/engine.js +1 -1
- package/package.json +1 -1
package/3.2.1_mur.js
CHANGED
|
@@ -80,6 +80,63 @@ function matcherEpaisseurTable(matcher) {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Matériaux de structure des parois dites « anciennes », seules concernées par l'enduit isolant :
|
|
85
|
+
* « parois […] constituées de matériaux traditionnels à savoir pierres, terre, mur à colombage,
|
|
86
|
+
* brique ancienne ».
|
|
87
|
+
* 2, 3 - pierre de taille et moellons ; 4 - pisé / béton de terre ; 5, 6 - pan de bois (colombage) ;
|
|
88
|
+
* 7 - bois (rondin) ; 8, 9 - briques pleines ; 21 - autre matériau traditionnel ancien.
|
|
89
|
+
*
|
|
90
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1
|
|
91
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
92
|
+
*/
|
|
93
|
+
export const MATERIAUX_PAROI_ANCIENNE = [2, 3, 4, 5, 6, 7, 8, 9, 21];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Umur0 interpolé linéairement entre les deux colonnes d'épaisseur de la table Umur0 qui encadrent
|
|
97
|
+
* strictement l'épaisseur saisie (pratique de certains logiciels, ex : béton banché de 32 cm).
|
|
98
|
+
*
|
|
99
|
+
* La méthode ne précise pas de règle d'interpolation pour Umur0 : cette valeur n'est utilisée qu'en
|
|
100
|
+
* compatibilité (bug_for_bug), lorsque le DPE d'origine l'a manifestement appliquée.
|
|
101
|
+
*
|
|
102
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1.2
|
|
103
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
104
|
+
*
|
|
105
|
+
* @param enumMateriauxStructureMurId {string|number}
|
|
106
|
+
* @param epaisseur {number|string}
|
|
107
|
+
* @returns {number|undefined} Umur0 interpolé, ou undefined si l'épaisseur n'est pas numérique,
|
|
108
|
+
* est une colonne de la table ou se situe hors des colonnes de la table
|
|
109
|
+
*/
|
|
110
|
+
export function umur0Interpole(enumMateriauxStructureMurId, epaisseur) {
|
|
111
|
+
const ep = Number(epaisseur);
|
|
112
|
+
if (!Number.isFinite(ep)) return undefined;
|
|
113
|
+
|
|
114
|
+
const points = tvs.umur0
|
|
115
|
+
.filter(
|
|
116
|
+
(row) =>
|
|
117
|
+
String(row.enum_materiaux_structure_mur_id) === String(enumMateriauxStructureMurId) &&
|
|
118
|
+
row.epaisseur_structure
|
|
119
|
+
)
|
|
120
|
+
.map((row) => ({
|
|
121
|
+
epaisseur: parseFloat(String(row.epaisseur_structure).replace(/[≤≥\s]/g, '')),
|
|
122
|
+
umur0: Number(row.umur0)
|
|
123
|
+
}))
|
|
124
|
+
.filter((point) => Number.isFinite(point.epaisseur))
|
|
125
|
+
.sort((a, b) => a.epaisseur - b.epaisseur);
|
|
126
|
+
|
|
127
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
128
|
+
const bas = points[i];
|
|
129
|
+
const haut = points[i + 1];
|
|
130
|
+
if (ep > bas.epaisseur && ep < haut.epaisseur) {
|
|
131
|
+
return (
|
|
132
|
+
bas.umur0 +
|
|
133
|
+
((haut.umur0 - bas.umur0) * (ep - bas.epaisseur)) / (haut.epaisseur - bas.epaisseur)
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
83
140
|
function tv_umur0(di, de, du) {
|
|
84
141
|
const matcher = {
|
|
85
142
|
enum_materiaux_structure_mur_id: de.enum_materiaux_structure_mur_id
|
|
@@ -142,6 +199,27 @@ function tv_umur0(di, de, du) {
|
|
|
142
199
|
if (row) {
|
|
143
200
|
di.umur0 = Number(row.umur0);
|
|
144
201
|
de.tv_umur0_id = Number(row.tv_umur0_id);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Certains logiciels interpolent Umur0 entre les deux colonnes d'épaisseur qui encadrent
|
|
205
|
+
* l'épaisseur saisie (ex : 2,28 pour un béton banché de 32 cm, entre 30 et 35 cm).
|
|
206
|
+
* Si l'Umur0 du DPE correspond à cette interpolation (tolérance empirique de 0,006), on la reprend.
|
|
207
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1.2
|
|
208
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
209
|
+
*/
|
|
210
|
+
if (bug_for_bug_compat && du.umur0_avant !== undefined) {
|
|
211
|
+
const umur0Interp = umur0Interpole(
|
|
212
|
+
de.enum_materiaux_structure_mur_id,
|
|
213
|
+
matcher.epaisseur_structure
|
|
214
|
+
);
|
|
215
|
+
if (
|
|
216
|
+
umur0Interp !== undefined &&
|
|
217
|
+
Math.abs(umur0Interp - di.umur0) > 0.001 &&
|
|
218
|
+
Math.abs(umur0Interp - Number(du.umur0_avant)) < 0.006
|
|
219
|
+
) {
|
|
220
|
+
di.umur0 = umur0Interp;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
145
223
|
} else {
|
|
146
224
|
console.error('!! pas de valeur forfaitaire trouvée pour umur0 !!');
|
|
147
225
|
}
|
|
@@ -242,7 +320,25 @@ function calc_umur0(di, de, du, versionDpe) {
|
|
|
242
320
|
* @see https://github.com/Open3CL/engine/issues/146
|
|
243
321
|
*/
|
|
244
322
|
const type_isolation = parseInt(de.enum_type_isolation_id) || 1;
|
|
245
|
-
const
|
|
323
|
+
const isolationIteOuIti = [3, 4, 6, 7, 8].includes(type_isolation);
|
|
324
|
+
let hasIteOrIti = versionDpe >= 2.4 && isolationIteOuIti;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* DPE antérieurs à la version 2.4 : certains logiciels n'y cumulent déjà pas le doublage avec une
|
|
328
|
+
* isolation ITE/ITI. Si l'Umur0 du DPE vaut min(2,5 ; Umur0 de la paroi nue), le doublage n'a pas
|
|
329
|
+
* été appliqué : on ne l'applique pas non plus.
|
|
330
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1
|
|
331
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
332
|
+
*/
|
|
333
|
+
if (
|
|
334
|
+
bug_for_bug_compat &&
|
|
335
|
+
!hasIteOrIti &&
|
|
336
|
+
isolationIteOuIti &&
|
|
337
|
+
umur0_avant !== undefined &&
|
|
338
|
+
Math.abs(Number(umur0_avant) - Math.min(2.5, Number(di.umur0))) < 0.001
|
|
339
|
+
) {
|
|
340
|
+
hasIteOrIti = true;
|
|
341
|
+
}
|
|
246
342
|
|
|
247
343
|
// Si présence d'un doublage et pas d'isolation ITE/ITI
|
|
248
344
|
if (type_doublage > 2 && !hasIteOrIti) {
|
|
@@ -272,10 +368,24 @@ function calc_umur0(di, de, du, versionDpe) {
|
|
|
272
368
|
const umur0Enduit = 1 / (1 / di.umur0 + 0.7);
|
|
273
369
|
di.umur0Enduit = umur0Enduit;
|
|
274
370
|
|
|
371
|
+
/**
|
|
372
|
+
* L'Umur0 exporté par certains logiciels est la valeur avant enduit, alors que l'Umur exporté
|
|
373
|
+
* intègre l'enduit. Si l'Umur du DPE vaut min(2,5 ; Umur0 avec enduit), le DPE a bien appliqué
|
|
374
|
+
* l'enduit : l'Umur0 d'origine ne doit pas être conservé.
|
|
375
|
+
* Garde : l'enduit isolant ne vaut que pour les parois anciennes (MATERIAUX_PAROI_ANCIENNE).
|
|
376
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1
|
|
377
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
378
|
+
*/
|
|
379
|
+
const enduitAppliqueDansDpe =
|
|
380
|
+
MATERIAUX_PAROI_ANCIENNE.includes(parseInt(de.enum_materiaux_structure_mur_id)) &&
|
|
381
|
+
umur_avant !== undefined &&
|
|
382
|
+
Math.abs(Number(umur_avant) - Math.min(2.5, umur0Enduit)) < 0.001;
|
|
383
|
+
|
|
275
384
|
if (
|
|
276
385
|
bug_for_bug_compat &&
|
|
277
386
|
umur0_avant !== undefined &&
|
|
278
|
-
parseFloat(umur0_avant).toFixed(3) === Math.min(2.5, di.umur0).toFixed(3)
|
|
387
|
+
parseFloat(umur0_avant).toFixed(3) === Math.min(2.5, di.umur0).toFixed(3) &&
|
|
388
|
+
!enduitAppliqueDansDpe
|
|
279
389
|
) {
|
|
280
390
|
console.error(`
|
|
281
391
|
Le mur ${de.description} est une paroi dite 'paroi ancienne avec enduit', la valeur de Umur0 devrait prendre en compte cet enduit et devrait être ${umur0Enduit}.
|
package/3.2.1_mur.spec.js
CHANGED
|
@@ -37,7 +37,32 @@ vi.mock('./tv.js', () => ({
|
|
|
37
37
|
{ tv_umur0_id: '144', enum_materiaux_structure_mur_id: '26', epaisseur_structure: '10' },
|
|
38
38
|
{ tv_umur0_id: '145', enum_materiaux_structure_mur_id: '26', epaisseur_structure: '15' },
|
|
39
39
|
{ tv_umur0_id: '146', enum_materiaux_structure_mur_id: '26', epaisseur_structure: '20' },
|
|
40
|
-
{ tv_umur0_id: '151', enum_materiaux_structure_mur_id: '26', epaisseur_structure: '≥ 45' }
|
|
40
|
+
{ tv_umur0_id: '151', enum_materiaux_structure_mur_id: '26', epaisseur_structure: '≥ 45' },
|
|
41
|
+
// Béton banché (13) avec valeurs d'Umur0, pour l'interpolation (issue #295)
|
|
42
|
+
{
|
|
43
|
+
tv_umur0_id: '80',
|
|
44
|
+
enum_materiaux_structure_mur_id: '13',
|
|
45
|
+
epaisseur_structure: '35',
|
|
46
|
+
umur0: '2.2'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
tv_umur0_id: '79',
|
|
50
|
+
enum_materiaux_structure_mur_id: '13',
|
|
51
|
+
epaisseur_structure: '30',
|
|
52
|
+
umur0: '2.4'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
tv_umur0_id: '81',
|
|
56
|
+
enum_materiaux_structure_mur_id: '13',
|
|
57
|
+
epaisseur_structure: '≥ 45',
|
|
58
|
+
umur0: '1.9'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
tv_umur0_id: '82',
|
|
62
|
+
enum_materiaux_structure_mur_id: '13',
|
|
63
|
+
epaisseur_structure: 'non numérique',
|
|
64
|
+
umur0: '9'
|
|
65
|
+
}
|
|
41
66
|
]
|
|
42
67
|
}
|
|
43
68
|
}));
|
|
@@ -54,7 +79,12 @@ vi.mock('./utils.js', () => ({
|
|
|
54
79
|
}
|
|
55
80
|
}));
|
|
56
81
|
|
|
57
|
-
const {
|
|
82
|
+
const {
|
|
83
|
+
default: calc_mur,
|
|
84
|
+
epaisseurStructureTable,
|
|
85
|
+
umur0Interpole,
|
|
86
|
+
MATERIAUX_PAROI_ANCIENNE
|
|
87
|
+
} = await import('./3.2.1_mur.js');
|
|
58
88
|
const { tv, getThicknessFromDescription } = await import('./utils.js');
|
|
59
89
|
|
|
60
90
|
/** Table `umur0` figée : U0 forfaitaire de la paroi nue. */
|
|
@@ -1006,3 +1036,243 @@ describe('epaisseurStructureTable - épaisseur hors table (issue #138)', () => {
|
|
|
1006
1036
|
expect(mur.donnee_entree.epaisseur_structure).toBe(16);
|
|
1007
1037
|
});
|
|
1008
1038
|
});
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Correctifs de compatibilité (bug_for_bug) sur les murs.
|
|
1042
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.1
|
|
1043
|
+
* @see https://github.com/Open3CL/engine/issues/295
|
|
1044
|
+
*/
|
|
1045
|
+
describe('issue #295 - enduit isolant sur paroi ancienne (MUR1)', () => {
|
|
1046
|
+
/** Umur0 table (mock) = 2 ; Umur0 avec enduit = 1 / (1/2 + 0,7). */
|
|
1047
|
+
const UMUR0_ENDUIT = 0.8333333333333334;
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* @param materiau {string}
|
|
1051
|
+
* @param donnee_intermediaire {object}
|
|
1052
|
+
*/
|
|
1053
|
+
const murAvecEnduit = (materiau, donnee_intermediaire) => ({
|
|
1054
|
+
donnee_entree: {
|
|
1055
|
+
methode_saisie_u: 'non isolé',
|
|
1056
|
+
methode_saisie_u0:
|
|
1057
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
1058
|
+
enum_materiaux_structure_mur_id: materiau,
|
|
1059
|
+
epaisseur_structure: 30,
|
|
1060
|
+
enum_type_doublage_id: '2',
|
|
1061
|
+
enduit_isolant_paroi_ancienne: 1,
|
|
1062
|
+
description: 'mur'
|
|
1063
|
+
},
|
|
1064
|
+
donnee_intermediaire
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
beforeEach(() => {
|
|
1068
|
+
bugForBugCompat = true;
|
|
1069
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
test.each([['2'], ['8'], ['9']])(
|
|
1073
|
+
"matériau ancien %s : Umur0 exporté avant enduit mais Umur avec enduit => l'enduit est appliqué",
|
|
1074
|
+
(materiau) => {
|
|
1075
|
+
// Cas 2495E2914184U : umur0 exporté = 2 (avant enduit), umur exporté = Umur0 avec enduit
|
|
1076
|
+
const mur = murAvecEnduit(materiau, { umur0: 2, umur: UMUR0_ENDUIT });
|
|
1077
|
+
|
|
1078
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1079
|
+
|
|
1080
|
+
expect(mur.donnee_intermediaire.umur0).toBeCloseTo(UMUR0_ENDUIT, 9);
|
|
1081
|
+
expect(mur.donnee_intermediaire.umur).toBeCloseTo(UMUR0_ENDUIT, 9);
|
|
1082
|
+
}
|
|
1083
|
+
);
|
|
1084
|
+
|
|
1085
|
+
test("matériau ancien : Umur du DPE sans enduit => l'Umur0 d'origine est conservé", () => {
|
|
1086
|
+
const mur = murAvecEnduit('2', { umur0: 2, umur: 2 });
|
|
1087
|
+
|
|
1088
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1089
|
+
|
|
1090
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
test.each([['1'], ['12'], ['13']])(
|
|
1094
|
+
"matériau %s (paroi non ancienne) : la correspondance de l'Umur avec enduit n'est pas retenue",
|
|
1095
|
+
(materiau) => {
|
|
1096
|
+
const mur = murAvecEnduit(materiau, { umur0: 2, umur: UMUR0_ENDUIT });
|
|
1097
|
+
|
|
1098
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1099
|
+
|
|
1100
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
1101
|
+
}
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1104
|
+
test('hors bug_for_bug : l’enduit est toujours appliqué', () => {
|
|
1105
|
+
bugForBugCompat = false;
|
|
1106
|
+
const mur = murAvecEnduit('13', { umur0: 2, umur: 2 });
|
|
1107
|
+
|
|
1108
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1109
|
+
|
|
1110
|
+
expect(mur.donnee_intermediaire.umur0).toBeCloseTo(UMUR0_ENDUIT, 9);
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
test('liste des matériaux de paroi ancienne (pierres, terre, colombage, brique ancienne)', () => {
|
|
1114
|
+
expect(MATERIAUX_PAROI_ANCIENNE).toEqual([2, 3, 4, 5, 6, 7, 8, 9, 21]);
|
|
1115
|
+
// inconnu, blocs de béton creux et béton banché ne sont pas des parois anciennes
|
|
1116
|
+
[1, 12, 13].forEach((materiau) => expect(MATERIAUX_PAROI_ANCIENNE).not.toContain(materiau));
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
describe('issue #295 - interpolation de Umur0 entre deux épaisseurs (MUR2)', () => {
|
|
1121
|
+
test.each([
|
|
1122
|
+
{ epaisseur: 32, attendu: 2.32, cas: '32 cm entre 30 (2,4) et 35 (2,2)' },
|
|
1123
|
+
{ epaisseur: '33', attendu: 2.28, cas: 'épaisseur fournie en chaîne' },
|
|
1124
|
+
{ epaisseur: 40, attendu: 2.05, cas: '40 cm entre 35 (2,2) et « ≥ 45 » (1,9)' }
|
|
1125
|
+
])('umur0Interpole : $cas', ({ epaisseur, attendu }) => {
|
|
1126
|
+
expect(umur0Interpole('13', epaisseur)).toBeCloseTo(attendu, 9);
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
test.each([
|
|
1130
|
+
{ materiau: '13', epaisseur: 30, cas: 'épaisseur égale à une colonne' },
|
|
1131
|
+
{ materiau: '13', epaisseur: 25, cas: 'sous la première colonne' },
|
|
1132
|
+
{ materiau: '13', epaisseur: 50, cas: 'au-delà de la dernière colonne' },
|
|
1133
|
+
{ materiau: '13', epaisseur: 'abc', cas: 'épaisseur non numérique' },
|
|
1134
|
+
{ materiau: '13', epaisseur: undefined, cas: 'épaisseur absente' },
|
|
1135
|
+
{ materiau: '99', epaisseur: 32, cas: 'matériau absent de la table' }
|
|
1136
|
+
])('umur0Interpole : pas d’interpolation ($cas)', ({ materiau, epaisseur }) => {
|
|
1137
|
+
expect(umur0Interpole(materiau, epaisseur)).toBeUndefined();
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* @param epaisseur {number}
|
|
1142
|
+
* @param donnee_intermediaire {object|undefined}
|
|
1143
|
+
*/
|
|
1144
|
+
const murBetonBanche = (epaisseur, donnee_intermediaire) => ({
|
|
1145
|
+
donnee_entree: {
|
|
1146
|
+
methode_saisie_u: 'non isolé',
|
|
1147
|
+
methode_saisie_u0:
|
|
1148
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
1149
|
+
enum_materiaux_structure_mur_id: '13',
|
|
1150
|
+
epaisseur_structure: epaisseur,
|
|
1151
|
+
enum_type_doublage_id: '2',
|
|
1152
|
+
description: 'mur en béton banché'
|
|
1153
|
+
},
|
|
1154
|
+
donnee_intermediaire
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
beforeEach(() => {
|
|
1158
|
+
// La table (mockée) renvoie la colonne 30 cm du béton banché
|
|
1159
|
+
tv.mockImplementation((table) => {
|
|
1160
|
+
if (table === 'umur0') return { umur0: '2.4', tv_umur0_id: '79' };
|
|
1161
|
+
return null;
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
test("bug_for_bug : Umur0 du DPE égal à l'interpolation => valeur interpolée retenue", () => {
|
|
1166
|
+
bugForBugCompat = true;
|
|
1167
|
+
// Cas 2463E3034751T : Umur0 interpolé entre deux colonnes
|
|
1168
|
+
const mur = murBetonBanche(32, { umur0: 2.318 });
|
|
1169
|
+
|
|
1170
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1171
|
+
|
|
1172
|
+
expect(tv).toHaveBeenCalledWith('umur0', {
|
|
1173
|
+
enum_materiaux_structure_mur_id: '13',
|
|
1174
|
+
epaisseur_structure: 30
|
|
1175
|
+
});
|
|
1176
|
+
expect(mur.donnee_intermediaire.umur0).toBeCloseTo(2.32, 9);
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
test.each([
|
|
1180
|
+
{ bug: true, avant: { umur0: 2.4 }, cas: 'Umur0 du DPE égal à la colonne de la table' },
|
|
1181
|
+
{ bug: true, avant: { umur0: 2.326 }, cas: 'écart à l’interpolation ≥ 0,006' },
|
|
1182
|
+
{ bug: true, avant: undefined, cas: 'pas d’Umur0 dans le DPE' },
|
|
1183
|
+
{ bug: false, avant: { umur0: 2.32 }, cas: 'hors bug_for_bug' }
|
|
1184
|
+
])('valeur de la table conservée : $cas', ({ bug, avant }) => {
|
|
1185
|
+
bugForBugCompat = bug;
|
|
1186
|
+
|
|
1187
|
+
const mur = murBetonBanche(32, avant);
|
|
1188
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1189
|
+
|
|
1190
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2.4);
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
test('bug_for_bug : interpolation confondue avec la valeur de la table => table conservée', () => {
|
|
1194
|
+
bugForBugCompat = true;
|
|
1195
|
+
tv.mockImplementation((table) => {
|
|
1196
|
+
if (table === 'umur0') return { umur0: '2.3205', tv_umur0_id: '79' };
|
|
1197
|
+
return null;
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
const mur = murBetonBanche(32, { umur0: 2.32 });
|
|
1201
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1202
|
+
|
|
1203
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2.3205);
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
test('bug_for_bug : épaisseur égale à une colonne => table conservée', () => {
|
|
1207
|
+
bugForBugCompat = true;
|
|
1208
|
+
|
|
1209
|
+
const mur = murBetonBanche(30, { umur0: 2.4 });
|
|
1210
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
1211
|
+
|
|
1212
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2.4);
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
describe('issue #295 - doublage et isolation ITE/ITI pour les DPE < 2.4 (MUR3)', () => {
|
|
1217
|
+
/** Umur0 avec doublage type 5 : 1 / (1/2 + 0,21). */
|
|
1218
|
+
const UMUR0_AVEC_DOUBLAGE = 1.4084507042253522;
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* @param enum_type_isolation_id {string}
|
|
1222
|
+
* @param donnee_intermediaire {object|undefined}
|
|
1223
|
+
*/
|
|
1224
|
+
const murIsoleAvecDoublage = (enum_type_isolation_id, donnee_intermediaire) => ({
|
|
1225
|
+
donnee_entree: {
|
|
1226
|
+
methode_saisie_u: 'epaisseur isolation saisie justifiée par mesure ou observation',
|
|
1227
|
+
methode_saisie_u0:
|
|
1228
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
1229
|
+
enum_materiaux_structure_mur_id: '5',
|
|
1230
|
+
epaisseur_structure: 30,
|
|
1231
|
+
epaisseur_isolation: 10,
|
|
1232
|
+
enum_type_doublage_id: '5',
|
|
1233
|
+
enum_type_isolation_id
|
|
1234
|
+
},
|
|
1235
|
+
donnee_intermediaire
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
test.each([['3'], ['4'], ['6'], ['7'], ['8']])(
|
|
1239
|
+
'bug_for_bug, v2.2, isolation %s : Umur0 du DPE = paroi nue => doublage non appliqué',
|
|
1240
|
+
(typeIsolation) => {
|
|
1241
|
+
bugForBugCompat = true;
|
|
1242
|
+
// Cas 2375E0900891Q : Umur0 exporté = min(2,5 ; Umur0 nu)
|
|
1243
|
+
const mur = murIsoleAvecDoublage(typeIsolation, { umur0: 2 });
|
|
1244
|
+
|
|
1245
|
+
calc_mur(mur, 'h1a', '1', '0', 2.2);
|
|
1246
|
+
|
|
1247
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
1248
|
+
}
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1251
|
+
test.each([
|
|
1252
|
+
{
|
|
1253
|
+
bug: true,
|
|
1254
|
+
iso: '3',
|
|
1255
|
+
avant: { umur0: UMUR0_AVEC_DOUBLAGE },
|
|
1256
|
+
cas: 'Umur0 du DPE avec doublage'
|
|
1257
|
+
},
|
|
1258
|
+
{ bug: true, iso: '3', avant: undefined, cas: 'pas d’Umur0 dans le DPE' },
|
|
1259
|
+
{ bug: true, iso: '2', avant: { umur0: 2 }, cas: 'mur sans isolation ITE/ITI' },
|
|
1260
|
+
{ bug: false, iso: '3', avant: { umur0: 2 }, cas: 'hors bug_for_bug' }
|
|
1261
|
+
])('v2.2 : doublage appliqué ($cas)', ({ bug, iso, avant }) => {
|
|
1262
|
+
bugForBugCompat = bug;
|
|
1263
|
+
|
|
1264
|
+
const mur = murIsoleAvecDoublage(iso, avant);
|
|
1265
|
+
calc_mur(mur, 'h1a', '1', '0', 2.2);
|
|
1266
|
+
|
|
1267
|
+
expect(mur.donnee_intermediaire.umur0).toBeCloseTo(UMUR0_AVEC_DOUBLAGE, 9);
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
test("bug_for_bug, v2.4 : comportement inchangé (doublage non cumulé à l'ITI)", () => {
|
|
1271
|
+
bugForBugCompat = true;
|
|
1272
|
+
|
|
1273
|
+
const mur = murIsoleAvecDoublage('3', { umur0: UMUR0_AVEC_DOUBLAGE });
|
|
1274
|
+
calc_mur(mur, 'h1a', '1', '0', 2.4);
|
|
1275
|
+
|
|
1276
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
1277
|
+
});
|
|
1278
|
+
});
|
package/README.md
CHANGED
|
@@ -375,57 +375,57 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
375
375
|
|
|
376
376
|
<!-- CORPUS:START -->
|
|
377
377
|
|
|
378
|
-
> **Version `1.8.
|
|
378
|
+
> **Version `1.8.65`** · branche `main` · généré le 2026-10-15
|
|
379
379
|
> Seuil de tolérance **5%**
|
|
380
380
|
|
|
381
381
|
<table>
|
|
382
382
|
<tr>
|
|
383
383
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
384
384
|
<td align="center"><strong>89 418</strong><br/><sub>DPE analysés</sub></td>
|
|
385
|
-
<td align="center"><strong>71
|
|
386
|
-
<td align="center"><strong>79,
|
|
385
|
+
<td align="center"><strong>71 395</strong><br/><sub>DPE conformes</sub></td>
|
|
386
|
+
<td align="center"><strong>79,84 %</strong><br/><sub>réussite globale</sub></td>
|
|
387
387
|
</tr>
|
|
388
388
|
</table>
|
|
389
389
|
|
|
390
390
|
| | Corpus | Réussite | | DPE conformes |
|
|
391
391
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
392
|
-
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **
|
|
392
|
+
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **48,32 %** | `██████████░░░░░░░░░░` | 4 832 / 9 999 |
|
|
393
393
|
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **94,37 %** | `███████████████████░` | 9 437 / 10 000 |
|
|
394
394
|
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **93,84 %** | `███████████████████░` | 9 384 / 10 000 |
|
|
395
395
|
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **91,80 %** | `██████████████████░░` | 9 173 / 9 992 |
|
|
396
396
|
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **89,93 %** | `██████████████████░░` | 8 992 / 9 999 |
|
|
397
|
-
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **88,
|
|
398
|
-
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,
|
|
399
|
-
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **
|
|
397
|
+
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **88,40 %** | `██████████████████░░` | 8 676 / 9 814 |
|
|
398
|
+
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,37 %** | `█████████████████░░░` | 8 535 / 9 998 |
|
|
399
|
+
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **70,01 %** | `██████████████░░░░░░` | 6 732 / 9 616 |
|
|
400
400
|
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **56,34 %** | `███████████░░░░░░░░░` | 5 634 / 10 000 |
|
|
401
401
|
|
|
402
402
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
403
403
|
|
|
404
404
|
#### Temps d’exécution
|
|
405
405
|
|
|
406
|
-
> Durée de l’appel à `calcul_3cl` par DPE, sur 89
|
|
406
|
+
> Durée de l’appel à `calcul_3cl` par DPE, sur 89 384 calculs.
|
|
407
407
|
> La copie défensive de l’entrée et la lecture du fichier sont exclues de la mesure.
|
|
408
408
|
|
|
409
409
|
<table>
|
|
410
410
|
<tr>
|
|
411
|
-
<td align="center"><strong>6,
|
|
412
|
-
<td align="center"><strong>5,
|
|
413
|
-
<td align="center"><strong>0,
|
|
414
|
-
<td align="center"><strong>
|
|
411
|
+
<td align="center"><strong>6,57 ms</strong><br/><sub>moyenne</sub></td>
|
|
412
|
+
<td align="center"><strong>5,93 ms</strong><br/><sub>médiane</sub></td>
|
|
413
|
+
<td align="center"><strong>0,14 ms</strong><br/><sub>min</sub></td>
|
|
414
|
+
<td align="center"><strong>1,18 s</strong><br/><sub>max</sub></td>
|
|
415
415
|
</tr>
|
|
416
416
|
</table>
|
|
417
417
|
|
|
418
418
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
419
419
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
420
|
-
| **Généraliste** | 5,
|
|
421
|
-
| **Appartement · chauffage individuel (2025)** | 4,
|
|
422
|
-
| **Appartement · chauffage collectif (2025)** | 5,
|
|
423
|
-
| **Logement individuel (2025)** |
|
|
424
|
-
| **Maison individuelle (2025)** | 6,
|
|
425
|
-
| **Immeuble · chauffage collectif** | 6,
|
|
426
|
-
| **Immeuble · chauffage individuel** |
|
|
427
|
-
| **Immeuble · chauffage mixte** |
|
|
428
|
-
| **Individuel généré depuis l'immeuble (2026)** |
|
|
420
|
+
| **Généraliste** | 5,86 ms | 5,38 ms | 0,25 ms | 67,5 ms | 10,3 ms | 14,9 ms |
|
|
421
|
+
| **Appartement · chauffage individuel (2025)** | 4,75 ms | 4,45 ms | 1,70 ms | 31,4 ms | 7,78 ms | 9,98 ms |
|
|
422
|
+
| **Appartement · chauffage collectif (2025)** | 5,57 ms | 5,35 ms | 1,77 ms | 41,9 ms | 8,90 ms | 12,9 ms |
|
|
423
|
+
| **Logement individuel (2025)** | 6,78 ms | 6,33 ms | 1,78 ms | 40,9 ms | 11,9 ms | 15,7 ms |
|
|
424
|
+
| **Maison individuelle (2025)** | 6,44 ms | 5,79 ms | 2,00 ms | 44,3 ms | 11,6 ms | 16,0 ms |
|
|
425
|
+
| **Immeuble · chauffage collectif** | 6,89 ms | 6,20 ms | 0,14 ms | 125,0 ms | 12,5 ms | 18,9 ms |
|
|
426
|
+
| **Immeuble · chauffage individuel** | 8,10 ms | 7,01 ms | 0,17 ms | 155,3 ms | 16,0 ms | 26,6 ms |
|
|
427
|
+
| **Immeuble · chauffage mixte** | 6,87 ms | 5,71 ms | 0,21 ms | 1,18 s | 14,1 ms | 22,6 ms |
|
|
428
|
+
| **Individuel généré depuis l'immeuble (2026)** | 7,87 ms | 7,17 ms | 1,90 ms | 59,4 ms | 14,3 ms | 23,0 ms |
|
|
429
429
|
|
|
430
430
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
431
431
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/engine.js
CHANGED
|
@@ -35,7 +35,7 @@ import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
|
35
35
|
import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
|
|
36
36
|
import { reconstituerRdimEcsIndividuelleUnique } from './11_rdim_ecs_individuelle_unique.js';
|
|
37
37
|
|
|
38
|
-
const LIB_VERSION = '1.8.
|
|
38
|
+
const LIB_VERSION = '1.8.66';
|
|
39
39
|
|
|
40
40
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
41
41
|
|