« home

Convex Hull of Stability

materials sciencethermodynamicsphase diagramcetztikz

Reproduced from Bartel et al. http://dx.doi.org/10.1007/s10853-022-06915-4. The convex hull of stability represents the lowest energy surface in composition space for a given chemical system. It is constructed by connecting the energies of the most thermodynamically stable compounds at each composition. For visual clarity, only ground-state phases at each composition are shown. Legend:

Materials on the hull are stable against decomposition into other phases. The energy difference between a material and the hull (hull distance) quantifies its thermodynamic stability - larger distances meaning less stable and harder to synthesize, making the convex hull a powerful tool for guiding experimental synthesis efforts.


Convex Hull of Stability

  Download

PNGPDFSVG

  Code

  LaTeX

convex-hull-of-stability.tex (81 lines)

\documentclass[border=2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, positioning, calc, intersections, shapes.misc}

\begin{document}
\begin{tikzpicture}[
    >=Stealth,
    point/.style={circle, fill, inner sep=3pt},
    unstable/.style={rectangle, draw, fill=red, inner sep=3pt},
    font=\Large,
    line width=0.5pt,
    reaction box/.style={rounded corners, draw=#1, fill=#1!5, text=#1, align=left, font=\small},
]
% Define the main coordinates
\coordinate (Origin) at (0,0);
\coordinate (TopLeft) at (0,8);
\coordinate (BottomRight) at (14,0);
\coordinate (TopRight) at (14,9);
\coordinate (AX) at (7,2);
\coordinate (A2X5) at (9.5,1.5);

% Axes
\draw[->] (Origin) -- ($(TopLeft)+(0,1)$);
\node[rotate=90, anchor=center] at ($(Origin)!0.5!(TopLeft)-(1,0)$) {$\Delta E_f$ (energy/atom)};
\draw (Origin) -- (BottomRight);
\draw[->] (BottomRight) -- (TopRight);
\node[below] at ($(Origin)!0.5!(BottomRight)$) {$x$ in $A_{1-x}X_x$};

% Convex hull
\node[blue!70!black, align=left, fill=white, inner sep=1pt] at (14,5) {convex hull\\of stability};
\draw[blue!70!black, line width=2.5pt, name path=hull]
    (TopLeft) -- (AX) -- (A2X5) -- (14,7);

% Stable points
\node[point, blue!70!black] (A) at (TopLeft) {};
\node[point, blue!70!black] (AX) at (AX) {};
\node[point, blue!70!black] (A2X5) at (A2X5) {};
\node[point, blue!70!black] (X) at (14,7) {};

% Labels for stable points
\node[right=3pt of A] {A};
\node[below=3pt of AX] {AX};
\node[below=3pt of A2X5] {$A_2X_5$};
\node[left=3pt of X] {X};

% Unstable points
\node[unstable] (A2X) at ($(A)!0.5!(AX)+(0,2)$) {};
\node[unstable] (A2X7) at ($(A2X5)!0.15!(X)+(0,1.4)$) {};

% Labels for unstable points
\node[above=0pt of A2X] {$A_2X$};
\node[above left=-3pt of A2X7] {$A_2X_7$};

% Delta E arrows
\coordinate (A2X_hull) at ($(A)!0.5!(AX)$);
\draw[->, red, line width=1.5pt] (A2X_hull) -- (A2X) node[pos=0.65, left, red] {$\Delta E_d$};
\node[reaction box=red] at ($(A)!0.5!(AX)+(1.6,1.2)$) {$A + AX \to A_2X$};

% Hypothetical hull
\draw[gray, dashed, line width=1.5pt] (AX) -- (A2X7) -- (X);
\node[gray, above=3cm of $(A2X5)!0.1!(X)$, align=center] {hypothetical hull for\\evaluating $A_2X_5$};

% Delta E_d arrow for hypothetical hull
\coordinate (hull_midpoint) at ($(AX)!0.78!(A2X7)$);
\draw[->, green!50!black, line width=1.5pt] (hull_midpoint) -- (A2X5) node[pos=0.6, left, green!50!black] {$\Delta E_d$} node[reaction box=green!50!black,pos=0.6, right=0.1, line width=0.5pt] {$4/5 AX + 3/5 A_2X_7 \to A_2X_5$};

% Chemical potential range (orange line)
\coordinate (OrangeLow) at (0,5);
\draw[orange, dashed, line width=1.5pt] (OrangeLow) -- (AX)
    node[pos=0.4, above, sloped, align=center] {$\mu_A$ range\\where $AX$ is stable};

% Orange double arrow
\draw[<->, orange, line width=1.5pt] ($(TopLeft)+(0.2,-0.2)$) -- ($(OrangeLow)+(0.2,0)$);

% Legend
\node[point, blue!70!black, label={right:stable}] at (0.5,1) {};
\node[unstable, label={right:unstable}] at (0.5,0.4) {};

\end{tikzpicture}
\end{document}

  Typst

convex-hull-of-stability.typ (189 lines)

#import "@preview/cetz:0.3.2": canvas, draw, vector
#import draw: line, content, rect, circle, intersections

#set page(width: auto, height: auto, margin: 3pt)

#canvas({
  // Diagram dimensions and styles
  let width = 12
  let height = 8
  let point_radius = 0.15
  let line_thickness = 1.5pt
  let arrow_style = (mark: (end: "stealth"), stroke: black + line_thickness, fill: black)
  let hull_style = (stroke: blue.darken(20%) + 2.5pt)
  let hyp_hull_style = (stroke: (paint: gray, thickness: line_thickness, dash: "dashed"))

  // Draw axes first to establish named positions
  line((0, 0), (0, height), ..arrow_style, name: "y-axis-left")
  line((0, 0), (width, 0), ..arrow_style, name: "x-axis")
  line((width, 0), (width, height), ..arrow_style, name: "y-axis-right")

  // Draw stable points
  let stable_point(pos, label, anchor: "north", padding: none, ..rest) = {
    circle(pos, radius: point_radius, fill: blue.darken(20%), ..rest)
    content(pos, label, anchor: anchor, padding: padding)
  }

  stable_point((0, height - 1), "A", anchor: "west", padding: (left: 10pt), name: "a")
  stable_point((width / 2, 2), "AX", anchor: "north", padding: (top: 10pt), name: "ax")
  stable_point((width * 5 / 7, 1.5), $A_2X_5$, anchor: "north", padding: (top: 10pt), name: "a2x5")
  stable_point((width, height - 1.5), "X", anchor: "east", padding: (right: 10pt), name: "x")

  // Draw unstable points
  let unstable_point(pos, label, ..rest) = {
    let (x, y) = pos
    rect((x, y - 0.15), (x + 0.3, y + 0.15), fill: red, stroke: .5pt, ..rest)
    content(pos, label, anchor: "south", padding: (bottom: 8pt))
  }

  unstable_point((width / 3, height - 1.5), $A_2X$, name: "a2x")
  unstable_point((width * 7 / 9, 3.7), $A_2X_7$, name: "a2x7")

  // Draw convex hull
  line("a", "ax", ..hull_style, name: "hull-a-ax")
  line("ax", "a2x5", ..hull_style, name: "hull-ax-a2x5")
  line("a2x5", "x", ..hull_style, name: "hull-a2x5-x")
  content(
    (rel: (-1.8, -.8), to: "ax"),
    text(fill: blue.darken(20%), size: 12pt)[convex hull\ of stability],
    frame: "rect",
    stroke: none,
    padding: (left: 5pt),
    fill: white,
    name: "hull-label",
  )
  line(
    "hull-label.north",
    "hull-a-ax.90%",
    stroke: (paint: blue.darken(20%), thickness: .5pt),
    padding: 1pt,
    name: "hull-label-line",
  )

  // Draw hypothetical hull
  line("ax", "a2x7", ..hyp_hull_style, name: "hyp-hull-ax-a2x7")
  line("a2x7", "x", ..hyp_hull_style, name: "hyp-hull-a2x7-x")
  content(
    (rel: (0, 0.3), to: "hyp-hull-a2x7-x.mid"),
    text(fill: gray, size: 13pt)[hypothetical hull for\ evaluating $A_2X_5$],
    anchor: "east",
  )

  // Draw decomposition energy arrows
  // First draw invisible lines to find intersections
  line(
    (rel: (0, 3), to: "a2x"),
    (rel: (0, -3), to: "a2x"),
    stroke: none,
    name: "a2x-vertical",
  )
  intersections("a2x-isect", "a2x-vertical", "hull-a-ax", "hull-ax-a2x5")

  // Draw arrow between intersection points
  line(
    "a2x-isect.0",
    "a2x",
    mark: (end: ">", fill: red),
    stroke: red + line_thickness,
    name: "arrow-a2x",
  )
  content(
    (rel: (-0.5, 0), to: "arrow-a2x.60%"),
    text(fill: red)[$Delta E_d$],
  )
  content(
    (rel: (.2, 0), to: "arrow-a2x.30%"),
    text(fill: red, size: 12pt)[A + AX → A₂X],
    frame: "rect",
    padding: (1pt, 3pt),
    stroke: red + .3pt,
    name: "box1",
    anchor: "west",
    fill: red.lighten(90%),
  )

  // Second arrow - find intersections first
  line(
    (rel: (0, 3), to: "a2x5"),
    (rel: (0, -3), to: "a2x5"),
    stroke: none,
    name: "a2x5-vertical",
  )
  intersections("a2x5-isect", "a2x5-vertical", "hyp-hull-ax-a2x7", "hyp-hull-a2x7-x")

  // Draw arrow between intersection points
  line(
    "a2x5-isect.0",
    "a2x5",
    mark: (end: ">", fill: rgb("#4d8000")),
    stroke: rgb("#4d8000") + line_thickness,
    name: "arrow-a2x5",
  )
  content(
    "arrow-a2x5.mid",
    text(fill: rgb("#4d8000"))[$Delta E_d$],
    anchor: "east",
    padding: (right: 3pt),
  )
  content(
    (rel: (0.1, 0), to: "arrow-a2x5.mid"),
    text(fill: rgb("#4d8000"), size: 10pt)[4/5 AX + 3/5 A₂X₇ → A₂X₅],
    frame: "rect",
    padding: (1pt, 3pt),
    stroke: rgb("#4d8000") + .3pt,
    name: "box2-label",
    anchor: "west",
    fill: rgb("#4d8000").lighten(90%),
  )

  // Draw chemical potential range
  line(
    (0, height - 4.5),
    "ax",
    stroke: (paint: orange, thickness: line_thickness, dash: "dashed"),
    name: "mu-line",
  )
  content(
    (rel: (2.4, 0), to: "mu-line.start"),
    rotate(14deg)[#text(fill: orange, size: 13pt)[$μ_A$ range\ where AX is stable]],
  )

  // Draw orange double arrow
  line(
    "hull-a-ax.2%",
    "mu-line.4%",
    mark: (start: ">", end: ">", fill: orange),
    stroke: orange + line_thickness,
    name: "mu-arrow",
  )

  // Draw legend
  circle((0.5, 1), radius: point_radius, fill: blue.darken(20%), name: "legend-stable")
  content(
    "legend-stable.east",
    "stable",
    anchor: "west",
    padding: (left: 5pt),
  )
  rect(
    (rel: (-0.15, -0.6), to: "legend-stable"),
    (rel: (0.15, -0.3), to: "legend-stable"),
    fill: red,
    stroke: red,
    name: "legend-unstable",
  )
  content(
    "legend-unstable.east",
    "unstable",
    anchor: "west",
    padding: (left: 5pt),
  )

  // Add axis labels
  content(
    (rel: (-0.5, 0), to: "y-axis-left.mid"),
    [#rotate(-90deg)[$Delta E_f$ (energy/atom)]],
  )
  content((width / 2, -0.5), $x "in" A_(1-x)X_x$)
})