codexprecipitation-species-and-phase-balance.py
precipitation-species-and-phase-balance.py
"""Illustrative ion inventories; Python 3.14, matplotlib 3.10.7.
Run from _media; writes an opaque PNG to the current working directory.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, FancyArrowPatch, Rectangle
COLORS = {"A": "#2675ac", "B": "#d98527", "X": "#76569c", "Y": "#34876a"}
LABELS = {"A": "A+", "B": "B−", "X": "X−", "Y": "Y+"}
def ion(ax, x, y, species):
ax.add_patch(Circle((x, y), 0.16, color=COLORS[species]))
ax.text(x, y, LABELS[species], ha="center", va="center", color="white", fontsize=11)
def main():
fig, ax = plt.subplots(figsize=(11, 6), dpi=100)
fig.patch.set_facecolor("white")
ax.set_facecolor("white")
ax.set(xlim=(0, 11), ylim=(0, 6))
ax.axis("off")
ax.text(5.5, 5.72, "Same inventory, different phases", ha="center", fontsize=20, weight="bold")
ax.text(5.5, 5.33, "Invented one-to-one AB model: three pairs enter the solid", ha="center", fontsize=12)
ax.add_patch(Rectangle((0.45, 1.35), 3.6, 3.55, facecolor="#f2f8fc", edgecolor="#6b8291", lw=1.5))
ax.add_patch(Rectangle((6.45, 2.15), 4.1, 2.75, facecolor="#f2f8fc", edgecolor="#6b8291", lw=1.5))
ax.add_patch(Rectangle((6.45, 1.15), 4.1, 0.85, facecolor="#fff6e7", edgecolor="#ac8549", lw=1.5))
ax.text(2.25, 4.63, "Before: all dissolved", ha="center", fontsize=14, weight="bold")
ax.text(8.5, 4.63, "After: aqueous ions", ha="center", fontsize=14, weight="bold")
for row, species in enumerate(["A", "B", "X", "Y"]):
y = 4.05 - 0.65 * row
for column in range(4):
ion(ax, 1.15 + 0.72 * column, y, species)
ion(ax, 7.20, 4.02, "A")
ion(ax, 8.02, 4.02, "B")
ax.text(9.52, 4.02, "one of each", ha="center", va="center", fontsize=11)
for row, species in enumerate(["X", "Y"]):
for column in range(4):
ion(ax, 7.20 + 0.75 * column, 3.29 - 0.68 * row, species)
ax.text(8.48, 1.88, "AB solid: three of each", ha="center", va="center", fontsize=11)
for pair in range(3):
ion(ax, 7.16 + pair * 1.13, 1.47, "A")
ion(ax, 7.59 + pair * 1.13, 1.47, "B")
ax.add_patch(FancyArrowPatch((4.25, 3.3), (6.20, 3.3), arrowstyle="-|>", mutation_scale=24, lw=2, color="#334652"))
ax.text(5.2, 3.75, "precipitation", ha="center", fontsize=12)
ax.text(5.2, 2.72, "A+ + B−\n→ AB(s)", ha="center", va="center", fontsize=13)
ax.text(5.5, 0.66, "A: 4 = 1 dissolved + 3 in solid B: 4 = 1 dissolved + 3 in solid", ha="center", fontsize=13)
ax.text(5.5, 0.23, "Spectators unchanged: X = 4, Y = 4 • Total charge = 0 before and after", ha="center", fontsize=12)
fig.subplots_adjust(left=0.015, right=0.985, bottom=0.02, top=0.98)
fig.savefig(Path.cwd() / f"{Path(__file__).stem}.png", dpi=100, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()