codexgeiger-marsden-fixed-and-rotating-detectors.py
geiger-marsden-fixed-and-rotating-detectors.py
"""Draw distinct 1909/1913 scattering geometries; Python 3.14, Matplotlib 3.10.7.
Run from _media; creates the matching opaque white PNG in the current directory.
Schematic paths only: no trajectory calculation or historical dimensions implied.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Arc, FancyArrowPatch, FancyBboxPatch, Polygon, Rectangle
BLUE = "#176a9c"
RED = "#bc4937"
GREEN = "#237e53"
GRAY = "#596572"
def arrow(ax, start, end, color=BLUE, **kwargs):
ax.add_patch(FancyArrowPatch(start, end, arrowstyle="-|>", mutation_scale=14,
linewidth=1.8, color=color, **kwargs))
def main():
plt.rcParams.update({"font.size": 11, "font.family": "DejaVu Sans"})
fig, axes = plt.subplots(1, 2, figsize=(11.8, 6.2), facecolor="white")
fig.subplots_adjust(left=0.025, right=0.985, top=0.84, bottom=0.16, wspace=0.12)
fig.suptitle("Counting flashes to probe the atom", fontsize=19, y=0.97)
fig.text(0.5, 0.90, "Two different experimental arrangements • schematic, not to scale",
ha="center", color=GRAY, fontsize=12)
for ax in axes:
ax.set_aspect("equal")
ax.set_xlim(-5.2, 4.4)
ax.set_ylim(-3.1, 4.0)
ax.axis("off")
ax = axes[0]
ax.set_title("1909: fixed reflection screen", fontsize=14, pad=18)
source = Polygon([(-4.8, 1.85), (-4.8, 0.65), (-3.0, 1.15), (-3.0, 1.35)],
closed=True, facecolor="#e8eef2", edgecolor=GRAY, linewidth=1.5)
ax.add_patch(source)
ax.plot([-3.0, -3.0], [1.13, 1.37], color=GREEN, linewidth=3)
ax.text(-4.7, 2.25, "Source in conical tube", fontsize=11)
ax.annotate("Mica window", xy=(-3.0, 1.25), xytext=(-3.3, 0.52),
arrowprops={"arrowstyle": "-", "color": GRAY}, fontsize=10, ha="right")
foil_x, hit_y = 2.05, 1.25
ax.add_patch(Rectangle((foil_x, -1.1), 0.12, 3.5, facecolor="#d7ae49",
edgecolor="#9f7c23"))
ax.text(2.33, 2.65, "Metal\nreflector", ha="center", fontsize=11)
arrow(ax, (-2.8, hit_y), (foil_x, hit_y))
ax.text(-1.6, 1.55, r"Incident $\alpha$", color=BLUE)
screen = np.array([-0.65, -0.95])
arrow(ax, (foil_x, hit_y), screen, color=RED)
ax.text(1.2, -0.63, "Deflected\nparticle", color=RED, ha="center", fontsize=10)
ax.add_patch(Rectangle((-5.0, -0.05), 4.9, 0.22, facecolor=GRAY))
ax.text(-4.7, -0.5, "Lead shield blocks\ndirect source → screen paths", fontsize=10)
ax.plot([-1.15, -0.15], [-0.98, -0.98], color=GREEN, linewidth=5)
ax.text(-1.1, -1.48, "Fixed ZnS screen", color=GREEN, fontsize=10)
ax.add_patch(Rectangle((-0.91, -2.65), 0.52, 0.83, facecolor="#e8eef2",
edgecolor=GRAY, linewidth=1.4))
ax.plot([-0.65, -0.65], [-1.13, -1.79], color=GREEN, linestyle=":")
ax.text(0.1, -2.35, "Microscope:\ncount light flashes", fontsize=10)
ax.annotate("", xy=(-0.38, -2.18), xytext=(0.04, -2.18),
arrowprops={"arrowstyle": "-", "color": GRAY})
ax = axes[1]
ax.set_title("1913: rotating angular detector", fontsize=14, pad=18)
ax.add_patch(FancyBboxPatch((-5.05, -2.8), 9.25, 6.45,
boxstyle="round,pad=0,rounding_size=0.3",
facecolor="#f7fafb", edgecolor="#adb8c2", linewidth=1.2))
ax.text(0.2, -2.65, "Evacuated chamber", ha="center", color=GRAY, fontsize=10)
ax.add_patch(Rectangle((-4.7, -0.35), 0.75, 0.7, facecolor="#e8eef2",
edgecolor=GRAY))
ax.text(-4.35, -0.83, "Source", ha="center")
for y in [-0.95, 0.09]:
ax.add_patch(Rectangle((-2.6, y), 0.16, 0.86, facecolor=GRAY))
ax.annotate("Diaphragm\n(collimator)", xy=(-2.5, -0.35), xytext=(-3.9, -1.6),
arrowprops={"arrowstyle": "-", "color": GRAY}, fontsize=10)
arrow(ax, (-3.9, 0), (-0.02, 0))
ax.plot([0, 0], [-0.65, 0.65], color="#b18b2a", linewidth=4)
ax.annotate("Thin foil", xy=(0, -0.55), xytext=(0.55, -1.1),
arrowprops={"arrowstyle": "-", "color": GRAY})
ax.plot([0, 3.9], [0, 0], linestyle="--", color=GRAY, linewidth=1)
ax.text(2.6, -0.44, "Forward", color=GRAY, fontsize=10)
angle = np.deg2rad(64.0)
direction = np.array([np.cos(angle), np.sin(angle)])
tangent = np.array([-direction[1], direction[0]])
detector = 2.53 * direction
arrow(ax, (0, 0), detector, color=RED)
edge1, edge2 = detector - 0.36 * tangent, detector + 0.36 * tangent
ax.plot([edge1[0], edge2[0]], [edge1[1], edge2[1]], color=GREEN, linewidth=5)
# The microscope stays radially outside the screen and rotates with it.
p0 = 2.88 * direction
corners = [p0 - 0.24 * tangent, p0 + 0.24 * tangent,
p0 + 0.24 * tangent + 0.8 * direction,
p0 - 0.24 * tangent + 0.8 * direction]
ax.add_patch(Polygon(corners, closed=True, facecolor="#e8eef2", edgecolor=GRAY))
ax.annotate("ZnS screen", xy=detector, xytext=(2.1, 1.75), fontsize=10,
color=GREEN, arrowprops={"arrowstyle": "-", "color": GREEN})
ax.annotate("Microscope", xy=3.4 * direction, xytext=(-1.1, 3.45), fontsize=10,
arrowprops={"arrowstyle": "-", "color": GRAY})
ax.add_patch(Arc((0, 0), 1.7, 1.7, theta1=0, theta2=64, color=RED, linewidth=1.2))
ax.text(0.97, 0.45, r"$\theta$", fontsize=15, color=RED)
ax.add_patch(Arc((0, 0), 5.06, 5.06, theta1=83, theta2=152,
color=GREEN, linestyle="--", linewidth=1.2))
arrow(ax, (-2.18, 1.28), (-2.28, 1.08), color=GREEN)
ax.text(-2.0, 2.45, "Rotate together", color=GREEN, ha="center", fontsize=10)
fig.text(0.5, 0.07,
"Controls: reflector / foil removed • compare materials or foil thickness • correct backgrounds",
ha="center", fontsize=11, color=GRAY)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=110,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()