codex/mach-angle-versus-speed-ratio.py

mach-angle-versus-speed-ratio.py
"""Original Mach-angle curve; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.

Run from _media; writes mach-angle-versus-speed-ratio.png to the CWD.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    plt.rcParams.update({"font.size": 12, "axes.spines.top": False,
                         "axes.spines.right": False})
    fig, ax = plt.subplots(figsize=(8, 5.5), dpi=120, facecolor="white")
    ax.set_facecolor("white")
    mach = np.linspace(1, 6, 1200)
    angle = np.degrees(np.arcsin(1 / mach))
    ax.plot(mach, angle, color="#285b89", lw=2.6)
    ax.plot(1, 90, "o", ms=7, mfc="white", mec="#285b89", mew=1.8)
    ax.text(1.12, 84, r"$90^\circ$ limit as $M\to1^+$", fontsize=11)
    ax.plot([2, 2], [0, 30], "--", color="#c64632", lw=1.3)
    ax.plot([1, 2], [30, 30], "--", color="#c64632", lw=1.3)
    ax.plot(2, 30, "o", color="#c64632", ms=8)
    ax.annotate(r"$M=2\quad\Rightarrow\quad\mu=30^\circ$",
                (2, 30), (3.1, 52), color="#a63223",
                arrowprops={"arrowstyle": "->", "color": "#a63223"})
    ax.text(3.0, 71,
            r"$U=680\ \mathrm{m\,s^{-1}}$" + "\n" +
            r"$c=340\ \mathrm{m\,s^{-1}}$" + "\n" +
            "Chosen uniform-gas example",
            fontsize=11, bbox={"boxstyle": "round,pad=0.5",
                              "facecolor": "#f3f6f9", "edgecolor": "#c8d1db"})
    ax.set_xlim(0.95, 6.05)
    ax.set_ylim(0, 96)
    ax.set_xticks([1, 2, 3, 4, 5, 6])
    ax.set_yticks([0, 15, 30, 45, 60, 75, 90])
    ax.set_xlabel(r"Mach number $M=U/c$ (gas-relative speed)")
    ax.set_ylabel(r"Mach half-angle $\mu$ (degrees)")
    ax.grid(alpha=0.22)
    ax.set_title(r"A faster source makes a narrower sound cone: "
                 r"$\mu=\arcsin(1/M)$", fontsize=14, pad=14)
    fig.text(0.5, 0.075,
             "Weak linear waves, steady source, uniform isotropic 3D medium; M > 1.",
             ha="center", fontsize=10)
    fig.text(0.5, 0.029,
             "Model prediction; not a finite-deflection shock angle or measured boom strength.",
             ha="center", fontsize=10, color="#525b65")
    fig.subplots_adjust(left=0.12, right=0.975, top=0.875, bottom=0.23)
    fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"),
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()