SPB Git
11.2 KB · 185 lines python
1"""Tests du chapitre 11 — inspection synthétique du sujet du cas 1, convention d'âge effectif, dépréciation ventilée,2sensibilité et exercices. Tolérances : 0,01 $ sur les montants recalculés ; 0,5 $ sur les montants arrondis cités ;31e-9 sur les identités."""4import sys, pathlib5import numpy as np, pytest6sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))7import ch11_inspection as cas8from traite import cout as ct91011@pytest.fixture(scope="module")12def data():13    devis, sujet, cn = cas.charger()14    f = cas.fiche(devis, cn); trav = cas.travaux(devis, cn)15    return {"devis": devis, "sujet": sujet, "cn": cn, "f": f, "trav": trav, "dv": cas.depreciation(f, trav, cn), "sens": cas.sensibilite(f, trav, cn)}161718class TestConvention:19    def test_bornes_et_monotonie(self):20        L = 60.021        for a in np.linspace(0, 80, 17):22            prev = -1.023            for c in range(1, 6):24                v = cas.age_effectif(a, L, c)25                assert 0.0 <= v <= L and v >= prev          # croissant avec la note d'état, borné à [0, L]26                prev = v27        for c in range(1, 6):                                # croissant avec l'âge chronologique28            vals = [cas.age_effectif(a, L, c) for a in range(0, 61)]29            assert all(x <= y for x, y in zip(vals, vals[1:]))3031    def test_cas_limites(self):32        assert cas.age_effectif(0, 60, 1) == pytest.approx(1.5)      # neuf et excellent : 0,5 × 0,05 × 6033        assert cas.age_effectif(60, 60, 5) == 60.0                    # en fin de vie et mauvais : borne L34        assert cas.age_effectif(28, 60, 3) == pytest.approx(29.0)     # sujet : (1 − 0,5) × 28 + 0,5 × 0,5 × 6035        assert cas.age_effectif(28, 60, 3, lam=0.0) == 28.0           # λ = 0 : âge chronologique36        assert cas.age_effectif(28, 60, 3, lam=1.0) == 30.0           # λ = 1 : φ(3) L37        assert cas.age_effectif(28, 20, 4) == 20.0                    # VRC : 14 + 7,5 = 21,5 → borné à 203839    def test_phi(self):40        assert list(cas.PHI.values()) == [0.05, 0.25, 0.50, 0.75, 1.00]414243class TestFiche:44    def test_couverture_du_devis(self, data):45        f, cn = data["f"], data["cn"]46        assert len(f) == 20 and int(f["longue_duree"].sum()) == 147        assert f["cout_direct"].sum() == pytest.approx(cn["direct"], abs=0.01)48        assert f["cout_neuf"].sum() == pytest.approx(cn["total"], abs=0.01)49        assert cas.facteur_majoration(cn) == pytest.approx(1.33043, abs=5e-6)50        assert cn["total"] == pytest.approx(397567.33, abs=0.01)5152    def test_valeurs_citees(self, data):53        f = data["f"].set_index("composante")54        assert f.loc["Couverture (bardeaux d'asphalte)", "age_effectif"] == pytest.approx(12.0)55        assert f.loc["Couverture (bardeaux d'asphalte)", "cout_neuf"] == pytest.approx(10217.70, abs=0.01)56        assert f.loc["Gouttières et solins", "age_effectif"] == pytest.approx(23.375)57        assert f.loc["Fenêtres (PVC, double vitrage)", "age_effectif"] == pytest.approx(21.5)58        assert f.loc["Fenêtres (PVC, double vitrage)", "cout_neuf"] == pytest.approx(22311.42, abs=0.01)59        assert f.loc["Ventilateur récupérateur de chaleur (VRC)", "ratio"] == pytest.approx(1.0)60        assert f.loc["Structure, fondations, dalle, drain, isolation, cloisons", "cout_neuf"] == pytest.approx(172611.24, abs=0.01)61        assert f.loc["Structure, fondations, dalle, drain, isolation, cloisons", "age_effectif"] == pytest.approx(29.0)62        assert f.loc["Structure, fondations, dalle, drain, isolation, cloisons", "vie_restante"] == pytest.approx(31.0)63        courtes = data["f"][~data["f"]["longue_duree"]]64        assert courtes["cout_neuf"].sum() == pytest.approx(224956.09, abs=0.01)65        assert courtes["cout_neuf"].sum() / data["cn"]["total"] == pytest.approx(0.566, abs=5e-4)6667    def test_indice_etat(self, data):68        f = data["f"]69        assert cas.indice_etat(f) == pytest.approx(2.809, abs=5e-4)70        assert f["etat"].value_counts().to_dict() == {2: 7, 3: 8, 4: 4, 5: 1}71        r = (f["cout_neuf"] * f["ratio"]).sum() / f["cout_neuf"].sum()72        assert r == pytest.approx(0.547, abs=5e-4) and r * 60 == pytest.approx(32.8, abs=0.05)737475class TestTravaux:76    def test_total(self, data):77        t = data["trav"]78        assert len(t) == 579        assert t["cout"].sum() == pytest.approx(10479.98, abs=0.01)80        assert t["cout"].sum() / data["cn"]["total"] == pytest.approx(0.0264, abs=5e-5)81        assert t.set_index("poste_reference").loc["Entrée asphaltée", "cout"] == pytest.approx(3438.43, abs=0.01)82        assert t.set_index("poste_reference").loc["Fenêtres (PVC double vitrage)", "cout"] == pytest.approx(2788.93, abs=0.01)838485class TestDepreciation:86    def test_identites(self, data):87        dv, cn = data["dv"], data["cn"]88        assert dv["total"] == pytest.approx(dv["curable"] + sum(dv["courte_duree"].values()) + dv["longue_duree"], rel=1e-12)89        courtes_hors_entree = data["f"][(~data["f"]["longue_duree"]) & (data["f"]["composante"] != "Entrée asphaltée")]["cout_neuf"].sum()90        assert dv["base_longue"] == pytest.approx(cn["total"] - dv["curable"] - courtes_hors_entree, abs=1e-6)91        assert dv["longue_duree"] == pytest.approx(dv["base_longue"] * 29.0 / 60.0, rel=1e-12)92        assert dv["contribution_batiment"] == pytest.approx(cn["total"] - dv["total"], rel=1e-12)93        assert len(dv["courte_duree"]) == 189495    def test_valeurs_citees(self, data):96        dv = data["dv"]97        assert dv["curable"] == pytest.approx(10479.98, abs=0.01)98        assert sum(dv["courte_duree"].values()) == pytest.approx(130584.55, abs=0.01)99        assert dv["base_longue"] == pytest.approx(165569.69, abs=0.01)100        assert dv["longue_duree"] == pytest.approx(80025.35, abs=0.01)101        assert dv["total"] == pytest.approx(221089.88, abs=0.01)102        assert dv["taux"] == pytest.approx(0.5561, abs=5e-5)103        assert dv["contribution_batiment"] == pytest.approx(176477.45, abs=0.01)104        assert dv["age_vie_simple"] == pytest.approx(192157.55, abs=0.01)105        assert dv["age_vie_simple"] / dv["cout_neuf"] == pytest.approx(0.4833, abs=5e-5)106        assert dv["total"] - dv["age_vie_simple"] == pytest.approx(28932.34, abs=0.01)107        assert ct.depreciation_age_vie(dv["cout_neuf"], 28, 60) == pytest.approx(185531.42, abs=0.01)108        assert dv["courte_duree"]["Ventilateur récupérateur de chaleur (VRC)"] == pytest.approx(7501.27, abs=0.01)109        assert dv["courte_duree"]["Fenêtres (PVC, double vitrage)"] == pytest.approx(15989.85, abs=0.01)110111112class TestSensibilite:113    def test_grille(self, data):114        s = data["sens"].set_index(["lambda", "etat_structure"])115        assert s.loc[(0.5, 3), "taux"] == pytest.approx(0.5561, abs=5e-5)116        assert s.loc[(0.0, 3), "taux"] == pytest.approx(0.6368, abs=5e-5)117        assert s.loc[(1.0, 1), "taux"] == pytest.approx(0.2738, abs=5e-5)118        assert s.loc[(1.0, 5), "taux"] == pytest.approx(0.6694, abs=5e-5)119        assert s.loc[(0.5, 1), "taux"] == pytest.approx(0.4624, abs=5e-5)120        assert s.loc[(0.5, 5), "taux"] == pytest.approx(0.6602, abs=5e-5)121        for lam in (0.0, 0.25, 0.5, 0.75, 1.0):122            t = [s.loc[(lam, e), "taux"] for e in range(1, 6)]123            assert all(x <= y + 1e-12 for x, y in zip(t, t[1:]))   # croissante avec la note d'état124        # à λ = 0 la note d'état n'intervient pas125        assert len({round(s.loc[(0.0, e), "taux"], 9) for e in range(1, 6)}) == 1126        assert s.loc[(1.0, 5), "age_effectif_structure"] == 60.0 and s.loc[(1.0, 1), "age_effectif_structure"] == pytest.approx(3.0)127128129class TestValeurInformation:130    def test_regle(self):131        v = cas.valeur_information(0.10, 3000.0, 50.0)132        assert v["perte_esperee"] == 300.0 and v["valeur_info"] == 250.0 and v["recommander"]133        v = cas.valeur_information(0.05, 25000.0, 1200.0)134        assert v["valeur_info"] == pytest.approx(50.0) and v["recommander"]135        v = cas.valeur_information(0.02, 25000.0, 1200.0)136        assert v["valeur_info"] == pytest.approx(-700.0) and not v["recommander"]137        # seuil de probabilité : p* = t / C138        assert 1200.0 / 25000.0 == pytest.approx(0.048)139140141class TestExercices:142    """Chiffres des solutions (solutions/ch11.tex)."""143144    def test_ex3_ages_effectifs(self):145        # toiture 12 ans / L 20 / état 4 ; chauffe-eau 9 / 12 / état 2 ; fenêtres 35 / 30 / état 2 ; structure 45 / 60 / état 2146        assert cas.age_effectif(12, 20, 4) == pytest.approx(13.5)147        assert cas.age_effectif(9, 12, 2) == pytest.approx(6.0)148        assert cas.age_effectif(35, 30, 2) == pytest.approx(21.25)149        assert cas.age_effectif(45, 60, 2) == pytest.approx(30.0)150        assert ct.vie_restante(60, 30.0) == 30.0151152    def test_ex4_ventilee(self):153        comps = [ct.Composante("toiture", 12000.0, 13.5, 20), ct.Composante("chauffe-eau", 1500.0, 6.0, 12), ct.Composante("fenêtres", 24000.0, 21.25, 30)]154        dv = ct.depreciation_ventilee(300000.0, 4500.0, comps, 30.0, 60.0)155        assert dv["courte_duree"]["toiture"] == pytest.approx(8100.0)156        assert dv["courte_duree"]["chauffe-eau"] == pytest.approx(750.0)157        assert dv["courte_duree"]["fenêtres"] == pytest.approx(17000.0)158        assert dv["base_longue"] == pytest.approx(258000.0)159        assert dv["longue_duree"] == pytest.approx(129000.0)160        assert dv["total"] == pytest.approx(159350.0)161        assert dv["total"] / 300000.0 == pytest.approx(0.53117, abs=5e-5)162        assert ct.depreciation_age_vie(300000.0, 30.0, 60.0) == pytest.approx(150000.0)163164    def test_ex5_valeur_information(self):165        v = cas.valeur_information(0.08, 20000.0, 900.0)166        assert v["perte_esperee"] == pytest.approx(1600.0) and v["valeur_info"] == pytest.approx(700.0)167        assert 900.0 / 20000.0 == pytest.approx(0.045)168        # dosimètre radon : 50 $ ; seuil p* pour une correction de 2 500 $169        assert 50.0 / 2500.0 == pytest.approx(0.02)170171    def test_ex6_variante_lambda(self, data):172        s = data["sens"].set_index(["lambda", "etat_structure"])173        assert s.loc[(0.75, 3), "taux"] == pytest.approx(0.5093, abs=5e-5)174        assert s.loc[(0.25, 3), "taux"] == pytest.approx(0.5977, abs=5e-5)175        assert s.loc[(0.75, 3), "age_effectif_structure"] == pytest.approx(29.5)176        assert s.loc[(0.25, 3), "age_effectif_structure"] == pytest.approx(28.5)177        assert s.loc[(0.25, 3), "depreciation"] == pytest.approx(237631.21, abs=0.01)178        assert s.loc[(0.75, 3), "depreciation"] == pytest.approx(202500.46, abs=0.01)179        assert s.loc[(0.25, 3), "depreciation"] - s.loc[(0.75, 3), "depreciation"] == pytest.approx(35130.75, abs=0.01)180        d25 = cas.depreciation(data["f"], data["trav"], data["cn"], 0.25); d75 = cas.depreciation(data["f"], data["trav"], data["cn"], 0.75)181        assert d25["courte_duree"]["Revêtement extérieur (brique et fibrociment)"] - d75["courte_duree"]["Revêtement extérieur (brique et fibrociment)"] == pytest.approx(8568.74, abs=0.01)182        assert d25["courte_duree"]["Armoires de cuisine et comptoirs"] - d75["courte_duree"]["Armoires de cuisine et comptoirs"] == pytest.approx(5000.84, abs=0.01)183        assert d25["courte_duree"]["Fenêtres (PVC, double vitrage)"] - d75["courte_duree"]["Fenêtres (PVC, double vitrage)"] == pytest.approx(4834.14, abs=0.01)184        assert d25["longue_duree"] - d75["longue_duree"] == pytest.approx(-2759.49, abs=0.01)185
Files · 2eb2bf4e67f754808bae5090a9e53eab37149ddb
Menu

Theme