{"spec_id":"phase-diagram-pt","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nphase-diagram-pt: Thermodynamic Phase Diagram (Pressure-Temperature)\nLibrary: plotnine 0.15.5 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\nimport pandas as pd\n\n\n# Work around naming conflict: plotnine.py script shadows the plotnine package\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nif _script_dir in sys.path:\n    sys.path.remove(_script_dir)\nif \"\" in sys.path:\n    sys.path.remove(\"\")\nif \".\" in sys.path:\n    sys.path.remove(\".\")\n\nfrom plotnine import (\n    aes,\n    annotate,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_point,\n    geom_ribbon,\n    geom_vline,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    scale_y_log10,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint categorical palette (hybrid-v3 sort order)\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nANYPLOT_AMBER = \"#DDCC77\"  # warning / caution anchor — used for special point markers\n\n# Data — Water phase diagram (accurate physical constants)\n# Triple point: 273.16 K, 611.73 Pa\n# Critical point: 647.1 K, 22.064 MPa\nR = 8.314  # J/(mol·K)\nT_triple = 273.16\nP_triple = 611.73\nT_critical = 647.1\nP_critical = 22.064e6\n\n# Solid-gas boundary (sublimation curve) via Clausius-Clapeyron\nL_sub = 51060  # J/mol, latent heat of sublimation\nT_sub = np.linspace(200, T_triple, 80)\nP_sub = P_triple * np.exp((L_sub / R) * (1 / T_triple - 1 / T_sub))\n\n# Liquid-gas boundary (vaporization curve) via Clausius-Clapeyron\nL_vap = 40700  # J/mol, latent heat of vaporization\nT_vap = np.linspace(T_triple, T_critical, 100)\nP_vap = P_triple * np.exp((L_vap / R) * (1 / T_triple - 1 / T_vap))\n\n# Solid-liquid boundary (melting curve) — water's anomalous negative slope\nP_melt = np.logspace(np.log10(P_triple), np.log10(P_critical * 5), 80)\nT_melt = T_triple - 7.4e-8 * (P_melt - P_triple)\n\n# Build dataframe for boundary curves\ndf_sub = pd.DataFrame({\"temperature\": T_sub, \"pressure\": P_sub, \"boundary\": \"Solid–Gas\"})\ndf_vap = pd.DataFrame({\"temperature\": T_vap, \"pressure\": P_vap, \"boundary\": \"Liquid–Gas\"})\ndf_melt = pd.DataFrame({\"temperature\": T_melt, \"pressure\": P_melt, \"boundary\": \"Solid–Liquid\"})\ndf = pd.concat([df_sub, df_vap, df_melt], ignore_index=True)\n\n# Special points (triple + critical)\ndf_points = pd.DataFrame({\"temperature\": [T_triple, T_critical], \"pressure\": [P_triple, P_critical]})\n\n# Phase region fills — separate ribbons per region (plotnine layer composition)\nP_min = 10\nP_max = 2e9\nT_min = 180\nT_max = 750\n\nn_fill = 60\nT_solid_fill = np.linspace(T_min, T_triple - 1, n_fill)\nP_solid_lower = P_triple * np.exp((L_sub / R) * (1 / T_triple - 1 / T_solid_fill))\ndf_solid = pd.DataFrame({\"temperature\": T_solid_fill, \"ymin\": P_solid_lower, \"ymax\": np.full(n_fill, P_max)})\n\nT_gas_sub = np.linspace(T_min, T_triple, 30)\nP_gas_sub_upper = P_triple * np.exp((L_sub / R) * (1 / T_triple - 1 / T_gas_sub))\nT_gas_vap = np.linspace(T_triple, T_critical, 30)\nP_gas_vap_upper = P_triple * np.exp((L_vap / R) * (1 / T_triple - 1 / T_gas_vap))\nT_gas_beyond = np.linspace(T_critical, T_max, 15)\nP_gas_beyond_upper = np.full(15, P_critical)\ndf_gas = pd.DataFrame(\n    {\n        \"temperature\": np.concatenate([T_gas_sub, T_gas_vap, T_gas_beyond]),\n        \"ymin\": np.full(75, P_min),\n        \"ymax\": np.concatenate([P_gas_sub_upper, P_gas_vap_upper, P_gas_beyond_upper]),\n    }\n)\n\nT_liq = np.linspace(T_triple, T_critical, n_fill)\nP_liq_lower = P_triple * np.exp((L_vap / R) * (1 / T_triple - 1 / T_liq))\ndf_liquid = pd.DataFrame({\"temperature\": T_liq, \"ymin\": P_liq_lower, \"ymax\": np.full(n_fill, P_max)})\n\nT_sc = np.linspace(T_critical, T_max, 30)\ndf_supercritical = pd.DataFrame({\"temperature\": T_sc, \"ymin\": np.full(30, P_critical), \"ymax\": np.full(30, P_max)})\n\n# Imprint palette: boundaries in canonical order (positions 1–3)\nboundary_colors = {\n    \"Solid–Gas\": IMPRINT_PALETTE[0],  # brand green\n    \"Liquid–Gas\": IMPRINT_PALETTE[1],  # lavender\n    \"Solid–Liquid\": IMPRINT_PALETTE[2],  # blue\n}\n\n# Pressure axis: pre-computed breaks and labels spanning 9 log-scale decades\npressure_breaks = [10, 100, 1000, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]\npressure_labels = [\"10 Pa\", \"100 Pa\", \"1 kPa\", \"10 kPa\", \"100 kPa\", \"1 MPa\", \"10 MPa\", \"100 MPa\", \"1 GPa\"]\n\ntitle = \"phase-diagram-pt · python · plotnine · anyplot.ai\"\nn = len(title)\ntitle_fontsize = round(12 * 67 / n) if n > 67 else 12\n\nplot = (\n    ggplot()\n    # Phase region fills — color matches corresponding boundary curve\n    + geom_ribbon(\n        data=df_solid,\n        mapping=aes(x=\"temperature\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT_PALETTE[2],\n        alpha=0.15,\n        inherit_aes=False,\n    )\n    + geom_ribbon(\n        data=df_gas,\n        mapping=aes(x=\"temperature\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT_PALETTE[0],\n        alpha=0.15,\n        inherit_aes=False,\n    )\n    + geom_ribbon(\n        data=df_liquid,\n        mapping=aes(x=\"temperature\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT_PALETTE[1],\n        alpha=0.15,\n        inherit_aes=False,\n    )\n    + geom_ribbon(\n        data=df_supercritical,\n        mapping=aes(x=\"temperature\", ymin=\"ymin\", ymax=\"ymax\"),\n        fill=IMPRINT_PALETTE[3],\n        alpha=0.15,\n        inherit_aes=False,\n    )\n    # Boundary curves — Imprint palette canonical order\n    + geom_line(data=df, mapping=aes(x=\"temperature\", y=\"pressure\", color=\"boundary\"), size=1.5)\n    # Triple point and critical point: amber fill, ink border\n    + geom_point(\n        data=df_points,\n        mapping=aes(x=\"temperature\", y=\"pressure\"),\n        color=INK,\n        fill=ANYPLOT_AMBER,\n        size=4.5,\n        stroke=1.2,\n        shape=\"o\",\n        inherit_aes=False,\n    )\n    + scale_color_manual(values=boundary_colors)\n    + scale_y_log10(breaks=pressure_breaks, labels=pressure_labels, limits=(P_min, P_max))\n    + scale_x_continuous(limits=(T_min, T_max))\n    # Phase region labels — color matches fill color for visual coherence\n    + annotate(\"text\", x=218, y=5e7, label=\"SOLID\", size=3.5, color=IMPRINT_PALETTE[2], alpha=0.75, fontweight=\"bold\")\n    + annotate(\"text\", x=460, y=5e7, label=\"LIQUID\", size=3.5, color=IMPRINT_PALETTE[1], alpha=0.75, fontweight=\"bold\")\n    + annotate(\"text\", x=490, y=50, label=\"GAS\", size=3.5, color=IMPRINT_PALETTE[0], alpha=0.75, fontweight=\"bold\")\n    + annotate(\n        \"text\",\n        x=698,\n        y=2e8,\n        label=\"SUPERCRITICAL\",\n        size=2.8,\n        color=IMPRINT_PALETTE[3],\n        alpha=0.75,\n        fontweight=\"bold\",\n        ha=\"center\",\n    )\n    # Triple point coordinate annotation\n    + annotate(\n        \"text\",\n        x=T_triple + 18,\n        y=P_triple * 0.25,\n        label=f\"Triple Point\\n({T_triple} K, {P_triple:.0f} Pa)\",\n        size=2.8,\n        color=INK_SOFT,\n        ha=\"left\",\n        va=\"top\",\n    )\n    # Critical point coordinate annotation\n    + annotate(\n        \"text\",\n        x=T_critical - 15,\n        y=P_critical * 0.15,\n        label=f\"Critical Point\\n({T_critical} K, {P_critical / 1e6:.1f} MPa)\",\n        size=2.8,\n        color=INK_SOFT,\n        ha=\"right\",\n        va=\"top\",\n    )\n    # Dashed vertical line at critical temperature\n    + geom_vline(xintercept=T_critical, linetype=\"dashed\", color=INK_MUTED, size=0.4, alpha=0.5)\n    + labs(x=\"Temperature (K)\", y=\"Pressure\", title=title, color=\"Phase Boundary\")\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        text=element_text(size=7, color=INK_SOFT),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        plot_title=element_text(size=title_fontsize, color=INK),\n        legend_title=element_text(size=8, color=INK),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_position=\"right\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        panel_background=element_rect(fill=PAGE_BG),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_grid_minor=element_blank(),\n        axis_line_x=element_line(color=INK_SOFT, size=0.5),\n        axis_line_y=element_line(color=INK_SOFT, size=0.5),\n        panel_border=element_rect(color=INK_SOFT, fill=None),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}