codex/sphere-drag-coefficient-stokes-limit.py

sphere-drag-coefficient-stokes-limit.py
"""Plot the creeping-limit approximation; write an opaque PNG to CWD.

Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
"""
from pathlib import Path

import matplotlib

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


def main():
    reynolds = np.logspace(-4, -1, 401)
    toy_re = np.array([0.002, 0.004])
    fig, ax = plt.subplots(figsize=(7.4, 4.7), dpi=120)
    fig.patch.set_facecolor("white")
    ax.set_facecolor("white")
    ax.loglog(reynolds, 24 / reynolds, color="#246a9b", lw=2.5,
              label=r"Creeping-limit approximation: $C_D=24/\mathrm{Re}_d$")
    ax.scatter(toy_re, 24 / toy_re, color=["#ba5739", "#754c9b"], s=55, zorder=4)
    for re, cd, label, offset in [(0.002, 12000, r"$U_0$: (0.002, 12000)", (-90, 28)),
                                  (0.004, 6000, r"$2U_0$: (0.004, 6000)", (18, -5))]:
        ax.annotate(label, (re, cd), xytext=offset, textcoords="offset points",
                    fontsize=10, arrowprops={"arrowstyle": "-", "color": "#555555"})
    ax.set(xlim=(1e-4, 1e-1), ylim=(180, 3e5),
           xlabel=r"Diameter-based Reynolds number $\mathrm{Re}_d=2a\rho U/\mu$",
           ylabel=r"Drag coefficient $C_D$ (frontal area $A=\pi a^2$)",
           title="Sphere drag coefficient in the creeping-flow approximation")
    ax.grid(True, which="both", alpha=0.18)
    ax.legend(loc="upper right", fontsize=9)
    fig.text(0.5, 0.035, "Mathematical small-Re approximation; not empirical data or an all-Re law.",
             ha="center", fontsize=10)
    fig.tight_layout(rect=(0, 0.075, 1, 1))
    fig.savefig(Path.cwd() / "sphere-drag-coefficient-stokes-limit.png",
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()