spb/traite-evaluation-qc
Public
1#!/usr/bin/env python32"""Figures ch03 — Simulation d'un cycle stock-flux (SYNTHÉTIQUE, analysis/python/cases/ch03_cycles.py, paramètres pédagogiques).31. ch03-fig-cycle-simule.pdf : quatre panneaux (demande et stock ; inoccupation ; loyer ; mises en chantier) après un choc de demande.42. ch03-fig-cycle-sensibilite.pdf : trajectoires du loyer selon le délai de livraison L et selon la réactivité de l'offre κ."""5import sys, pathlib6ROOT = pathlib.Path(__file__).resolve().parents[3]7sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))8from dataclasses import replace9import matplotlib.pyplot as plt10from traite.style import setup, palette, savefig, LARGEUR11import ch03_cycles as cas121314def fig_cycle(pc):15 df = cas.simuler_cycle(pc); ss = cas.etat_stationnaire_cycle(pc); ss0 = cas.etat_stationnaire_cycle(pc, apres_choc=False)16 x = df["annee"]17 fig, axs = plt.subplots(2, 2, figsize=(LARGEUR, 4.6), sharex=True)18 c0, c1, c2 = palette.cat[0], palette.cat[1], palette.cat[2]19 a = axs[0, 0]20 a.plot(x, df["S"] / 1000, color=c0, label="stock $S_t$")21 a.plot(x, df["D"] / 1000, color=c1, label="demande occupée $D_t$")22 a.axhline(ss["S"] / 1000, color=c0, lw=0.7, ls=":")23 a.set_ylabel("milliers de logements"); a.legend(loc="lower right", fontsize=7.5); a.set_title("A. Stock et demande", loc="left")24 a = axs[0, 1]25 a.plot(x, 100 * df["v"], color=c0)26 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)27 a.set_ylabel("%"); a.set_title("B. Taux d'inoccupation $v_t$", loc="left"); a.set_ylim(0, None)28 a = axs[1, 0]29 a.plot(x, df["R_mois"], color=c0)30 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)31 a.set_ylabel("$/mois"); a.set_title("C. Loyer $R_t$", loc="left"); a.set_xlabel("années depuis le début")32 a = axs[1, 1]33 a.plot(x, df["C_annuel"], color=c0)34 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)35 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")36 for ax in axs.flat:37 ax.axvspan(pc.t_choc / 4, (pc.t_choc + pc.n_choc) / 4, color=palette.grid, alpha=0.6, lw=0)38 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)39 fig.tight_layout(h_pad=1.0, w_pad=1.2)40 return savefig(fig, "ch03/ch03-fig-cycle-simule.pdf")414243def fig_sensibilite(pc):44 fig, axs = plt.subplots(1, 2, figsize=(LARGEUR, 2.7), sharey=True)45 ss = cas.etat_stationnaire_cycle(pc)46 for k, L in enumerate((2, 6, 12)):47 p = replace(pc, L=L); d = cas.simuler_cycle(p); s = cas.etat_stationnaire_cycle(p)48 axs[0].plot(d["annee"], 100 * (d["R"] / s["R"] - 1), color=palette.cat[k], label=f"$L$ = {L} trimestres")49 axs[0].set_title("A. Délai de livraison ($\\kappa$ = 0,48)", loc="left")50 for k, m in enumerate((1.0, 0.5, 0.25)):51 p = replace(pc, mult_c1=m); d = cas.simuler_cycle(p); s = cas.etat_stationnaire_cycle(p)52 axs[1].plot(d["annee"], 100 * (d["R"] / s["R"] - 1), color=palette.cat[k], label=f"$\\kappa$ = {p.kappa:.2f}")53 axs[1].set_title("B. Réactivité de l'offre ($L$ = 6)", loc="left")54 for ax in axs:55 ax.axhline(0, color=palette.ink2, lw=0.8, ls="--"); ax.set_xlabel("années depuis le début"); ax.legend(fontsize=7.5)56 axs[0].set_ylabel("loyer, écart au stationnaire final (%)")57 fig.tight_layout(w_pad=1.2)58 return savefig(fig, "ch03/ch03-fig-cycle-sensibilite.pdf")596061def main():62 setup()63 pc = cas.ParametresCycle()64 print("OK", fig_cycle(pc)); print("OK", fig_sensibilite(pc))656667if __name__ == "__main__":68 main()69