{"spec_id":"phase-diagram-pt","library":"muix","language":"javascript","code":"// anyplot.ai\n// phase-diagram-pt: Thermodynamic Phase Diagram (Pressure-Temperature)\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-20\n\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\nimport Box from \"@mui/material/Box\";\nimport Typography from \"@mui/material/Typography\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst W = window.ANYPLOT_SIZE.width;\nconst H = window.ANYPLOT_SIZE.height;\nconst TITLE_H = 60;\n\n// ChartsYAxis places the rotated axis label at `tickFontSize + tickSize + 10`\n// px from the axis line (its only lever for that offset, independent of the\n// actual rendered tick fontSize, which tickLabelStyle controls below) — sized\n// to clear the widest pressure tick label (\"100 kPa\").\nconst Y_AXIS_LABEL_OFFSET_PX = 85;\n\n// --- Data (representative water phase diagram, deterministic) --------------\n// Boundary curves follow the Clausius-Clapeyron relation (sublimation,\n// vaporization) and the Simon-Glatzel equation (melting, which captures\n// water's anomalous negative solid-liquid slope), each anchored to the\n// real IAPWS triple point and critical point of water.\n\nconst R = 8.314; // J / (mol K)\nconst TRIPLE_T = 273.16; // K\nconst TRIPLE_P = 611.657; // Pa\nconst CRITICAL_T = 647.1; // K\nconst CRITICAL_P = 22.064e6; // Pa\n\nfunction makeSublimation() {\n  // Solid-gas boundary: L_sub ≈ 51 kJ/mol\n  const LR = 51000 / R;\n  const n = 30;\n  const T = [];\n  const P = [];\n  for (let i = 0; i < n; i += 1) {\n    const temp = 200 + (i * (TRIPLE_T - 200)) / (n - 1);\n    T.push(temp);\n    P.push(TRIPLE_P * Math.exp(-LR * (1 / temp - 1 / TRIPLE_T)));\n  }\n  return { T, P };\n}\n\nfunction makeVaporization() {\n  // Liquid-gas boundary, two Clausius-Clapeyron segments (triple->boiling,\n  // boiling->critical) so the curve passes through the normal boiling point.\n  const BOIL_T = 373.15;\n  const BOIL_P = 101325;\n  const LR1 = 43364 / R;\n  const LR2 = 39448 / R;\n  const n1 = 18;\n  const n2 = 24;\n  const T = [];\n  const P = [];\n  for (let i = 0; i < n1; i += 1) {\n    const temp = TRIPLE_T + (i * (BOIL_T - TRIPLE_T)) / (n1 - 1);\n    T.push(temp);\n    P.push(TRIPLE_P * Math.exp(-LR1 * (1 / temp - 1 / TRIPLE_T)));\n  }\n  for (let i = 1; i < n2; i += 1) {\n    const temp = BOIL_T + (i * (CRITICAL_T - BOIL_T)) / (n2 - 1);\n    T.push(temp);\n    P.push(BOIL_P * Math.exp(-LR2 * (1 / temp - 1 / BOIL_T)));\n  }\n  T[T.length - 1] = CRITICAL_T;\n  P[P.length - 1] = CRITICAL_P;\n  return { T, P };\n}\n\nfunction makeMelting() {\n  // Solid-liquid boundary via the Simon-Glatzel equation for ice Ih, whose\n  // negative \"a\" reproduces water's anomalous negative melting slope.\n  const a = -395.2e6;\n  const c = 9;\n  const n = 36;\n  const pMax = 2.4e8;\n  const logLo = Math.log10(TRIPLE_P);\n  const logHi = Math.log10(pMax);\n  const T = [];\n  const P = [];\n  for (let i = 0; i < n; i += 1) {\n    const p = Math.pow(10, logLo + (i * (logHi - logLo)) / (n - 1));\n    const temp = TRIPLE_T * Math.pow(1 + (p - TRIPLE_P) / a, 1 / c);\n    T.push(temp);\n    P.push(p);\n  }\n  return { T: T.reverse(), P: P.reverse() };\n}\n\nconst melting = makeMelting();\nconst vaporization = makeVaporization();\nconst sublimation = makeSublimation();\n\nconst X_MIN = 190;\nconst X_MAX = 700;\nconst Y_MIN = 0.1;\nconst Y_MAX = 3e8;\n\nconst formatPressure = (v) => {\n  if (v >= 1e6) return `${v / 1e6} MPa`;\n  if (v >= 1e3) return `${v / 1e3} kPa`;\n  return `${v} Pa`;\n};\n\n// --- Phase-region labels, point markers and the supercritical guide --------\n// (annotations are explicitly required by the spec: triple/critical points\n// marked + annotated, phase regions clearly labeled)\n\nfunction PhaseAnnotations() {\n  const xScale = useXScale(\"x-melt\");\n  const yScale = useYScale(\"pressure\");\n\n  const tripleX = xScale(TRIPLE_T);\n  const tripleY = yScale(TRIPLE_P);\n  const criticalX = xScale(CRITICAL_T);\n  const criticalY = yScale(CRITICAL_P);\n  const topY = yScale(Y_MAX);\n  const rightX = xScale(X_MAX);\n\n  const guide = {\n    stroke: t.inkSoft,\n    strokeDasharray: \"8 6\",\n    strokeWidth: 1.5,\n    opacity: 0.55,\n  };\n  const regionLabel = {\n    fill: t.ink,\n    fontSize: 22,\n    fontWeight: 600,\n    opacity: 0.5,\n    textAnchor: \"middle\",\n  };\n\n  return (\n    <g>\n      {/* Supercritical-fluid boundary, extending from the critical point */}\n      <line x1={criticalX} x2={criticalX} y1={criticalY} y2={topY} {...guide} />\n      <line x1={criticalX} x2={rightX} y1={criticalY} y2={criticalY} {...guide} />\n\n      {/* Phase region labels */}\n      <text x={xScale(235)} y={yScale(2e5)} {...regionLabel}>\n        Solid\n      </text>\n      <text x={xScale(460)} y={yScale(8e6)} {...regionLabel}>\n        Liquid\n      </text>\n      <text x={xScale(430)} y={yScale(30)} {...regionLabel}>\n        Gas\n      </text>\n      <text\n        x={xScale(678)}\n        y={yScale(8e7)}\n        fill={t.inkSoft}\n        fontSize={17}\n        fontWeight={500}\n        textAnchor=\"middle\"\n      >\n        <tspan x={xScale(678)} dy=\"0\">\n          Supercritical\n        </tspan>\n        <tspan x={xScale(678)} dy=\"1.2em\">\n          fluid\n        </tspan>\n      </text>\n\n      {/* Triple point */}\n      <circle cx={tripleX} cy={tripleY} r={9} fill={t.ink} stroke={t.pageBg} strokeWidth={2} />\n      <text x={tripleX + 16} y={tripleY - 10} fill={t.ink} fontSize={16} fontWeight={600}>\n        Triple point\n      </text>\n      <text x={tripleX + 16} y={tripleY + 10} fill={t.inkSoft} fontSize={14}>\n        273.16 K, 611.7 Pa\n      </text>\n\n      {/* Critical point — both lines sit above the point, clear of the\n          incoming vaporization curve and the vertical supercritical guide */}\n      <circle cx={criticalX} cy={criticalY} r={10} fill={t.ink} stroke={t.pageBg} strokeWidth={2} />\n      <text\n        x={criticalX - 18}\n        y={criticalY - 34}\n        fill={t.ink}\n        fontSize={16}\n        fontWeight={600}\n        textAnchor=\"end\"\n      >\n        Critical point\n      </text>\n      <text\n        x={criticalX - 18}\n        y={criticalY - 14}\n        fill={t.inkSoft}\n        fontSize={14}\n        textAnchor=\"end\"\n      >\n        647.1 K, 22.06 MPa\n      </text>\n    </g>\n  );\n}\n\n// --- Chart (default-exported component — the harness mounts it) -------------\n\nexport default function Chart() {\n  return (\n    <Box\n      sx={{\n        width: W,\n        height: H,\n        display: \"flex\",\n        flexDirection: \"column\",\n        bgcolor: t.pageBg,\n      }}\n    >\n      <Typography\n        sx={{\n          height: TITLE_H,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          color: t.ink,\n          fontSize: 34,\n          fontWeight: 500,\n          flexShrink: 0,\n        }}\n      >\n        phase-diagram-pt · javascript · muix · anyplot.ai\n      </Typography>\n      <LineChart\n        width={W}\n        height={H - TITLE_H}\n        skipAnimation\n        grid={{ horizontal: true }}\n        xAxis={[\n          {\n            id: \"x-melt\",\n            data: melting.T,\n            scaleType: \"linear\",\n            min: X_MIN,\n            max: X_MAX,\n            label: \"Temperature (K)\",\n            labelStyle: { fontSize: 18 },\n            tickLabelStyle: { fontSize: 15 },\n            tickNumber: 9,\n          },\n          { id: \"x-vap\", data: vaporization.T, scaleType: \"linear\", min: X_MIN, max: X_MAX },\n          { id: \"x-sub\", data: sublimation.T, scaleType: \"linear\", min: X_MIN, max: X_MAX },\n        ]}\n        yAxis={[\n          {\n            id: \"pressure\",\n            scaleType: \"log\",\n            min: Y_MIN,\n            max: Y_MAX,\n            label: \"Pressure (Pa, log scale)\",\n            labelStyle: { fontSize: 18 },\n            tickLabelStyle: { fontSize: 15 },\n            tickFontSize: Y_AXIS_LABEL_OFFSET_PX,\n            tickInterval: [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8],\n            valueFormatter: formatPressure,\n          },\n        ]}\n        series={[\n          {\n            id: \"melting\",\n            xAxisId: \"x-melt\",\n            yAxisId: \"pressure\",\n            data: melting.P,\n            label: \"Solid–Liquid boundary (melting)\",\n            color: t.palette[0],\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n          {\n            id: \"vaporization\",\n            xAxisId: \"x-vap\",\n            yAxisId: \"pressure\",\n            data: vaporization.P,\n            label: \"Liquid–Gas boundary (vaporization)\",\n            color: t.palette[1],\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n          {\n            id: \"sublimation\",\n            xAxisId: \"x-sub\",\n            yAxisId: \"pressure\",\n            data: sublimation.P,\n            label: \"Solid–Gas boundary (sublimation)\",\n            color: t.palette[2],\n            showMark: false,\n            curve: \"monotoneX\",\n          },\n        ]}\n        slotProps={{\n          legend: {\n            position: { vertical: \"bottom\", horizontal: \"middle\" },\n            direction: \"row\",\n            labelStyle: { fontSize: 15 },\n          },\n        }}\n        sx={{\n          \".MuiLineElement-series-melting\": { strokeWidth: 3.5 },\n          \".MuiLineElement-series-vaporization\": { strokeWidth: 3.5 },\n          \".MuiLineElement-series-sublimation\": { strokeWidth: 3.5 },\n        }}\n        margin={{ left: 165, right: 60, top: 30, bottom: 120 }}\n      >\n        <PhaseAnnotations />\n      </LineChart>\n    </Box>\n  );\n}\n"}