codexsingle-slit-diffraction-width.py
single-slit-diffraction-width.py
"""Generate single-slit-diffraction-width.png in CWD on an opaque white background.
Tested with Python 3.14.4, numpy 2.3.5 and matplotlib 3.10.7.
The plotted scalar Fraunhofer envelope uses exact screen-angle geometry;
central width means first-minimum separation, not FWHM.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
wavelength, distance = 500e-9, 1.0
widths = (0.1e-3, 0.2e-3)
colors = ("#1768ac", "#c76319")
y = np.linspace(-15e-3, 15e-3, 6001)
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white",
"axes.facecolor": "white", "axes.spines.top": False,
"axes.spines.right": False})
fig, (ax, geometry) = plt.subplots(2, 1, figsize=(10.2, 7.2), dpi=110,
gridspec_kw={"height_ratios": [2.2, 1]})
fig.subplots_adjust(left=.10, right=.94, top=.86, bottom=.10, hspace=.66)
for index, (width, color) in enumerate(zip(widths, colors)):
theta = np.arctan(y / distance)
# numpy.sinc(t) = sin(pi*t)/(pi*t), including value one at zero.
intensity = np.sinc(width * np.sin(theta) / wavelength) ** 2
first_angle = np.arcsin(wavelength / width)
first_y = distance * np.tan(first_angle)
assert np.isclose(np.sinc(width * np.sin(first_angle) / wavelength), 0,
atol=1e-14)
ax.plot(y * 1e3, intensity, color=color, lw=2.2,
label=f"a = {width * 1e3:.1f} mm")
ax.vlines([-first_y * 1e3, first_y * 1e3], 0, 1,
color=color, lw=1, linestyle="--", alpha=.45)
level = -.10 - .15 * index
ax.annotate("", xy=(first_y * 1e3, level),
xytext=(-first_y * 1e3, level),
arrowprops={"arrowstyle": "<->", "color": color, "lw": 1.6})
ax.text(0, level + .028, f"First-minimum width: {2 * first_y * 1e3:.5f} mm",
ha="center", color=color, fontsize=10)
ax.set(xlim=(-15, 15), ylim=(-.32, 1.06), xlabel="Screen position y (mm)",
ylabel=r"Normalized scalar intensity $I/I_0$")
ax.set_yticks([0, .25, .5, .75, 1])
ax.grid(alpha=.17)
ax.legend(loc="upper right", frameon=False)
# z is the propagation coordinate; y is shown in mm, enlarging its scale.
first_y_mm = distance * np.tan(np.arcsin(wavelength / widths[0])) * 1e3
geometry.plot([0, distance], [0, 0], color=".5", lw=1, linestyle=":")
for sign in (-1, 1):
geometry.plot([0, distance], [0, sign * first_y_mm], color=colors[0], lw=1.8)
geometry.plot([distance, distance], [-6, 6], color=".25", lw=2)
geometry.scatter([0], [0], color=".25", s=30, zorder=4)
geometry.text(0, 1.0, "Slit centre", ha="left", fontsize=10)
geometry.text(distance, 6.6, "Screen", ha="center", fontsize=10)
geometry.annotate("", xy=(distance + .025, first_y_mm),
xytext=(distance + .025, 0),
arrowprops={"arrowstyle": "<->", "color": colors[0]})
geometry.text(distance + .04, 2.2, r"$y_1$", color=colors[0])
geometry.text(.42, 3.7, r"$\theta_1 = \arcsin(\lambda/a)$", color=colors[0])
geometry.text(.48, -1.5, r"$y_1=L\tan\theta_1$", ha="center")
geometry.annotate("", xy=(distance, -7), xytext=(0, -7),
arrowprops={"arrowstyle": "<->", "color": ".3"})
geometry.text(.5, -8.7, "L = 1 m", ha="center", fontsize=10)
geometry.set(xlim=(-.04, 1.13), ylim=(-9.5, 8))
geometry.axis("off")
geometry.set_title("Screen geometry for a = 0.1 mm; vertical scale enlarged", fontsize=11)
fig.suptitle("Single-slit diffraction: narrower opening, wider beam", fontsize=16, y=.98)
fig.text(.5, .915, r"$\lambda=500$ nm, $L=1$ m; $\theta=\arctan(y/L)$; each curve has its own $I_0$",
ha="center", fontsize=11)
fig.text(.5, .025, "Ideal uniform coherent slit, scalar far-field model. Width is between first minima, not half-height.",
ha="center", fontsize=10)
fig.savefig(Path(__file__).stem + ".png", facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()