@open3cl/engine 1.2.8 → 1.3.0

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_besoin_ecs.js CHANGED
@@ -1,6 +1,15 @@
1
1
  import { tvs } from './tv.js';
2
2
  import { mois_liste, Njj } from './utils.js';
3
3
 
4
+ /**
5
+ * Calcul du besoin ecs en kw/h
6
+ * @param ca
7
+ * @param mois
8
+ * @param zc
9
+ * @param nadeq
10
+ * @param depensier
11
+ * @return {number}
12
+ */
4
13
  export function calc_besoin_ecs_j(ca, mois, zc, nadeq, depensier) {
5
14
  const tefsj = tvs.tefs[ca][mois][zc];
6
15
  const njj = Njj[mois];
@@ -75,27 +75,28 @@ function tv_facteur_couverture_solaire(di, de, zc_id, th) {
75
75
  }
76
76
  }
77
77
 
78
- // 15.2.3
79
78
  export function calc_Qdw_j(instal_ecs, becs_j) {
80
79
  const de = instal_ecs.donnee_entree;
81
80
  const du = instal_ecs.donnee_utilisateur || {};
82
81
 
83
82
  const type_installation = requestInput(de, du, 'type_installation');
84
83
 
85
- let Qdw_ind_vc_j;
86
- let Qdw_col_vc_j = 0;
84
+ const Qdw_ind_vc = calc_Qdw_ind_j(becs_j);
85
+ const Qdw_coll_vc = calc_Qdw_col_j(instal_ecs, becs_j, type_installation);
87
86
 
88
- const Rat_ecs = 1;
89
- const Lvc = 0.2 * Rat_ecs;
87
+ return (Qdw_ind_vc + Qdw_coll_vc) * 0.48;
88
+ }
90
89
 
91
- Qdw_ind_vc_j = 0.5 * Lvc * becs_j;
90
+ // 15.2.3
91
+ export function calc_Qdw_ind_j(becs_j) {
92
+ return (0.1 * becs_j) / 8760.0;
93
+ }
92
94
 
95
+ export function calc_Qdw_col_j(instal_ecs, becs_j, type_installation) {
93
96
  if (type_installation.includes('installation collective')) {
94
- Qdw_col_vc_j = 0.112 * becs_j;
97
+ return (0.112 * becs_j) / 8760.0;
95
98
  }
96
-
97
- instal_ecs.donnee_utilisateur = du;
98
- return Qdw_col_vc_j + Qdw_ind_vc_j;
99
+ return 0;
99
100
  }
100
101
 
