@open3cl/engine 1.0.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.
Files changed (85) hide show
  1. package/10_besoin_fr.js +76 -0
  2. package/10_clim.js +45 -0
  3. package/11_besoin_ecs.js +25 -0
  4. package/11_ecs.js +95 -0
  5. package/11_nadeq.js +87 -0
  6. package/11_nadeq.spec.js +55 -0
  7. package/12.4_pac.js +54 -0
  8. package/13.2_generateur_combustion.js +295 -0
  9. package/13.2_generateur_combustion_bouilleur.js +173 -0
  10. package/13.2_generateur_combustion_ch.js +195 -0
  11. package/13.2_generateur_combustion_chaudiere.js +151 -0
  12. package/13.2_generateur_pac.js +36 -0
  13. package/13_rendement_distribution_ecs.js +12 -0
  14. package/14_generateur_ecs.js +388 -0
  15. package/15_conso_aux.js +257 -0
  16. package/16.2_production_enr.js +328 -0
  17. package/16.2_production_enr.spec.js +251 -0
  18. package/16_conso_eclairage.js +37 -0
  19. package/2021_04_13_confort_ete.js +61 -0
  20. package/2021_04_13_qualite_isolation.js +174 -0
  21. package/3.1_b.js +141 -0
  22. package/3.2.1_mur.js +331 -0
  23. package/3.2.1_mur.spec.js +46 -0
  24. package/3.2.2_plancher_bas.js +259 -0
  25. package/3.2.2_plancher_bas.spec.js +88 -0
  26. package/3.2.3_plancher_haut.js +158 -0
  27. package/3.3.1.4_porte.js +32 -0
  28. package/3.3_baie_vitree.js +308 -0
  29. package/3.3_baie_vitree.spec.js +333 -0
  30. package/3.4_pont_thermique.js +463 -0
  31. package/3_deperdition.js +258 -0
  32. package/4_ventilation.js +197 -0
  33. package/5_conso_ventilation.js +127 -0
  34. package/6.1_apport_gratuit.js +61 -0
  35. package/6.1_apport_gratuit.spec.js +181 -0
  36. package/6.2_surface_sud_equivalente.js +109 -0
  37. package/7_inertie.js +178 -0
  38. package/7_inertie.spec.js +263 -0
  39. package/8_intermittence.js +5 -0
  40. package/9_besoin_ch.js +198 -0
  41. package/9_chauffage.js +291 -0
  42. package/9_chauffage.spec.js +101 -0
  43. package/9_conso_ch.js +95 -0
  44. package/9_conso_ch.spec.js +255 -0
  45. package/9_emetteur_ch.js +122 -0
  46. package/9_generateur_ch.js +230 -0
  47. package/9_generateur_ch.spec.js +87 -0
  48. package/README.md +43 -0
  49. package/apport_et_besoin.js +55 -0
  50. package/conso.js +529 -0
  51. package/conso.spec.js +90 -0
  52. package/core/assets/domain/add-additionnal-ue-values-tables.js +57 -0
  53. package/core/assets/domain/synchronize-assets.js +29 -0
  54. package/core/assets/domain/synchronize-assets.spec.js +37 -0
  55. package/core/assets/domain/synchronize-c1-tables.js +61 -0
  56. package/core/assets/domain/synchronize-c1-tables.spec.js +35 -0
  57. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.js +73 -0
  58. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +72 -0
  59. package/core/assets/domain/synchronize-enum-tables.js +77 -0
  60. package/core/assets/domain/synchronize-enum-tables.spec.js +31 -0
  61. package/core/assets/domain/synchronize-solicitations-tables.js +72 -0
  62. package/core/assets/domain/synchronize-solicitations-tables.spec.js +47 -0
  63. package/core/assets/domain/synchronize-valeur-tables.js +146 -0
  64. package/core/assets/domain/synchronize-valeur-tables.spec.js +54 -0
  65. package/core/conf/infrastructure/application.config.js +33 -0
  66. package/core/file/infrastructure/adapter/file.store.js +75 -0
  67. package/core/file/infrastructure/adapter/file.store.spec.js +30 -0
  68. package/core/tv/infrastructure/assets/additional-ue-values.js +69 -0
  69. package/core/tv/infrastructure/tvs.store.js +40 -0
  70. package/core/tv/infrastructure/tvs.store.spec.js +34 -0
  71. package/core/util/infrastructure/object-util.js +23 -0
  72. package/core/util/infrastructure/object-util.spec.js +25 -0
  73. package/engine.js +503 -0
  74. package/enums.js +1155 -0
  75. package/ficheTechnique.js +86 -0
  76. package/ficheTechnique.spec.js +181 -0
  77. package/index.js +4 -0
  78. package/package.json +87 -0
  79. package/tv/18.2_sollicitations_ext.ods +0 -0
  80. package/tv/18.5_c1.ods +0 -0
  81. package/tv/dpe_ges_limit_values.ods +0 -0
  82. package/tv.js +80811 -0
  83. package/tvs.d.ts +7 -0
  84. package/utils.js +500 -0
  85. package/utils.spec.js +36 -0
