codex/past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2023-scalar-diagrams.py

past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2023-scalar-diagrams.py
"""Original QFT diagrams; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7."""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(11.2, 7.8), facecolor='white', layout='constrained')
grid = fig.add_gridspec(2, 6)
axes = [fig.add_subplot(grid[0, :2]), fig.add_subplot(grid[0, 2:4]),
        fig.add_subplot(grid[0, 4:]), fig.add_subplot(grid[1, 1:3]),
        fig.add_subplot(grid[1, 3:5])]
ink = '#16334c'

def line(ax, a, b, heavy=False, arrow=True):
    a, b = np.asarray(a), np.asarray(b)
    ax.plot([a[0], b[0]], [a[1], b[1]], color=ink, lw=1.8,
            ls=(0, (4, 2.5)) if heavy else '-')
    if arrow:
        mid = (a+b)/2
        delta = (b-a)*.13
        ax.annotate('', xy=mid+delta, xytext=mid-delta,
                    arrowprops=dict(arrowstyle='-|>', color=ink, lw=1.5,
                                    mutation_scale=11))

def vertex(ax, p):
    ax.plot(*p, 'o', ms=4.3, color=ink)

def label(ax, p, s, **kw):
    ax.text(*p, s, ha='center', va='center', fontsize=11, color=ink, **kw)

for ax in axes:
    ax.set(xlim=(-1.4, 1.4), ylim=(-1.22, 1.3), aspect='equal')
    ax.axis('off')

ax = axes[0]
line(ax, (-1.1, -.45), (0, -.45), arrow=False)
line(ax, (0, -.45), (1.1, -.45), arrow=False)
t = np.linspace(-np.pi/2, 3*np.pi/2, 220)
ax.plot(.46*np.cos(t), .20+.65*np.sin(t), color=ink, lw=1.8)
vertex(ax, (0, -.45))
label(ax, (-.9, -.68), '$p$'); label(ax, (.9, -.68), '$p$')
label(ax, (.68, .6), '$k$')
ax.set_title('(a) One-loop tadpole\nreal scalar in 1+1 dimensions', fontsize=11)

ax = axes[1]; a=(-.38,0); b=(.38,0)
for p in [(-1.12,.65),(-1.12,-.65)]: line(ax,p,a)
line(ax,a,b,heavy=True)
for p in [(1.12,.65),(1.12,-.65)]: line(ax,b,p)
for p in [a,b]: vertex(ax,p)
label(ax,(-1.08,.9),'$\\phi_1$');label(ax,(-1.08,-.9),'$\\phi_2$')
label(ax,(1.08,.9),'$\\phi_3$');label(ax,(1.08,-.9),'$\\phi_4$')
label(ax,(0,.27),'$\\Phi$')
ax.set_title('(b) Light particles\n$s$-channel exchange',fontsize=11)

ax = axes[2]; a=(0,.48);b=(0,-.48)
line(ax,(-1.12,.78),a);line(ax,(1.12,.78),a)
line(ax,a,b,heavy=True)
line(ax,b,(-1.12,-.78));line(ax,b,(1.12,-.78))
for p in [a,b]:vertex(ax,p)
label(ax,(-1.12,1.03),'$\\phi_1$');label(ax,(1.12,1.03),'$\\bar\\phi_4$')
label(ax,(-1.12,-1.03),'$\\bar\\phi_2$');label(ax,(1.12,-1.03),'$\\phi_3$')
label(ax,(.3,0),'$\\Phi$')
ax.set_title('(c) Light particle–antiparticle\n$u$-channel exchange',fontsize=11)

for ax, mixed in zip(axes[3:], [False,True]):
    a=(-.53,.5);b=(.53,.5);c=(.53,-.5);d=(-.53,-.5)
    for start,end in [(a,b),(a,d),(c,b),(c,d)]:line(ax,start,end)
    ends=[(-1.12,.94),(1.12,.94),(1.12,-.94),(-1.12,-.94)]
    for p,end,source in zip([a,b,c,d],ends,[True,False,True,False]):
        line(ax,end,p,heavy=True) if source else line(ax,p,end,heavy=True)
        vertex(ax,p)
    texts=['$\\Phi_1$ in','$\\Phi_3$ out',
           '$\\bar\\Phi_4$ out' if mixed else '$\\Phi_2$ in',
           '$\\bar\\Phi_2$ in' if mixed else '$\\Phi_4$ out']
    for end,s in zip(ends,texts):label(ax,(end[0]*.83,end[1]+(.19 if end[1]>0 else -.19)),s)
    ax.set_title('(e) Heavy particle–antiparticle box' if mixed else '(d) Heavy-particle box',fontsize=11)
fig.suptitle('Positive charge follows each arrow: solid = 1, dashed = 2', fontsize=13, color=ink)
fig.savefig(Path.cwd()/Path(__file__).with_suffix('.png').name, dpi=115, facecolor='white', transparent=False)
plt.close(fig)