{"spec_id":"phase-diagram-pt","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nphase-diagram-pt: Thermodynamic Phase Diagram (Pressure-Temperature)\nLibrary: bokeh 3.9.1 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-08\n\"\"\"\n\nimport io\nimport os\nimport sys\n\n\n# Prevent self-import: this file is named bokeh.py, so Python adds its directory\n# to sys.path[0] and finds this script instead of the installed bokeh package.\n_here = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not (p and os.path.abspath(p) == _here)]\ndel _here\n\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import Arrow, ColumnDataSource, HoverTool, Label, NormalHead, Span\nfrom bokeh.plotting import figure\nfrom PIL import Image\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\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 — 8 hues, theme-independent\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Data — Water phase diagram (realistic Clausius-Clapeyron curves)\n# Triple point: 273.16 K, 611.73 Pa | Critical point: 647.1 K, 22.064 MPa\ntriple_T = 273.16\ntriple_P = 611.73\ncritical_T = 647.1\ncritical_P = 22.064e6\nR = 8.314\n\n# Sublimation curve (solid-gas boundary, 200 K → triple point)\nT_solid_gas = np.linspace(200, triple_T, 100)\nL_sub = 51059  # J/mol, sublimation enthalpy of water\nP_solid_gas = triple_P * np.exp((L_sub / R) * (1 / triple_T - 1 / T_solid_gas))\n\n# Vaporization curve (liquid-gas boundary, triple point → critical point)\nT_liquid_gas = np.linspace(triple_T, critical_T, 150)\nL_vap = 40700  # J/mol, vaporization enthalpy of water\nP_liquid_gas = triple_P * np.exp((L_vap / R) * (1 / triple_T - 1 / T_liquid_gas))\n\n# Melting curve (solid-liquid boundary, negative slope — water anomaly)\nP_solid_liquid = np.logspace(np.log10(triple_P), np.log10(critical_P * 5), 100)\ndelta_P = np.maximum(P_solid_liquid - triple_P, 0)\nT_solid_liquid = triple_T - delta_P * 7.4e-8 + np.power(delta_P / 1e9, 1.5) * 5\n\n# Title — len=46 < 67 baseline, no shrinking needed\ntitle = \"phase-diagram-pt · python · bokeh · anyplot.ai\"\n\n# Plot\np = figure(\n    width=3200,\n    height=1800,\n    title=title,\n    x_axis_label=\"Temperature (K)\",\n    y_axis_label=\"Pressure (Pa)\",\n    y_axis_type=\"log\",\n    toolbar_location=None,\n    tools=\"\",\n    x_range=(180, 800),\n    y_range=(50, 5e8),\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\n# Phase region fills — Imprint palette with semantic color cues\n# Ice → cyan (#2ABCCD), water → blue (#4467A3), steam → ochre (#BD8233), supercritical → purple (#C475FD)\nsolid_T = [180, 180] + T_solid_gas.tolist() + T_solid_liquid.tolist()[::-1]\nsolid_P = [5e8, 100] + P_solid_gas.tolist() + P_solid_liquid.tolist()[::-1]\np.patch(solid_T, solid_P, fill_color=\"#2ABCCD\", fill_alpha=0.18, line_color=None)\n\ngas_T = T_solid_gas.tolist() + [triple_T] + T_liquid_gas.tolist() + [800, 800, 180]\ngas_P = P_solid_gas.tolist() + [triple_P] + P_liquid_gas.tolist() + [critical_P, 100, 100]\np.patch(gas_T, gas_P, fill_color=\"#BD8233\", fill_alpha=0.18, line_color=None)\n\nliquid_T = [triple_T] + T_liquid_gas.tolist() + [critical_T] + T_solid_liquid.tolist()[::-1]\nliquid_P = [triple_P] + P_liquid_gas.tolist() + [5e8] + P_solid_liquid.tolist()[::-1]\np.patch(liquid_T, liquid_P, fill_color=\"#4467A3\", fill_alpha=0.18, line_color=None)\n\nsc_T = [critical_T, 800, 800, critical_T]\nsc_P = [critical_P, critical_P, 5e8, 5e8]\np.patch(sc_T, sc_P, fill_color=\"#C475FD\", fill_alpha=0.15, line_color=None)\n\n# Phase boundary lines — INK_SOFT (structural chrome, same weight for all boundaries)\np.line(T_solid_gas, P_solid_gas, line_width=4, line_color=INK_SOFT, line_alpha=0.85)\np.line(T_liquid_gas, P_liquid_gas, line_width=4, line_color=INK_SOFT, line_alpha=0.85)\np.line(T_solid_liquid, P_solid_liquid, line_width=4, line_color=INK_SOFT, line_alpha=0.85)\n\n# Boundary curve labels — italic, secondary ink, theme-adaptive background fill\np.add_layout(\n    Label(\n        x=215,\n        y=150,\n        text=\"Sublimation\",\n        text_font_size=\"22pt\",\n        text_color=INK_MUTED,\n        text_font_style=\"italic\",\n        angle=0.7,\n        background_fill_color=PAGE_BG,\n        background_fill_alpha=0.75,\n        level=\"overlay\",\n    )\n)\np.add_layout(\n    Label(\n        x=382,\n        y=3e4,\n        text=\"Vaporization\",\n        text_font_size=\"22pt\",\n        text_color=INK_MUTED,\n        text_font_style=\"italic\",\n        angle=0.42,\n        background_fill_color=PAGE_BG,\n        background_fill_alpha=0.75,\n        level=\"overlay\",\n    )\n)\n# Melting label repositioned horizontally to avoid cramping against the nearly-vertical curve\np.add_layout(\n    Label(\n        x=278,\n        y=3e7,\n        text=\"Melting\",\n        text_font_size=\"22pt\",\n        text_color=INK_MUTED,\n        text_font_style=\"italic\",\n        angle=0,\n        background_fill_color=PAGE_BG,\n        background_fill_alpha=0.75,\n        level=\"overlay\",\n    )\n)\n\n# Triple point — matte red (#AE3030, semantic special condition) with enlarged glow ring\ntp_source = ColumnDataSource(\n    data={\n        \"x\": [triple_T],\n        \"y\": [triple_P],\n        \"name\": [\"Triple Point\"],\n        \"temp\": [\"273.16 K\"],\n        \"pres\": [\"611.73 Pa\"],\n        \"desc\": [\"All three phases coexist\"],\n    }\n)\np.scatter(x=\"x\", y=\"y\", source=tp_source, size=52, color=\"#AE3030\", alpha=0.15, marker=\"circle\")\ntp_glyph = p.scatter(\n    x=\"x\", y=\"y\", source=tp_source, size=34, color=\"#AE3030\", marker=\"circle\", line_color=PAGE_BG, line_width=3\n)\n\n# Critical point — Imprint blue (#4467A3), diamond marker\ncp_source = ColumnDataSource(\n    data={\n        \"x\": [critical_T],\n        \"y\": [critical_P],\n        \"name\": [\"Critical Point\"],\n        \"temp\": [\"647.1 K\"],\n        \"pres\": [\"22.064 MPa\"],\n        \"desc\": [\"Liquid-gas distinction vanishes\"],\n    }\n)\np.scatter(x=\"x\", y=\"y\", source=cp_source, size=56, color=\"#4467A3\", alpha=0.15, marker=\"diamond\")\ncp_glyph = p.scatter(\n    x=\"x\", y=\"y\", source=cp_source, size=40, color=\"#4467A3\", marker=\"diamond\", line_color=PAGE_BG, line_width=3\n)\n\n# HoverTool — Bokeh-specific interactive feature for special points\nhover = HoverTool(\n    renderers=[tp_glyph, cp_glyph],\n    tooltips=[(\"Point\", \"@name\"), (\"Temperature\", \"@temp\"), (\"Pressure\", \"@pres\"), (\"Significance\", \"@desc\")],\n    point_policy=\"snap_to_data\",\n)\np.add_tools(hover)\n\n# Span reference lines at critical coordinates — Bokeh distinctive feature\np.add_layout(\n    Span(\n        location=critical_P, dimension=\"width\", line_color=INK_MUTED, line_width=1.5, line_alpha=0.3, line_dash=\"dotted\"\n    )\n)\np.add_layout(\n    Span(\n        location=critical_T,\n        dimension=\"height\",\n        line_color=INK_MUTED,\n        line_width=1.5,\n        line_alpha=0.3,\n        line_dash=\"dotted\",\n    )\n)\n\n# Dashed extension above critical point marking where liquid-gas boundary terminates\np.line(\n    [critical_T, critical_T], [critical_P, 5e8], line_width=3, line_color=INK_SOFT, line_alpha=0.4, line_dash=\"dashed\"\n)\n\n# Phase region labels — bold, INK color, placed within each region\np.add_layout(\n    Label(x=205, y=5e6, text=\"SOLID\", text_font_size=\"30pt\", text_color=INK, text_alpha=0.5, text_font_style=\"bold\")\n)\np.add_layout(\n    Label(x=390, y=5e7, text=\"LIQUID\", text_font_size=\"30pt\", text_color=INK, text_alpha=0.5, text_font_style=\"bold\")\n)\np.add_layout(\n    Label(x=490, y=700, text=\"GAS\", text_font_size=\"30pt\", text_color=INK, text_alpha=0.5, text_font_style=\"bold\")\n)\n# Supercritical label repositioned from right edge toward center of the region\np.add_layout(\n    Label(\n        x=658,\n        y=9e7,\n        text=\"SUPERCRITICAL\\nFLUID\",\n        text_font_size=\"22pt\",\n        text_color=INK_SOFT,\n        text_alpha=0.65,\n        text_font_style=\"bold\",\n    )\n)\n\n# Triple point annotation with arrow\np.add_layout(\n    Label(\n        x=triple_T + 22,\n        y=triple_P * 0.12,\n        text=\"Triple Point\\n(273.16 K, 611.73 Pa)\",\n        text_font_size=\"18pt\",\n        text_color=\"#AE3030\",\n        text_font_style=\"bold\",\n        background_fill_color=ELEVATED_BG,\n        background_fill_alpha=0.88,\n    )\n)\np.add_layout(\n    Arrow(\n        end=NormalHead(size=12, fill_color=\"#AE3030\", line_color=\"#AE3030\"),\n        x_start=triple_T + 22,\n        y_start=triple_P * 0.58,\n        x_end=triple_T + 2,\n        y_end=triple_P * 0.95,\n        line_color=\"#AE3030\",\n        line_alpha=0.7,\n        line_width=2,\n    )\n)\n\n# Critical point annotation with arrow\np.add_layout(\n    Label(\n        x=critical_T - 160,\n        y=critical_P * 4,\n        text=\"Critical Point\\n(647.1 K, 22.06 MPa)\",\n        text_font_size=\"18pt\",\n        text_color=\"#4467A3\",\n        text_font_style=\"bold\",\n        background_fill_color=ELEVATED_BG,\n        background_fill_alpha=0.88,\n    )\n)\np.add_layout(\n    Arrow(\n        end=NormalHead(size=12, fill_color=\"#4467A3\", line_color=\"#4467A3\"),\n        x_start=critical_T - 55,\n        y_start=critical_P * 3.5,\n        x_end=critical_T - 2,\n        y_end=critical_P * 1.1,\n        line_color=\"#4467A3\",\n        line_alpha=0.7,\n        line_width=2,\n    )\n)\n\n# Style — Imprint sizing standards (50pt/42pt/34pt) + theme-adaptive chrome\np.title.text_font_size = \"50pt\"\np.title.text_color = INK\np.title.text_font_style = \"bold\"\n\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.12\np.ygrid.grid_line_alpha = 0.12\n\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\n# Save HTML (interactive catalog artifact) then screenshot via headless Chrome\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H + 200}\",\n    \"--hide-scrollbars\",\n    \"--force-device-scale-factor=1\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H + 200)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\nraw = driver.get_screenshot_as_png()\ndriver.quit()\nImage.open(io.BytesIO(raw)).crop((0, 0, W, H)).save(f\"plot-{THEME}.png\")\n"}