@@ -0,0 +1,88 @@
1
+ import calc_pb from './3.2.2_plancher_bas.js';
2
+
3
+ describe('Recherche de bugs dans le calcul de déperdition des planchers bas', () => {
4
+ describe('calcul de déperdition pour les planchers bas de 2287E1043883T', () => {
5
+ test('Mur 1 Nord', () => {
6
+ const zc = 3;
7
+ const pc_id = 1;
8
+ const ej = 0;
9
+ const pb1 = {
10
+ donnee_entree: {
11
+ description:
12
+ 'Plancher 1 - Plancher lourd type entrevous terre-cuite, poutrelles béton donnant sur un sous-sol non chauffé avec isolation intrinsèque ou en sous-face (5 cm)',
13
+ reference: '2022_05_11_17_10_23_1067244006611344',
14
+ tv_coef_reduction_deperdition_id: 6,
15
+ enum_type_adjacence_id: '6',
16
+ surface_paroi_opaque: 42.08,
17
+ tv_upb0_id: 11,
18
+ enum_type_plancher_bas_id: '11',
19
+ enum_methode_saisie_u0_id: '2',
20
+ enum_type_isolation_id: '4',
21
+ epaisseur_isolation: 5,
22
+ enum_methode_saisie_u_id: '3',
23
+ calcul_ue: 1,
24
+ perimetre_ue: 27,
25
+ surface_ue: 42.08,
26
+ ue: 0.37117494
27
+ },
28
+ donnee_intermediaire: {
29
+ b: 1,
30
+ upb: 0.5915492957746479,
31
+ upb_final: 0.37117494,
32
+ upb0: 2
33
+ }
34
+ };
35
+
36
+ const pb = {
37
+ donnee_entree: {
38
+ description: 'Plancher 2 - Dalle béton donnant sur un terre-plein',
39
+ reference: '2022_05_11_17_10_51_2415353009491032',
40
+ tv_coef_reduction_deperdition_id: 5,
41
+ enum_type_adjacence_id: '5', // Terre-Plein
42
+ surface_paroi_opaque: 24.34,
43
+ tv_upb0_id: 9,
44
+ enum_type_plancher_bas_id: '9', // Dalle béton
45
+ enum_methode_saisie_u0_id: '2', // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
46
+ enum_type_isolation_id: '1', // inconnu
47
+ enum_periode_isolation_id: '1', // avant 1948
48
+ tv_upb_id: 2,
49
+ enum_methode_saisie_u_id: '8', // année de construction saisie (table forfaitaire)
50
+ calcul_ue: 1,
51
+ perimetre_ue: 22,
52
+ surface_ue: 24.34,
53
+ ue: 0.61789474
54
+ },
55
+ donnee_intermediaire: {
56
+ b: 1,
57
+ upb: 2,
58
+ upb_final: 0.61789474,
59
+ upb0: 2
60
+ }
61
+ };
62
+ const pb_list = [pb1, pb];
63
+
64
+ /**
65
+ * Sortie lib (différence)
66
+ * tv_upb_id: 8 (2 dans dpe origine)
67
+ * ue: 0.44 (0.61789474 dans dpe origine)
68
+ * upb: 0.9 (2 dans dpe origine)
69
+ * upb_final: 0.44 (0.61789474 dans dpe origine)
70
+ */
71
+ calc_pb(pb, zc, pc_id, ej, pb_list);
72
+
73
+ /**
74
+ * P = 22
75
+ * S = 24.34
76
+ * 2S/P = (2 * 24.34) / 22 = 2,212727273 = 2 (arrondi à l'entier le plus proche)
77
+ * upb0 = 2
78
+ * upb = Min(upb0; upbtab) = Min(2; 2) = 2
79
+ * ue = Umoyen pour tous les planchers du batiment
80
+ * upb_final = ue
81
+ */
82
+ expect(pb.donnee_intermediaire.b).toBe(1);
83
+ expect(pb.donnee_intermediaire.upb0).toBe(2);
84
+ expect(pb.donnee_intermediaire.upb).toBe(2);
85
+ expect(pb.donnee_intermediaire.upb_final).toBe(pb.donnee_entree.ue);
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,158 @@
1
+ import enums from './enums.js';
2
+ import b from './3.1_b.js';
3
+ import { tv, requestInput, getKeyByValue, bug_for_bug_compat } from './utils.js';
4
+
5
+ function tv_uph0(di, de, du) {
6
+ requestInput(de, du, 'type_plancher_haut');
7
+ const matcher = {
8
+ enum_type_plancher_haut_id: de.enum_type_plancher_haut_id
9
+ };
10
+ const row = tv('uph0', matcher, de);
11
+ if (row) {
12
+ di.uph0 = Number(row.uph0);
13
+ de.tv_uph0_id = Number(row.tv_uph0_id);
14
+ } else {
15
+ console.error('!! pas de valeur forfaitaire trouvée pour uph0 !!');
16
+ }
17
+ }
18
+
19
+ function tv_uph(di, de, du, pc_id, zc, effetJoule) {
20
+ const type_adjacence = requestInput(de, du, 'type_adjacence');
21
+ const type_ph = requestInput(de, du, 'type_plancher_haut');
22
+ let type_toiture;
23
+
24
+ // From "3CL-DPE 2021" page 21 => "Lorsque le local au-dessus du logement est un local non chauffé, ou un local autre que d’habitation., Uph_tab est pris dans la catégorie « Terrasse »."
25
+ if (type_adjacence === 'locaux non chauffés non accessible') {
26
+ type_toiture = 'terrasse';
27
+ } else if (type_adjacence !== 'extérieur') {
28
+ type_toiture = 'combles';
29
+ } else {
30
+ if (type_ph === 'combles aménagés sous rampant') type_toiture = 'combles';
31
+ else type_toiture = 'terrasse';
32
+ }
33
+
34
+ if (bug_for_bug_compat && de.tv_uph_id) {
35
+ /**
36
+ * Pour certains DPE le type d'adjacence du plancher haut n'est pas bien défini.
37
+ * On va le récupérer depuis les données d'entrée si besoin
38
+ */
39
+ const rowUph = tv('uph', {
40
+ tv_uph_id: de.tv_uph_id
41
+ });
42
+
43
+ if (rowUph) {
44
+ if (rowUph.type_toiture === 'combles' && rowUph.type_toiture !== type_toiture) {
45
+ console.error(
46
+ `Le type d'adjacence pour le plancher haut ${de.description} ne correspond pas aux données saisies (${rowUph.type_toiture})`
47
+ );
48
+ type_toiture = rowUph.type_toiture;
49
+ }
50
+
51
+ if (rowUph.effet_joule !== effetJoule) {
52
+ console.error(
53
+ `La variable effet_joule utilisée dans le DPE pour le plancher haut '${de.description}' est ${rowUph.effet_joule}.
54
+ Celle-ci devrait être ${effetJoule}. La valeur ${rowUph.effet_joule} est conservée dans la suite des calculs`
55
+ );
56
+
57
+ effetJoule = rowUph.effet_joule;
58
+ }
59
+ }
60
+ }
61
+
62
+ const matcher = {
63
+ enum_periode_construction_id: pc_id,
64
+ enum_zone_climatique_id: zc,
65
+ effet_joule: effetJoule,
66
+ type_toiture
67
+ };
68
+ const row = tv('uph', matcher);
69
+ if (row) {
70
+ di.uph = Number(row.uph);
71
+ de.tv_uph_id = Number(row.tv_uph_id);
72
+ } else {
73
+ console.error('!! pas de valeur forfaitaire trouvée pour uph !!');
74
+ }
75
+ }
76
+
77
+ function calc_uph0(di, de, du) {
78
+ const methode_saisie_u0 = requestInput(de, du, 'methode_saisie_u0');
79
+ switch (methode_saisie_u0) {
80
+ case 'type de paroi inconnu (valeur par défaut)':
81
+ case 'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire':
82
+ tv_uph0(di, de, du);
83
+ break;
84
+ case 'saisie direct u0 justifiée à partir des documents justificatifs autorisés':
85
+ case "saisie direct u0 correspondant à la performance de la paroi avec son isolation antérieure iti (umur_iti) lorsqu'il y a une surisolation ite réalisée":
86
+ di.uph0 = requestInput(de, du, 'uph0_saisi');
87
+ break;
88
+ case 'u0 non saisi car le u est saisi connu et justifié.':
89
+ break;
90
+ default:
91
+ console.warn('methode_saisie_u0 inconnue:', methode_saisie_u0);
92
+ }
93
+ }
94
+
95
+ export default function calc_ph(ph, zc, pc_id, effetJoule) {
96
+ const de = ph.donnee_entree;
97
+ const du = {};
98
+ const di = {};
99
+ di.uph0 = ph.donnee_intermediaire.uph0;
100
+
101
+ b(di, de, du, zc);
102
+
103
+ const methode_saisie_u = requestInput(de, du, 'methode_saisie_u');
104
+
105
+ switch (methode_saisie_u) {
106
+ case 'non isolé':
107
+ calc_uph0(di, de, du);
108
+ di.uph = di.uph0;
109
+ break;
110
+ case 'epaisseur isolation saisie justifiée par mesure ou observation':
111
+ case 'epaisseur isolation saisie justifiée à partir des documents justificatifs autorisés': {
112
+ const e = requestInput(de, du, 'epaisseur_isolation', 'int') * 0.01;
113
+ calc_uph0(di, de, du);
114
+ di.uph = 1 / (1 / di.uph0 + e / 0.04);
115
+ break;
116
+ }
117
+ case "resistance isolation saisie justifiée observation de l'isolant installé et mesure de son épaisseur":
118
+ case 'resistance isolation saisie justifiée à partir des documents justificatifs autorisés': {
119
+ const r = requestInput(de, du, 'resistance_isolation', 'float');
120
+ calc_uph0(di, de, du);
121
+ di.uph = 1 / (1 / di.uph0 + r);
122
+ break;
123
+ }
124
+ case 'isolation inconnue (table forfaitaire)':
125
+ case "année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)":
126
+ tv_uph(di, de, du, de.enum_periode_isolation_id || pc_id, zc, effetJoule);
127
+ calc_uph0(di, de, du);
128
+ di.uph = Math.min(di.uph, di.uph0);
129
+ break;
130
+ case 'année de construction saisie (table forfaitaire)': {
131
+ let pi_id = pc_id;
132
+ if (de.enum_periode_isolation_id) {
133
+ pi_id = de.enum_periode_isolation_id;
134
+ } else {
135
+ const pc = enums.periode_construction[pc_id];
136
+ switch (pc) {
137
+ case 'avant 1948':
138
+ case '1948-1974':
139
+ pi_id = getKeyByValue(enums.periode_isolation, '1975-1977');
140
+ break;
141
+ }
142
+ }
143
+ calc_uph0(di, de, du);
144
+ tv_uph(di, de, du, pi_id, zc, effetJoule);
145
+ di.uph = Math.min(di.uph, di.uph0);
146
+ break;
147
+ }
148
+ case 'saisie direct u justifiée (à partir des documents justificatifs autorisés)':
149
+ case 'saisie direct u depuis rset/rsee( etude rt2012/re2020)':
150
+ di.uph = requestInput(de, du, 'uph_saisi', 'float');
151
+ break;
152
+ default:
153
+ console.warn('methode_saisie_u inconnue:', methode_saisie_u);
154
+ }
155
+
156
+ ph.donnee_utilisateur = du;
157
+ ph.donnee_intermediaire = di;
158
+ }
@@ -0,0 +1,32 @@
1
+ import b from './3.1_b.js';
2
+ import { tv, requestInput } from './utils.js';
3
+
4
+ function tv_uporte(di, de, du) {
5
+ requestInput(de, du, 'type_porte');
6
+ const matcher = {
7
+ enum_type_porte_id: de.enum_type_porte_id
8
+ };
9
+ const row = tv('uporte', matcher, de);
10
+ if (row) {
11
+ di.uporte = Number(row.uporte);
12
+ de.tv_uporte_id = Number(row.tv_uporte_id);
13
+ } else {
14
+ console.error('!! pas de valeur forfaitaire trouvée pour uporte !!');
15
+ }
16
+ }
17
+
18
+ export default function calc_porte(porte, zc) {
19
+ const de = porte.donnee_entree;
20
+ const di = {};
21
+ const du = {};
22
+
23
+ requestInput(de, du, 'surface_porte', 'float');
24
+
25
+ b(di, de, du, zc);
26
+ const methode_saisie_uporte = requestInput(de, du, 'methode_saisie_uporte');
27
+ if (methode_saisie_uporte === 'valeur forfaitaire') tv_uporte(di, de, du);
28
+ else di.uporte = requestInput(de, du, 'uporte_saisi', 'float');
29
+
30
+ porte.donnee_utilisateur = du;
31
+ porte.donnee_intermediaire = di;
32
+ }
@@ -0,0 +1,308 @@
1
+ import b from './3.1_b.js';
2
+ import { tv, requestInput, requestInputID, bug_for_bug_compat, getRange } from './utils.js';
3
+ import tvs from './tv.js';
4
+
5
+ function tv_ug(di, de, du) {
6
+ const matcher = {
7
+ enum_type_vitrage_id: requestInputID(de, du, 'type_vitrage')
8
+ };
9
+
10
+ if (matcher.enum_type_vitrage_id && matcher.enum_type_vitrage_id !== '1') {
11
+ // inside if because simple vitrage does not have these fields
12
+ matcher.enum_type_gaz_lame_id = requestInputID(de, du, 'type_gaz_lame');
13
+ matcher.enum_inclinaison_vitrage_id = requestInputID(de, du, 'inclinaison_vitrage');
14
+ matcher.vitrage_vir = requestInput(de, du, 'vitrage_vir', 'bool');
15
+ matcher.epaisseur_lame = requestInput(de, du, 'epaisseur_lame', 'float');
16
+ }
17
+ const row = tv('ug', matcher);
18
+ if (row) {
19
+ di.ug = Number(row.ug);
20
+ de.tv_ug_id = Number(row.tv_ug_id);
21
+ } else {
22
+ console.error('!! pas de valeur forfaitaire trouvée pour ug !!');
23
+ }
24
+ }
25
+
26
+ function tv_uw(di, de) {
27
+ const enum_type_baie_id = de.enum_type_baie_id;
28
+ const matcher = { enum_type_baie_id };
29
+ let uw;
30
+
31
+ /**
32
+ * Pas de notion de ug pour les parois polycarbonate ou verre
33
+ * enum_type_baie_id
34
+ * 1 - paroi en brique de verre pleine
35
+ * 2 - paroi en brique de verre creuse
36
+ * 3 - paroi en polycarbonnate
37
+ */
38
+ if (matcher.enum_type_baie_id && ['1', '2', '3'].includes(enum_type_baie_id)) {
39
+ const row = tv('uw', matcher);
40
+ if (row) {
41
+ uw = Number(row.uw);
42
+ de.tv_uw_id = Number(row.tv_uw_id);
43
+ }
44
+ } else {
45
+ const enum_type_materiaux_menuiserie_id = de.enum_type_materiaux_menuiserie_id;
46
+ matcher.enum_type_materiaux_menuiserie_id = enum_type_materiaux_menuiserie_id;
47
+
48
+ // Récupération de toutes les valeurs de Ug présentes dans les tables Uw pour le type de baie et matériaux de la baie vitrée
49
+ const ugValues = tvs.uw
50
+ .filter((row) => {
51
+ return (
52
+ row.enum_type_baie_id.split('|').includes(enum_type_baie_id) &&
53
+ row.enum_type_materiaux_menuiserie_id
54
+ .split('|')
55
+ .includes(enum_type_materiaux_menuiserie_id)
56
+ );
57
+ })
58
+ .map((row) => parseFloat(row.ug));
59
+
60
+ /**
61
+ * 3.3.2 Coefficients Uw des fenêtres / portes-fenêtres
62
+ * Les Uw associés à des Ug non présents dans les tableaux peuvent être obtenus par interpolation ou
63
+ * extrapolation avec les deux Ug tabulés les plus proches.
64
+ */
65
+ let ug1, ug2;
66
+ [ug1, ug2] = getRange(di.ug, ugValues);
67
+
68
+ const matcher_1 = { ...matcher, ...{ ug: `^${ug1}$` } };
69
+ const row_1 = tv('uw', matcher_1);
70
+ const delta_ug = ug2 - ug1;
71
+
72
+ if (delta_ug === 0) {
73
+ if (row_1) {
74
+ uw = Number(row_1.uw);
75
+ }
76
+ } else {
77
+ const matcher_2 = { ...matcher, ...{ ug: `^${ug2}$` } };
78
+ const row_2 = tv('uw', matcher_2);
79
+
80
+ if (row_1 && row_2) {
81
+ const delta_uw = Number(row_2.uw) - Number(row_1.uw);
82
+ uw = Number(row_1.uw) + (delta_uw * (di.ug - ug1)) / delta_ug;
83
+ }
84
+ }
85
+ }
86
+
87
+ if (uw) {
88
+ di.uw = uw;
89
+ } else {
90
+ console.error(`
91
+ Pas de valeur forfaitaire uw trouvée pour la baie vitrée ${de.description}.
92
+ `);
93
+ }
94
+ }
95
+
96
+ function tv_deltar(di, de, du) {
97
+ const matcher = {
98
+ enum_type_fermeture_id: requestInputID(de, du, 'type_fermeture')
99
+ };
100
+ const row = tv('deltar', matcher);
101
+ if (row) {
102
+ di.deltar = Number(row.deltar);
103
+ de.tv_deltar_id = Number(row.tv_deltar_id);
104
+ } else {
105
+ console.error('!! pas de valeur forfaitaire trouvée pour deltar !!');
106
+ }
107
+ }
108
+
109
+ function tv_ujn(di, de, du) {
110
+ tv_deltar(di, de, du);
111
+ const matcher = {
112
+ deltar: di.deltar,
113
+ uw: Number.isInteger(di.uw) ? `^${Number(di.uw)}$` : `^${Number(di.uw).toPrecision(2)}$`
114
+ };
115
+ const row = tv('ujn', matcher);
116
+ if (row) {
117
+ di.ujn = Number(row.ujn);
118
+ de.tv_ujn_id = Number(row.tv_ujn_id);
119
+ } else {
120
+ console.error('!! pas de valeur forfaitaire trouvée pour ujn !!');
121
+ }
122
+ }
123
+
124
+ function tv_sw(di, de, du) {
125
+ const matcher = {
126
+ enum_type_vitrage_id: requestInputID(de, du, 'type_vitrage')
127
+ };
128
+
129
+ matcher.enum_type_baie_id = requestInputID(de, du, 'type_baie');
130
+ matcher.enum_type_materiaux_menuiserie_id = requestInputID(de, du, 'type_materiaux_menuiserie');
131
+
132
+ /**
133
+ * 6.2.1 Détermination du facteur solaire
134
+ * Les champs vitrage_vir et enum_type_pose_id ne sont pas présentes pour les matériaux 'brique de verre' et 'polycarbonate'
135
+ *
136
+ * enum_type_materiaux_menuiserie_id
137
+ * 1 - brique de verre
138
+ * 2 - polycarbonate
139
+ */
140
+ if (![1, 2].includes(parseInt(matcher.enum_type_materiaux_menuiserie_id))) {
141
+ matcher.vitrage_vir = requestInput(de, du, 'vitrage_vir', 'bool');
142
+ matcher.enum_type_pose_id = requestInputID(de, du, 'type_pose');
143
+ }
144
+
145
+ const row = tv('sw', matcher);
146
+ if (row) {
147
+ di.sw = Number(row.sw);
148
+ de.tv_sw_id = Number(row.tv_sw_id);
149
+ } else {
150
+ console.error('!! pas de valeur forfaitaire trouvée pour sw !!');
151
+ }
152
+ }
153
+
154
+ function tv_masque_proche(di, de) {
155
+ if (!de.tv_coef_masque_proche_id) {
156
+ di.fe1 = 1;
157
+ return;
158
+ }
159
+ const matcher = {
160
+ tv_coef_masque_proche_id: de.tv_coef_masque_proche_id // TODO remove
161
+ };
162
+ const row = tv('coef_masque_proche', matcher);
163
+ if (row) {
164
+ di.fe1 = Number(row.fe1);
165
+ de.tv_coef_masque_proche_id = Number(row.tv_coef_masque_proche_id);
166
+ } else {
167
+ console.error('!! pas de valeur forfaitaire trouvée pour coef_masque_proche !!');
168
+ }
169
+ }
170
+
171
+ function tv_masque_lointain_homogene(di, de) {
172
+ if (!de.tv_coef_masque_lointain_homogene_id) return;
173
+ const matcher = {
174
+ tv_coef_masque_lointain_homogene_id: de.tv_coef_masque_lointain_homogene_id // TODO remove
175
+ };
176
+ const row = tv('coef_masque_lointain_homogene', matcher);
177
+ if (row) {
178
+ di.fe2 = Number(row.fe2);
179
+ de.tv_coef_masque_lointain_homogene_id = Number(row.tv_coef_masque_lointain_homogene_id);
180
+ } else {
181
+ console.log('!! pas de valeur forfaitaire trouvée pour coef_masque_lointain_homogene !!');
182
+ }
183
+ }
184
+
185
+ function calc_omb(ml) {
186
+ const matcher = {
187
+ tv_coef_masque_lointain_non_homogene_id: ml.tv_coef_masque_lointain_non_homogene_id // TODO remove
188
+ };
189
+ const row = tv('coef_masque_lointain_non_homoge', matcher);
190
+ if (row) {
191
+ const omb = Number(row.omb);
192
+ /* de.tv_coef_masque_lointain_non_homogene_id = Number(row.tv_coef_masque_lointain_homogene_id) */
193
+ return omb;
194
+ } else {
195
+ console.log('!! pas de valeur forfaitaire trouvée pour coef_masque_lointain_non_homog !!');
196
+ }
197
+ }
198
+
199
+ export default function calc_bv(bv, zc) {
200
+ const de = bv.donnee_entree;
201
+ const du = {};
202
+ const di = {};
203
+
204
+ b(di, de, du, zc);
205
+
206
+ if (de.sw_saisi) di.sw = de.sw_saisi;
207
+ else tv_sw(di, de, du);
208
+
209
+ tv_ug(di, de, du);
210
+ if (de.uw_saisi) di.uw = de.uw_saisi;
211
+ else tv_uw(di, de);
212
+
213
+ /**
214
+ * S'il existe une double-fenêtre, calcul des facteurs sw et uw équivalents
215
+ * 3.3.2 Coefficients Uw des fenêtres / portes-fenêtres - Traitement des doubles fenêtre
216
+ * 6.2.1 Détermination du facteur solaire
217
+ */
218
+ if (de.double_fenetre === 1 && bv.baie_vitree_double_fenetre) {
219
+ const deDoubleFenetre = bv.baie_vitree_double_fenetre.donnee_entree;
220
+ const diDoubleFenetre = bv.baie_vitree_double_fenetre.donnee_intermediaire;
221
+
222
+ if (deDoubleFenetre.sw_saisi) {
223
+ diDoubleFenetre.sw = deDoubleFenetre.sw_saisi;
224
+ } else {
225
+ tv_sw(diDoubleFenetre, deDoubleFenetre, du);
226
+ }
227
+
228
+ const sw = di.sw * (diDoubleFenetre.sw || 1);
229
+
230
+ if (!de.sw_saisi) {
231
+ di.sw = sw;
232
+ }
233
+
234
+ if (
235
+ bug_for_bug_compat &&
236
+ de.sw_saisi &&
237
+ bv.donnee_intermediaire.sw.toFixed(5) === sw.toFixed(5)
238
+ ) {
239
+ console.error(
240
+ `Le coefficient sw pour la double fenêtre '${de.description}' est saisi mais il est égale au produit des facteurs
241
+ sw des deux fenêtres. Il devrait être sw_saisi, le produit des sw est utilisé.`
242
+ );
243
+ di.sw = sw;
244
+ }
245
+
246
+ if (deDoubleFenetre.uw_saisi) {
247
+ diDoubleFenetre.uw = deDoubleFenetre.uw_saisi;
248
+ } else {
249
+ tv_uw(diDoubleFenetre, deDoubleFenetre);
250
+ }
251
+
252
+ const uw = 1 / (1 / di.uw + 1 / diDoubleFenetre.uw + 0.07);
253
+
254
+ if (!de.uw_saisi) {
255
+ di.uw = uw;
256
+ }
257
+
258
+ if (
259
+ bug_for_bug_compat &&
260
+ de.uw_saisi &&
261
+ bv.donnee_intermediaire.uw.toFixed(5) === uw.toFixed(5)
262
+ ) {
263
+ console.error(
264
+ `Le coefficient uw pour la double fenêtre '${de.description}' est saisi mais il est égale au uw équivalent
265
+ des deux fenêtres. Il devrait être uw_saisi, le facteur uw équivalent est utilisé.`
266
+ );
267
+ di.uw = uw;
268
+ }
269
+ }
270
+
271
+ const type_fermeture = requestInput(de, du, 'type_fermeture');
272
+ if (type_fermeture !== 'abscence de fermeture pour la baie vitrée') {
273
+ /**
274
+ * 3.3.3 Coefficients Ujn des fenêtres/portes-fenêtres
275
+ * Si le Ujn d’une menuiserie est connu et justifié, le saisir directement
276
+ */
277
+ if (de.ujn_saisi) {
278
+ di.ujn = de.ujn_saisi;
279
+ } else {
280
+ tv_ujn(di, de, du);
281
+ }
282
+
283
+ di.u_menuiserie = di.ujn;
284
+ } else {
285
+ di.u_menuiserie = di.uw;
286
+ }
287
+
288
+ di.fe2 = 1;
289
+ if (de.masque_lointain_non_homogene_collection) {
290
+ let mlnh = de.masque_lointain_non_homogene_collection.masque_lointain_non_homogene || [];
291
+
292
+ if (!Array.isArray(mlnh)) {
293
+ mlnh = [mlnh];
294
+ }
295
+
296
+ di.fe2 = Math.max(
297
+ 0,
298
+ mlnh.reduce((acc, ml) => acc - calc_omb(ml) / 100, 1)
299
+ );
300
+ }
301
+ tv_masque_proche(di, de, du);
302
+ tv_masque_lointain_homogene(di, de, du);
303
+
304
+ delete di.deltar;
305
+
306
+ bv.donnee_utilisateur = du;
307
+ bv.donnee_intermediaire = di;
308
+ }