@open3cl/engine 1.8.13 → 1.8.15
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.js +69 -7
- package/13.2_generateur_combustion.spec.js +196 -0
- package/13.2_generateur_combustion_ch.js +164 -3
- package/13.2_generateur_combustion_ch.spec.js +286 -2
- package/README.md +24 -24
- package/engine.js +1 -1
- package/features/engine/domain/ch/emetteur-ch.service.js +164 -40
- package/features/engine/domain/ch/emetteur-ch.service.spec.js +240 -2
- package/features/engine/domain/ch/generateur-ch.service.js +15 -1
- package/features/engine/domain/ch/generateur-ch.service.spec.js +81 -0
- package/package.json +1 -1
|
@@ -200,6 +200,7 @@ export function tv_generateur_combustion(dpe, di, de, type, GV, tbase, methodeSa
|
|
|
200
200
|
if (
|
|
201
201
|
bug_for_bug_compat &&
|
|
202
202
|
type === 'ecs' &&
|
|
203
|
+
row &&
|
|
203
204
|
row.tv_generateur_combustion_id !== de.tv_generateur_combustion_id
|
|
204
205
|
) {
|
|
205
206
|
const rowDpe = tv('generateur_combustion', {
|
|
@@ -223,7 +224,7 @@ export function tv_generateur_combustion(dpe, di, de, type, GV, tbase, methodeSa
|
|
|
223
224
|
|
|
224
225
|
if (!row) {
|
|
225
226
|
console.error(
|
|
226
|
-
|
|
227
|
+
`Pas de valeur forfaitaire trouvée pour le générateur à combustion ${de.description} (${typeGenerateurKey} = '${enumTypeGenerateurId}')`
|
|
227
228
|
);
|
|
228
229
|
return;
|
|
229
230
|
}
|
|
@@ -377,21 +378,21 @@ function checkEcsVsChauffageForMixteGeneration(dpe, de, typeGenerateurKey) {
|
|
|
377
378
|
);
|
|
378
379
|
}, []);
|
|
379
380
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
const chGenerateurMixte = findGenerateurChMixteJumeau(generateursChMixtes, de);
|
|
382
|
+
|
|
383
|
+
if (chGenerateurMixte) {
|
|
384
|
+
const chGenerateurMixteId = chGenerateurMixte.donnee_entree.enum_type_generateur_ch_id;
|
|
384
385
|
const typeGenerateurEcs = enums.type_generateur_ecs;
|
|
385
386
|
const typeGenerateurCh = enums.type_generateur_ch;
|
|
386
387
|
|
|
387
388
|
const ecsGenerateurType = typeGenerateurEcs[ecsGenerateurId];
|
|
388
|
-
const chGenerateurType = typeGenerateurCh[
|
|
389
|
+
const chGenerateurType = typeGenerateurCh[chGenerateurMixteId];
|
|
389
390
|
|
|
390
391
|
// Si les 2 générateurs n'ont pas le même type
|
|
391
392
|
if (ecsGenerateurType !== chGenerateurType) {
|
|
392
393
|
// Recherche de l'identifiant du générateur ECS identique au générateur de chauffage
|
|
393
394
|
const newEcsGenerateurId = Object.keys(typeGenerateurEcs).find(
|
|
394
|
-
(key) => typeGenerateurEcs[key] === typeGenerateurCh[
|
|
395
|
+
(key) => typeGenerateurEcs[key] === typeGenerateurCh[chGenerateurMixteId]
|
|
395
396
|
);
|
|
396
397
|
|
|
397
398
|
if (newEcsGenerateurId) {
|
|
@@ -407,3 +408,64 @@ function checkEcsVsChauffageForMixteGeneration(dpe, de, typeGenerateurKey) {
|
|
|
407
408
|
|
|
408
409
|
return ecsGenerateurId;
|
|
409
410
|
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Recherche du générateur de chauffage mixte « jumeau » du générateur ECS mixte.
|
|
414
|
+
* Méthode 3CL-DPE 2021 §13.2 et §14 : un générateur mixte chauffage + ECS est un seul et même
|
|
415
|
+
* appareil, avec les mêmes caractéristiques pour les deux usages. Il ne faut donc pas apparier le
|
|
416
|
+
* générateur ECS avec n'importe quel générateur de chauffage mixte du DPE (issue #210).
|
|
417
|
+
*
|
|
418
|
+
* Ordre de recherche :
|
|
419
|
+
* 1. même `reference_generateur_mixte` ;
|
|
420
|
+
* 2. même `tv_generateur_combustion_id` ;
|
|
421
|
+
* 3. même énergie (`enum_type_energie_id`), si un seul générateur de chauffage mixte correspond ;
|
|
422
|
+
* 4. générateur de chauffage mixte unique du DPE, si son énergie n'est pas incompatible.
|
|
423
|
+
*
|
|
424
|
+
* @param generateursChMixtes {GenerateurChauffageItem[]} générateurs de chauffage d'usage 'chauffage + ecs'
|
|
425
|
+
* @param de {Donnee_entree} données du générateur ECS
|
|
426
|
+
* @returns {GenerateurChauffageItem|null} null si aucun jumeau ne peut être identifié sans ambiguïté
|
|
427
|
+
*/
|
|
428
|
+
export function findGenerateurChMixteJumeau(generateursChMixtes, de) {
|
|
429
|
+
const sameValue = (a, b) =>
|
|
430
|
+
a !== undefined && a !== null && b !== undefined && b !== null && String(a) === String(b);
|
|
431
|
+
|
|
432
|
+
if (de.reference_generateur_mixte) {
|
|
433
|
+
const generateur = generateursChMixtes.find((ch) =>
|
|
434
|
+
sameValue(ch.donnee_entree.reference_generateur_mixte, de.reference_generateur_mixte)
|
|
435
|
+
);
|
|
436
|
+
if (generateur) return generateur;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (de.tv_generateur_combustion_id) {
|
|
440
|
+
const generateur = generateursChMixtes.find((ch) =>
|
|
441
|
+
sameValue(ch.donnee_entree.tv_generateur_combustion_id, de.tv_generateur_combustion_id)
|
|
442
|
+
);
|
|
443
|
+
if (generateur) return generateur;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (de.enum_type_energie_id) {
|
|
447
|
+
const generateurs = generateursChMixtes.filter((ch) =>
|
|
448
|
+
sameValue(ch.donnee_entree.enum_type_energie_id, de.enum_type_energie_id)
|
|
449
|
+
);
|
|
450
|
+
if (generateurs.length === 1) return generateurs[0];
|
|
451
|
+
// Plusieurs générateurs de même énergie et de même type : jumeaux identiques,
|
|
452
|
+
// aucune ambiguïté sur le type à retenir pour l'ECS (cas 2275E1945671L)
|
|
453
|
+
const type = generateurs[0]?.donnee_entree.enum_type_generateur_ch_id;
|
|
454
|
+
if (
|
|
455
|
+
generateurs.length > 1 &&
|
|
456
|
+
generateurs.every((ch) => sameValue(ch.donnee_entree.enum_type_generateur_ch_id, type))
|
|
457
|
+
) {
|
|
458
|
+
return generateurs[0];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (generateursChMixtes.length === 1) {
|
|
463
|
+
const generateur = generateursChMixtes[0];
|
|
464
|
+
const energieCh = generateur.donnee_entree.enum_type_energie_id;
|
|
465
|
+
if (!de.enum_type_energie_id || !energieCh || sameValue(energieCh, de.enum_type_energie_id)) {
|
|
466
|
+
return generateur;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
@@ -62,6 +62,7 @@ vi.mock('./13.2_generateur_combustion_pn.js', () => ({
|
|
|
62
62
|
|
|
63
63
|
const {
|
|
64
64
|
tv_generateur_combustion,
|
|
65
|
+
findGenerateurChMixteJumeau,
|
|
65
66
|
updateGenerateurCombustion,
|
|
66
67
|
caracteristiquesGenerateurCombustion
|
|
67
68
|
} = await import('./13.2_generateur_combustion.js');
|
|
@@ -607,6 +608,201 @@ describe('tv_generateur_combustion - génération mixte (checkEcsVsChauffageForM
|
|
|
607
608
|
});
|
|
608
609
|
});
|
|
609
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Issue #210 : l'ECS mixte doit être appariée avec SON générateur de chauffage mixte (même appareil),
|
|
613
|
+
* et non avec le premier générateur de chauffage mixte du DPE.
|
|
614
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2 et §14
|
|
615
|
+
*/
|
|
616
|
+
describe('findGenerateurChMixteJumeau - appariement ECS / chauffage mixte (issue #210)', () => {
|
|
617
|
+
const ch = (donnees) => ({ donnee_entree: { enum_usage_generateur_id: '3', ...donnees } });
|
|
618
|
+
|
|
619
|
+
test('même reference_generateur_mixte : prioritaire sur le tv_generateur_combustion_id', () => {
|
|
620
|
+
const chTv = ch({ tv_generateur_combustion_id: '11' });
|
|
621
|
+
const chRef = ch({ reference_generateur_mixte: 'GEN#1', tv_generateur_combustion_id: '13' });
|
|
622
|
+
const de = { reference_generateur_mixte: 'GEN#1', tv_generateur_combustion_id: '11' };
|
|
623
|
+
expect(findGenerateurChMixteJumeau([chTv, chRef], de)).toBe(chRef);
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('reference_generateur_mixte sans correspondance : repli sur le tv_generateur_combustion_id', () => {
|
|
627
|
+
const chElec = ch({ reference_generateur_mixte: 'AUTRE', tv_generateur_combustion_id: null });
|
|
628
|
+
const chGaz = ch({ tv_generateur_combustion_id: 11 });
|
|
629
|
+
const de = { reference_generateur_mixte: 'GEN#1', tv_generateur_combustion_id: '11' };
|
|
630
|
+
// Comparaison insensible au type (nombre / chaîne)
|
|
631
|
+
expect(findGenerateurChMixteJumeau([chElec, chGaz], de)).toBe(chGaz);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test('cas 2448E4606320D : ECS gaz (tv 11) appariée au CH 95 (tv 11) et non au premier CH mixte (106, électrique)', () => {
|
|
635
|
+
const ch106 = ch({ enum_type_generateur_ch_id: '106', enum_type_energie_id: '1' });
|
|
636
|
+
const ch95 = ch({
|
|
637
|
+
enum_type_generateur_ch_id: '95',
|
|
638
|
+
tv_generateur_combustion_id: '11',
|
|
639
|
+
enum_type_energie_id: '2'
|
|
640
|
+
});
|
|
641
|
+
const de = {
|
|
642
|
+
enum_type_generateur_ecs_id: '55',
|
|
643
|
+
tv_generateur_combustion_id: '11',
|
|
644
|
+
enum_type_energie_id: '2'
|
|
645
|
+
};
|
|
646
|
+
expect(findGenerateurChMixteJumeau([ch106, ch95], de)).toBe(ch95);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
test('cas 2193E1043519Y : ECS gaz (tv 13) appariée au CH 97 (tv 13) et non au CH 111 (réseau de chaleur)', () => {
|
|
650
|
+
const ch111 = ch({ enum_type_generateur_ch_id: '111', enum_type_energie_id: '2' });
|
|
651
|
+
const ch97 = ch({
|
|
652
|
+
enum_type_generateur_ch_id: '97',
|
|
653
|
+
tv_generateur_combustion_id: '13',
|
|
654
|
+
enum_type_energie_id: '2'
|
|
655
|
+
});
|
|
656
|
+
const de = {
|
|
657
|
+
enum_type_generateur_ecs_id: '57',
|
|
658
|
+
tv_generateur_combustion_id: '13',
|
|
659
|
+
enum_type_energie_id: '2'
|
|
660
|
+
};
|
|
661
|
+
expect(findGenerateurChMixteJumeau([ch111, ch97], de)).toBe(ch97);
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
test('pas de référence ni de tv commun : un seul générateur de même énergie → retenu', () => {
|
|
665
|
+
const chElec = ch({ enum_type_energie_id: '1' });
|
|
666
|
+
const chFioul = ch({ enum_type_energie_id: '3' });
|
|
667
|
+
const de = { tv_generateur_combustion_id: '99', enum_type_energie_id: '3' };
|
|
668
|
+
expect(findGenerateurChMixteJumeau([chElec, chFioul], de)).toBe(chFioul);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
test('plusieurs générateurs de même énergie sans autre critère : ambiguïté → aucun appariement', () => {
|
|
672
|
+
const de = { enum_type_energie_id: '2' };
|
|
673
|
+
const generateurs = [ch({ enum_type_energie_id: '2' }), ch({ enum_type_energie_id: '2' })];
|
|
674
|
+
expect(findGenerateurChMixteJumeau(generateurs, de)).toBeNull();
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
test('plusieurs générateurs de même énergie et de types différents : ambiguïté → aucun appariement', () => {
|
|
678
|
+
const de = { enum_type_energie_id: '13' };
|
|
679
|
+
const generateurs = [
|
|
680
|
+
ch({ enum_type_generateur_ch_id: '134', enum_type_energie_id: '13' }),
|
|
681
|
+
ch({ enum_type_generateur_ch_id: '92', enum_type_energie_id: '13' })
|
|
682
|
+
];
|
|
683
|
+
expect(findGenerateurChMixteJumeau(generateurs, de)).toBeNull();
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
test('cas 2275E1945671L : plusieurs générateurs de même énergie et de même type → le premier est retenu', () => {
|
|
687
|
+
const ch1 = ch({
|
|
688
|
+
enum_type_generateur_ch_id: '134',
|
|
689
|
+
enum_type_energie_id: '13',
|
|
690
|
+
tv_generateur_combustion_id: '8'
|
|
691
|
+
});
|
|
692
|
+
const ch2 = ch({
|
|
693
|
+
enum_type_generateur_ch_id: 134,
|
|
694
|
+
enum_type_energie_id: 13,
|
|
695
|
+
tv_generateur_combustion_id: '8'
|
|
696
|
+
});
|
|
697
|
+
const de = {
|
|
698
|
+
enum_type_generateur_ecs_id: '92',
|
|
699
|
+
tv_generateur_combustion_id: '1',
|
|
700
|
+
enum_type_energie_id: '13'
|
|
701
|
+
};
|
|
702
|
+
expect(findGenerateurChMixteJumeau([ch1, ch2], de)).toBe(ch1);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test('générateur de chauffage mixte unique sans énergie renseignée : retenu (comportement historique)', () => {
|
|
706
|
+
const unique = ch({ enum_type_generateur_ch_id: 'X' });
|
|
707
|
+
expect(findGenerateurChMixteJumeau([unique], { enum_type_energie_id: '2' })).toBe(unique);
|
|
708
|
+
expect(findGenerateurChMixteJumeau([unique], {})).toBe(unique);
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
test('générateur de chauffage mixte unique d’une autre énergie : pas son jumeau → aucun appariement', () => {
|
|
712
|
+
const unique = ch({ enum_type_energie_id: '1' });
|
|
713
|
+
expect(findGenerateurChMixteJumeau([unique], { enum_type_energie_id: '2' })).toBeNull();
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test('aucun générateur de chauffage mixte : aucun appariement', () => {
|
|
717
|
+
expect(findGenerateurChMixteJumeau([], { tv_generateur_combustion_id: '11' })).toBeNull();
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
describe('tv_generateur_combustion - génération mixte avec plusieurs générateurs de chauffage (issue #210)', () => {
|
|
722
|
+
function dpeAvecChMixtes(generateurs) {
|
|
723
|
+
return {
|
|
724
|
+
logement: {
|
|
725
|
+
installation_chauffage_collection: {
|
|
726
|
+
installation_chauffage: generateurs.map((donnees) => ({
|
|
727
|
+
generateur_chauffage_collection: {
|
|
728
|
+
generateur_chauffage: [
|
|
729
|
+
{ donnee_entree: { enum_usage_generateur_id: '3', ...donnees } }
|
|
730
|
+
]
|
|
731
|
+
}
|
|
732
|
+
}))
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
test('le type ECS est aligné sur le jumeau (même tv) et non sur le premier générateur mixte', () => {
|
|
739
|
+
state.bug = true;
|
|
740
|
+
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
741
|
+
// 1er CH mixte 'X' (fioul, autre tv), jumeau 'Y' (gaz, même tv 42) -> ECS 'A' (gaz) conservée
|
|
742
|
+
const dpe = dpeAvecChMixtes([
|
|
743
|
+
{ enum_type_generateur_ch_id: 'X', tv_generateur_combustion_id: '7' },
|
|
744
|
+
{ enum_type_generateur_ch_id: 'Y', tv_generateur_combustion_id: '42' }
|
|
745
|
+
]);
|
|
746
|
+
const de = {
|
|
747
|
+
enum_type_generateur_ecs_id: 'A',
|
|
748
|
+
enum_usage_generateur_id: '3',
|
|
749
|
+
tv_generateur_combustion_id: '42',
|
|
750
|
+
ratio_virtualisation: 1,
|
|
751
|
+
presence_ventouse: 0
|
|
752
|
+
};
|
|
753
|
+
tv_generateur_combustion(dpe, { pn: 20000 }, de, 'ecs', 200, -9, 1);
|
|
754
|
+
expect(tv).toHaveBeenCalledWith(
|
|
755
|
+
'generateur_combustion',
|
|
756
|
+
expect.objectContaining({ enum_type_generateur_ecs_id: 'A' })
|
|
757
|
+
);
|
|
758
|
+
expect(tv).not.toHaveBeenCalledWith(
|
|
759
|
+
'generateur_combustion',
|
|
760
|
+
expect.objectContaining({ enum_type_generateur_ecs_id: 'B' })
|
|
761
|
+
);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test('jumeau non identifiable (ambiguïté) : type ECS conservé', () => {
|
|
765
|
+
state.bug = true;
|
|
766
|
+
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
767
|
+
// Deux CH mixtes de même énergie mais de types différents : aucun jumeau certain
|
|
768
|
+
const dpe = dpeAvecChMixtes([
|
|
769
|
+
{ enum_type_generateur_ch_id: 'X', enum_type_energie_id: '2' },
|
|
770
|
+
{ enum_type_generateur_ch_id: 'Z', enum_type_energie_id: '2' }
|
|
771
|
+
]);
|
|
772
|
+
const de = {
|
|
773
|
+
enum_type_generateur_ecs_id: 'A',
|
|
774
|
+
enum_usage_generateur_id: '3',
|
|
775
|
+
enum_type_energie_id: '2',
|
|
776
|
+
tv_generateur_combustion_id: '42',
|
|
777
|
+
ratio_virtualisation: 1,
|
|
778
|
+
presence_ventouse: 0
|
|
779
|
+
};
|
|
780
|
+
tv_generateur_combustion(dpe, { pn: 20000 }, de, 'ecs', 200, -9, 1);
|
|
781
|
+
expect(tv).toHaveBeenCalledWith(
|
|
782
|
+
'generateur_combustion',
|
|
783
|
+
expect.objectContaining({ enum_type_generateur_ecs_id: 'A' })
|
|
784
|
+
);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
test('aucune ligne forfaitaire pour un générateur ECS mixte : erreur sans plantage', () => {
|
|
788
|
+
state.bug = true;
|
|
789
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
790
|
+
tv.mockReturnValue(null);
|
|
791
|
+
const de = {
|
|
792
|
+
enum_type_generateur_ecs_id: 'A',
|
|
793
|
+
enum_usage_generateur_id: '3',
|
|
794
|
+
tv_generateur_combustion_id: '42',
|
|
795
|
+
ratio_virtualisation: 1,
|
|
796
|
+
presence_ventouse: 0
|
|
797
|
+
};
|
|
798
|
+
expect(() =>
|
|
799
|
+
tv_generateur_combustion(dpeAvecChMixtes([]), { pn: 20000 }, de, 'ecs', 200, -9, 1)
|
|
800
|
+
).not.toThrow();
|
|
801
|
+
expect(errSpy).toHaveBeenCalledWith(expect.stringContaining('Pas de valeur forfaitaire'));
|
|
802
|
+
errSpy.mockRestore();
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
|
|
610
806
|
/**
|
|
611
807
|
* 13.2 - Orchestration des substitutions de générateurs et enrichissement fiches techniques.
|
|
612
808
|
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2
|
|
@@ -62,7 +62,170 @@ export function tempDistributionChPourTempFonc(emChDe, emChDu) {
|
|
|
62
62
|
return tempDistribution;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Périodes d'installation des émetteurs (table temp_fonc_30 / temp_fonc_100), de la plus ancienne
|
|
67
|
+
* à la plus récente.
|
|
68
|
+
*/
|
|
69
|
+
export const PERIODES_INSTALLATION_EMETTEUR = ['avant 1981', 'entre 1981 et 2000', 'après 2000'];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Tolérance (°C) pour considérer qu'une température de fonctionnement issue des tables reproduit
|
|
73
|
+
* celle stockée dans le DPE.
|
|
74
|
+
*/
|
|
75
|
+
export const TOLERANCE_TEMP_FONC = 0.01;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 13.2.1.5 Chaudières basse température et condensation
|
|
79
|
+
* « Si l'année d'installation des émetteurs est inconnue, prendre l'année de construction du
|
|
80
|
+
* bâtiment. »
|
|
81
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.5
|
|
82
|
+
*
|
|
83
|
+
* @param ac {number} année de construction du bâtiment
|
|
84
|
+
* @return {string} période d'installation des émetteurs
|
|
85
|
+
*/
|
|
86
|
+
export function periodeEmetteurAnneeConstruction(ac) {
|
|
87
|
+
if (ac < 1981) return PERIODES_INSTALLATION_EMETTEUR[0];
|
|
88
|
+
if (ac < 2000) return PERIODES_INSTALLATION_EMETTEUR[1];
|
|
89
|
+
return PERIODES_INSTALLATION_EMETTEUR[2];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function estNumerique(valeur) {
|
|
93
|
+
return (
|
|
94
|
+
valeur !== null && valeur !== undefined && valeur !== '' && Number.isFinite(Number(valeur))
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Lignes des tables temp_fonc_30 / temp_fonc_100 retenues pour un générateur (maximum sur les
|
|
100
|
+
* émetteurs), en affectant `periodeParDefaut` aux émetteurs dont la période d'installation n'est
|
|
101
|
+
* pas saisie. Les émetteurs sans réseau de distribution (enum_temp_distribution_ch_id = 1) sont
|
|
102
|
+
* exclus. Utilisé uniquement pour la déduction de période en mode bug_for_bug_compat.
|
|
103
|
+
*
|
|
104
|
+
* @param de {object} donnee_entree du générateur
|
|
105
|
+
* @param em_ch {object[]} émetteurs de l'installation
|
|
106
|
+
* @param periodeParDefaut {string} période affectée aux émetteurs sans période saisie
|
|
107
|
+
* @return {{row_30: object|null, row_100: object|null}}
|
|
108
|
+
*/
|
|
109
|
+
export function temp_fonc_pour_periode(de, em_ch, periodeParDefaut) {
|
|
110
|
+
let row_30 = null;
|
|
111
|
+
let row_100 = null;
|
|
112
|
+
for (const em of em_ch) {
|
|
113
|
+
const em_ch_de = em.donnee_entree;
|
|
114
|
+
const em_ch_du = em.donnee_utilisateur;
|
|
115
|
+
if (String(requestInputID(em_ch_de, em_ch_du, 'temp_distribution_ch')) === '1') continue;
|
|
116
|
+
const matcher = {
|
|
117
|
+
enum_type_generateur_ch_id: de.enum_type_generateur_ch_id,
|
|
118
|
+
enum_temp_distribution_ch_id: tempDistributionChPourTempFonc(em_ch_de, em_ch_du),
|
|
119
|
+
periode_emetteurs:
|
|
120
|
+
requestInput(em_ch_de, em_ch_du, 'periode_installation_emetteur') || periodeParDefaut
|
|
121
|
+
};
|
|
122
|
+
const r30 = tv('temp_fonc_30', matcher);
|
|
123
|
+
const r100 = tv('temp_fonc_100', matcher);
|
|
124
|
+
if (r30 && (!row_30 || Number(r30.temp_fonc_30) > Number(row_30.temp_fonc_30))) row_30 = r30;
|
|
125
|
+
if (r100 && (!row_100 || Number(r100.temp_fonc_100) > Number(row_100.temp_fonc_100))) {
|
|
126
|
+
row_100 = r100;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { row_30, row_100 };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Mode bug_for_bug_compat uniquement (issue #220).
|
|
134
|
+
*
|
|
135
|
+
* Certains logiciels (ex. 4.1.1 / moteur 3cl-2024.6.1.0, LICIEL) calculent les températures de
|
|
136
|
+
* fonctionnement avec une période d'installation des émetteurs qu'ils n'exportent pas, mais
|
|
137
|
+
* stockent temp_fonc_30 / temp_fonc_100 dans les données intermédiaires du générateur. La période
|
|
138
|
+
* est alors retrouvée en testant chaque période candidate (appliquée à tous les émetteurs sans
|
|
139
|
+
* période saisie) et en ne retenant que celles qui reproduisent les DEUX valeurs du DPE.
|
|
140
|
+
*
|
|
141
|
+
* - une seule candidate : retenue ;
|
|
142
|
+
* - plusieurs : celle de l'année de construction si elle en fait partie, sinon la plus récente ;
|
|
143
|
+
* - aucune : null (repli sur l'année de construction, valeurs hors tables).
|
|
144
|
+
*
|
|
145
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.5
|
|
146
|
+
*
|
|
147
|
+
* @param tempFonc30Dpe {number} temp_fonc_30 d'origine du DPE
|
|
148
|
+
* @param tempFonc100Dpe {number} temp_fonc_100 d'origine du DPE
|
|
149
|
+
* @param de {object} donnee_entree du générateur
|
|
150
|
+
* @param em_ch {object[]} émetteurs de l'installation
|
|
151
|
+
* @param ac {number} année de construction
|
|
152
|
+
* @return {string|null} période déduite, ou null si aucune période ne reproduit le DPE
|
|
153
|
+
*/
|
|
154
|
+
export function periodeEmetteursDeduiteDuDpe(tempFonc30Dpe, tempFonc100Dpe, de, em_ch, ac) {
|
|
155
|
+
const candidates = PERIODES_INSTALLATION_EMETTEUR.filter((periode) => {
|
|
156
|
+
const { row_30, row_100 } = temp_fonc_pour_periode(de, em_ch, periode);
|
|
157
|
+
return (
|
|
158
|
+
row_30 &&
|
|
159
|
+
row_100 &&
|
|
160
|
+
Math.abs(Number(row_30.temp_fonc_30) - tempFonc30Dpe) <= TOLERANCE_TEMP_FONC &&
|
|
161
|
+
Math.abs(Number(row_100.temp_fonc_100) - tempFonc100Dpe) <= TOLERANCE_TEMP_FONC
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
if (candidates.length === 0) return null;
|
|
165
|
+
const periodeConstruction = periodeEmetteurAnneeConstruction(ac);
|
|
166
|
+
if (candidates.includes(periodeConstruction)) return periodeConstruction;
|
|
167
|
+
return candidates[candidates.length - 1];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Indique si la déduction de la période des émetteurs à partir des données intermédiaires du DPE
|
|
172
|
+
* doit être tentée : mode bug_for_bug_compat, au moins un émetteur (avec réseau de distribution)
|
|
173
|
+
* sans période saisie, et temp_fonc_30 ET temp_fonc_100 d'origine présentes et numériques.
|
|
174
|
+
*
|
|
175
|
+
* @param tempFonc30Dpe {*} temp_fonc_30 d'origine du DPE
|
|
176
|
+
* @param tempFonc100Dpe {*} temp_fonc_100 d'origine du DPE
|
|
177
|
+
* @param em_ch {object[]} émetteurs de l'installation
|
|
178
|
+
* @return {boolean}
|
|
179
|
+
*/
|
|
180
|
+
export function deductionPeriodeEmetteursActive(tempFonc30Dpe, tempFonc100Dpe, em_ch) {
|
|
181
|
+
if (!bug_for_bug_compat) return false;
|
|
182
|
+
if (!estNumerique(tempFonc30Dpe) || !estNumerique(tempFonc100Dpe)) return false;
|
|
183
|
+
return em_ch.some(
|
|
184
|
+
(em) =>
|
|
185
|
+
String(requestInputID(em.donnee_entree, em.donnee_utilisateur, 'temp_distribution_ch')) !==
|
|
186
|
+
'1' &&
|
|
187
|
+
!requestInput(em.donnee_entree, em.donnee_utilisateur, 'periode_installation_emetteur')
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Températures de fonctionnement à 30 % et 100 % de charge.
|
|
193
|
+
*
|
|
194
|
+
* `di` est la donnee_intermediaire du générateur issue du DPE d'entrée : à l'appel, temp_fonc_30 /
|
|
195
|
+
* temp_fonc_100 contiennent les valeurs d'origine du DPE (jamais une valeur recalculée par
|
|
196
|
+
* Open3CL). En mode bug_for_bug_compat, elles servent uniquement à retrouver la période
|
|
197
|
+
* d'installation des émetteurs non exportée (issue #220) ; les valeurs retenues proviennent
|
|
198
|
+
* toujours des tables (tv_temp_fonc_30_id / tv_temp_fonc_100_id renseignés).
|
|
199
|
+
*
|
|
200
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.5
|
|
201
|
+
*/
|
|
65
202
|
export function tv_temp_fonc_30_100(di, de, du, em_ch, ac) {
|
|
203
|
+
const tempFonc30Dpe = di.temp_fonc_30;
|
|
204
|
+
const tempFonc100Dpe = di.temp_fonc_100;
|
|
205
|
+
if (deductionPeriodeEmetteursActive(tempFonc30Dpe, tempFonc100Dpe, em_ch)) {
|
|
206
|
+
const periode = periodeEmetteursDeduiteDuDpe(
|
|
207
|
+
Number(tempFonc30Dpe),
|
|
208
|
+
Number(tempFonc100Dpe),
|
|
209
|
+
de,
|
|
210
|
+
em_ch,
|
|
211
|
+
ac
|
|
212
|
+
);
|
|
213
|
+
if (periode) {
|
|
214
|
+
const { row_30, row_100 } = temp_fonc_pour_periode(de, em_ch, periode);
|
|
215
|
+
console.warn(
|
|
216
|
+
`période d'installation des émetteurs déduite des données intermédiaires du DPE : ${periode} (générateur ${de.description}, temp_fonc_30 = ${row_30.temp_fonc_30}, temp_fonc_100 = ${row_100.temp_fonc_100})`
|
|
217
|
+
);
|
|
218
|
+
de.tv_temp_fonc_30_id = row_30.tv_temp_fonc_30_id;
|
|
219
|
+
di.temp_fonc_30 = Number(row_30.temp_fonc_30);
|
|
220
|
+
de.tv_temp_fonc_100_id = row_100.tv_temp_fonc_100_id;
|
|
221
|
+
di.temp_fonc_100 = Number(row_100.temp_fonc_100);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
console.warn(
|
|
225
|
+
`période d'installation des émetteurs non déductible pour le générateur ${de.description} : valeurs hors tables / donnée d'entrée incohérente (temp_fonc_30 = ${tempFonc30Dpe}, temp_fonc_100 = ${tempFonc100Dpe}). Repli sur l'année de construction.`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
66
229
|
for (const em of em_ch) {
|
|
67
230
|
const em_ch_de = em.donnee_entree;
|
|
68
231
|
const em_ch_du = em.donnee_utilisateur;
|
|
@@ -73,9 +236,7 @@ export function tv_temp_fonc_30_100(di, de, du, em_ch, ac) {
|
|
|
73
236
|
};
|
|
74
237
|
|
|
75
238
|
if (!matcher.periode_emetteurs) {
|
|
76
|
-
|
|
77
|
-
else if (ac < 2000) matcher.periode_emetteurs = 'entre 1981 et 2000';
|
|
78
|
-
else matcher.periode_emetteurs = 'après 2000';
|
|
239
|
+
matcher.periode_emetteurs = periodeEmetteurAnneeConstruction(ac);
|
|
79
240
|
}
|
|
80
241
|
|
|
81
242
|
const row_30 = tv('temp_fonc_30', matcher);
|