codexpast-exam-of-the-mathematics-course-of-the-university-of-cambridge2010iiipaper-71-mountain-frames.py
past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2010/iii/paper-71-mountain-frames.py
"""Sketch mountain-wave phase and energy directions; output to caller CWD.
Tested with Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def arrow(ax, origin, delta, color, label, offset):
end = np.asarray(origin) + delta
ax.annotate("", end, origin,
arrowprops={"arrowstyle": "-|>", "color": color, "lw": 2.5})
ax.text(*(end + offset), label, color=color, fontsize=10,
ha="center", va="center",
bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.9})
def main():
fig, axes = plt.subplots(1, 2, figsize=(11, 4.7), dpi=100)
fig.set_facecolor("white")
x, z = np.meshgrid(np.linspace(0, 4.4, 400), np.linspace(0, 3.2, 300))
for ax in axes:
ax.contour(x, z, x + 2 * z, levels=np.arange(-2, 13, 1),
colors="#a7b5c1", linewidths=1.2)
ax.set(xlim=(0, 4.4), ylim=(0, 3.2), ylabel="Height z")
ax.set_aspect("equal")
ax.text(3.3, 0.85, "Wave crests", rotation=-27, color="#5c6974",
bbox={"facecolor": "white", "edgecolor": "none", "alpha": 0.9})
origin = np.array([2.05, 1.55])
axes[0].plot(np.linspace(0, 4.4, 400),
0.08 + 0.05 * np.sin(2 * np.pi * np.linspace(0, 4.4, 400)),
color="black", lw=1.5)
arrow(axes[0], origin, np.array([0.6, 1.2]), "#176a9e",
"Energy: up and downwind", np.array([0.15, 0.22]))
axes[0].scatter(*origin, s=20, color="black", zorder=5)
axes[0].text(1.55, 1.3, "Phase pattern stationary", fontsize=10,
ha="center", bbox={"facecolor": "white", "edgecolor": "none"})
axes[0].set(xlabel="Downwind coordinate x", title="Frame fixed to the hills")
arrow(axes[1], origin, np.array([-1.2, 0.6]), "#176a9e",
"Energy: upstream and up", np.array([0.15, 0.3]))
arrow(axes[1], origin, np.array([-0.45, -0.9]), "#bc5138",
"Phase: upstream and down", np.array([0.0, -0.28]))
axes[1].scatter(*origin, s=20, color="black", zorder=5)
axes[1].set(xlabel="Moving coordinate x′ = x − U₀t",
title="Frame moving with the mean wind")
fig.suptitle("Uniform mountain wave: m/k = 2; arrows show directions", fontsize=12)
fig.tight_layout()
fig.savefig(Path.cwd() / "paper-71-mountain-frames.png",
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()