codexneutral-kaon-beam-and-two-track-reconstruction.py
neutral-kaon-beam-and-two-track-reconstruction.py
"""Original functional kaon schematic; Python 3.14, matplotlib 3.10, NumPy 2.3.
Run from _media. Apparatus functions: Christenson et al., PRL 13 (1964),
p.138; Fitch's 1980 Nobel lecture, pp.598-600. Geometry is not to scale.
"""
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, FancyBboxPatch, Rectangle
plt.rcParams.update({'font.size': 11, 'font.family': 'DejaVu Sans'})
fig = plt.figure(figsize=(11, 7.5), dpi=100, facecolor='white')
ax = fig.add_axes((.035, .40, .93, .51))
ax.set(xlim=(0, 12), ylim=(-3.0, 3.0)); ax.axis('off')
fig.suptitle('Neutral kaon decay: beam preparation and event reconstruction', y=.975, fontsize=17)
fig.text(.5, .925, 'Functional schematic • geometry and trajectories are illustrative', ha='center', color='#555555')
def arrow(a, b, color='#314b61', style='-|>', lw=1.8):
ax.add_patch(FancyArrowPatch(a, b, arrowstyle=style, mutation_scale=12, color=color, lw=lw))
def box(x, y, w, h, label, color='#edf3f7', size=10):
ax.add_patch(FancyBboxPatch((x, y), w, h, boxstyle='round,pad=0.06', facecolor=color, edgecolor='#607486'))
ax.text(x+w/2, y+h/2, label, ha='center', va='center', fontsize=size)
box(.05, -.42, 1.25, .84, 'Target\nBe + protons')
arrow((1.35, 0), (1.95, 0))
box(2.0, -.42, 1.35, .84, 'Neutral beam\ncollimation')
arrow((3.4, 0), (4.05, 0))
box(4.1, -.75, 2.1, 1.5, '', '#eff7ed', 11)
ax.text(4.25, .50, 'Helium decay', ha='left', va='center', fontsize=11)
ax.text(4.25, .16, 'volume', ha='left', va='center', fontsize=11)
ax.plot(5.65, 0, 'o', color='#253b4d', ms=5)
ax.text(4.25, -1.2, 'Decay vertex', fontsize=10)
ax.plot([5.05, 5.62], [-.98, -.06], color='#607486', lw=.8)
for sign, color, particle in [(1, '#b84433', r'$\pi^+$'), (-1, '#2268a1', r'$\pi^-$')]:
points = np.array([[5.65, 0], [7.05, sign*.9], [8.3, sign*1.65], [9.05, sign*1.84], [10.2, sign*2.03]])
ax.plot(points[:, 0], points[:, 1], color=color, lw=2.4)
ax.text(6.55, sign*.95, particle, color=color, fontsize=14)
for x, y in [(7.05, sign*.9), (9.05, sign*1.84)]:
ax.add_patch(Rectangle((x-.06, y-.35), .12, .7, facecolor='#a7c0cf', edgecolor='#607486'))
box(7.7, sign*1.6-.32, .82, .64, 'Magnet', '#f7e7ce', 9)
box(10.25, sign*2.02-.38, 1.6, .76, 'Counters', '#e7eef8', 10)
ax.text(7.0, 2.65, 'Two spectrometer arms', ha='center', fontsize=12, fontweight='bold')
ax.text(7.0, 2.20, 'Spark chambers before / after each magnet', fontsize=9, ha='center')
ax.text(10.8, 0, 'Coincident\ntrigger', ha='center', va='center', fontsize=10)
ax.text(.1, -2.35, 'Calibration / control: insert a tungsten regenerator in the beam', color='#526340', fontsize=11)
ax.text(.1, -2.85, 'Free-decay run: avoid mistaking material regeneration for a long-lived decay', color='#555555', fontsize=10)
left = fig.add_axes((.065, .075, .33, .27))
left.set(xlim=(-2.2, 2.2), ylim=(-.35, 3.65), aspect='equal'); left.axis('off')
left.text(0, 3.5, 'Synthetic momenta before deflection', ha='center', fontsize=11)
for dx, c, label in [(np.sqrt(3), '#b84433', r'$\mathbf{p}_+$'), (-np.sqrt(3), '#2268a1', r'$\mathbf{p}_-$')]:
left.annotate('', (dx, 3), (0, 0), arrowprops={'arrowstyle':'->', 'color':c, 'lw':2})
left.text(dx*.75, 2.0, label, color=c, fontsize=14)
left.plot([0, 0], [0, 3.1], '--', color='#555555', lw=1)
left.text(.09, .9, r'$\alpha=60^\circ$', fontsize=12)
left.text(0, -.25, r'Beam along $+z$; transverse momenta cancel', ha='center', fontsize=10)
fig.text(.43, .305, 'Two independent tests', fontsize=13, fontweight='bold')
fig.text(.43, .255, r'$m_\pi=1,\quad \mathbf{p}_\pm=(\pm\sqrt{3},0,3),\quad E_\pm=\sqrt{13}$', fontsize=14)
fig.text(.43, .200, r'$M_{\pi\pi}^2=(2\sqrt{13})^2-6^2=16\quad\Rightarrow\quad M_{\pi\pi}=4$', fontsize=14)
fig.text(.43, .145, r'$\mathbf{P}=(0,0,6)\quad\Rightarrow\quad\theta=0$', fontsize=14)
fig.text(.43, .09, 'Correct mass + parent pointing along the beam', fontsize=11)
fig.savefig(Path(__file__).stem+'.png', facecolor='white', dpi=100)
plt.close(fig)