#!/usr/bin/env python3 """Figures ch03 — Simulation d'un cycle stock-flux (SYNTHÉTIQUE, analysis/python/cases/ch03_cycles.py, paramètres pédagogiques). 1. ch03-fig-cycle-simule.pdf : quatre panneaux (demande et stock ; inoccupation ; loyer ; mises en chantier) après un choc de demande. 2. ch03-fig-cycle-sensibilite.pdf : trajectoires du loyer selon le délai de livraison L et selon la réactivité de l'offre κ.""" 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")) from dataclasses import replace import matplotlib.pyplot as plt from traite.style import setup, palette, savefig, LARGEUR import ch03_cycles as cas def fig_cycle(pc): df = cas.simuler_cycle(pc); ss = cas.etat_stationnaire_cycle(pc); ss0 = cas.etat_stationnaire_cycle(pc, apres_choc=False) x = df["annee"] fig, axs = plt.subplots(2, 2, figsize=(LARGEUR, 4.6), sharex=True) c0, c1, c2 = palette.cat[0], palette.cat[1], palette.cat[2] a = axs[0, 0] a.plot(x, df["S"] / 1000, color=c0, label="stock $S_t$") a.plot(x, df["D"] / 1000, color=c1, label="demande occupée $D_t$") a.axhline(ss["S"] / 1000, color=c0, lw=0.7, ls=":") a.set_ylabel("milliers de logements"); a.legend(loc="lower right", fontsize=7.5); a.set_title("A. Stock et demande", loc="left") a = axs[0, 1] a.plot(x, 100 * df["v"], color=c0) a.axhline(100 * pc.v_nat, color=palette.ink2, lw=0.8, ls="--"); a.text(x.iloc[-1], 100 * pc.v_nat + 0.15, "$v^*$", ha="right", fontsize=8, color=palette.ink2) a.set_ylabel("%"); a.set_title("B. Taux d'inoccupation $v_t$", loc="left"); a.set_ylim(0, None) a = axs[1, 0] a.plot(x, df["R_mois"], color=c0) a.axhline(ss["R"] / 12, color=palette.ink2, lw=0.8, ls="--"); a.text(x.iloc[-1], ss["R"] / 12 + 8, "nouveau stationnaire", ha="right", fontsize=7.5, color=palette.ink2) a.set_ylabel("$/mois"); a.set_title("C. Loyer $R_t$", loc="left"); a.set_xlabel("années depuis le début") a = axs[1, 1] a.plot(x, df["C_annuel"], color=c0) a.axhline(ss["C"], color=palette.ink2, lw=0.8, ls="--"); a.text(x.iloc[-1], ss["C"] + 80, "$\\delta S^*$", ha="right", fontsize=8, color=palette.ink2) a.set_ylabel("logements / an (rythme annuel)"); a.set_title("D. Mises en chantier $C_t$", loc="left"); a.set_xlabel("années depuis le début") for ax in axs.flat: ax.axvspan(pc.t_choc / 4, (pc.t_choc + pc.n_choc) / 4, color=palette.grid, alpha=0.6, lw=0) axs[0, 0].text((pc.t_choc + pc.n_choc / 2) / 4, axs[0, 0].get_ylim()[1] * 0.995, "choc de\ndemande", ha="center", va="top", fontsize=7, color=palette.ink2) fig.tight_layout(h_pad=1.0, w_pad=1.2) return savefig(fig, "ch03/ch03-fig-cycle-simule.pdf") def fig_sensibilite(pc): fig, axs = plt.subplots(1, 2, figsize=(LARGEUR, 2.7), sharey=True) ss = cas.etat_stationnaire_cycle(pc) for k, L in enumerate((2, 6, 12)): p = replace(pc, L=L); d = cas.simuler_cycle(p); s = cas.etat_stationnaire_cycle(p) axs[0].plot(d["annee"], 100 * (d["R"] / s["R"] - 1), color=palette.cat[k], label=f"$L$ = {L} trimestres") axs[0].set_title("A. Délai de livraison ($\\kappa$ = 0,48)", loc="left") for k, m in enumerate((1.0, 0.5, 0.25)): p = replace(pc, mult_c1=m); d = cas.simuler_cycle(p); s = cas.etat_stationnaire_cycle(p) axs[1].plot(d["annee"], 100 * (d["R"] / s["R"] - 1), color=palette.cat[k], label=f"$\\kappa$ = {p.kappa:.2f}") axs[1].set_title("B. Réactivité de l'offre ($L$ = 6)", loc="left") for ax in axs: ax.axhline(0, color=palette.ink2, lw=0.8, ls="--"); ax.set_xlabel("années depuis le début"); ax.legend(fontsize=7.5) axs[0].set_ylabel("loyer, écart au stationnaire final (%)") fig.tight_layout(w_pad=1.2) return savefig(fig, "ch03/ch03-fig-cycle-sensibilite.pdf") def main(): setup() pc = cas.ParametresCycle() print("OK", fig_cycle(pc)); print("OK", fig_sensibilite(pc)) if __name__ == "__main__": main()