codex/two-beam-interference-fringes.py

two-beam-interference-fringes.py
"""Generate two-beam-interference-fringes.png in CWD on white.

Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
Ideal plane waves at +/-30 degrees, both E along y. Lambda is the length
unit; I0 is each beam's along-beam flux. Plot scalar E-squared intensity,
not z-normal flux, which has an extra cos(theta) projection.
"""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    wavelength, theta, i0 = 1., np.pi / 6, 1.
    k = 2 * np.pi / wavelength
    x = np.linspace(-3, 3, 1201)
    z = np.linspace(0, 4, 401)
    xx, zz = np.meshgrid(x, z)
    phase_plus = k * (xx * np.sin(theta) + zz * np.cos(theta))
    phase_minus = k * (-xx * np.sin(theta) + zz * np.cos(theta))
    field = np.exp(1j * phase_plus) + np.exp(1j * phase_minus)
    scalar_intensity = i0 * np.abs(field) ** 2
    plt.rcParams.update({"font.size": 11, "figure.facecolor": "white",
                         "axes.facecolor": "white"})
    fig, (geometry, grid) = plt.subplots(1, 2, figsize=(10, 5.6), dpi=110,
                                       gridspec_kw={"width_ratios": [1, 2.25]})
    fig.subplots_adjust(left=.065, right=.96, top=.78, bottom=.23, wspace=.32)
    geometry.set(xlim=(-1.3, 1.3), ylim=(-.3, 2.5), aspect="equal")
    geometry.axis("off")
    geometry.annotate("", xy=(0, 2.25), xytext=(0, 0),
                      arrowprops={"arrowstyle": "->", "color": ".45", "lw": 1.2})
    geometry.annotate("", xy=(1.2, 0), xytext=(-1.2, 0),
                      arrowprops={"arrowstyle": "->", "color": ".45", "lw": 1.2})
    for sign, colour in [(1, "#1768ac"), (-1, "#cb6418")]:
        geometry.annotate("", xy=(sign * 2 * np.sin(theta), 2 * np.cos(theta)),
                          xytext=(0, 0),
                          arrowprops={"arrowstyle": "->", "color": colour, "lw": 2.7})
    geometry.text(0, 2.3, "z", ha="center")
    geometry.text(1.2, -.18, "x", ha="center")
    geometry.text(.58, 2, "+30°", color="#1768ac", ha="center")
    geometry.text(-.58, 2, "−30°", color="#cb6418", ha="center")
    geometry.text(0, -.48, "Both E along y\n(out of the page)", ha="center", va="top", fontsize=10)
    geometry.set_title("Propagation directions", fontsize=11, pad=15)
    mesh = grid.pcolormesh(xx / wavelength, zz / wavelength,
                          scalar_intensity / i0, shading="gouraud",
                          cmap="magma", vmin=0, vmax=4, rasterized=True)
    grid.set(xlim=(-3, 3), ylim=(0, 4), aspect="equal",
             xlabel=r"$x/\lambda$ (dimensionless)", ylabel=r"$z/\lambda$ (dimensionless)")
    grid.set_xticks(np.arange(-3, 4))
    grid.set_yticks(np.arange(5))
    grid.set_title(r"Scalar fringes: $\Lambda/\lambda=1$; no $z$ dependence", fontsize=11, pad=15)
    colourbar = fig.colorbar(mesh, ax=grid, pad=.025, fraction=.045, ticks=np.arange(5))
    colourbar.set_label(r"$I_{\mathrm{scalar}}/I_0$", fontsize=11)
    fig.suptitle("Two ideal plane waves: stationary interference stripes", fontsize=15, y=.98)
    fig.text(.5, .89, r"$\delta=2kx\sin\theta$, $\theta=30^\circ$, length unit $\lambda=1$",
             ha="center", fontsize=12)
    fig.text(.5, .105, r"$I_0$: each beam's along-beam flux; $\langle S_z\rangle=I_{\mathrm{scalar}}\cos\theta$.",
             ha="center", fontsize=11)
    fig.text(.5, .045, "Ideal plane-wave model; no slits, point sources or near-field calculation.",
             ha="center", fontsize=10)
    fig.savefig(Path.cwd() / "two-beam-interference-fringes.png",
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()