101
102
  function calc_Qgw(di, de, du, ecs_de) {
@@ -7,7 +7,7 @@ export function calc_ai_j(Sh, nadeq, nrefj) {
7
7
  * L'unité retenue est le Wh conformément à la spécification, cependant
8
8
  * il est possible de rencontrer des valeurs avec une unité en kWh dans les DPE.
9
9
  */
10
- return ((3.18 + 0.34) * Sh + 90 * (132 / 168) * nadeq) * nrefj;
10
+ return ((3.18 + 0.34) * Sh + (132 / 168) * nadeq * 90) * nrefj;
11
11
  }
12
12
 
13
13
  export function calc_as_j(ssej, ej) {
@@ -1,5 +1,4 @@
1
1
  export function calc_intermittence(GV, Sh, Hsp, i0) {
2
2
  const G = GV / (Sh * Hsp);
3
- const INT = i0 / (1 + 0.1 * (G - 1));
4
- return INT;
3
+ return i0 / (1 + 0.1 * (G - 1));
5
4
  }
package/9_besoin_ch.js CHANGED
@@ -32,35 +32,37 @@ export default function calc_besoin_ch(
32
32
  const Nref21 = tvs.nref21[ilpa];
33
33
  const Nref19 = tvs.nref19[ilpa];
34
34
 
35
- let sumNref19 = 0;
36
- let sumNref21 = 0;
37
35
  let sumDh19 = 0;
38
36
  let sumDh21 = 0;
39
- let QrecDistr = 0;
40
- let QrecDistrDepensier = 0;
41
37
  const e = tvs.e[ilpa];
42
38
 
43
39
  let pertes_distribution_ecs_recup = 0;
44
40
  let pertes_distribution_ecs_recup_depensier = 0;
45
41
  let pertes_stockage_ecs_recup = 0;
46
- let pertes_stockage_ecs_recup_depensier = 0;
47
42
  let pertes_generateur_ch_recup = 0;
48
43
  let pertes_generateur_ch_recup_depensier = 0;
49
44
  let fraction_apport_gratuit_ch = 0;
50
45
  let fraction_apport_gratuit_depensier_ch = 0;
51
46
 
52
- const Qgw_total = instal_ecs.reduce((acc, instal_ecs) => {
47
+ /**
48
+ * 11.4 Plusieurs systèmes d’ECS (limité à 2 systèmes différents par logement)
49
+ * Les besoins en ECS pour chaque générateur sont / 2
50
+ */
51
+ const prorataEcs = instal_ecs.length > 1 ? 0.5 : 1;
52
+
53
+ let Qdw_total_ecs = 0;
54
+ let Qdw_total_ecs_dep = 0;
55
+ let Qgw_total_ecs = 0;
56
+ instal_ecs.forEach((instal_ecs) => {
57
+ let Qgw;
53
58
  const gen_ecs = instal_ecs.generateur_ecs_collection.generateur_ecs;
54
59
 
55
60
  // 17.2.1.1 Calcul des consommations de chauffage, de refroidissement, d’ECS et d’auxiliaires
56
61
  // Pour les installations ECS collectives, pas de récupération de stockage d'ECS
57
62
  if (Number.parseInt(instal_ecs.donnee_entree.enum_type_installation_id) !== 1) {
58
- return acc;
59
- }
60
-
61
- return (
62
- acc +
63
- gen_ecs.reduce((acc, gen_ecs) => {
63
+ Qgw = 0;
64
+ } else {
65
+ Qgw = gen_ecs.reduce((acc, gen_ecs) => {
64
66
  // Pas de récupération de stockage si le ballon est hors volume chauffé
65
67
  if (
66
68
  gen_ecs.donnee_entree.position_volume_chauffe_stockage === 0 ||
@@ -70,15 +72,21 @@ export default function calc_besoin_ch(
70
72
  }
71
73
 
72
74
  return acc + (gen_ecs.donnee_intermediaire.Qgw || 0);
73
- }, 0)
74
- );
75
- }, 0);
75
+ }, 0);
76
+ }
76
77
 
77
- /**
78
- * 11.4 Plusieurs systèmes d’ECS (limité à 2 systèmes différents par logement)
79
- * Les besoins en ECS pour chaque générateur sont / 2
80
- */
81
- const prorataEcs = instal_ecs.length > 1 ? 0.5 : 1;
78
+ Qgw_total_ecs += (0.48 * Qgw * (instal_ecs.donnee_entree.rdim || 1)) / 8760;
79
+ });
80
+
81
+ for (const mois of mois_liste) {
82
+ // en kw/h
83
+ const becsj = calc_besoin_ecs_j(ca, mois, zc, nadeq, false) * prorataEcs;
84
+ // en kw/h
85
+ const becs_j_dep = calc_besoin_ecs_j(ca, mois, zc, nadeq, true) * prorataEcs;
86
+
87
+ Qdw_total_ecs += instal_ecs.reduce((acc, ecs) => acc + calc_Qdw_j(ecs, becsj), 0);
88
+ Qdw_total_ecs_dep += instal_ecs.reduce((acc, ecs) => acc + calc_Qdw_j(ecs, becs_j_dep), 0);
89
+ }
82
90
 
83
91
  /**
84
92
  * Création de la liste des générateurs de chauffage pour lesquels il y a une récupération d'énergie
@@ -97,29 +105,17 @@ export default function calc_besoin_ch(
97
105
  )
98
106
  );
99
107
 
108
+ const besoin_ch_mois = {};
109
+ const besoin_ch_mois_dep = {};
100
110
  for (const mois of mois_liste) {
101
111
  const nref19 = Nref19[ca][mois][zc];
102
112
  const nref21 = Nref21[ca][mois][zc];
103
113
 
104
- const Qrec_stock_19 = (0.48 * nref19 * Qgw_total) / (24 * 365);
105
- const Qrec_stock_21 = (0.48 * nref21 * Qgw_total) / (24 * 365);
106
- pertes_stockage_ecs_recup += Qrec_stock_19 / 1000;
107
- pertes_stockage_ecs_recup_depensier += Qrec_stock_21 / 1000;
108
-
109
- // pertes distribution
110
- const becs_j = calc_besoin_ecs_j(ca, mois, zc, nadeq, false) * prorataEcs;
111
- const becs_j_dep = calc_besoin_ecs_j(ca, mois, zc, nadeq, true) * prorataEcs;
112
- sumNref19 += nref19;
113
- sumNref21 += nref21;
114
-
115
- QrecDistr += instal_ecs.reduce((acc, ecs) => acc + calc_Qdw_j(ecs, becs_j), 0);
116
- QrecDistrDepensier += instal_ecs.reduce((acc, ecs) => acc + calc_Qdw_j(ecs, becs_j_dep), 0);
117
-
118
114
  // bvj
119
115
  const dh19j = dh19[ca][mois][zc];
120
- sumDh19 += dh19j;
116
+ sumDh19 += dh19j * GV;
121
117
  const dh21j = dh21[ca][mois][zc];
122
- sumDh21 += dh21j;
118
+ sumDh21 += dh21j * GV;
123
119
  const aij = calc_ai_j(Sh, nadeq, nref19);
124
120
  const aij_dep = calc_ai_j(Sh, nadeq, nref21);
125
121
  const ssej = calc_sse_j(bv, ets, ca, zc, mois);
@@ -128,38 +124,46 @@ export default function calc_besoin_ch(
128
124
  const Fj = calc_Fj(GV, asj, aij, dh19j, inertie);
129
125
  const Fj_dep = calc_Fj(GV, asj, aij_dep, dh21j, inertie);
130
126
 
131
- fraction_apport_gratuit_ch += Fj * dh19j;
132
- fraction_apport_gratuit_depensier_ch += Fj_dep * dh21j;
127
+ fraction_apport_gratuit_ch += Fj * GV * dh19j;
128
+ fraction_apport_gratuit_depensier_ch += Fj_dep * GV * dh21j;
133
129
 
134
130
  const bvj = dh19j === 0 ? 0 : calc_bvj(GV, Fj);
135
131
  const bvj_dep = dh21j === 0 ? 0 : calc_bvj(GV, Fj_dep);
136
132
 
137
- // pertes generation
138
133
  const Bch_hp_j = bvj * dh19j;
139
134
  const Bch_hp_j_dep = bvj_dep * dh21j;
140
135
 
136
+ let gen_recup = 0;
137
+ let gen_recup_dep = 0;
138
+
141
139
  gen_ch_recup.forEach((gen_ch) => {
142
- pertes_generateur_ch_recup += calc_Qrec_gen_j(gen_ch, nref19, Bch_hp_j) / (1000 * 1000);
143
- pertes_generateur_ch_recup_depensier +=
144
- calc_Qrec_gen_j(gen_ch, nref21, Bch_hp_j_dep) / (1000 * 1000);
140
+ gen_recup += calc_Qrec_gen_j(gen_ch, nref19, Bch_hp_j);
141
+ gen_recup_dep += calc_Qrec_gen_j(gen_ch, nref21, Bch_hp_j_dep);
145
142
  });
146
143
 
147
- besoin_ch += Bch_hp_j / 1000;
148
- besoin_ch_depensier += Bch_hp_j_dep / 1000;
149
- }
144
+ pertes_generateur_ch_recup += gen_recup;
145
+ pertes_generateur_ch_recup_depensier += gen_recup_dep;
146
+
147
+ const pertes_distribution_ecs_recup_j = nref19 * Qdw_total_ecs * 1000;
148
+ const pertes_distribution_ecs_recup_j_dep = nref21 * Qdw_total_ecs_dep * 1000;
149
+ pertes_distribution_ecs_recup += pertes_distribution_ecs_recup_j;
150
+ pertes_distribution_ecs_recup_depensier += pertes_distribution_ecs_recup_j_dep;
150
151
 
151
- pertes_distribution_ecs_recup = (0.48 * sumNref19 * QrecDistr) / 8760;
152
- pertes_distribution_ecs_recup_depensier = (0.48 * sumNref21 * QrecDistrDepensier) / 8760;
152
+ const pertes_stockage_ecs_recup_j = nref19 * Qgw_total_ecs;
153
+ pertes_stockage_ecs_recup += pertes_stockage_ecs_recup_j;
154
+ const pertes_stockage_ecs_recup_j_dep = nref21 * Qgw_total_ecs;
153
155
 
154
- const recup =
155
- pertes_distribution_ecs_recup + pertes_stockage_ecs_recup + pertes_generateur_ch_recup;
156
- const recup_depensier =
157
- pertes_distribution_ecs_recup_depensier +
158
- pertes_stockage_ecs_recup_depensier +
159
- pertes_generateur_ch_recup_depensier;
156
+ besoin_ch_mois[mois] = bvj * dh19j;
157
+ besoin_ch_mois_dep[mois] = bvj_dep * dh21j;
158
+ besoin_ch_mois[mois] -=
159
+ pertes_distribution_ecs_recup_j + pertes_stockage_ecs_recup_j + gen_recup;
160
+ besoin_ch_mois_dep[mois] -=
161
+ pertes_distribution_ecs_recup_j_dep + pertes_stockage_ecs_recup_j_dep + gen_recup_dep;
162
+ besoin_ch_mois[mois] = Math.max(besoin_ch_mois[mois], 0);
163
+ besoin_ch += besoin_ch_mois[mois] / 1000;
160
164
 
161
- besoin_ch -= recup;
162
- besoin_ch_depensier -= recup_depensier;
165
+ besoin_ch_depensier += besoin_ch_mois_dep[mois] / 1000;
166
+ }
163
167
 
164
168
  fraction_apport_gratuit_ch /= sumDh19;
165
169
  fraction_apport_gratuit_depensier_ch /= sumDh21;
@@ -167,10 +171,10 @@ export default function calc_besoin_ch(
167
171
  return {
168
172
  besoin_ch,
169
173
  besoin_ch_depensier,
174
+ besoin_ch_mois,
170
175
  pertes_distribution_ecs_recup,
171
176
  pertes_distribution_ecs_recup_depensier,
172
177
  pertes_stockage_ecs_recup,
173
- /* pertes_stockage_ecs_recup_depensier: pertes_stockage_ecs_recup_depensier, */
174
178
  pertes_generateur_ch_recup,
175
179
  pertes_generateur_ch_recup_depensier,
176
180
  fraction_apport_gratuit_ch,
@@ -179,20 +183,17 @@ export default function calc_besoin_ch(
179
183
  }
180
184
 
181
185
  function calc_Fj(GV, asj, aij, dhj, inertie) {
182
- if (dhj == 0) return 0;
186
+ if (dhj === 0) return 0;
183
187
 
184
- let pow;
185
- if (inertie === 'très lourde' || inertie === 'lourde') pow = 3.6;
186
- else if (inertie === 'moyenne') pow = 2.9;
187
- else if (inertie === 'légère') pow = 2.5;
188
+ let alpha;
189
+ if (inertie === 'très lourde' || inertie === 'lourde') alpha = 3.6;
190
+ else if (inertie === 'moyenne') alpha = 2.9;
191
+ else if (inertie === 'légère') alpha = 2.5;
188
192
 
189
193
  const Xj = (asj + aij) / (GV * dhj);
190
- const Fj = (Xj - Xj ** pow) / (1 - Xj ** pow);
191
- /* console.warn(Fj) */
192
- return Fj;
194
+ return (Xj - Xj ** alpha) / (1 - Xj ** alpha);
193
195
  }
194
196
 
195
197
  function calc_bvj(GV, Fj) {
196
- const bvj = GV * (1 - Fj);
197
- return bvj;
198
+ return GV * (1 - Fj);
198
199
  }
package/9_chauffage.js CHANGED
@@ -9,6 +9,9 @@ import { tv_generateur_combustion } from './13.2_generateur_combustion.js';
9
9
  import { tv_temp_fonc_30_100 } from './13.2_generateur_combustion_ch.js';
10
10
  import enums from './enums.js';
11
11
 
12
+ /**
13
+ * @param dpe {FullDpe}
14
+ */
12
15
  export default function calc_chauffage(
13
16
  dpe,
14
17
  ch,
@@ -22,7 +25,8 @@ export default function calc_chauffage(
22
25
  Sh,
23
26
  hsp,
24
27
  ac,
25
- ilpa
28
+ ilpa,
29
+ besoin_ch_mois
26
30
  ) {
27
31
  const de = ch.donnee_entree;
28
32
  const di = {};
@@ -150,7 +154,11 @@ export default function calc_chauffage(
150
154
  hsp,
151
155
  ca_id,
152
156
  zc_id,
153
- ilpa
157
+ ilpa,
158
+ tbase,
159
+ besoin_ch_mois,
160
+ ch.donnee_entree.surface_chauffee,
161
+ gen_ch
154
162
  );
155
163
 
156
164
  // Si plusieurs générateurs de chauffage, la consommation des auxiliaires est répartie sur chacun d'eux
package/9_conso_ch.js CHANGED
@@ -1,6 +1,8 @@
1
- import { requestInputID } from './utils.js';
1
+ import { mois_liste, requestInputID } from './utils.js';
2
2
  import { rendement_emission } from './9_emetteur_ch.js';
3
3
  import { calc_intermittence } from './8_intermittence.js';
4
+ import tvs from './tv.js';
5
+ import enums from './enums.js';
4
6
 
5
7
  function coef_ch(Fch) {
6
8
  return {
@@ -56,9 +58,32 @@ function coef_ch(Fch) {
56
58
  };
57
59
  }
58
60
 
59
- export function conso_ch(di, de, du, _pos, cfg_ch, em_list, GV, Sh, hsp, bch, bch_dep) {
61
+ export function conso_ch(
62
+ di,
63
+ de,
64
+ du,
65
+ _pos,
66
+ cfg_ch,
67
+ em_list,
68
+ GV,
69
+ Sh,
70
+ hsp,
71
+ bch,
72
+ bch_dep,
73
+ tbase,
74
+ ilpa,
75
+ ca_id,
76
+ zc_id,
77
+ besoin_ch_mois,
78
+ s_chauffee_inst,
79
+ gen_ch_list
80
+ ) {
60
81
  const gen_lge_id = requestInputID(de, du, 'lien_generateur_emetteur');
61
82
  const coef = coef_ch(de.fch || 0.5)[cfg_ch][_pos] || 1;
83
+
84
+ let conso_ch = 0;
85
+ let conso_ch_dep;
86
+
62
87
  let em_filt;
63
88
 
64
89
  if (em_list.length === 1) {
@@ -69,27 +94,203 @@ export function conso_ch(di, de, du, _pos, cfg_ch, em_list, GV, Sh, hsp, bch, bc
69
94
  );
70
95
  }
71
96
 
72
- const hasMultipleEmetteur = em_filt.length > 1;
73
-
74
- const emetteur_eq = em_filt.reduce((acc, em) => {
75
- const int = calc_intermittence(GV, Sh, hsp, em.donnee_intermediaire.i0);
76
- const r_em = rendement_emission(em);
77
-
78
- /**
79
- * 9.1.3 Installation avec plusieurs émissions pour un même générateur
80
- * La part de la consommation traitée par chaque émetteur est proratisé par le ratio des surfaces habitables.
81
- * @type {number|number}
82
- */
83
- const ratio_s = hasMultipleEmetteur
84
- ? em.donnee_entree.surface_chauffee / de.surface_chauffee
85
- : 1;
86
-
87
- const Ich = 1 / r_em;
88
- return acc + ratio_s * int * Ich;
89
- }, 0);
90
-
91
- const Ich = emetteur_eq / di.rg;
92
- const Ich_dep = emetteur_eq / di.rg_dep;
93
- di.conso_ch = coef * Ich * bch;
94
- di.conso_ch_depensier = coef * Ich_dep * bch_dep;
97
+ switch (cfg_ch) {
98
+ case 'installation de chauffage collectif avec base + appoint': {
99
+ calc_ch_base_appoint(
100
+ di,
101
+ de,
102
+ bch,
103
+ besoin_ch_mois,
104
+ zc_id,
105
+ ca_id,
106
+ em_list,
107
+ gen_ch_list,
108
+ _pos,
109
+ GV,
110
+ Sh,
111
+ hsp,
112
+ s_chauffee_inst,
113
+ ilpa,
114
+ tbase
115
+ );
116
+ break;
117
+ }
118
+ case 'installation de chauffage avec chaudière en relève de pac avec insert ou poêle bois en appoint': {
119
+ calc_ch_pac_insert_bois(di, de, em_list, _pos, bch, GV, Sh, hsp, gen_ch_list);
120
+ break;
121
+ }
122
+ default: {
123
+ const hasMultipleEmetteur = em_filt.length > 1;
124
+
125
+ const emetteur_eq = em_filt.reduce((acc, em) => {
126
+ const int = calc_intermittence(GV, Sh, hsp, em.donnee_intermediaire.i0);
127
+ const r_em = rendement_emission(em);
128
+
129
+ /**
130
+ * 9.1.3 Installation avec plusieurs émissions pour un même générateur
131
+ * La part de la consommation traitée par chaque émetteur est proratisé par le ratio des surfaces habitables.
132
+ * @type {number|number}
133
+ */
134
+ const ratio_s = hasMultipleEmetteur
135
+ ? em.donnee_entree.surface_chauffee / de.surface_chauffee
136
+ : 1;
137
+
138
+ const Ich = 1 / r_em;
139
+ return acc + ratio_s * int * Ich;
140
+ }, 0);
141
+
142
+ const Ich = emetteur_eq / di.rg;
143
+ const Ich_dep = emetteur_eq / di.rg_dep;
144
+ conso_ch = coef * Ich * bch;
145
+ conso_ch_dep = coef * Ich_dep * bch_dep;
146
+
147
+ di.conso_ch = conso_ch;
148
+ di.conso_ch_depensier = conso_ch_dep;
149
+ break;
150
+ }
151
+ }
152
+ }
153
+
154
+ /**
155
+ * 9.8 Installation de chauffage collectif avec base + appoint
156
+ */
157
+ function calc_ch_base_appoint(
158
+ di,
159
+ de,
160
+ bch,
161
+ besoin_ch_mois,
162
+ zc_id,
163
+ ca_id,
164
+ em_list,
165
+ gen_ch_list,
166
+ _pos,
167
+ GV,
168
+ Sh,
169
+ hsp,
170
+ s_chauffee_inst,
171
+ ilpa,
172
+ tbase
173
+ ) {
174
+ if (bch > 0) {
175
+ let bch_base = 0;
176
+
177
+ const zc = enums.zone_climatique[zc_id];
178
+ const ca = enums.classe_altitude[ca_id];
179
+
180
+ const emBase = em_list[0];
181
+ const genBase = gen_ch_list[0];
182
+ const emAppoint = em_list[1];
183
+ const genAppoint = gen_ch_list[1];
184
+ const em = _pos === 0 ? emBase : emAppoint;
185
+ const gen = _pos === 0 ? genBase : genAppoint;
186
+ const isAppoint = _pos !== 0;
187
+ const Int = calc_intermittence(GV, Sh, hsp, em.donnee_intermediaire.i0);
188
+ const Rend = rendement_emission(em);
189
+ const Ich = 1 / (gen.donnee_intermediaire.rendement_generation || 1);
190
+
191
+ const rd = emBase.donnee_intermediaire.rendement_distribution;
192
+ const rr = emBase.donnee_intermediaire.rendement_regulation;
193
+ const re = emBase.donnee_intermediaire.rendement_emission;
194
+
195
+ const pn = genBase.donnee_intermediaire.pn;
196
+ const pe = pn > 0 ? (pn / 1000) * rd * rr * re : 0;
197
+
198
+ const dh14 = tvs.dh14;
199
+ const text = tvs.text;
200
+ const nrefs = tvs.nref19;
201
+ const idx_ilpa = ilpa ? 1 : 0;
202
+
203
+ const dh14Saison = mois_liste.reduce((acc, mois) => acc + dh14[idx_ilpa][ca][mois][zc], 0);
204
+ const rdim = de.rdim || 1;
205
+ const ratio_s = (s_chauffee_inst * rdim) / Sh;
206
+ const bch1 = (bch * ratio_s) / rdim;
207
+ const t = 14 - (pe * dh14Saison) / bch1;
208
+
209
+ di.conso_ch = 0;
210
+ for (const mois of mois_liste) {
211
+ let bch_base_j = 0;
212
+
213
+ if (pe > 0) {
214
+ // Nombre d’heures de chauffage sur le mois j
215
+ const nrefj = nrefs[idx_ilpa][ca][mois][zc];
216
+ // Température extérieure moyenne dans la zone climatique sur le mois j (°C)
217
+ const textj = text[idx_ilpa][ca][mois][zc];
218
+ // Degrés heures de base 14 pour le mois j (°Ch)
219
+ const dh14j = dh14[idx_ilpa][ca][mois][zc];
220
+
221
+ const xj = 0.5 * ((t - tbase) / (textj - tbase));
222
+
223
+ // Degré heure base T sur le mois j
224
+ let dhtj =
225
+ nrefj *
226
+ (textj - tbase) *
227
+ Math.pow(xj, 5) *
228
+ (14 - 28 * xj + 20 * Math.pow(xj, 2) - 5 * Math.pow(xj, 3));
229
+ if (dhtj < 0) {
230
+ dhtj = 0;
231
+ }
232
+
233
+ let ratioDh = dh14j > 0 ? 1 - dhtj / dh14j : 0;
234
+ if (ratioDh <= 0) {
235
+ bch_base_j = 0;
236
+ } else {
237
+ bch_base_j = besoin_ch_mois[mois] * ratioDh;
238
+ }
239
+ } else {
240
+ bch_base_j = besoin_ch_mois[mois] * 0.5;
241
+ }
242
+ bch_base += bch_base_j;
243
+ }
244
+ if (!isAppoint) {
245
+ const ratio_s_em = em.donnee_entree.surface_chauffee / de.surface_chauffee;
246
+ di.conso_ch = (ratio_s_em * (bch_base / 1000) * Ich * Int) / Rend;
247
+ } else {
248
+ const ratio_s_em =
249
+ em.donnee_entree.surface_chauffee / emAppoint.donnee_entree.surface_chauffee;
250
+ di.conso_ch = (ratio_s_em * (bch - bch_base / 1000) * Int * Ich) / Rend;
251
+ }
252
+
253
+ di.conso_ch = Math.max(di.conso_ch, 0);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * 9.7 Installation de chauffage avec chaudière en relève de PAC avec insert ou
259
+ * poêle bois en appoint
260
+ *
261
+ * @param di {Donnee_intermediaire}
262
+ * @param de {Donnee_entree}
263
+ * @param em_list {EmetteurChauffageItem[]}
264
+ * @param bch {number}
265
+ * @param _pos {number}
266
+ * @param GV {number}
267
+ * @param Sh {number}
268
+ * @param hsp {number}
269
+ * @param gen_ch_list {GenerateurChauffageItem[]}
270
+ */
271
+ function calc_ch_pac_insert_bois(di, de, em_list, _pos, bch, GV, Sh, hsp, gen_ch_list) {
272
+ if (bch > 0) {
273
+ const Ich1 = 1 / (gen_ch_list[0].donnee_intermediaire.rendement_generation || 1);
274
+ const Ich2 = 1 / (gen_ch_list[1].donnee_intermediaire.rendement_generation || 1);
275
+ const Ich3 = 1 / (gen_ch_list[2].donnee_intermediaire.rendement_generation || 1);
276
+
277
+ em_list.forEach((em) => {
278
+ const ratio_s_em = em.donnee_entree.surface_chauffee / de.surface_chauffee;
279
+ const Int = calc_intermittence(GV, Sh, hsp, em.donnee_intermediaire.i0);
280
+ const Rend = rendement_emission(em);
281
+
282
+ if (em.donnee_entree.enum_lien_generateur_emetteur_id === '1' && (_pos === 0 || _pos === 1)) {
283
+ if (_pos === 0) {
284
+ di.conso_ch = (ratio_s_em * Ich1 * bch * Int * 0.8 * 0.75) / Rend;
285
+ }
286
+ if (_pos === 1) {
287
+ di.conso_ch = (ratio_s_em * Ich2 * bch * Int * 0.8 * 0.25) / Rend;
288
+ }
289
+ } else if (_pos === 2) {
290
+ di.conso_ch = ratio_s_em * ((Ich3 * bch * Int * 0.25) / Rend);
291
+ }
292
+ });
293
+
294
+ console.log(di.conso_ch);
295
+ }
95
296
  }
package/9_emetteur_ch.js CHANGED
@@ -3,11 +3,11 @@ import { TvsStore } from './core/tv/infrastructure/tvs.store.js';
3
3
 
4
4
  const tvsStore = new TvsStore();
5
5
 
6
- export function rendement_emission(em) {
6
+ export function rendement_emission(em, rg = 1) {
7
7
  const re = em.donnee_intermediaire.rendement_emission;
8
8
  const rd = em.donnee_intermediaire.rendement_distribution;
9
9
  const rr = em.donnee_intermediaire.rendement_regulation;
10
- return re * rd * rr;
10
+ return rg * re * rd * rr;
11
11
  }
12
12
 
13
13
  function tv_rendement_distribution_ch(di, de) {
@@ -6,14 +6,15 @@ import { calc_generateur_combustion_ch } from './13.2_generateur_combustion_ch.j
6
6
  import { scopOrCop } from './12.4_pac.js';
7
7
  import { updateGenerateurCombustion } from './13.2_generateur_combustion.js';
8
8
 
9
+ const ENUM_MATERIAUX_STRUCTURE_MUR_ANCIEN_IDS = ['2', '3', '4', '6', '8', '9', '14', '21'];
10
+ const ENUM_CLASSES_INERTIE_LOURDES_IDS = ['1', '2'];
11
+
9
12
  function pertes_gen_ch(Bch_hp_j, pn) {
10
- const pertes = (1.3 * Bch_hp_j) / (0.3 * pn);
11
- return pertes;
13
+ return (1.3 * Bch_hp_j) / (0.3 * pn);
12
14
  }
13
15
 
14
16
  function pertes_gen_ecs(nrefj) {
15
- const pertes = (nrefj * 1790) / 8760;
16
- return pertes;
17
+ return (nrefj * 1790) / 8760;
17
18
  }
18
19
 
19
20
  export function calc_Qrec_gen_j(gen_ch, nrefj, Bch_hp_j) {
@@ -40,8 +41,7 @@ export function calc_Qrec_gen_j(gen_ch, nrefj, Bch_hp_j) {
40
41
  }
41
42
 
42
43
  gen_ch.donnee_utilisateur = du;
43
- const Qrec_gen_j = 0.48 * Cper * di.qp0 * Dperj || 0;
44
- return Qrec_gen_j;
44
+ return 0.48 * Cper * di.qp0 * Dperj || 0;
45
45
  }
46
46
 
47
47
  function tv_rendement_generation(di, de, du) {
@@ -159,7 +159,11 @@ export function calc_generateur_ch(
159
159
  hsp,
160
160
  ca_id,
161
161
  zc_id,
162
- ilpa
162
+ ilpa,
163
+ tbase,
164
+ besoin_ch_mois,
165
+ s_chauffee_inst,
166
+ gen_ch_list
163
167
  ) {
164
168
  const de = gen_ch.donnee_entree;
165
169
  const du = gen_ch.donnee_utilisateur || {};
@@ -201,8 +205,27 @@ export function calc_generateur_ch(
201
205
  if (hasConsoForAuxDistribution(de.enum_type_generateur_ch_id)) {
202
206
  conso_aux_distribution_ch(em_ch, de, di, du, Sh, zc_id, ca_id, ilpa, GV);
203
207
  }
204
-
205
- conso_ch(di, de, du, _pos, cfg_ch, em_ch, GV, Sh, hsp, bch, bch_dep);
208
+ const paroi_ancienne = isParoisAncienneInertieLourde(dpe);
209
+ conso_ch(
210
+ di,
211
+ de,
212
+ du,
213
+ _pos,
214
+ cfg_ch,
215
+ em_ch,
216
+ GV,
217
+ Sh,
218
+ hsp,
219
+ bch,
220
+ bch_dep,
221
+ tbase,
222
+ paroi_ancienne,
223
+ ca_id,
224
+ zc_id,
225
+ besoin_ch_mois,
226
+ s_chauffee_inst,
227
+ gen_ch_list
228
+ );
206
229
 
207
230
  gen_ch.donnee_intermediaire = di;
208
231
  gen_ch.donnee_utilisateur = du;
@@ -228,3 +251,32 @@ export function hasConsoForAuxDistribution(enum_type_generateur_ch_id) {
228
251
  (enum_type_generateur_ch_id >= 4 && enum_type_generateur_ch_id <= 19)
229
252
  );
230
253
  }
254
+
255
+ /**
256
+ * 18.3 Cas des bâtiments à inertie lourde, constitués de parois anciennes
257
+ *
258
+ * Afin d’être considéré comme un bâtiment à inertie lourde, constitués de parois anciennes, le bâtiment doit :
259
+ * * Etre constitué de murs en matériaux anciens : terre, pierre, brique ancienne, colombage, … ;
260
+ * * Avoir une inertie lourde.
261
+ *
262
+ * En présence de plusieurs types de parois, le bâtiment sera considéré comme constitué de parois anciennes si la surface
263
+ * de parois anciennes est majoritaire.
264
+ * @param dpe {FullDpe}
265
+ */
266
+ function isParoisAncienneInertieLourde(dpe) {
267
+ const murs = dpe.logement.enveloppe.mur_collection.mur.filter(
268
+ (mur) => mur.donnee_intermediaire.b > 0
269
+ );
270
+ const nbTotalMurs = murs.length;
271
+ const nbMursAnciens = murs.filter((mur) =>
272
+ ENUM_MATERIAUX_STRUCTURE_MUR_ANCIEN_IDS.includes(
273
+ mur.donnee_entree.enum_materiaux_structure_mur_id
274
+ )
275
+ ).length;
276
+
277
+ const isInertieLourde = ENUM_CLASSES_INERTIE_LOURDES_IDS.includes(
278
+ dpe.logement.enveloppe.inertie.enum_classe_inertie_id
279
+ );
280
+
281
+ return isInertieLourde && nbMursAnciens / nbTotalMurs >= 0.5;
282
+ }
@@ -3,6 +3,11 @@ import { describe, expect, test } from 'vitest';
3
3
 
4
4
  describe('Recherche de bugs dans le calcul de la consommation des generateurs de chauffage', () => {
5
5
  test('calcul de la consommation des generateurs de chauffage pour 2475E2510509B', () => {
6
+ const dpe = {
7
+ logement: {
8
+ enveloppe: { inertie: { enum_classe_inertie_id: '5' }, mur_collection: { mur: [] } }
9
+ }
10
+ };
6
11
  const gen_ch = {
7
12
  donnee_entree: {
8
13
  description: 'Electrique - Convecteur électrique NFC, NF** et NF***',
@@ -60,6 +65,7 @@ describe('Recherche de bugs dans le calcul de la consommation des generateurs de
60
65
  const ac = 1;
61
66
 
62
67
  calc_generateur_ch(
68
+ dpe,
63
69
  gen_ch,
64
70
  _pos,
65
71
  em_ch,
package/README.md CHANGED
@@ -110,11 +110,10 @@ const result = calcul_3cl(dpeData);
110
110
 
111
111
  ## Variables d'environnements
112
112
 
113
- | Nom | Description |
114
- | ----------------------- | ---------------------------------------------------------- |
115
- | ADEME_API_CLIENT_ID | Client id pour l'api de l'ademe |
116
- | ADEME_API_CLIENT_SECRET | Client secret pour l'api de l'ademe |
117
- | CORPUS_DPES_FILE_PATH | Chemin vers le dossier contenant tous les DPES téléchargés |
113
+ | Nom | Description |
114
+ | ----------------------- | ----------------------------------- |
115
+ | ADEME_API_CLIENT_ID | Client id pour l'api de l'ademe |
116
+ | ADEME_API_CLIENT_SECRET | Client secret pour l'api de l'ademe |
118
117
 
119
118
  Attention aux quotas sur l'api:
120
119
 
@@ -167,12 +166,16 @@ deperdition_mur,
167
166
 
168
167
  Résultats des tests de corpus avec le mode de compatibilité activé.
169
168
 
170
- | Version librairie | corpus | Taux d'erreur | Nb de DPES analysés | Nb en dessous tu taux d'erreur | Taux de réussite | Détail des valeurs |
169
+ | Version librairie | corpus | Taux d'erreur | Nb de DPES analysés | Nb en dessous du taux d'erreur | Taux de réussite | Détail des valeurs |
171
170
  | :---------------- | :------------------------------------ | :------------ | ------------------- | ------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------- |
172
- | 1.2.3 | corpus_dpe.csv | 5% | 9980 | 4489 | 45% | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=corpus_dpe.csv) |
173
- | 1.2.3 | dpe_immeuble_chauffage_individuel.csv | 5% | 9998 | 3257 | 32% | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_individuel.csv) |
174
- | 1.2.3 | dpe_immeuble_chauffage_collectif.csv | 5% | 10000 | 5279 | 53% | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_collectif.csv) |
175
- | 1.2.3 | dpe_immeuble_chauffage_mixte.csv | 5% | 10000 | 2728 | 27% | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_mixte.csv) |
171
+ | 1.2.3 | corpus_dpe.csv | 5% | 9980 | 4489 | 45% | |
172
+ | 1.2.3 | dpe_immeuble_chauffage_individuel.csv | 5% | 9998 | 3257 | 32% | |
173
+ | 1.2.3 | dpe_immeuble_chauffage_collectif.csv | 5% | 10000 | 5279 | 53% | |
174
+ | 1.2.3 | dpe_immeuble_chauffage_mixte.csv | 5% | 10000 | 2728 | 27% | |
175
+ | 1.2.8 | corpus_dpe.csv | 5% | 9980 | 4489 | 45% | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=corpus_dpe.csv) |
176
+ | 1.2.8 | dpe_immeuble_chauffage_individuel.csv | 5% | 9998 | 5275 (+2018) | 53% (+21%) | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_individuel.csv) |
177
+ | 1.2.8 | dpe_immeuble_chauffage_collectif.csv | 5% | 10000 | 5747 (+468) | 57% (+4%) | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_collectif.csv) |
178
+ | 1.2.8 | dpe_immeuble_chauffage_mixte.csv | 5% | 10000 | 3142 (+414) | 31% (+4%) | [Voir le détail](https://open3cl.github.io/engine/reports/corpus?corpus_file=dpe_immeuble_chauffage_mixte.csv) |
176
179
 
177
180
  ## Roadmap
178
181
 
package/conso.js CHANGED
@@ -300,7 +300,7 @@ export function classe_bilan_dpe(ep_conso_5_usages_m2, zc_id, ca_id, Sh) {
300
300
 
301
301
  const cut = tvs.dpe_class_limit[ca][Math.round(Sh)] ?? [];
302
302
 
303
- if (!ep_conso_5_usages_m2) return null;
303
+ if (ep_conso_5_usages_m2 == null) return null;
304
304
  if (ep_conso_5_usages_m2 < (cut['A'] ?? 70)) return 'A';
305
305
  if (ep_conso_5_usages_m2 < (cut['B'] ?? 110)) return 'B';
306
306
  if (ep_conso_5_usages_m2 < (cut['C'] ?? 180)) return 'C';
@@ -323,7 +323,7 @@ export function classe_emission_ges(emission_ges_5_usages_m2, zc_id, ca_id, Sh)
323
323
 
324
324
  const cut = tvs.ges_class_limit[ca][Math.round(Sh)] ?? [];
325
325
 
326
- if (!emission_ges_5_usages_m2) return null;
326
+ if (emission_ges_5_usages_m2 == null) return null;
327
327
  if (emission_ges_5_usages_m2 < (cut['A'] ?? 6)) return 'A';
328
328
  if (emission_ges_5_usages_m2 < (cut['B'] ?? 11)) return 'B';
329
329
  if (emission_ges_5_usages_m2 < (cut['C'] ?? 30)) return 'C';
package/engine.js CHANGED
@@ -434,6 +434,8 @@ export function calcul_3cl(dpe) {
434
434
 
435
435
  const bch = apport_et_besoin.besoin_ch;
436
436
  const bch_dep = apport_et_besoin.besoin_ch_depensier;
437
+ const besoin_ch_mois = { ...apport_et_besoin.besoin_ch_mois };
438
+ delete apport_et_besoin.besoin_ch_mois;
437
439
 
438
440
  /**
439
441
  * 13.2.1.2 Présence d’un ou plusieurs générateurs à combustion indépendants
@@ -455,7 +457,8 @@ export function calcul_3cl(dpe) {
455
457
  ShChauffageAndEcs,
456
458
  hsp,
457
459
  ac,
458
- ilpa
460
+ ilpa,
461
+ besoin_ch_mois
459
462
  );
460
463
  });
461
464
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open3cl/engine",
3
- "version": "1.2.8",
3
+ "version": "1.3.0",
4
4
  "description": "Open Source 3CL-DPE engine",
5
5
  "main": "index.js",
6
6
  "directories": {
package/utils.js CHANGED
@@ -57,6 +57,7 @@ export const Njj = {
57
57
  // sum all Njj values
58
58
  export const Njj_sum = Object.values(Njj).reduce((acc, val) => acc + val, 0);
59
59
 
60
+ /** @type {string[]} **/
60
61
  export const mois_liste = [
61
62
  'Janvier',
62
63
  'Février',