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

past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2020-yukawa-tree-channels.py
"""Draw original momentum-labelled Yukawa tree channels (Python 3.14)."""
from pathlib import Path

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np


def fermion(ax, start, end, reverse=False):
    start, end = np.array(start), np.array(end)
    ax.plot(*zip(start, end), color='#172b4d', lw=1.9)
    first, last = (0.61, 0.42) if reverse else (0.42, 0.61)
    ax.annotate('', xy=start + last * (end - start),
                xytext=start + first * (end - start),
                arrowprops={'arrowstyle': '-|>', 'color': '#172b4d', 'lw': 1.4,
                            'mutation_scale': 13})


def scalar(ax, start, end, label, label_xy):
    ax.plot(*zip(start, end), '--', color='#b54708', lw=1.8, dashes=(4, 3))
    ax.text(*label_xy, label, color='#8c3606', ha='center', va='center', fontsize=12,
            bbox={'facecolor': 'white', 'edgecolor': 'none', 'pad': 1.5})


def prepare(ax, title):
    ax.set(xlim=(-0.07, 1.07), ylim=(-0.09, 1.1))
    ax.axis('off')
    ax.set_title(title, fontsize=14, weight='bold', pad=6)


def exchange(ax, title, species=(1, 2), crossed=False, anti=False):
    prepare(ax, title)
    a, b = (0.42, 0.74), (0.42, 0.26)
    incoming = [(0.01, 0.92), (0.01, 0.08)]
    outgoing = [(0.99, 0.92), (0.99, 0.08)]
    fermion(ax, incoming[0], a)
    fermion(ax, incoming[1], b, reverse=anti)
    fermion(ax, a, outgoing[1] if crossed else outgoing[0])
    fermion(ax, b, outgoing[0] if crossed else outgoing[1], reverse=anti)
    scalar(ax, a, b, r'$q_u=p_1-p_4$' if crossed else r'$q_t=p_1-p_3$', (0.20, 0.50))
    for node in (a, b):
        ax.plot(*node, 'o', color='#172b4d', ms=4)
    labels = [(incoming[0], rf'$f_{species[0]}(p_1)$', 0.07),
              (incoming[1], rf'$\bar f_{species[1]}(p_2)$' if anti else rf'$f_{species[1]}(p_2)$', -0.07),
              (outgoing[0], rf'$f_{species[0]}(p_3)$', 0.07),
              (outgoing[1], rf'$\bar f_{species[1]}(p_4)$' if anti else rf'$f_{species[1]}(p_4)$', -0.07)]
    for point, label, offset in labels:
        ax.text(point[0], point[1] + offset, label, fontsize=13, ha='center', va='center')


def annihilation(ax):
    prepare(ax, '(iii) Same species: s channel')
    a, b = (0.32, 0.5), (0.68, 0.5)
    ends = [(0.01, 0.92), (0.01, 0.08), (0.99, 0.92), (0.99, 0.08)]
    fermion(ax, ends[0], a)
    fermion(ax, ends[1], a, reverse=True)
    fermion(ax, b, ends[2])
    fermion(ax, b, ends[3], reverse=True)
    scalar(ax, a, b, r'$q_s=p_1+p_2$', (0.5, 0.72))
    for node in (a, b):
        ax.plot(*node, 'o', color='#172b4d', ms=4)
    for point, label, dy in zip(ends, [r'$f_1(p_1)$', r'$\bar f_1(p_2)$',
                                      r'$f_1(p_3)$', r'$\bar f_1(p_4)$'],
                                [0.07, -0.07, 0.07, -0.07]):
        ax.text(point[0], point[1] + dy, label, ha='center', va='center', fontsize=13)


fig, axes = plt.subplots(3, 2, figsize=(10.6, 9.4), facecolor='white')
fig.subplots_adjust(left=0.07, right=0.94, bottom=0.055, top=0.91, hspace=0.55, wspace=0.38)
fig.suptitle('Scalar exchange in Yukawa theory', fontsize=19, weight='bold', y=0.975)
exchange(axes[0, 0], '(i) Distinct species: t channel')
axes[0, 1].axis('off')
axes[0, 1].text(0.5, 0.64, r'$p_1+p_2=p_3+p_4$', fontsize=16, ha='center')
axes[0, 1].text(0.5, 0.38, 'Arrows: fermion flow\nDashed lines: scalar\nCrossings carry no vertex',
                fontsize=13, ha='center', va='center', linespacing=1.6)
exchange(axes[1, 0], '(ii) Identical fermions: t channel', species=(1, 1))
exchange(axes[1, 1], '(ii) Identical fermions: u channel', species=(1, 1), crossed=True)
exchange(axes[2, 0], '(iii) Same species: t channel', species=(1, 1), anti=True)
annihilation(axes[2, 1])
fig.savefig(Path.cwd() / Path(__file__).with_suffix('.png').name,
            dpi=110, facecolor='white', transparent=False,
            metadata={'Software': 'Matplotlib'})
plt.close(fig)