codex/past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2022-fermion-tree-channels.py

past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2022-fermion-tree-channels.py
"""Draw the four auxiliary-scalar tree channels; tested with Python 3.14."""

from pathlib import Path

import matplotlib

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


def fermion(ax, start, end, reverse=False):
    ax.plot([start[0], end[0]], [start[1], end[1]], color="#17324d", lw=2)
    a, b = (end, start) if reverse else (start, end)
    lo = tuple(a[j] + 0.41 * (b[j] - a[j]) for j in range(2))
    hi = tuple(a[j] + 0.62 * (b[j] - a[j]) for j in range(2))
    ax.add_patch(FancyArrowPatch(lo, hi, arrowstyle="-|>", mutation_scale=15,
                                color="#17324d", lw=1.5))


def scalar(ax, a, b, label, offset=(0, 0)):
    ax.plot([a[0], b[0]], [a[1], b[1]], "--", color="#a44a19", lw=2)
    x, y = ((a[j] + b[j]) / 2 + offset[j] for j in range(2))
    ax.text(x, y, label, ha="center", va="center", fontsize=12,
            bbox={"facecolor": "white", "edgecolor": "none", "pad": 2})
    for p in (a, b):
        ax.plot(*p, "o", color="#17324d", ms=4)


fig, axes = plt.subplots(2, 2, figsize=(11.2, 7.2), facecolor="white")
for ax in axes.flat:
    ax.set(xlim=(-0.23, 4.23), ylim=(-0.35, 2.48), aspect="equal")
    ax.axis("off")

# Momentum travels left to right on every external leg. Arrowheads show fermion flow.
for row in range(2):
    anti = row == 1
    ax = axes[row, 0]
    fermion(ax, (0, 2), (2, 2))
    fermion(ax, (2, 2), (4, 2))
    fermion(ax, (0, 0), (2, 0), anti)
    fermion(ax, (2, 0), (4, 0), anti)
    scalar(ax, (2, 2), (2, 0), "$\\sigma$\n$q=p_1-p_3$", (.72, 0))
    for x, y, label in [(0, 2.17, "$p_1, f$"), (4, 2.17, "$p_3, f$"),
                        (0, -.25, "$p_2, \\bar f$" if anti else "$p_2, f$"),
                        (4, -.25, "$p_4, \\bar f$" if anti else "$p_4, f$")]:
        ax.text(x, y, label, ha="center", fontsize=12)
    ax.set_title("(ii) Fermion–antifermion: t channel" if anti
                 else "(i) Two fermions: t channel", fontsize=13, pad=10)

ax = axes[0, 1]
fermion(ax, (0, 2), (2, 2))
fermion(ax, (2, 2), (4, 2))
fermion(ax, (0, 0), (2, 0))
fermion(ax, (2, 0), (4, 0))
scalar(ax, (2, 2), (2, 0), "$\\sigma$\n$q=p_1-p_4$", (.72, 0))
for x, y, label in [(0, 2.17, "$p_1, f$"), (4, 2.17, "$p_4, f$"),
                    (0, -.25, "$p_2, f$"), (4, -.25, "$p_3, f$")]:
    ax.text(x, y, label, ha="center", fontsize=12)
ax.set_title("(i) Two fermions: u channel (outputs exchanged)", fontsize=13, pad=10)

ax = axes[1, 1]
left, right = (1.15, 1), (2.85, 1)
fermion(ax, (0, 2), left)
fermion(ax, (0, 0), left, True)
fermion(ax, right, (4, 2))
fermion(ax, right, (4, 0), True)
scalar(ax, left, right, "$\\sigma$\n$q=p_1+p_2$", (0, .42))
for x, y, label in [(0, 2.17, "$p_1, f$"), (4, 2.17, "$p_3, f$"),
                    (0, -.25, "$p_2, \\bar f$"), (4, -.25, "$p_4, \\bar f$")]:
    ax.text(x, y, label, ha="center", fontsize=12)
ax.set_title("(ii) Fermion–antifermion: s channel", fontsize=13, pad=10)

fig.text(.5, .025, "Solid arrows: fermion flow. Dashed lines: auxiliary scalar. External momenta enter at left and leave at right.",
         ha="center", fontsize=11)
fig.subplots_adjust(left=.025, right=.975, top=.94, bottom=.10, hspace=.38, wspace=.16)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name, dpi=140,
            facecolor="white", transparent=False)
plt.close(fig)