codexpast-exam-of-the-astrophysical-fluid-dynamics-course-of-the-university-of-cambridge2017-subsonic-breezes.py
past-exam-of-the-astrophysical-fluid-dynamics-course-of-the-university-of-cambridge/2017-subsonic-breezes.py
"""Original Q4 isothermal breeze comparison; PNG basename goes 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 mach_profile(radius, peak):
"""Invert M^2/2-log(M) on the strictly subsonic branch."""
target = peak**2 / 2 - np.log(peak) + 2 * np.log(radius) + 2 / radius - 2
lo = np.full_like(radius, 1e-14)
hi = np.full_like(radius, peak)
for _ in range(65):
middle = (lo + hi) / 2
value = middle**2 / 2 - np.log(middle)
lo = np.where(value > target, middle, lo)
hi = np.where(value > target, hi, middle)
return (lo + hi) / 2
def main():
radius = np.unique(np.r_[np.geomspace(0.2, 12, 700), 1.0])
fig, axes = plt.subplots(1, 2, figsize=(9.6, 4), layout="constrained")
fig.patch.set_facecolor("white")
for peak, colour in [(0.25, "#17638d"), (0.5, "#b96e21"), (0.8, "#28784a")]:
mach = mach_profile(radius, peak)
label = rf"$\mathcal{{M}}_M={peak:g}$"
axes[0].plot(radius, mach, color=colour, linewidth=2.1, label=label)
axes[1].plot(radius, 1 / (radius**2 * mach), color=colour, linewidth=2.1, label=label)
axes[0].scatter(1, peak, color=colour, s=25, zorder=4)
axes[1].scatter(1, 1 / peak, color=colour, s=25, zorder=4)
for ax in axes:
ax.set_facecolor("white")
ax.set_xscale("log")
ax.axvline(1, color="#777777", linewidth=1, linestyle="--")
ax.set(xlim=(0.2, 12), xlabel=r"Radius $r/r_M$")
ax.grid(alpha=0.2, which="both")
ax.spines[["top", "right"]].set_visible(False)
axes[0].set(ylim=(0, 1), ylabel=r"Mach number $u/a$", title="Same radius, different peak speeds")
axes[0].legend(fontsize=9)
axes[1].set_yscale("log")
axes[1].set(ylabel=r"Density $\rho/\rho_{\rm char}$", title="Same mass flux, different densities")
fig.savefig(Path("2017-subsonic-breezes.png"), dpi=160, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()