codexpast-exam-of-the-physics-of-atmospheres-and-oceans-course-of-the-university-of-oxford2021-pluto-streamlines.py
past-exam-of-the-physics-of-atmospheres-and-oceans-course-of-the-university-of-oxford/2021-pluto-streamlines.py
"""Original exact linear-drag basin streamlines; Python 3.14, numpy, matplotlib."""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
a, period, forcing = 1188e3, 6.4 * 86400, 1e-10
drag, beta = 1 / period, 4 * np.pi / (period * a)
b, length = a * np.pi / 3, a * np.deg2rad(50)
m = np.pi / b
lam = (beta + np.sqrt(beta**2 + 4 * drag**2 * m**2)) / (2 * drag)
gamma = m**2 / lam
cw = -np.expm1(-gamma * length) / -np.expm1(-(lam + gamma) * length)
ce = -np.expm1(-lam * length) / -np.expm1(-(lam + gamma) * length)
amplitude = -forcing / (drag * m**2)
x, y = np.linspace(0, length, 500), np.linspace(-b / 2, b / 2, 350)
xx, yy = np.meshgrid(x, y)
profile = amplitude * (1 - cw * np.exp(-lam * xx) - ce * np.exp(gamma * (xx - length)))
psi = profile * np.cos(m * yy)
fig, ax = plt.subplots(figsize=(7.4, 6), dpi=120, facecolor="white")
ax.set_facecolor("white")
scale = forcing * length / beta
ax.contour(xx / 1000, yy / 1000, psi / scale, levels=np.linspace(psi.min() / scale * 0.97, psi.min() / scale * 0.08, 11), colors="#28639a", linewidths=1.4)
for start, end in [((550, 480), (350, 480)), ((350, -480), (550, -480)), ((50, 130), (50, -100)), ((850, -100), (850, 130))]:
ax.annotate("", xy=end, xytext=start, arrowprops={"arrowstyle": "-|>", "color": "#b24632", "lw": 2, "mutation_scale": 16})
ax.axvspan(0, drag / beta / 1000, color="#e4edf4", alpha=0.45, zorder=-2)
ax.text(120, 0, "Fast southward\nreturn", fontsize=11, va="center")
ax.text(600, 0, "Broad northward\ninterior flow", fontsize=11, va="center")
ax.set(xlim=(0, length / 1000), ylim=(-b / 2000, b / 2000), aspect="equal", xlabel="Eastward distance $x$ (km)", ylabel="Northward distance $y$ (km)", title="Closed basin circulation: exact streamfunction contours")
fig.tight_layout()
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()