codex/past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2025-light-scalar-scattering.py

past-exam-of-the-quantum-field-theory-course-of-the-university-of-oxford/2025-light-scalar-scattering.py
"""Original Oxford 2025 QFT diagram. Tested with Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.

Run from the desired output directory. The only saved artifact is the
corresponding opaque PNG in the current directory; this script never chdir()s.
Arrows follow positive conserved charge, not necessarily particle travel.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import FancyArrowPatch

LIGHT="#333333"
HEAVY="#175e97"

def setup(ax, title=""):
    ax.set(xlim=(-0.35,6.35),ylim=(-0.35,4.35),aspect="equal")
    ax.axis("off")
    ax.set_title(title,fontsize=13,pad=12)

def label(ax,p,text,**kw):
    ax.text(*p,text,ha="center",va="center",fontsize=12,**kw)

def edge(ax,a,b,heavy=False,arrow=0,dashed=True):
    a=np.array(a,dtype=float);b=np.array(b,dtype=float)
    color=HEAVY if heavy else LIGHT
    normal=np.array([-(b-a)[1],(b-a)[0]])/np.linalg.norm(b-a)
    offsets=(-0.035,0.035) if heavy else (0,)
    for off in offsets:
        pts=np.array([a+off*normal,b+off*normal])
        ax.plot(pts[:,0],pts[:,1],color=color,lw=1.8,ls="--" if dashed else "-")
    if arrow:
        start=a+(.39 if arrow>0 else .61)*(b-a)
        end=a+(.61 if arrow>0 else .39)*(b-a)
        ax.add_patch(FancyArrowPatch(start,end,arrowstyle="-|>",mutation_scale=14,color=color,lw=1.5))

def vertex(ax,p):
    ax.plot(*p,"o",color="black",ms=5)

def save(fig):
    out=Path.cwd()/(Path(__file__).stem+".png")
    out.parent.mkdir(parents=True,exist_ok=True)
    fig.savefig(out,dpi=145,facecolor="white",transparent=False,bbox_inches="tight")
    plt.close(fig)
    print(out)


fig,ax=plt.subplots(figsize=(7,3.8),facecolor="white");setup(ax,r"2(c)(i): $\phi\phi\to\phi\phi$")
l=(2,2);r=(4,2)
for a in [(.1,3.6),(.1,.4)]:edge(ax,a,l,arrow=1)
for b in [(5.9,3.6),(5.9,.4)]:edge(ax,r,b,arrow=1)
edge(ax,l,r,heavy=True,arrow=1)
for v in [l,r]:vertex(ax,v)
for p,t in [((.7,3.9),r"$\phi(p_1)$"),((.7,.1),r"$\phi(p_2)$"),((5.3,3.9),r"$\phi(p_3)$"),((5.3,.1),r"$\phi(p_4)$"),((3,2.75),r"$\bar\Phi\;(+2)$"),((3,1.25),r"$q=p_1+p_2$")]:label(ax,p,t)
fig.tight_layout();save(fig)