codex/wavenumber-phase-crest-distance.py

wavenumber-phase-crest-distance.py
"""Synthetic monochromatic-wave phase and crest distances.

Tested with Python 3.14, Matplotlib 3.10.7 and NumPy 2.3.5.
Run from _media; the opaque PNG is written to the current directory.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    wavelength, frequency = 0.4, 5.0
    k, omega = 2 * np.pi / wavelength, 2 * np.pi * frequency
    period = 1 / frequency
    x = np.linspace(0, 1, 2001)
    fig, (wave, phase) = plt.subplots(2, 1, figsize=(8, 6), sharex=True,
                                     layout="constrained", facecolor="white")
    for time, color, label in [(0, "#14699c", r"$t=0$"),
                               (period / 4, "#bd4f1c", r"$t=T/4=0.05$ s")]:
        phi = k * x - omega * time
        wave.plot(x, np.cos(phi), color=color, lw=2, label=label)
        phase.plot(x, phi / (2 * np.pi), color=color, lw=2, label=label)
    wave.scatter([0.4, 0.8], [1, 1], color="#14699c", zorder=5)
    wave.annotate("", (0.8, 1.32), (0.4, 1.32),
                  arrowprops={"arrowstyle": "<->", "color": "#14699c"})
    wave.text(0.6, 1.4, r"$\lambda=0.40$ m (crest to crest)", ha="center")
    wave.annotate("", (0.5, -1.32), (0.4, -1.32),
                  arrowprops={"arrowstyle": "->", "color": "#bd4f1c"})
    wave.text(0.57, -1.41, r"crest advance $\lambda/4=0.10$ m", ha="center", va="top")
    wave.set_ylim(-1.75, 1.75)
    wave.set_ylabel(r"Disturbance $u/A$")
    wave.legend(loc="lower left", fontsize=9)
    phase.scatter([0.4, 0.8], [1, 2], color="#14699c", zorder=5)
    phase.annotate(r"$\Delta\phi=2\pi$ over $\Delta x=\lambda$",
                   xy=(0.8, 2), xytext=(0.38, 2.4), fontsize=10,
                   arrowprops={"arrowstyle": "->", "color": "#14699c"})
    phase.set_ylim(-0.4, 2.8)
    phase.set_ylabel(r"Unwrapped phase $\phi/(2\pi)$")
    phase.set_xlabel("Position x (m)")
    phase.set_xticks(np.arange(0, 1.01, 0.2))
    for ax in (wave, phase):
        ax.set_facecolor("white")
        ax.set_xlim(0, 1)
        ax.grid(alpha=0.2)
        ax.spines[["top", "right"]].set_visible(False)
    fig.suptitle("Crest spacing and phase slope: synthetic plane wave\n"
                 r"$k=5\pi$ rad/m, $\nu=2.5$ cycles/m, $f=5$ Hz, $v_p=2$ m/s", fontsize=12)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=140,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()