#!/usr/bin/env python3 """Figures ch11 — (1) fiche d'état du sujet du cas 1 : fraction de vie utile consommée a_eff/L par composante, colorée par note d'état, taille ∝ coût neuf ; (2) sensibilité de la dépréciation physique totale à λ et à la note d'état de la structure. DONNÉES SYNTHÉTIQUES (cas 1, graine 20261031 ; constats d'inspection fictifs de analysis/python/cases/ch11_inspection.py). Sorties : assets/figures/ch11/ch11-fig-fiche-etat.pdf, assets/figures/ch11/ch11-fig-sensibilite-age-effectif.pdf""" import sys, pathlib ROOT = pathlib.Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases")) import numpy as np import matplotlib.pyplot as plt from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars import ch11_inspection as cas COUL_ETAT = {1: palette.cat[4], 2: palette.cat[0], 3: palette.cat[2], 4: palette.cat[1], 5: palette.cat[3]} def fiche_etat(f): fig, ax = plt.subplots(figsize=(LARGEUR, 4.2)) f = f.sort_values("ratio", ascending=True).reset_index(drop=True) y = np.arange(len(f)) ax.hlines(y, f["age"] / f["vie_utile"], f["ratio"], color=palette.grid, lw=1.2, zorder=1) ax.scatter(f["age"] / f["vie_utile"], y, marker="|", s=60, color=palette.ink2, zorder=2, label="âge chronologique / vie utile") tailles = 18 + 220 * f["cout_neuf"] / f["cout_neuf"].max() for e in sorted(f["etat"].unique()): m = f["etat"] == e ax.scatter(f.loc[m, "ratio"], y[m], s=tailles[m], color=COUL_ETAT[int(e)], edgecolor=palette.ink2, lw=0.4, zorder=3, label=f"état {int(e)}") ax.set_yticks(y); ax.set_yticklabels([c.replace(" (", "\n(") if len(c) > 44 else c for c in f["composante"]], fontsize=7) ax.set_xlim(0, 1.45); ax.axvline(1.0, color=palette.ink2, lw=0.7, ls="--") ax.set_xlabel("Fraction de la vie utile consommée\n(trait : âge chronologique ; disque : âge effectif, taille selon le coût neuf)", fontsize=7.5) ax.grid(axis="y", visible=False) ax.legend(loc="upper left", bbox_to_anchor=(0.01, 0.99), fontsize=6.8, ncol=1, title="Note d'état constatée", title_fontsize=7) fig.tight_layout() return savefig(fig, "ch11/ch11-fig-fiche-etat.pdf") def sensibilite(sens, cn): fig, ax = plt.subplots(figsize=(LARGEUR, 3.0)) for i, e in enumerate(sorted(sens["etat_structure"].unique())): s = sens[sens["etat_structure"] == e].sort_values("lambda") ax.plot(s["lambda"], s["taux"] * 100, marker="o", ms=4, color=palette.cat[i], label=f"structure en état {int(e)}") base = sens[(sens["lambda"] == cas.LAMBDA) & (sens["etat_structure"] == 3)]["taux"].iloc[0] * 100 ax.plot([cas.LAMBDA], [base], marker="*", ms=11, color=palette.brand, ls="none", zorder=5) ax.annotate(f"convention retenue : λ = {cas.LAMBDA}, état 3 → {base:.1f} %", (cas.LAMBDA, base), xytext=(0.52, 40), fontsize=7.5, arrowprops=dict(arrowstyle="-", color=palette.ink2, lw=0.7)) ax.set_xlabel("Poids λ de la note d'état dans l'âge effectif (0 = âge chronologique seul ; 1 = note d'état seule)") ax.set_ylabel("Dépréciation physique totale (% du coût neuf)") ax.set_xticks([0, 0.25, 0.5, 0.75, 1.0]); ax.set_ylim(20, 72) ax.legend(fontsize=7, ncol=3, loc="lower left") fig.tight_layout() return savefig(fig, "ch11/ch11-fig-sensibilite-age-effectif.pdf") def main(): setup() devis, sujet, cn = cas.charger() f = cas.fiche(devis, cn); trav = cas.travaux(devis, cn) print("OK", fiche_etat(f)) print("OK", sensibilite(cas.sensibilite(f, trav, cn), cn)) if __name__ == "__main__": main()