codex/past-exam-of-the-physics-b-course-of-the-university-of-cambridge/2010/paper-2a-triangular-modes.py

past-exam-of-the-physics-b-course-of-the-university-of-cambridge/2010/paper-2a-triangular-modes.py
"""Original equilateral-molecule mode sketch.

Python 3.14; numpy 2.3.5; matplotlib 3.10.7 (root pyproject.toml).
Writes paper-2a-triangular-modes.png to the current working directory.
Only displacement directions are illustrated; no frequencies are assigned.
"""

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import FancyArrowPatch


def main():
    plt.rcParams.update({"font.size": 10, "figure.facecolor": "white"})
    vertices = np.array([[0.0, 1.0], [-np.sqrt(3) / 2, -0.5], [np.sqrt(3) / 2, -0.5]])
    transforms = [np.eye(2), np.diag([1.0, -1.0]), np.array([[0.0, 1.0], [1.0, 0.0]])]
    titles = ["Symmetric breathing", "Degenerate pair: basis 1", "Degenerate pair: basis 2"]
    formulas = [
        r"$\mathbf{u}_j=\mathbf{r}_j$",
        r"$\mathbf{u}_j=(x_j,-y_j)$",
        r"$\mathbf{u}_j=(y_j,x_j)$",
    ]
    colors = ["#176493", "#a44720", "#a44720"]
    fig, axes = plt.subplots(1, 3, figsize=(8.6, 3.75))
    fig.subplots_adjust(left=0.025, right=0.985, top=0.77, bottom=0.24, wspace=0.12)
    fig.suptitle(r"H$_3^+$: three in-plane vibrational modes", y=0.965, fontsize=13)
    closed = np.vstack([vertices, vertices[0]])
    for ax, transform, title, formula, color in zip(axes, transforms, titles, formulas, colors):
        vectors = vertices @ transform.T
        ax.plot(closed[:, 0], closed[:, 1], "--", color="0.62", lw=1.2)
        ax.scatter(vertices[:, 0], vertices[:, 1], s=150, color="#f1f1f1", edgecolor="0.35", zorder=4)
        ax.plot(0, 0, "+", color="0.55", ms=6)
        for vertex, vector in zip(vertices, vectors):
            ax.add_patch(
                FancyArrowPatch(vertex, vertex + 0.43 * vector, arrowstyle="-|>",
                                mutation_scale=15, lw=2.2, color=color, zorder=5)
            )
        ax.set_title(title, fontsize=10, pad=12)
        ax.text(0.5, -0.12, formula, transform=ax.transAxes, ha="center", fontsize=11)
        ax.set(xlim=(-1.45, 1.45), ylim=(-1.04, 1.57), aspect="equal")
        ax.axis("off")
    fig.text(0.5, 0.135, r"$\mathbf{r}_j=(x_j,y_j)$ is measured from the centre (+); dashed bonds mark equilibrium.",
             ha="center", fontsize=9)
    fig.text(0.5, 0.075, "Arrows show one phase; reversing all arrows gives the opposite phase.", ha="center", fontsize=9)
    fig.text(0.5, 0.02, "The two distortions span a symmetry-degenerate subspace; no measured frequencies are implied.",
             ha="center", fontsize=9)
    fig.savefig("paper-2a-triangular-modes.png", dpi=120, facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()