codex/knudsen-scale-and-flight-survival.py

knudsen-scale-and-flight-survival.py
"""Draw scale comparisons and an explicitly fixed-hazard free-flight toy model.

Run from _media: MPLCONFIGDIR=/tmp/codex-wiki-matplotlib python3 ../knudsen-scale-and-flight-survival.py
Output: matching PNG basename in the current working directory.
Tested with Python 3.14.4, matplotlib 3.10.7, numpy 2.3.5.
No experimental data; lambda is a prescribed toy mean free path.
"""

from pathlib import Path

import matplotlib

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


def main():
    plt.rcParams.update({"font.size": 11, "axes.spines.top": False, "axes.spines.right": False})
    fig = plt.figure(figsize=(9.4, 6.9), facecolor="white", layout="constrained")
    grid = fig.add_gridspec(2, 3, height_ratios=[1.05, 1.15])
    ratios = [0.03, 0.5, 3.0]
    colors = ["#1670a7", "#90539c", "#bb5a20"]
    labels = ["Much shorter than the gap", "Comparable scales", "Longer than the gap"]
    for i, (kn, color, label) in enumerate(zip(ratios, colors, labels)):
        ax = fig.add_subplot(grid[0, i])
        ax.set(xlim=(0, 1), ylim=(0, 2.9))
        ax.axis("off")
        # All three wall separations represent L=0.7 drawing units.
        low, high = 0.35, 1.05
        ax.plot([0.08, 0.92], [low, low], lw=5, color="#79848e", solid_capstyle="butt")
        ax.plot([0.08, 0.92], [high, high], lw=5, color="#79848e", solid_capstyle="butt")
        ax.annotate("", (0.18, high), (0.18, low), arrowprops={"arrowstyle": "<->", "color": "#333333"})
        ax.text(0.11, (low + high) / 2, "$L$", ha="right", va="center")
        ax.plot([0.56, 0.56], [low, low + kn * (high - low)], lw=4, color=color, solid_capstyle="butt")
        ax.plot([0.51, 0.61], [low + kn * (high - low)] * 2, lw=2, color=color)
        if i == 0:
            ax.annotate(r"$\ell_{\rm mfp}$", (0.56, low + kn * (high - low)), (0.72, 0.63),
                        color=color, va="center", arrowprops={"arrowstyle": "-", "color": color})
        else:
            ax.text(0.66, low + kn * (high - low), r"$\ell_{\rm mfp}$", color=color, va="center")
        ax.text(0.5, 2.75, rf"$\mathrm{{Kn}}={kn:g}$", ha="center", weight="bold", fontsize=14)
        ax.text(0.5, 0.08, label, ha="center", fontsize=10)
    ax = fig.add_subplot(grid[1, :])
    kns = np.geomspace(0.01, 100, 700)
    survival = np.exp(-1 / kns)
    ax.semilogx(kns, survival, color="#293e54", lw=2.6)
    for kn, color in zip(ratios, colors):
        p = float(np.exp(-1 / kn))
        ax.plot(kn, p, "o", color=color, ms=7, zorder=3)
    ax.set(xlabel=r"Prescribed ratio $\mathrm{Kn}=\ell_{\rm mfp}/L$", ylabel="Probability of no gas–gas collision",
           ylim=(-0.025, 1.06), xlim=(0.01, 100))
    ax.set_title(r"Single flight of length $L$: $P_0=e^{-L/\ell_{\rm mfp}}$", fontsize=12)
    ax.grid(alpha=0.18, which="both")
    fig.suptitle("Mean free path is a molecular scale; the gap is an apparatus scale", fontsize=14)
    fig.supxlabel("Toy constant hazard per distance. Chosen points are not regime boundaries or measured data.", fontsize=10)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=120, facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()