@open3cl/engine 1.0.6 → 1.0.8
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/11_nadeq.spec.js +3 -2
- package/16.2_production_enr.spec.js +1 -0
- package/3.2.1_mur.spec.js +1 -0
- package/3.2.2_plancher_bas.spec.js +1 -0
- package/3.3_baie_vitree.spec.js +1 -0
- package/6.1_apport_gratuit.spec.js +1 -0
- package/7_inertie.spec.js +4 -3
- package/9_chauffage.spec.js +1 -0
- package/9_conso_ch.spec.js +1 -0
- package/9_generateur_ch.spec.js +1 -0
- package/conso.spec.js +1 -0
- package/core/assets/domain/synchronize-assets.spec.js +9 -9
- package/core/assets/domain/synchronize-c1-tables.spec.js +4 -4
- package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +6 -6
- package/core/assets/domain/synchronize-enum-tables.spec.js +10 -10
- package/core/assets/domain/synchronize-solicitations-tables.spec.js +6 -6
- package/core/assets/domain/synchronize-valeur-tables.spec.js +14 -14
- package/core/file/infrastructure/adapter/file.store.spec.js +5 -4
- package/core/tv/infrastructure/tvs.store.spec.js +3 -2
- package/core/util/infrastructure/object-util.spec.js +2 -1
- package/core/util/logger/log-service.js +47 -0
- package/features/dpe/domain/models/baie-ets.model.ts +12 -0
- package/features/dpe/domain/models/baie-vitree.model.ts +97 -0
- package/features/dpe/domain/models/climatisation.model.ts +25 -0
- package/features/dpe/domain/models/dpe.model.ts +194 -0
- package/features/dpe/domain/models/ets.model.ts +19 -0
- package/features/dpe/domain/models/installation-chauffage.model.ts +101 -0
- package/features/dpe/domain/models/installation-ecs.model.ts +76 -0
- package/features/dpe/domain/models/mur.model.ts +37 -0
- package/features/dpe/domain/models/plancher-bas.model.ts +38 -0
- package/features/dpe/domain/models/plancher-haut.model.ts +33 -0
- package/features/dpe/domain/models/pont-thermique.model.ts +21 -0
- package/features/dpe/domain/models/porte.model.ts +31 -0
- package/features/dpe/domain/models/production-elec-enr.model.ts +27 -0
- package/features/dpe/domain/models/sortie.model.ts +178 -0
- package/features/dpe/domain/models/type-habitation.model.js +8 -0
- package/features/dpe/domain/models/type-ventilation.model.js +8 -0
- package/features/dpe/domain/models/ventilation.model.ts +33 -0
- package/features/dpe/infrastructure/baieVitreeTv.store.js +292 -0
- package/features/dpe/infrastructure/baieVitreeTv.store.spec.js +352 -0
- package/features/dpe/infrastructure/tv.store.js +356 -0
- package/features/dpe/infrastructure/tv.store.spec.js +607 -0
- package/features/engine/domain/constants.js +1 -0
- package/features/engine/domain/contexte.builder.js +140 -0
- package/features/engine/domain/contexte.builder.spec.js +152 -0
- package/features/engine/domain/engine.service.js +139 -0
- package/features/engine/domain/engine.service.spec.js +7 -0
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.js +292 -0
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +484 -0
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +278 -0
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +282 -0
- package/features/engine/domain/enveloppe/deperdition.service.js +101 -0
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +168 -0
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +345 -0
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +169 -0
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +200 -0
- package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.js +136 -0
- package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +211 -0
- package/features/engine/domain/enveloppe/porte/deperdition-porte.service.js +56 -0
- package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +116 -0
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +338 -0
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +442 -0
- package/features/engine/domain/models/contexte.model.ts +10 -0
- package/features/engine/domain/models/deperdition.model.ts +8 -0
- package/features/normalizer/domain/dpe-normalizer.service.js +24 -0
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +3 -0
- package/ficheTechnique.spec.js +4 -3
- package/output.js +1 -0
- package/package.json +9 -8
- package/tv-v2.js +79121 -0
- package/utils.js +2 -2
- package/utils.spec.js +4 -3
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { logger } from '../../../../../core/util/logger/log-service.js';
|
|
2
|
+
import { inject } from 'dioma';
|
|
3
|
+
import { PRECISION } from '../../constants.js';
|
|
4
|
+
import { DeperditionService } from '../deperdition.service.js';
|
|
5
|
+
import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calcul des déperditions de l’enveloppe GV
|
|
9
|
+
* Chapitre 3.2.1 Calcul des Umur
|
|
10
|
+
*
|
|
11
|
+
* Méthode de calcul 3CL-DPE 2021
|
|
12
|
+
* Octobre 2021
|
|
13
|
+
* @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
|
|
14
|
+
*/
|
|
15
|
+
export class DeperditionMurService extends DeperditionService {
|
|
16
|
+
/**
|
|
17
|
+
* @param tvStore {TvStore}
|
|
18
|
+
*/
|
|
19
|
+
constructor(tvStore = inject(TvStore)) {
|
|
20
|
+
super(tvStore);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param ctx {Contexte}
|
|
25
|
+
* @param murDE {MurDE}
|
|
26
|
+
* @return {MurDI}
|
|
27
|
+
*/
|
|
28
|
+
execute(ctx, murDE) {
|
|
29
|
+
const umur0 = this.#umur0(murDE);
|
|
30
|
+
const umur = this.#umur(murDE, umur0, ctx);
|
|
31
|
+
const b = this.b({
|
|
32
|
+
enumTypeAdjacenceId: murDE.enum_type_adjacence_id,
|
|
33
|
+
surfaceAiu: murDE.surface_aiu,
|
|
34
|
+
surfaceAue: murDE.surface_aue,
|
|
35
|
+
enumCfgIsolationLncId: murDE.enum_cfg_isolation_lnc_id,
|
|
36
|
+
tvCoefReductionDeperditionId: murDE.tv_coef_reduction_deperdition_id,
|
|
37
|
+
zoneClimatiqueId: ctx.zoneClimatiqueId
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** @type {MurDI} */
|
|
41
|
+
return { umur0, umur, b };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param murDE {MurDE}
|
|
46
|
+
* @param umur0 {number}
|
|
47
|
+
* @param ctx {Contexte}
|
|
48
|
+
* @return {number|undefined}
|
|
49
|
+
*/
|
|
50
|
+
#umur(murDE, umur0, ctx) {
|
|
51
|
+
// On determine umur_nu (soit umur0 soit 2.5 comme valeur minimale forfaitaire)
|
|
52
|
+
const umurNu = Math.min(umur0, 2.5);
|
|
53
|
+
|
|
54
|
+
const enumPeriodeIsolationId = this.getEnumPeriodeIsolationId(
|
|
55
|
+
murDE.enum_periode_isolation_id,
|
|
56
|
+
ctx
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Selon l'isolation, on applique un calcul au mur nu pour simuler son isolation
|
|
60
|
+
let umur;
|
|
61
|
+
switch (murDE.enum_methode_saisie_u_id) {
|
|
62
|
+
case '1': // non isolé
|
|
63
|
+
umur = umurNu;
|
|
64
|
+
break;
|
|
65
|
+
case '2': // isolation inconnue (table forfaitaire)
|
|
66
|
+
umur = Math.min(
|
|
67
|
+
umurNu,
|
|
68
|
+
this.tvStore.getUmur(ctx.enumPeriodeConstructionId, ctx.zoneClimatiqueId, ctx.effetJoule)
|
|
69
|
+
);
|
|
70
|
+
break;
|
|
71
|
+
case '7': // année d'isolation différente de l'année de construction
|
|
72
|
+
case '8': // année de construction saisie
|
|
73
|
+
umur = Math.min(
|
|
74
|
+
umurNu,
|
|
75
|
+
this.tvStore.getUmur(enumPeriodeIsolationId, ctx.zoneClimatiqueId, ctx.effetJoule)
|
|
76
|
+
);
|
|
77
|
+
break;
|
|
78
|
+
case '3': // epaisseur isolation saisie justifiée par mesure ou observation
|
|
79
|
+
case '4': {
|
|
80
|
+
// epaisseur isolation saisie (en cm)
|
|
81
|
+
const epaisseurIsolation = murDE.epaisseur_isolation;
|
|
82
|
+
if (epaisseurIsolation) {
|
|
83
|
+
umur = 1 / (1 / umurNu + (murDE.epaisseur_isolation * 0.01) / 0.04);
|
|
84
|
+
} else {
|
|
85
|
+
logger.warn(
|
|
86
|
+
`Le mur ${murDE.description} ne possède pas de donnée d'entrée pour epaisseur_isolation
|
|
87
|
+
alors que methode_saisie_u = 'epaisseur isolation saisie'`
|
|
88
|
+
);
|
|
89
|
+
umur = Math.min(umur0, 2.5);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case '5':
|
|
94
|
+
case '6':
|
|
95
|
+
// resistance isolation saisie
|
|
96
|
+
umur = 1 / (1 / umurNu + murDE.resistance_isolation);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
// saisie direct de la valeur de u
|
|
100
|
+
umur = murDE.umur_saisi;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Math.round(parseFloat(umur) * PRECISION) / PRECISION;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param murDE {MurDE}
|
|
109
|
+
* @return {number|undefined}
|
|
110
|
+
*/
|
|
111
|
+
#umur0(murDE) {
|
|
112
|
+
let umur0;
|
|
113
|
+
switch (murDE.enum_methode_saisie_u0_id) {
|
|
114
|
+
case '1':
|
|
115
|
+
umur0 = this.tvStore.getUmur0(murDE.enum_materiaux_structure_mur_id);
|
|
116
|
+
break;
|
|
117
|
+
case '2':
|
|
118
|
+
umur0 = this.tvStore.getUmur0(
|
|
119
|
+
murDE.enum_materiaux_structure_mur_id,
|
|
120
|
+
murDE.epaisseur_structure
|
|
121
|
+
);
|
|
122
|
+
break;
|
|
123
|
+
case '5':
|
|
124
|
+
// Valeur u saisie directement umur0 vide
|
|
125
|
+
return;
|
|
126
|
+
default:
|
|
127
|
+
// Valeur saisie
|
|
128
|
+
return murDE.umur0_saisi;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Pour l’ensemble des parois, la présence d’un doublage apporte une résistance thermique supplémentaire
|
|
133
|
+
*/
|
|
134
|
+
switch (murDE.enum_type_doublage_id) {
|
|
135
|
+
case '3':
|
|
136
|
+
// doublage indéterminé ou lame d'air inf 15 mm
|
|
137
|
+
umur0 = 1 / (1 / umur0 + 0.1);
|
|
138
|
+
break;
|
|
139
|
+
case '4': // doublage indéterminé ou lame d'air sup 15 mm
|
|
140
|
+
case '5': // doublage connu (plâtre brique bois)
|
|
141
|
+
umur0 = 1 / (1 / umur0 + 0.21);
|
|
142
|
+
break;
|
|
143
|
+
default:
|
|
144
|
+
// absence de doublage ou inconnu
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pour les parois dites « anciennes », c’est-à-dire constituées de matériaux traditionnels à savoir pierres, terre, mur à
|
|
150
|
+
* colombage, brique ancienne, la présence d’un enduit isolant n’est pas considérée comme une isolation. Cependant,
|
|
151
|
+
* cet enduit apporte une correction d’isolation qu’il faut prendre en compte
|
|
152
|
+
*
|
|
153
|
+
* la paroi est une paroi ancienne sur laquelle a été appliquée un enduit isolant (Renduit=0,7m².K.W-1) 0 : non 1 : oui.
|
|
154
|
+
* (Attention ! nom de propriété pas tout à fait explicite)
|
|
155
|
+
* OBSOLETE > remplacé par enduit_isolant_paroi_ancienne
|
|
156
|
+
*/
|
|
157
|
+
if (murDE.enduit_isolant_paroi_ancienne) {
|
|
158
|
+
umur0 = 1 / (1 / umur0 + 0.7);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @todo Comportement non clairement documenté
|
|
163
|
+
* Dans beaucoup de cas umur0 retourné en le min entre 2.5 et la valeur trouvée,
|
|
164
|
+
* mais pas de documentation sur ce point dans la méthode 3CL
|
|
165
|
+
*/
|
|
166
|
+
return Math.min(2.5, Math.round(parseFloat(umur0) * PRECISION) / PRECISION);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import corpus from '../../../../../../test/corpus-sano.json';
|
|
2
|
+
import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
3
|
+
import { DeperditionMurService } from './deperdition-mur.service.js';
|
|
4
|
+
import { ContexteBuilder } from '../../contexte.builder.js';
|
|
5
|
+
import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
|
|
6
|
+
import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
|
|
7
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
8
|
+
|
|
9
|
+
/** @type {DeperditionMurService} **/
|
|
10
|
+
let service;
|
|
11
|
+
|
|
12
|
+
/** @type {DpeNormalizerService} **/
|
|
13
|
+
let normalizerService;
|
|
14
|
+
|
|
15
|
+
/** @type {ContexteBuilder} **/
|
|
16
|
+
let contexteBuilder;
|
|
17
|
+
|
|
18
|
+
describe('Calcul de déperdition des murs', () => {
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
service = new DeperditionMurService(new TvStore());
|
|
21
|
+
normalizerService = new DpeNormalizerService();
|
|
22
|
+
contexteBuilder = new ContexteBuilder();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('Determination de uMur et uMur0', () => {
|
|
26
|
+
test.each([
|
|
27
|
+
{
|
|
28
|
+
label: 'cas 2187E1039187C mur 1',
|
|
29
|
+
enumMateriauxStructureMurId: '2',
|
|
30
|
+
enumMethodeSaisieUId: '8',
|
|
31
|
+
enumMethodeSaisieU0Id: '2',
|
|
32
|
+
paroiAncienne: false,
|
|
33
|
+
epaisseurStructure: 60,
|
|
34
|
+
enumTypeIsolationId: '9',
|
|
35
|
+
enumTypeDoublageId: '2',
|
|
36
|
+
enumPeriodeConstructionId: '2',
|
|
37
|
+
zoneClimatiqueId: '3',
|
|
38
|
+
umurExpected: 1,
|
|
39
|
+
umur0Expected: 1.8
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label:
|
|
43
|
+
"(2287E0577966W) Mur 2 Sud, Est, Ouest (p1) - Mur en blocs de béton creux d'épaisseur ≥ 25 cm avec un doublage rapporté donnant sur l'extérieur",
|
|
44
|
+
enumMateriauxStructureMurId: '12',
|
|
45
|
+
enumMethodeSaisieUId: '7',
|
|
46
|
+
enumMethodeSaisieU0Id: '2',
|
|
47
|
+
paroiAncienne: false,
|
|
48
|
+
enumTypeIsolationId: '1',
|
|
49
|
+
enumTypeDoublageId: '4',
|
|
50
|
+
enumPeriodeConstructionId: '1',
|
|
51
|
+
enumIsolationId: '5',
|
|
52
|
+
epaisseurStructure: 25,
|
|
53
|
+
zoneClimatiqueId: '3',
|
|
54
|
+
umurExpected: 0.8, // 0.7 dans le DPE original, mais certainement lié à une erreur
|
|
55
|
+
umur0Expected: 1.55091
|
|
56
|
+
}
|
|
57
|
+
])(
|
|
58
|
+
'$label',
|
|
59
|
+
({
|
|
60
|
+
enumMateriauxStructureMurId,
|
|
61
|
+
enumMethodeSaisieUId,
|
|
62
|
+
enumMethodeSaisieU0Id,
|
|
63
|
+
enumTypeDoublageId,
|
|
64
|
+
enumTypeIsolationId,
|
|
65
|
+
umurSaisi,
|
|
66
|
+
epaisseurStructure,
|
|
67
|
+
paroiAncienne,
|
|
68
|
+
resistanceIsolation,
|
|
69
|
+
umurExpected,
|
|
70
|
+
umur0Expected,
|
|
71
|
+
enumPeriodeConstructionId = 1,
|
|
72
|
+
enumIsolationId,
|
|
73
|
+
effetJoule = false,
|
|
74
|
+
zoneClimatiqueId = '1'
|
|
75
|
+
}) => {
|
|
76
|
+
/** @type {Contexte} */
|
|
77
|
+
const ctx = {
|
|
78
|
+
effetJoule,
|
|
79
|
+
enumPeriodeConstructionId,
|
|
80
|
+
zoneClimatiqueId
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** @type {MurDE} */
|
|
84
|
+
const de = {
|
|
85
|
+
enum_materiaux_structure_mur_id: enumMateriauxStructureMurId,
|
|
86
|
+
enum_methode_saisie_u_id: enumMethodeSaisieUId,
|
|
87
|
+
enum_methode_saisie_u0_id: enumMethodeSaisieU0Id,
|
|
88
|
+
umur_saisi: umurSaisi,
|
|
89
|
+
resistance_isolation: resistanceIsolation,
|
|
90
|
+
paroi_ancienne: paroiAncienne,
|
|
91
|
+
epaisseur_structure: epaisseurStructure,
|
|
92
|
+
enum_type_doublage_id: enumTypeDoublageId,
|
|
93
|
+
enum_type_isolation_id: enumTypeIsolationId,
|
|
94
|
+
enum_periode_isolation_id: enumIsolationId
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const di = service.execute(ctx, de);
|
|
98
|
+
expect(di.umur0).toBe(umur0Expected);
|
|
99
|
+
expect(di.umur).toBeCloseTo(umurExpected);
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
test("Mur 1 Sud, Est, Ouest (p1) - Mur en blocs de béton pleins d'épaisseur ≤ 20 cm avec isolation intérieure (10 cm) donnant sur l'extérieur", () => {
|
|
104
|
+
/** @type {Contexte} */
|
|
105
|
+
const ctx = {
|
|
106
|
+
effetJoule: false,
|
|
107
|
+
enumPeriodeConstructionId: '6',
|
|
108
|
+
zoneClimatiqueId: '3'
|
|
109
|
+
};
|
|
110
|
+
/** @type {MurDE} */
|
|
111
|
+
const de = {
|
|
112
|
+
enum_materiaux_structure_mur_id: '11',
|
|
113
|
+
enum_methode_saisie_u_id: '3',
|
|
114
|
+
enum_methode_saisie_u0_id: '2',
|
|
115
|
+
paroi_ancienne: false,
|
|
116
|
+
epaisseur_structure: 20,
|
|
117
|
+
enum_type_doublage_id: '2',
|
|
118
|
+
enum_type_isolation_id: '3',
|
|
119
|
+
epaisseur_isolation: 10
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const di = service.execute(ctx, de);
|
|
123
|
+
expect(di.umur0).toBe(2.5);
|
|
124
|
+
expect(di.umur).toBeCloseTo(0.3448275862068969);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('Mur 1 Nord - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 70 cm avec isolation intérieure (10 cm) donnant sur l'extérieur', () => {
|
|
128
|
+
/** @type {Contexte} */
|
|
129
|
+
const ctx = {
|
|
130
|
+
effetJoule: false,
|
|
131
|
+
enumPeriodeConstructionId: '6',
|
|
132
|
+
zoneClimatiqueId: '3'
|
|
133
|
+
};
|
|
134
|
+
/** @type {MurDE} */
|
|
135
|
+
const de = {
|
|
136
|
+
enum_materiaux_structure_mur_id: '3',
|
|
137
|
+
enum_methode_saisie_u_id: '3',
|
|
138
|
+
enum_methode_saisie_u0_id: '2',
|
|
139
|
+
paroi_ancienne: true,
|
|
140
|
+
epaisseur_structure: 70,
|
|
141
|
+
enum_type_doublage_id: '2',
|
|
142
|
+
enum_type_isolation_id: '3',
|
|
143
|
+
epaisseur_isolation: 10
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const di = service.execute(ctx, de);
|
|
147
|
+
expect(di.umur0).toBe(1.45);
|
|
148
|
+
expect(di.umur).toBeCloseTo(0.31351351351351353);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("(2387E0430619S) Mur 3 Nord, Est (p1) - Mur en placoplatre isolé par l'intérieur (environ 10 cm) avec isolation intérieure donnant sur des circulations avec ouverture directe sur l'extérieur", () => {
|
|
152
|
+
/** @type {Contexte} */
|
|
153
|
+
const ctx = {
|
|
154
|
+
effetJoule: false,
|
|
155
|
+
enumPeriodeConstructionId: '6',
|
|
156
|
+
zoneClimatiqueId: '3'
|
|
157
|
+
};
|
|
158
|
+
/** @type {MurDE} */
|
|
159
|
+
const de = {
|
|
160
|
+
enum_materiaux_structure_mur_id: '23',
|
|
161
|
+
enum_methode_saisie_u_id: '9',
|
|
162
|
+
enum_methode_saisie_u0_id: '5',
|
|
163
|
+
paroi_ancienne: false,
|
|
164
|
+
enum_type_doublage_id: '2',
|
|
165
|
+
enum_type_isolation_id: '3',
|
|
166
|
+
umur_saisi: 0.32
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const di = service.execute(ctx, de);
|
|
170
|
+
expect(di.umur0).toBeUndefined();
|
|
171
|
+
expect(di.umur).toBeCloseTo(0.32);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("Mur 1 Nord, Sud, Ouest (p1) - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm non isolé donnant sur l'extérieur", () => {
|
|
175
|
+
/** @type {Contexte} */
|
|
176
|
+
const ctx = {
|
|
177
|
+
effetJoule: false,
|
|
178
|
+
enumPeriodeConstructionId: '6',
|
|
179
|
+
zoneClimatiqueId: '3'
|
|
180
|
+
};
|
|
181
|
+
/** @type {MurDE} */
|
|
182
|
+
const de = {
|
|
183
|
+
enum_materiaux_structure_mur_id: '3',
|
|
184
|
+
enum_methode_saisie_u_id: '1',
|
|
185
|
+
enum_methode_saisie_u0_id: '2',
|
|
186
|
+
enduit_isolant_paroi_ancienne: true,
|
|
187
|
+
epaisseur_structure: 50,
|
|
188
|
+
enum_type_doublage_id: '2',
|
|
189
|
+
enum_type_isolation_id: '2'
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const di = service.execute(ctx, de);
|
|
193
|
+
expect(di.umur0).toBeCloseTo(0.81545);
|
|
194
|
+
expect(di.umur).toBeCloseTo(0.81545);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("Mur 8 Est - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm avec un doublage rapporté non isolé donnant sur l'extérieur", () => {
|
|
198
|
+
/** @type {Contexte} */
|
|
199
|
+
const ctx = {
|
|
200
|
+
effetJoule: false,
|
|
201
|
+
enumPeriodeConstructionId: '6',
|
|
202
|
+
zoneClimatiqueId: '3'
|
|
203
|
+
};
|
|
204
|
+
/** @type {MurDE} */
|
|
205
|
+
const de = {
|
|
206
|
+
enum_materiaux_structure_mur_id: '3',
|
|
207
|
+
enum_methode_saisie_u_id: '1',
|
|
208
|
+
enum_methode_saisie_u0_id: '2',
|
|
209
|
+
paroi_ancienne: true,
|
|
210
|
+
epaisseur_structure: 50,
|
|
211
|
+
enum_type_doublage_id: '3',
|
|
212
|
+
enum_type_isolation_id: '2'
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const di = service.execute(ctx, de);
|
|
216
|
+
expect(di.umur0).toBeCloseTo(1.59664);
|
|
217
|
+
expect(di.umur).toBeCloseTo(1.59664);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('(2287E0577966W) umur 0.7 au lieu de 0.8', () => {
|
|
221
|
+
/** @type {Contexte} */
|
|
222
|
+
const ctx = {
|
|
223
|
+
effetJoule: true,
|
|
224
|
+
enumPeriodeConstructionId: '1',
|
|
225
|
+
zoneClimatiqueId: '3'
|
|
226
|
+
};
|
|
227
|
+
/** @type {MurDE} */
|
|
228
|
+
const de = {
|
|
229
|
+
enum_materiaux_structure_mur_id: '12',
|
|
230
|
+
enum_methode_saisie_u_id: '7',
|
|
231
|
+
enum_methode_saisie_u0_id: '2',
|
|
232
|
+
paroi_ancienne: false,
|
|
233
|
+
epaisseur_structure: 25,
|
|
234
|
+
enum_type_doublage_id: '4',
|
|
235
|
+
enum_type_isolation_id: '1',
|
|
236
|
+
enum_periode_isolation_id: '5'
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const di = service.execute(ctx, de);
|
|
240
|
+
expect(di.umur0).toBeCloseTo(1.55091);
|
|
241
|
+
expect(di.umur).toBeCloseTo(0.7); // 0.7 dans le DPE d'origine, comme si effet joule
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("(2387E0430619S) Mur 3 Nord, Est (p1) - Mur en placoplatre isolé par l'intérieur (environ 10 cm) avec isolation intérieure donnant sur des circulations avec ouverture directe sur l'extérieur", () => {
|
|
245
|
+
/** @type {Contexte} */
|
|
246
|
+
const ctx = {
|
|
247
|
+
effetJoule: true,
|
|
248
|
+
enumPeriodeConstructionId: '1',
|
|
249
|
+
zoneClimatiqueId: '3'
|
|
250
|
+
};
|
|
251
|
+
/** @type {MurDE} */
|
|
252
|
+
const de = {
|
|
253
|
+
enum_materiaux_structure_mur_id: '23',
|
|
254
|
+
enum_methode_saisie_u_id: '9',
|
|
255
|
+
enum_methode_saisie_u0_id: '5',
|
|
256
|
+
paroi_ancienne: false,
|
|
257
|
+
umur_saisi: 0.32,
|
|
258
|
+
epaisseur_structure: 25,
|
|
259
|
+
enum_type_doublage_id: '2',
|
|
260
|
+
enum_type_isolation_id: '3'
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const di = service.execute(ctx, de);
|
|
264
|
+
expect(di.umur0).toBeUndefined(); // umur saisie directement
|
|
265
|
+
expect(di.umur).toBeCloseTo(0.32);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('(2287E1724516Y) Mur 4 Nord, Sud (p1) - Mur en pan de bois sans remplissage tout venant d'épaisseur 18 cm avec isolation intérieure (R=2.5m².K/W) donnant sur l'extérieur', () => {
|
|
269
|
+
/** @type {Contexte} */
|
|
270
|
+
const ctx = {
|
|
271
|
+
effetJoule: false,
|
|
272
|
+
enumPeriodeConstructionId: '1',
|
|
273
|
+
zoneClimatiqueId: '3'
|
|
274
|
+
};
|
|
275
|
+
/** @type {MurDE} */
|
|
276
|
+
const de = {
|
|
277
|
+
enum_materiaux_structure_mur_id: '5',
|
|
278
|
+
enum_methode_saisie_u_id: '6',
|
|
279
|
+
enum_methode_saisie_u0_id: '2',
|
|
280
|
+
enduit_isolant_paroi_ancienne: true,
|
|
281
|
+
epaisseur_structure: 18,
|
|
282
|
+
resistance_isolation: 2.5,
|
|
283
|
+
enum_type_doublage_id: '2',
|
|
284
|
+
enum_type_isolation_id: '3'
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const di = service.execute(ctx, de);
|
|
288
|
+
expect(di.umur0).toBeCloseTo(0.82983999999999991);
|
|
289
|
+
expect(di.umur).toBeCloseTo(0.26990177584075975);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('(2187E0982013C) Mur Nord, Sud, Est, Ouest - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm non isolé donnant sur l'extérieur', () => {
|
|
293
|
+
/** @type {Contexte} */
|
|
294
|
+
const ctx = {
|
|
295
|
+
effetJoule: false,
|
|
296
|
+
enumPeriodeConstructionId: '1',
|
|
297
|
+
zoneClimatiqueId: '3'
|
|
298
|
+
};
|
|
299
|
+
/** @type {MurDE} */
|
|
300
|
+
const de = {
|
|
301
|
+
enum_type_adjacence_id: '1',
|
|
302
|
+
enum_materiaux_structure_mur_id: '3',
|
|
303
|
+
enum_methode_saisie_u_id: '1',
|
|
304
|
+
enum_methode_saisie_u0_id: '2',
|
|
305
|
+
enduit_isolant_paroi_ancienne: true,
|
|
306
|
+
epaisseur_structure: 18,
|
|
307
|
+
resistance_isolation: 2.5,
|
|
308
|
+
enum_type_doublage_id: '2',
|
|
309
|
+
enum_type_isolation_id: '2'
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const di = service.execute(ctx, de);
|
|
313
|
+
expect(di.umur0).toBeCloseTo(0.81545);
|
|
314
|
+
expect(di.umur).toBeCloseTo(0.81545);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe("Test d'intégration de mur", () => {
|
|
319
|
+
test.each(corpus)('vérification des DI des murs pour dpe %s', (ademeId) => {
|
|
320
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
321
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
322
|
+
|
|
323
|
+
/** @type {Contexte} */
|
|
324
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
325
|
+
|
|
326
|
+
const murs = dpeRequest.logement.enveloppe.mur_collection?.mur || [];
|
|
327
|
+
|
|
328
|
+
murs.forEach((m) => {
|
|
329
|
+
// dans ces cas de figure, les données paroi_ancienne sont bien traitées
|
|
330
|
+
if (['2187E0982013C', '2287E2336469P', '2387E0045247S'].includes(ademeId)) {
|
|
331
|
+
m.donnee_entree.enduit_isolant_paroi_ancienne = m.donnee_entree.paroi_ancienne;
|
|
332
|
+
}
|
|
333
|
+
const di = service.execute(ctx, m.donnee_entree);
|
|
334
|
+
|
|
335
|
+
if (m.donnee_intermediaire.umur0) {
|
|
336
|
+
expect(di.umur0).toBeCloseTo(m.donnee_intermediaire.umur0, 2);
|
|
337
|
+
} else {
|
|
338
|
+
expect(di.umur0).toBeUndefined();
|
|
339
|
+
}
|
|
340
|
+
expect(di.umur).toBeCloseTo(m.donnee_intermediaire.umur, 2);
|
|
341
|
+
expect(di.b).toBeCloseTo(m.donnee_intermediaire.b, 2);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { inject } from 'dioma';
|
|
2
|
+
import { PRECISION } from '../../constants.js';
|
|
3
|
+
import { DeperditionService } from '../deperdition.service.js';
|
|
4
|
+
import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Calcul des déperditions de l’enveloppe GV
|
|
8
|
+
* Chapitre 3.2.2 Calcul des planchers bas
|
|
9
|
+
*
|
|
10
|
+
* Méthode de calcul 3CL-DPE 2021
|
|
11
|
+
* Octobre 2021
|
|
12
|
+
* @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
|
|
13
|
+
*/
|
|
14
|
+
export class DeperditionPlancherBasService extends DeperditionService {
|
|
15
|
+
/**
|
|
16
|
+
* @param tvStore {TvStore}
|
|
17
|
+
*/
|
|
18
|
+
constructor(tvStore = inject(TvStore)) {
|
|
19
|
+
super(tvStore);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param ctx {Contexte}
|
|
24
|
+
* @param pbDE {PlancherBasDE}
|
|
25
|
+
* @param plancherBas {PlancherBas[]}
|
|
26
|
+
* @return {PlancherBasDI}
|
|
27
|
+
*/
|
|
28
|
+
execute(ctx, pbDE, plancherBas) {
|
|
29
|
+
const upb0 = this.#upb0(pbDE);
|
|
30
|
+
const upb = this.#upb(pbDE, upb0, ctx);
|
|
31
|
+
const upb_final = this.#upbFinal(pbDE, upb, ctx, plancherBas);
|
|
32
|
+
const b = this.b({
|
|
33
|
+
enumTypeAdjacenceId: pbDE.enum_type_adjacence_id,
|
|
34
|
+
surfaceAiu: pbDE.surface_aiu,
|
|
35
|
+
surfaceAue: pbDE.surface_aue,
|
|
36
|
+
enumCfgIsolationLncId: pbDE.enum_cfg_isolation_lnc_id,
|
|
37
|
+
zoneClimatiqueId: ctx.zoneClimatiqueId
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** @type {PlancherBasDI} */
|
|
41
|
+
return { upb0, upb, upb_final, b };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param pbDE {PlancherBasDE}
|
|
46
|
+
* @param upb0 {number}
|
|
47
|
+
* @param ctx {Contexte}
|
|
48
|
+
* @return {number|undefined}
|
|
49
|
+
*/
|
|
50
|
+
#upb(pbDE, upb0, ctx) {
|
|
51
|
+
// On determine upb_nu (soit upb0 soit 2 comme valeur minimale forfaitaire)
|
|
52
|
+
const upbNu = Math.min(upb0, 2);
|
|
53
|
+
|
|
54
|
+
const enumPeriodeIsolationId = this.getEnumPeriodeIsolationId(
|
|
55
|
+
pbDE.enum_periode_isolation_id,
|
|
56
|
+
ctx
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Selon l'isolation, on applique un calcul au upb nu pour simuler son isolation
|
|
60
|
+
let upb;
|
|
61
|
+
switch (pbDE.enum_methode_saisie_u_id) {
|
|
62
|
+
case '1': // non isolé
|
|
63
|
+
upb = upbNu;
|
|
64
|
+
break;
|
|
65
|
+
case '2': // isolation inconnue (table forfaitaire)
|
|
66
|
+
case '7': // année d'isolation différente de l'année de construction
|
|
67
|
+
case '8': // année de construction saisie
|
|
68
|
+
upb = Math.min(
|
|
69
|
+
upbNu,
|
|
70
|
+
this.tvStore.getUpb(enumPeriodeIsolationId, ctx.zoneClimatiqueId, ctx.effetJoule)
|
|
71
|
+
);
|
|
72
|
+
break;
|
|
73
|
+
case '3': // epaisseur isolation saisie justifiée par mesure ou observation
|
|
74
|
+
case '4': // epaisseur isolation saisie (en cm)
|
|
75
|
+
upb = 1 / (1 / upbNu + (pbDE.epaisseur_isolation * 0.01) / 0.042);
|
|
76
|
+
break;
|
|
77
|
+
case '5':
|
|
78
|
+
case '6': // resistance isolation saisie
|
|
79
|
+
upb = 1 / (1 / upbNu + pbDE.resistance_isolation);
|
|
80
|
+
break;
|
|
81
|
+
default: // saisie direct de la valeur de u
|
|
82
|
+
upb = pbDE.upb_saisi;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return Math.round(parseFloat(upb) * PRECISION) / PRECISION;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param pbDE {PlancherBasDE}
|
|
91
|
+
* @return {number|undefined}
|
|
92
|
+
*/
|
|
93
|
+
#upb0(pbDE) {
|
|
94
|
+
let upb0;
|
|
95
|
+
switch (pbDE.enum_methode_saisie_u0_id) {
|
|
96
|
+
case '1': // 'type de paroi inconnu (valeur par défaut)'
|
|
97
|
+
case '2': // 'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire'
|
|
98
|
+
upb0 = this.tvStore.getUpb0(pbDE.enum_type_plancher_bas_id);
|
|
99
|
+
break;
|
|
100
|
+
case '5': // 'u0 non saisi car le u est saisi connu et justifié.'
|
|
101
|
+
return;
|
|
102
|
+
default: // Valeur saisie
|
|
103
|
+
return pbDE.upb0_saisi;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return upb0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Pour les vides sanitaires, les sous-sol non chauffés et terre-plein, le calcul des déperditions se fait avec un coefficient
|
|
111
|
+
* Ue en remplacement de Upb.
|
|
112
|
+
*
|
|
113
|
+
* @param pbDE {PlancherBasDE}
|
|
114
|
+
* @param upb {number}
|
|
115
|
+
* @param ctx {Contexte}
|
|
116
|
+
* @param plancherBas {PlancherBas[]}
|
|
117
|
+
* @return {number|undefined}
|
|
118
|
+
*/
|
|
119
|
+
#upbFinal(pbDE, upb, ctx, plancherBas) {
|
|
120
|
+
if (pbDE.calcul_ue === 1) {
|
|
121
|
+
return pbDE.ue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 3 - vide sanitaire
|
|
126
|
+
* 5 - terre-plein
|
|
127
|
+
* 6 - sous-sol non chauffé
|
|
128
|
+
*/
|
|
129
|
+
if (!['3', '5', '6'].includes(pbDE.enum_type_adjacence_id)) {
|
|
130
|
+
return upb;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* La surface Ue est la surface de tous les planchers bas ayant le même type d'adjacence
|
|
135
|
+
* Le périmètre Ue est le périmètre de tous les planchers bas ayant le même type d'adjacence
|
|
136
|
+
*/
|
|
137
|
+
const { surfaceUe, perimetreUe } = plancherBas
|
|
138
|
+
.filter(
|
|
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
|
+
);
|
|
151
|
+
|
|
152
|
+
const available2sp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
|
|
153
|
+
let dsp = perimetreUe ? Math.round((2 * surfaceUe) / perimetreUe) : 1;
|
|
154
|
+
|
|
155
|
+
// Recherche de la valeur la plus proche dans les valeurs disponibles
|
|
156
|
+
dsp = available2sp.reduce((prev, curr) => {
|
|
157
|
+
return Math.abs(curr - dsp) < Math.abs(prev - dsp) ? curr : prev;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const upbFinal = this.tvStore.getUeByUpd(
|
|
161
|
+
pbDE.enum_type_adjacence_id,
|
|
162
|
+
ctx.enumPeriodeConstructionId,
|
|
163
|
+
dsp,
|
|
164
|
+
upb
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return Math.round(parseFloat(upbFinal) * PRECISION) / PRECISION;
|
|
168
|
+
}
|
|
169
|
+
}
|