codexantiproton-momentum-velocity-selection-simulation.py
antiproton-momentum-velocity-selection-simulation.py
"""Original toy simulation, NOT discovery data or historical abundances.
Python 3.14; numpy 2.3.5; matplotlib 3.10.7. Run from _media.
The C1/C2 boundaries follow UCRL-3172 p. 5. All event populations,
noise, momentum spread and the constant 40 MeV loss are chosen here.
The left panel is an ideal constant-speed flight; the right is a later
local speed, so the panels must not be treated as simultaneous speeds.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
C = 0.299792458 # metres per nanosecond
M_PROTON = 0.93827 # GeV/c^2, deliberately rounded model input
M_PION = 0.13957
LENGTH = 12.0 # m, illustrative
LOSS = 0.040 # GeV, illustrative energy loss after the ideal flight
def beta(momentum, mass):
return momentum / np.hypot(momentum, mass)
def downstream_beta(momentum, mass):
energy = np.hypot(momentum, mass) - LOSS
return np.sqrt(1 - (mass / energy)**2)
def main():
rng = np.random.default_rng(1955)
fig, (ax, av) = plt.subplots(1, 2, figsize=(10, 5.2), dpi=120, facecolor="white")
fig.suptitle("SIMULATION — selected momentum needs independent velocity tests",
fontsize=13, weight="bold", y=0.97)
fig.text(0.5, 0.90, "Chosen events and noise • no historical event data or abundance information",
ha="center", fontsize=10, color="#4c5968")
p_grid = np.linspace(1.145, 1.255, 300)
series = [(M_PROTON, "Antiproton", "#23699a", "o"),
(M_PION, "Negative pion", "#ba5b17", "^")]
for mass, label, color, marker in series:
p = rng.uniform(1.16, 1.24, 38)
t = LENGTH / (C * beta(p, mass)) + rng.normal(0, 0.30, len(p))
v = downstream_beta(p, mass) + rng.normal(0, 0.0015, len(p))
ax.plot(p_grid, LENGTH / (C * beta(p_grid, mass)), color=color, lw=1.5)
ax.scatter(p, t, s=20, c=color, marker=marker, alpha=0.75, label=label)
av.plot(p_grid, downstream_beta(p_grid, mass), color=color, lw=1.5)
av.scatter(p, v, s=20, c=color, marker=marker, alpha=0.75)
ax.set(xlim=(1.145, 1.255), ylim=(38, 54), xlabel=r"Selected $p$ (GeV/$c$)",
ylabel="Ideal-flight time (ns)", title="A. Constant speed over 12 m")
ax.legend(loc="center right", framealpha=1, fontsize=10)
ax.text(1.151, 52.9, r"$m^2c^4=p^2c^2[(ct/L)^2-1]$", fontsize=10)
av.axhspan(0.75, 0.78, color="#39865a", alpha=0.17)
av.axhspan(0.79, 1.015, color="#b93643", alpha=0.055)
av.axhline(0.79, color="#b93643", ls="--", lw=1.2)
av.text(1.149, 0.754, "C2 acceptance", color="#236143", fontsize=10)
av.text(1.149, 0.807, "C1 guard: reject fast particles", color="#a02432", fontsize=9)
av.scatter([1.17, 1.19, 1.21, 1.23], [0.768, 0.775, 0.762, 0.773],
marker="x", s=55, color="#ba5b17", linewidths=2, zorder=5)
av.annotate("Artificial false C2 responses\nfrom scattering: C1 still rejects",
xy=(1.21, 0.762), xytext=(1.151, 0.86), fontsize=9,
arrowprops={"arrowstyle": "->", "color": "#ba5b17"}, color="#91400d")
av.set(xlim=(1.145, 1.255), ylim=(0.735, 1.015), xlabel=r"Upstream selected $p$ (GeV/$c$)",
ylabel=r"Later local $\beta=v/c$", title="B. Toy 40 MeV loss, then local speed")
for axis in (ax, av):
axis.grid(alpha=0.18)
axis.tick_params(labelsize=10)
axis.spines[["top", "right"]].set_visible(False)
fig.text(0.5, 0.04, "Different stages: flight assumes constant speed; later energy loss changes the local speed.",
ha="center", fontsize=10, color="#4c5968")
fig.subplots_adjust(left=0.075, right=0.985, bottom=0.17, top=0.79, wspace=0.30)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()