@open3cl/engine 1.8.21 → 1.8.23
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/13.2_generateur_combustion_ch.js +9 -3
- package/13.2_generateur_combustion_ch.spec.js +92 -3
- package/3.2.2_plancher_bas.js +14 -18
- package/3.2.2_plancher_bas.spec.js +111 -3
- package/3_deperdition.js +1 -1
- package/README.md +22 -22
- package/engine.js +1 -1
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +1 -5
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +2 -2
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +9 -21
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +250 -0
- package/package.json +1 -1
|
@@ -14,7 +14,13 @@ const coef_pond = {
|
|
|
14
14
|
0.95: 0
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Coefficients de conversion PCS / PCI.
|
|
19
|
+
* Le propane et le butane sont des GPL : k = 1.09 (et non 1.11, valeur du gaz naturel), comme
|
|
20
|
+
* dans le moteur de référence CSTB (Tribu).
|
|
21
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.2
|
|
22
|
+
*/
|
|
23
|
+
export const K = {
|
|
18
24
|
électricité: 1,
|
|
19
25
|
'gaz naturel': 1.11,
|
|
20
26
|
gpl: 1.09,
|
|
@@ -25,8 +31,8 @@ const K = {
|
|
|
25
31
|
'bois – plaquettes d’industrie': 1.08,
|
|
26
32
|
'réseau de chauffage urbain': 1,
|
|
27
33
|
charbon: 1.04,
|
|
28
|
-
propane: 1.
|
|
29
|
-
butane: 1.
|
|
34
|
+
propane: 1.09,
|
|
35
|
+
butane: 1.09,
|
|
30
36
|
"électricité d'origine renouvelable utilisée dans le bâtiment": undefined,
|
|
31
37
|
'autre combustible fossile': undefined,
|
|
32
38
|
'réseau de froid urbain': undefined
|
|
@@ -19,11 +19,16 @@ vi.mock('./enums.js', () => ({
|
|
|
19
19
|
70: 'chaudière gaz basse température 2001-2015',
|
|
20
20
|
12: 'radiateur à gaz',
|
|
21
21
|
33: 'générateur à air chaud',
|
|
22
|
-
99: 'type de générateur inconnu'
|
|
22
|
+
99: 'type de générateur inconnu',
|
|
23
|
+
127: 'chaudière gpl/propane/butane classique avant 1981',
|
|
24
|
+
137: 'chaudière gpl/propane/butane à condensation 1986-2000'
|
|
23
25
|
},
|
|
24
26
|
type_energie: {
|
|
25
27
|
2: 'gaz naturel',
|
|
26
|
-
4: 'bois – bûches'
|
|
28
|
+
4: 'bois – bûches',
|
|
29
|
+
9: 'propane',
|
|
30
|
+
10: 'butane',
|
|
31
|
+
13: 'gpl'
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
34
|
}));
|
|
@@ -44,7 +49,8 @@ const {
|
|
|
44
49
|
calc_generateur_combustion_ch,
|
|
45
50
|
periodeEmetteursDeduiteDuDpe,
|
|
46
51
|
periodeEmetteurAnneeConstruction,
|
|
47
|
-
PERIODES_INSTALLATION_EMETTEUR
|
|
52
|
+
PERIODES_INSTALLATION_EMETTEUR,
|
|
53
|
+
K
|
|
48
54
|
} = await import('./13.2_generateur_combustion_ch.js');
|
|
49
55
|
const { tv } = await import('./utils.js');
|
|
50
56
|
|
|
@@ -738,3 +744,86 @@ describe('calc_generateur_combustion_ch - rendement de génération', () => {
|
|
|
738
744
|
expect(avecPertes.rg).toBeLessThan(sansPertes.rg);
|
|
739
745
|
});
|
|
740
746
|
});
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Coefficient de conversion PCS / PCI : le propane et le butane sont des GPL (k = 1.09).
|
|
750
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.2
|
|
751
|
+
*/
|
|
752
|
+
describe('K - coefficient PCS / PCI', () => {
|
|
753
|
+
test('propane et butane : même coefficient que le gpl (1.09), distinct du gaz naturel', () => {
|
|
754
|
+
expect(K.gpl).toBe(1.09);
|
|
755
|
+
expect(K.propane).toBe(K.gpl);
|
|
756
|
+
expect(K.butane).toBe(K.gpl);
|
|
757
|
+
expect(K['gaz naturel']).toBe(1.11);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Référence externe : autotests État CSTB `APP5-0-4` / `APP5-0-43` (Moteur_DPE.dll, Tribu).
|
|
762
|
+
* Chaudières propane (valeurs par défaut) en cascade avec priorité : le générateur prioritaire
|
|
763
|
+
* reçoit tout le besoin (Cdimref = 0.05 / TchFinal(Ch_5) de la sortie Tribu).
|
|
764
|
+
* Avant correctif (k = 1.11) : rg = 0.9319264144042921 (APP5-0-4) et 0.7459952855462689
|
|
765
|
+
* (APP5-0-43), soit −0,019 % sur Rg.
|
|
766
|
+
*/
|
|
767
|
+
test.each([
|
|
768
|
+
{
|
|
769
|
+
cas: 'APP5-0-4 (condensation 1986-2000)',
|
|
770
|
+
type: '137',
|
|
771
|
+
di: { pn: 17538.46, rpn: 0.9330102995756797, rpint: 0.9930102995756798, qp0: 175.3846 },
|
|
772
|
+
tf: [38, 80],
|
|
773
|
+
tch5: 0.01481070099462132,
|
|
774
|
+
rgTribu: 0.9321065689001576
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
cas: 'APP5-0-43 (classique avant 1981)',
|
|
778
|
+
type: '127',
|
|
779
|
+
di: { pn: 13153.85, rpn: 0.883521827720847, rpint: 0.8652827415812706, qp0: 526.154 },
|
|
780
|
+
tf: [59, 80],
|
|
781
|
+
tch5: 0.019747593819765787,
|
|
782
|
+
rgTribu: 0.7461323116237667
|
|
783
|
+
}
|
|
784
|
+
])('autotest État $cas : rg propane identique à Tribu', ({ type, di, tf, tch5, rgTribu }) => {
|
|
785
|
+
const d = { ...di, pveil: 0, temp_fonc_30: tf[0], temp_fonc_100: tf[1] };
|
|
786
|
+
const de = {
|
|
787
|
+
enum_type_generateur_ch_id: type,
|
|
788
|
+
enum_type_energie_id: '9',
|
|
789
|
+
type_energie: 'propane',
|
|
790
|
+
presence_regulation_combustion: true,
|
|
791
|
+
description: 't'
|
|
792
|
+
};
|
|
793
|
+
const cdimref = 0.05 / tch5;
|
|
794
|
+
calc_generateur_combustion_ch({}, d, de, { cdimref, cdimrefDep: cdimref });
|
|
795
|
+
expect(d.rg).toBeCloseTo(rgTribu, 12);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
test('butane et propane donnent le même rendement que le gpl', () => {
|
|
799
|
+
const run = (energieId, typeEnergie) => {
|
|
800
|
+
const di = {
|
|
801
|
+
pn: 20000,
|
|
802
|
+
rpn: 0.93,
|
|
803
|
+
rpint: 0.99,
|
|
804
|
+
qp0: 200,
|
|
805
|
+
pveil: 0,
|
|
806
|
+
temp_fonc_30: 38,
|
|
807
|
+
temp_fonc_100: 80
|
|
808
|
+
};
|
|
809
|
+
const de = {
|
|
810
|
+
enum_type_generateur_ch_id: '137',
|
|
811
|
+
enum_type_energie_id: energieId,
|
|
812
|
+
type_energie: typeEnergie,
|
|
813
|
+
presence_regulation_combustion: true,
|
|
814
|
+
description: 't'
|
|
815
|
+
};
|
|
816
|
+
calc_generateur_combustion_ch({}, di, de, { cdimref: 3, cdimrefDep: 2.5 });
|
|
817
|
+
return di;
|
|
818
|
+
};
|
|
819
|
+
const gpl = run('13', 'gpl');
|
|
820
|
+
for (const [id, nom] of [
|
|
821
|
+
['9', 'propane'],
|
|
822
|
+
['10', 'butane']
|
|
823
|
+
]) {
|
|
824
|
+
const di = run(id, nom);
|
|
825
|
+
expect(di.rg).toBeCloseTo(gpl.rg, 12);
|
|
826
|
+
expect(di.rg_dep).toBeCloseTo(gpl.rg_dep, 12);
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
});
|
package/3.2.2_plancher_bas.js
CHANGED
|
@@ -55,7 +55,15 @@ function tv_upb(di, de, du, pc_id, zc, effetJoule) {
|
|
|
55
55
|
|
|
56
56
|
const values_2s_p = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Ue du plancher bas courant, calculé plancher par plancher (issue #46).
|
|
60
|
+
* 2S/P est déterminé avec la surface (surface_ue, à défaut surface_paroi_opaque) et le périmètre
|
|
61
|
+
* (perimetre_ue) du seul plancher courant, sans sommer les autres planchers de même adjacence.
|
|
62
|
+
* Les valeurs absentes du tableau sont obtenues par interpolation linéaire sur upb.
|
|
63
|
+
*
|
|
64
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.2 (tableau ue)
|
|
65
|
+
*/
|
|
66
|
+
function tv_ue(di, de, du, pc_id) {
|
|
59
67
|
const type_adjacence = enums.type_adjacence[de.enum_type_adjacence_id];
|
|
60
68
|
let type_adjacence_plancher;
|
|
61
69
|
let upb1, upb2;
|
|
@@ -72,21 +80,9 @@ function tv_ue(di, de, du, pc_id, pb_list) {
|
|
|
72
80
|
[upb1, upb2] = getRange(di.upb, [0.31, 0.34, 0.37, 0.41, 0.45, 0.83, 1.43, 3.33]);
|
|
73
81
|
}
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
*/
|
|
79
|
-
const { surfaceUe, perimetreUe } = pb_list.reduce(
|
|
80
|
-
(acc, plancherBas) => {
|
|
81
|
-
if (plancherBas.donnee_entree.enum_type_adjacence_id === de.enum_type_adjacence_id) {
|
|
82
|
-
acc.surfaceUe +=
|
|
83
|
-
plancherBas.donnee_entree.surface_ue || plancherBas.donnee_entree.surface_paroi_opaque;
|
|
84
|
-
acc.perimetreUe += plancherBas.donnee_entree.perimetre_ue || 0;
|
|
85
|
-
}
|
|
86
|
-
return acc;
|
|
87
|
-
},
|
|
88
|
-
{ surfaceUe: 0, perimetreUe: 0 }
|
|
89
|
-
);
|
|
83
|
+
// Surface et périmètre Ue du plancher courant uniquement
|
|
84
|
+
const surfaceUe = de.surface_ue || de.surface_paroi_opaque;
|
|
85
|
+
const perimetreUe = de.perimetre_ue || 0;
|
|
90
86
|
|
|
91
87
|
const matcher = {
|
|
92
88
|
type_adjacence_plancher,
|
|
@@ -134,7 +130,7 @@ function calc_upb0(di, de, du) {
|
|
|
134
130
|
}
|
|
135
131
|
}
|
|
136
132
|
|
|
137
|
-
export default function calc_pb(pb, zc, pc_id, effetJoule
|
|
133
|
+
export default function calc_pb(pb, zc, pc_id, effetJoule) {
|
|
138
134
|
const de = pb.donnee_entree;
|
|
139
135
|
const du = {};
|
|
140
136
|
const di = {};
|
|
@@ -246,7 +242,7 @@ export default function calc_pb(pb, zc, pc_id, effetJoule, pb_list) {
|
|
|
246
242
|
case 'vide sanitaire':
|
|
247
243
|
case 'sous-sol non chauffé':
|
|
248
244
|
case 'terre-plein':
|
|
249
|
-
tv_ue(di, de, du, pc_id
|
|
245
|
+
tv_ue(di, de, du, pc_id);
|
|
250
246
|
di.upb_final = de.ue;
|
|
251
247
|
break;
|
|
252
248
|
default:
|
|
@@ -561,14 +561,17 @@ describe('calc_pb - calcul de Ue (tv_ue)', () => {
|
|
|
561
561
|
enum_type_plancher_bas_id: '9',
|
|
562
562
|
type_adjacence: 'terre-plein',
|
|
563
563
|
enum_type_adjacence_id: '5',
|
|
564
|
-
surface_paroi_opaque: 4 // pas de surface_ue => fallback
|
|
564
|
+
surface_paroi_opaque: 4, // pas de surface_ue => fallback
|
|
565
|
+
perimetre_ue: 2
|
|
565
566
|
}
|
|
566
567
|
};
|
|
567
568
|
|
|
568
569
|
calc_pb(pb, 'h1a', '1', '0', [pb1, pb, autre]);
|
|
569
570
|
|
|
570
|
-
//
|
|
571
|
-
//
|
|
571
|
+
// Calcul plancher par plancher (#46) : les autres planchers (pb1, autre) sont ignorés.
|
|
572
|
+
// Ce test encodait auparavant la somme par adjacence (S = 20 + 4, P = 12 + 0) ; perimetre_ue
|
|
573
|
+
// est désormais porté par le plancher courant pour conserver 2S/P = 4.
|
|
574
|
+
// surfaceUe = 4 (pb.surface_paroi_opaque) ; perimetreUe = 2 => 2S/P = round(8/2) = 4
|
|
572
575
|
expect(tv).toHaveBeenCalledWith('ue', {
|
|
573
576
|
type_adjacence_plancher: 'terre plein bâtiment construit avant 2001',
|
|
574
577
|
'2s_p': '^4$',
|
|
@@ -652,4 +655,109 @@ describe('calc_pb - calcul de Ue (tv_ue)', () => {
|
|
|
652
655
|
|
|
653
656
|
expect(pb.donnee_intermediaire.upb_final).toBe(0.19);
|
|
654
657
|
});
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Issue #46 : ue calculé plancher par plancher, 2S/P issu du seul plancher courant.
|
|
661
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.2 (tableau ue : « Les données ne figurant
|
|
662
|
+
* pas dans le tableau peuvent être obtenues par interpolation et extrapolation »)
|
|
663
|
+
*/
|
|
664
|
+
describe('calcul plancher par plancher (#46)', () => {
|
|
665
|
+
/** Plancher non isolé (methode 1) dont upb = upb0 = 1,45. */
|
|
666
|
+
const plancher = (donnees) => ({
|
|
667
|
+
donnee_entree: {
|
|
668
|
+
enum_methode_saisie_u_id: '1',
|
|
669
|
+
methode_saisie_u0:
|
|
670
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
671
|
+
enum_type_plancher_bas_id: '2',
|
|
672
|
+
type_adjacence: 'terre-plein',
|
|
673
|
+
enum_type_adjacence_id: '5',
|
|
674
|
+
...donnees
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
/** Table ue mockée par couple (2s_p, upb) : terre-plein avant 2001. */
|
|
679
|
+
const UE = {
|
|
680
|
+
'^7$': { 0.85: 0.33, 1.5: 0.39 },
|
|
681
|
+
'^2$': { 0.85: 0.55, 1.5: 0.8 },
|
|
682
|
+
'^1$': { 0.85: 0.7, 1.5: 0.9 },
|
|
683
|
+
'^4$': { 0.85: 0.45, 1.5: 0.6 }
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
beforeEach(() => {
|
|
687
|
+
getRange.mockReturnValue([0.85, 1.5]);
|
|
688
|
+
tv.mockImplementation((table, matcher) => {
|
|
689
|
+
if (table === 'upb0') return { upb0: '1.45', tv_upb0_id: '2' };
|
|
690
|
+
if (table === 'ue') return { ue: String(UE[matcher['2s_p']][matcher.upb]) };
|
|
691
|
+
return null;
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
test('2187E0982591I plancher 1 : 2S/P = 7 et ue = 0,385384615 malgré un autre terre-plein', () => {
|
|
696
|
+
const pb1 = plancher({ surface_paroi_opaque: 44.8, perimetre_ue: 12.8 });
|
|
697
|
+
const pb3 = plancher({ surface_paroi_opaque: 10.5, perimetre_ue: 10 });
|
|
698
|
+
|
|
699
|
+
calc_pb(pb1, 'h1a', '1', '0', [pb1, pb3]);
|
|
700
|
+
|
|
701
|
+
// 2S/P = 2 * 44,8 / 12,8 = 7 (somme par adjacence : 2 * 55,3 / 22,8 ≈ 4,85 => 5)
|
|
702
|
+
expect(tv).toHaveBeenCalledWith('ue', {
|
|
703
|
+
type_adjacence_plancher: 'terre plein bâtiment construit avant 2001',
|
|
704
|
+
'2s_p': '^7$',
|
|
705
|
+
upb: '0.85'
|
|
706
|
+
});
|
|
707
|
+
expect(tv).toHaveBeenCalledWith('ue', {
|
|
708
|
+
type_adjacence_plancher: 'terre plein bâtiment construit avant 2001',
|
|
709
|
+
'2s_p': '^7$',
|
|
710
|
+
upb: '1.5'
|
|
711
|
+
});
|
|
712
|
+
expect(tv).not.toHaveBeenCalledWith('ue', expect.objectContaining({ '2s_p': '^5$' }));
|
|
713
|
+
// ue = 0,33 + (0,39 - 0,33) * (1,45 - 0,85) / (1,5 - 0,85) = 0,385384615 (valeur ADEME)
|
|
714
|
+
expect(pb1.donnee_entree.ue).toBeCloseTo(0.385384615, 9);
|
|
715
|
+
expect(pb1.donnee_intermediaire.upb_final).toBeCloseTo(0.385384615, 9);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
test('deux planchers de même adjacence avec surface_ue / perimetre_ue différents : ue différents', () => {
|
|
719
|
+
const pbA = plancher({ surface_paroi_opaque: 50, surface_ue: 70, perimetre_ue: 20 });
|
|
720
|
+
const pbB = plancher({ surface_paroi_opaque: 50, surface_ue: 10, perimetre_ue: 10 });
|
|
721
|
+
const liste = [pbA, pbB];
|
|
722
|
+
|
|
723
|
+
liste.forEach((pb) => calc_pb(pb, 'h1a', '1', '0', liste));
|
|
724
|
+
|
|
725
|
+
// pbA : 2 * 70 / 20 = 7 => ue interpolé entre 0,33 et 0,39
|
|
726
|
+
expect(pbA.donnee_entree.ue).toBeCloseTo(0.385384615, 9);
|
|
727
|
+
// pbB : 2 * 10 / 10 = 2 => 0,55 + 0,25 * 0,6 / 0,65 (valeur de référence de régression)
|
|
728
|
+
expect(pbB.donnee_entree.ue).toBeCloseTo(0.780769231, 9);
|
|
729
|
+
expect(pbA.donnee_entree.ue).not.toBeCloseTo(pbB.donnee_entree.ue, 3);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
test('surface_ue absente : surface_paroi_opaque du plancher courant utilisée', () => {
|
|
733
|
+
const pb = plancher({ surface_paroi_opaque: 20, perimetre_ue: 10 });
|
|
734
|
+
const autre = plancher({ surface_ue: 500, surface_paroi_opaque: 500, perimetre_ue: 10 });
|
|
735
|
+
|
|
736
|
+
calc_pb(pb, 'h1a', '1', '0', [pb, autre]);
|
|
737
|
+
|
|
738
|
+
// 2S/P = 2 * 20 / 10 = 4
|
|
739
|
+
expect(tv).toHaveBeenCalledWith('ue', expect.objectContaining({ '2s_p': '^4$' }));
|
|
740
|
+
// ue = 0,45 + 0,15 * 0,6 / 0,65 (valeur de référence de régression)
|
|
741
|
+
expect(pb.donnee_entree.ue).toBeCloseTo(0.588461538, 9);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
test('surface_ue prioritaire sur surface_paroi_opaque', () => {
|
|
745
|
+
const pb = plancher({ surface_paroi_opaque: 20, surface_ue: 35, perimetre_ue: 10 });
|
|
746
|
+
|
|
747
|
+
calc_pb(pb, 'h1a', '1', '0', [pb]);
|
|
748
|
+
|
|
749
|
+
// 2S/P = 2 * 35 / 10 = 7
|
|
750
|
+
expect(tv).toHaveBeenCalledWith('ue', expect.objectContaining({ '2s_p': '^7$' }));
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test('perimetre_ue absent : comportement inchangé (division par 0 => 2S/P = 1), même si un autre plancher en a un', () => {
|
|
754
|
+
const pb = plancher({ surface_paroi_opaque: 20 });
|
|
755
|
+
const autre = plancher({ surface_paroi_opaque: 20, perimetre_ue: 10 });
|
|
756
|
+
|
|
757
|
+
calc_pb(pb, 'h1a', '1', '0', [pb, autre]);
|
|
758
|
+
|
|
759
|
+
expect(tv).toHaveBeenCalledWith('ue', expect.objectContaining({ '2s_p': '^1$' }));
|
|
760
|
+
expect(pb.donnee_entree.ue).toBeCloseTo(0.884615385, 9);
|
|
761
|
+
});
|
|
762
|
+
});
|
|
655
763
|
});
|
package/3_deperdition.js
CHANGED
|
@@ -230,7 +230,7 @@ export default function calc_deperdition(cg, zc, th, effetJoule, dpe, Sh) {
|
|
|
230
230
|
*/
|
|
231
231
|
const versionDpe = parseFloat(dpe.administratif?.enum_version_id);
|
|
232
232
|
mur_list.forEach((mur) => calc_mur(mur, zc, pc, effetJoule, versionDpe));
|
|
233
|
-
pb_list.forEach((pb) => calc_pb(pb, zc, pc, effetJoule
|
|
233
|
+
pb_list.forEach((pb) => calc_pb(pb, zc, pc, effetJoule));
|
|
234
234
|
ph_list.forEach((ph) => calc_ph(ph, zc, pc, effetJoule));
|
|
235
235
|
bv_list.forEach((bv) => calc_bv(bv, zc));
|
|
236
236
|
porte_list.forEach((porte) => calc_porte(porte, zc));
|
package/README.md
CHANGED
|
@@ -370,28 +370,28 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
370
370
|
|
|
371
371
|
<!-- CORPUS:START -->
|
|
372
372
|
|
|
373
|
-
> **Version `1.8.
|
|
373
|
+
> **Version `1.8.22`** · branche `main` · généré le 2026-10-04
|
|
374
374
|
> Seuil de tolérance **5%**
|
|
375
375
|
|
|
376
376
|
<table>
|
|
377
377
|
<tr>
|
|
378
378
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
379
379
|
<td align="center"><strong>89 990</strong><br/><sub>DPE analysés</sub></td>
|
|
380
|
-
<td align="center"><strong>60
|
|
381
|
-
<td align="center"><strong>67,
|
|
380
|
+
<td align="center"><strong>60 717</strong><br/><sub>DPE conformes</sub></td>
|
|
381
|
+
<td align="center"><strong>67,47 %</strong><br/><sub>réussite globale</sub></td>
|
|
382
382
|
</tr>
|
|
383
383
|
</table>
|
|
384
384
|
|
|
385
385
|
| | Corpus | Réussite | | DPE conformes |
|
|
386
386
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
387
|
-
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **46,
|
|
388
|
-
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **92,
|
|
389
|
-
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **89,
|
|
390
|
-
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,
|
|
391
|
-
| 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **76,
|
|
387
|
+
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **46,02 %** | `█████████░░░░░░░░░░░` | 4 602 / 10 000 |
|
|
388
|
+
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **92,53 %** | `███████████████████░` | 9 253 / 10 000 |
|
|
389
|
+
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **89,49 %** | `██████████████████░░` | 8 942 / 9 992 |
|
|
390
|
+
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,58 %** | `██████████████████░░` | 8 858 / 10 000 |
|
|
391
|
+
| 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **76,89 %** | `███████████████░░░░░` | 7 688 / 9 999 |
|
|
392
392
|
| 🟡 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **71,24 %** | `██████████████░░░░░░` | 7 124 / 10 000 |
|
|
393
|
-
| 🟡 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **63,
|
|
394
|
-
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **51,
|
|
393
|
+
| 🟡 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **63,93 %** | `█████████████░░░░░░░` | 6 392 / 9 999 |
|
|
394
|
+
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **51,64 %** | `██████████░░░░░░░░░░` | 5 164 / 10 000 |
|
|
395
395
|
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **26,94 %** | `█████░░░░░░░░░░░░░░░` | 2 694 / 10 000 |
|
|
396
396
|
|
|
397
397
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
@@ -403,24 +403,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
403
403
|
|
|
404
404
|
<table>
|
|
405
405
|
<tr>
|
|
406
|
-
<td align="center"><strong>
|
|
407
|
-
<td align="center"><strong>4,
|
|
406
|
+
<td align="center"><strong>5,36 ms</strong><br/><sub>moyenne</sub></td>
|
|
407
|
+
<td align="center"><strong>4,64 ms</strong><br/><sub>médiane</sub></td>
|
|
408
408
|
<td align="center"><strong>0,10 ms</strong><br/><sub>min</sub></td>
|
|
409
|
-
<td align="center"><strong>
|
|
409
|
+
<td align="center"><strong>823,0 ms</strong><br/><sub>max</sub></td>
|
|
410
410
|
</tr>
|
|
411
411
|
</table>
|
|
412
412
|
|
|
413
413
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
414
414
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
415
|
-
| **Généraliste** |
|
|
416
|
-
| **Appartement · chauffage individuel (2025)** |
|
|
417
|
-
| **Logement individuel (2025)** | 4,
|
|
418
|
-
| **Maison individuelle (2025)** |
|
|
419
|
-
| **Immeuble · chauffage individuel** |
|
|
420
|
-
| **Appartement · chauffage collectif (2025)** |
|
|
421
|
-
| **Immeuble · chauffage collectif** | 5,
|
|
422
|
-
| **Immeuble · chauffage mixte** | 5,
|
|
423
|
-
| **Individuel généré depuis l'immeuble (2026)** | 5,
|
|
415
|
+
| **Généraliste** | 5,23 ms | 4,71 ms | 0,23 ms | 52,3 ms | 9,47 ms | 13,8 ms |
|
|
416
|
+
| **Appartement · chauffage individuel (2025)** | 4,08 ms | 3,70 ms | 1,52 ms | 32,7 ms | 7,06 ms | 9,53 ms |
|
|
417
|
+
| **Logement individuel (2025)** | 4,71 ms | 4,17 ms | 1,61 ms | 28,7 ms | 8,70 ms | 12,0 ms |
|
|
418
|
+
| **Maison individuelle (2025)** | 6,26 ms | 5,66 ms | 1,96 ms | 47,2 ms | 11,3 ms | 15,1 ms |
|
|
419
|
+
| **Immeuble · chauffage individuel** | 6,21 ms | 5,23 ms | 0,10 ms | 81,6 ms | 12,6 ms | 19,8 ms |
|
|
420
|
+
| **Appartement · chauffage collectif (2025)** | 5,76 ms | 4,99 ms | 1,45 ms | 35,9 ms | 12,5 ms | 19,0 ms |
|
|
421
|
+
| **Immeuble · chauffage collectif** | 5,22 ms | 4,34 ms | 0,12 ms | 119,3 ms | 9,61 ms | 17,5 ms |
|
|
422
|
+
| **Immeuble · chauffage mixte** | 5,11 ms | 4,13 ms | 0,18 ms | 823,0 ms | 10,5 ms | 16,6 ms |
|
|
423
|
+
| **Individuel généré depuis l'immeuble (2026)** | 5,63 ms | 4,86 ms | 1,65 ms | 44,3 ms | 10,6 ms | 15,8 ms |
|
|
424
424
|
|
|
425
425
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
426
426
|
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
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.8.
|
|
36
|
+
const LIB_VERSION = '1.8.23';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -207,11 +207,7 @@ export class DeperditionEnveloppeService {
|
|
|
207
207
|
|
|
208
208
|
const plancherBas = enveloppe.plancher_bas_collection.plancher_bas || [];
|
|
209
209
|
plancherBas?.forEach((pb) => {
|
|
210
|
-
pb.donnee_intermediaire = this.#deperditionPlancherBasService.execute(
|
|
211
|
-
ctx,
|
|
212
|
-
pb.donnee_entree,
|
|
213
|
-
plancherBas
|
|
214
|
-
);
|
|
210
|
+
pb.donnee_intermediaire = this.#deperditionPlancherBasService.execute(ctx, pb.donnee_entree);
|
|
215
211
|
deperditions.deperdition_plancher_bas +=
|
|
216
212
|
pb.donnee_intermediaire.b *
|
|
217
213
|
pb.donnee_entree.surface_paroi_opaque *
|
|
@@ -158,10 +158,10 @@ describe('DeperditionEnveloppeService (§3 déperditions de l’enveloppe)', ()
|
|
|
158
158
|
});
|
|
159
159
|
|
|
160
160
|
// Délégation aux services de paroi
|
|
161
|
+
// Ue calculé plancher par plancher (#46) : la liste des planchers n'est plus transmise
|
|
161
162
|
expect(plancherBasService.execute).toHaveBeenCalledWith(
|
|
162
163
|
ctx,
|
|
163
|
-
env.plancher_bas_collection.plancher_bas[0].donnee_entree
|
|
164
|
-
env.plancher_bas_collection.plancher_bas
|
|
164
|
+
env.plancher_bas_collection.plancher_bas[0].donnee_entree
|
|
165
165
|
);
|
|
166
166
|
expect(pontThermiqueService.execute).toHaveBeenCalledWith(
|
|
167
167
|
ctx,
|
|
@@ -22,13 +22,12 @@ export class DeperditionPlancherBasService extends DeperditionService {
|
|
|
22
22
|
/**
|
|
23
23
|
* @param ctx {Contexte}
|
|
24
24
|
* @param pbDE {PlancherBasDE}
|
|
25
|
-
* @param plancherBas {PlancherBas[]}
|
|
26
25
|
* @return {PlancherBasDI}
|
|
27
26
|
*/
|
|
28
|
-
execute(ctx, pbDE
|
|
27
|
+
execute(ctx, pbDE) {
|
|
29
28
|
const upb0 = this.#upb0(pbDE);
|
|
30
29
|
const upb = this.#upb(pbDE, upb0, ctx);
|
|
31
|
-
const upb_final = this.#upbFinal(pbDE, upb, ctx
|
|
30
|
+
const upb_final = this.#upbFinal(pbDE, upb, ctx);
|
|
32
31
|
const b = this.b({
|
|
33
32
|
enumTypeAdjacenceId: pbDE.enum_type_adjacence_id,
|
|
34
33
|
surfaceAiu: pbDE.surface_aiu,
|
|
@@ -113,10 +112,9 @@ export class DeperditionPlancherBasService extends DeperditionService {
|
|
|
113
112
|
* @param pbDE {PlancherBasDE}
|
|
114
113
|
* @param upb {number}
|
|
115
114
|
* @param ctx {Contexte}
|
|
116
|
-
* @param plancherBas {PlancherBas[]}
|
|
117
115
|
* @return {number|undefined}
|
|
118
116
|
*/
|
|
119
|
-
#upbFinal(pbDE, upb, ctx
|
|
117
|
+
#upbFinal(pbDE, upb, ctx) {
|
|
120
118
|
if (pbDE.calcul_ue === 1) {
|
|
121
119
|
return pbDE.ue;
|
|
122
120
|
}
|
|
@@ -131,23 +129,13 @@ export class DeperditionPlancherBasService extends DeperditionService {
|
|
|
131
129
|
}
|
|
132
130
|
|
|
133
131
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
132
|
+
* Calcul plancher par plancher (issue #46) : 2S/P est déterminé avec la surface (surface_ue, à
|
|
133
|
+
* défaut surface_paroi_opaque) et le périmètre (perimetre_ue) du seul plancher courant, sans
|
|
134
|
+
* sommer les autres planchers de même adjacence.
|
|
135
|
+
* @see Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.2 (tableau ue)
|
|
136
136
|
*/
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
(plancherBas) =>
|
|
140
|
-
pbDE.enum_type_adjacence_id === plancherBas.donnee_entree.enum_type_adjacence_id
|
|
141
|
-
)
|
|
142
|
-
.reduce(
|
|
143
|
-
(acc, plancherBas) => {
|
|
144
|
-
acc.surfaceUe +=
|
|
145
|
-
plancherBas.donnee_entree.surface_ue || plancherBas.donnee_entree.surface_paroi_opaque;
|
|
146
|
-
acc.perimetreUe += plancherBas.donnee_entree.perimetre_ue || 0;
|
|
147
|
-
return acc;
|
|
148
|
-
},
|
|
149
|
-
{ surfaceUe: 0, perimetreUe: 0 }
|
|
150
|
-
);
|
|
137
|
+
const surfaceUe = pbDE.surface_ue || pbDE.surface_paroi_opaque;
|
|
138
|
+
const perimetreUe = pbDE.perimetre_ue || 0;
|
|
151
139
|
|
|
152
140
|
const available2sp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
|
|
153
141
|
let dsp = perimetreUe ? Math.round((2 * surfaceUe) / perimetreUe) : 1;
|
package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { DeperditionPlancherBasService } from './deperdition-plancher-bas.service.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests unitaires isolés de `DeperditionPlancherBasService` (nouvelle architecture).
|
|
6
|
+
*
|
|
7
|
+
* Le `TvStore` est remplacé par un double de test (spies `vi.fn`) et le coefficient b (classe de
|
|
8
|
+
* base) est espionné : seule la logique propre au service (Upb0, Upb, Ue, type d'isolation) est
|
|
9
|
+
* vérifiée, sans dépendre des tables de valeurs réelles.
|
|
10
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.2
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** @type {{ getUpb0: import('vitest').Mock, getUpb: import('vitest').Mock, getUeByUpd: import('vitest').Mock }} */
|
|
14
|
+
let tvStore;
|
|
15
|
+
|
|
16
|
+
/** @type {DeperditionPlancherBasService} */
|
|
17
|
+
let service;
|
|
18
|
+
|
|
19
|
+
const ctx = {
|
|
20
|
+
zoneClimatique: { id: '1', value: 'h1a' },
|
|
21
|
+
effetJoule: false,
|
|
22
|
+
enumPeriodeConstructionId: '1'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tvStore = {
|
|
27
|
+
getUpb0: vi.fn(() => 1.45),
|
|
28
|
+
getUpb: vi.fn(() => 0.4),
|
|
29
|
+
getUeByUpd: vi.fn(() => 0.385384615)
|
|
30
|
+
};
|
|
31
|
+
service = new DeperditionPlancherBasService(tvStore);
|
|
32
|
+
vi.spyOn(service, 'b').mockReturnValue(1);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** Plancher non isolé, U0 forfaitaire, adjacence extérieur par défaut. */
|
|
36
|
+
const plancherDE = (donnees = {}) => ({
|
|
37
|
+
enum_methode_saisie_u0_id: '2',
|
|
38
|
+
enum_methode_saisie_u_id: '1',
|
|
39
|
+
enum_type_plancher_bas_id: '2',
|
|
40
|
+
enum_type_adjacence_id: '1',
|
|
41
|
+
...donnees
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('DeperditionPlancherBasService - execute()', () => {
|
|
45
|
+
test('transmet les données du plancher au calcul de b', () => {
|
|
46
|
+
const pbDE = plancherDE({
|
|
47
|
+
surface_aiu: 10,
|
|
48
|
+
surface_aue: 20,
|
|
49
|
+
enum_cfg_isolation_lnc_id: '3'
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const di = service.execute(ctx, pbDE);
|
|
53
|
+
|
|
54
|
+
expect(service.b).toHaveBeenCalledWith({
|
|
55
|
+
enumTypeAdjacenceId: '1',
|
|
56
|
+
surfaceAiu: 10,
|
|
57
|
+
surfaceAue: 20,
|
|
58
|
+
enumCfgIsolationLncId: '3',
|
|
59
|
+
zoneClimatique: 'h1a'
|
|
60
|
+
});
|
|
61
|
+
expect(di).toEqual({ upb0: 1.45, upb: 1.45, upb_final: 1.45, b: 1 });
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('DeperditionPlancherBasService - calcul de Upb0', () => {
|
|
66
|
+
test.each(['1', '2'])('méthode u0 %s : valeur forfaitaire de la table upb0', (methode) => {
|
|
67
|
+
const di = service.execute(ctx, plancherDE({ enum_methode_saisie_u0_id: methode }));
|
|
68
|
+
|
|
69
|
+
expect(tvStore.getUpb0).toHaveBeenCalledWith('2');
|
|
70
|
+
expect(di.upb0).toBe(1.45);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('méthode u0 5 (u saisi) : upb0 non déterminé', () => {
|
|
74
|
+
const di = service.execute(
|
|
75
|
+
ctx,
|
|
76
|
+
plancherDE({ enum_methode_saisie_u0_id: '5', enum_methode_saisie_u_id: '9', upb_saisi: 0.3 })
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(di.upb0).toBeUndefined();
|
|
80
|
+
expect(di.upb).toBe(0.3);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('u0 saisi : valeur saisie retenue', () => {
|
|
84
|
+
const di = service.execute(
|
|
85
|
+
ctx,
|
|
86
|
+
plancherDE({ enum_methode_saisie_u0_id: '3', upb0_saisi: 1.2 })
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(tvStore.getUpb0).not.toHaveBeenCalled();
|
|
90
|
+
expect(di.upb0).toBe(1.2);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('DeperditionPlancherBasService - calcul de Upb', () => {
|
|
95
|
+
test('non isolé : upb = min(upb0, 2)', () => {
|
|
96
|
+
tvStore.getUpb0.mockReturnValue(3);
|
|
97
|
+
|
|
98
|
+
expect(service.execute(ctx, plancherDE()).upb).toBe(2);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test.each(['2', '7', '8'])(
|
|
102
|
+
'méthode u %s (table forfaitaire) : min(upb nu, upb de la table)',
|
|
103
|
+
(methode) => {
|
|
104
|
+
const di = service.execute(ctx, plancherDE({ enum_methode_saisie_u_id: methode }));
|
|
105
|
+
|
|
106
|
+
// Construction avant 1975 sans période d'isolation => période d'isolation 3 (1975-1977)
|
|
107
|
+
expect(tvStore.getUpb).toHaveBeenCalledWith('3', '1', false);
|
|
108
|
+
expect(di.upb).toBe(0.4);
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
test.each(['3', '4'])('méthode u %s : épaisseur d’isolation', (methode) => {
|
|
113
|
+
const di = service.execute(
|
|
114
|
+
ctx,
|
|
115
|
+
plancherDE({ enum_methode_saisie_u_id: methode, epaisseur_isolation: 10 })
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// 1 / (1 / 1,45 + 0,1 / 0,042) arrondi à 1e-5 (valeur de référence de régression)
|
|
119
|
+
expect(di.upb).toBe(0.32567);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test.each(['5', '6'])('méthode u %s : résistance d’isolation', (methode) => {
|
|
123
|
+
const di = service.execute(
|
|
124
|
+
ctx,
|
|
125
|
+
plancherDE({ enum_methode_saisie_u_id: methode, resistance_isolation: 2 })
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// 1 / (1 / 1,45 + 2) arrondi à 1e-5 (valeur de référence de régression)
|
|
129
|
+
expect(di.upb).toBe(0.37179);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('saisie directe : upb saisi', () => {
|
|
133
|
+
const di = service.execute(ctx, plancherDE({ enum_methode_saisie_u_id: '9', upb_saisi: 0.25 }));
|
|
134
|
+
|
|
135
|
+
expect(di.upb).toBe(0.25);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('DeperditionPlancherBasService - calcul de upb_final (Ue)', () => {
|
|
140
|
+
test('calcul_ue = 1 : ue du DPE retenu', () => {
|
|
141
|
+
const di = service.execute(
|
|
142
|
+
ctx,
|
|
143
|
+
plancherDE({ enum_type_adjacence_id: '5', calcul_ue: 1, ue: 0.38538462 })
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(di.upb_final).toBe(0.38538462);
|
|
147
|
+
expect(tvStore.getUeByUpd).not.toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('adjacence sans Ue (extérieur) : upb_final = upb', () => {
|
|
151
|
+
const di = service.execute(ctx, plancherDE({ enum_type_adjacence_id: '1' }));
|
|
152
|
+
|
|
153
|
+
expect(di.upb_final).toBe(1.45);
|
|
154
|
+
expect(tvStore.getUeByUpd).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Issue #46 : ue calculé plancher par plancher, 2S/P issu du seul plancher courant.
|
|
159
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.2.2 (tableau ue)
|
|
160
|
+
*/
|
|
161
|
+
describe('calcul plancher par plancher (#46)', () => {
|
|
162
|
+
test('2187E0982591I plancher 1 : 2S/P = 2 * 44,8 / 12,8 = 7', () => {
|
|
163
|
+
const di = service.execute(
|
|
164
|
+
ctx,
|
|
165
|
+
plancherDE({ enum_type_adjacence_id: '5', surface_paroi_opaque: 44.8, perimetre_ue: 12.8 })
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(tvStore.getUeByUpd).toHaveBeenCalledWith('5', '1', 7, 1.45);
|
|
169
|
+
expect(di.upb_final).toBe(0.38538);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('deux planchers de même adjacence : chacun son propre 2S/P', () => {
|
|
173
|
+
const pbA = plancherDE({
|
|
174
|
+
enum_type_adjacence_id: '5',
|
|
175
|
+
surface_paroi_opaque: 50,
|
|
176
|
+
surface_ue: 70,
|
|
177
|
+
perimetre_ue: 20
|
|
178
|
+
});
|
|
179
|
+
const pbB = plancherDE({
|
|
180
|
+
enum_type_adjacence_id: '5',
|
|
181
|
+
surface_paroi_opaque: 50,
|
|
182
|
+
surface_ue: 10,
|
|
183
|
+
perimetre_ue: 10
|
|
184
|
+
});
|
|
185
|
+
tvStore.getUeByUpd.mockImplementation((adj, pc, dsp) => (dsp === 7 ? 0.38 : 0.78));
|
|
186
|
+
|
|
187
|
+
const diA = service.execute(ctx, pbA);
|
|
188
|
+
const diB = service.execute(ctx, pbB);
|
|
189
|
+
|
|
190
|
+
// pbA : 2 * 70 / 20 = 7 ; pbB : 2 * 10 / 10 = 2 (somme par adjacence : 2 * 80 / 30 => 5)
|
|
191
|
+
expect(tvStore.getUeByUpd).toHaveBeenNthCalledWith(1, '5', '1', 7, 1.45);
|
|
192
|
+
expect(tvStore.getUeByUpd).toHaveBeenNthCalledWith(2, '5', '1', 2, 1.45);
|
|
193
|
+
expect(diA.upb_final).toBe(0.38);
|
|
194
|
+
expect(diB.upb_final).toBe(0.78);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('surface_ue absente : surface_paroi_opaque utilisée', () => {
|
|
198
|
+
service.execute(
|
|
199
|
+
ctx,
|
|
200
|
+
plancherDE({ enum_type_adjacence_id: '3', surface_paroi_opaque: 20, perimetre_ue: 10 })
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
expect(tvStore.getUeByUpd).toHaveBeenCalledWith('3', '1', 4, 1.45);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('2S/P hors tableau : valeur disponible la plus proche (11 => 10)', () => {
|
|
207
|
+
service.execute(
|
|
208
|
+
ctx,
|
|
209
|
+
plancherDE({ enum_type_adjacence_id: '6', surface_ue: 55, perimetre_ue: 10 })
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
expect(tvStore.getUeByUpd).toHaveBeenCalledWith('6', '1', 10, 1.45);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('perimetre_ue absent : 2S/P = 1 (comportement inchangé)', () => {
|
|
216
|
+
service.execute(ctx, plancherDE({ enum_type_adjacence_id: '5', surface_paroi_opaque: 20 }));
|
|
217
|
+
|
|
218
|
+
expect(tvStore.getUeByUpd).toHaveBeenCalledWith('5', '1', 1, 1.45);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe('DeperditionPlancherBasService - typeIsolation()', () => {
|
|
224
|
+
test.each([
|
|
225
|
+
['terre-plein avant 2001', '5', undefined, '6', 2],
|
|
226
|
+
['terre-plein à partir de 2001', '5', undefined, '7', 4],
|
|
227
|
+
['terre-plein, période d’isolation prioritaire', '5', '7', '1', 4],
|
|
228
|
+
['autre adjacence avant 1975', '3', undefined, '2', 2],
|
|
229
|
+
['autre adjacence à partir de 1975', '3', undefined, '3', 4]
|
|
230
|
+
])('isolation inconnue, %s', (_, adjacence, periodeIsolation, periodeConstruction, attendu) => {
|
|
231
|
+
expect(
|
|
232
|
+
service.typeIsolation(
|
|
233
|
+
{ enumPeriodeConstructionId: periodeConstruction },
|
|
234
|
+
{
|
|
235
|
+
enum_type_isolation_id: '1',
|
|
236
|
+
enum_type_adjacence_id: adjacence,
|
|
237
|
+
enum_periode_isolation_id: periodeIsolation
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
).toBe(attendu);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('isolé mais type d’isolation inconnu (9) : ITE (4)', () => {
|
|
244
|
+
expect(service.typeIsolation(ctx, { enum_type_isolation_id: '9' })).toBe(4);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test('type d’isolation connu : renvoyé tel quel', () => {
|
|
248
|
+
expect(service.typeIsolation(ctx, { enum_type_isolation_id: '3' })).toBe(3);
|
|
249
|
+
});
|
|
250
|
+
});
|