codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2015/iii/paper-79-rossby-dispersion.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2015/iii/paper-79-rossby-dispersion.py
"""Generate an original Rossby dispersion/reflection sketch in the caller's cwd.
Tested with Python 3.14, NumPy 2.3.5 and matplotlib 3.10.7.
MPLCONFIGDIR is supplied by the caller and is never overridden here.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np


def main():
    fig, (ax, circle) = plt.subplots(1, 2, figsize=(9, 5), dpi=100, facecolor='white')
    k = np.linspace(-5, 5, 1601)
    for ell, color in [(0, '#006e9c'), (0.6, '#a53f00'), (1.5, '#42783a')]:
        ax.plot(k, -k/(k*k+ell*ell+1), color=color, linewidth=2, label=f'$l/a={ell:g}$')
    w, ell = -0.3, 0.6
    centre = -1/(2*w)
    radius = np.sqrt(1/(4*w*w)-1)
    offset = np.sqrt(radius*radius-ell*ell)
    ki, kr = centre-offset, centre+offset
    ax.axhline(0, color='0.55', linewidth=0.7)
    ax.axvline(0, color='0.55', linewidth=0.7)
    ax.axhline(w, color='0.55', linestyle=':', linewidth=1)
    ax.scatter([ki, kr], [w, w], color='black', s=23, zorder=4)
    ax.set(xlabel='$k/a$', ylabel=r'$\omega a/\beta$', title='Frequency at fixed meridional wavenumber')
    ax.legend(loc='upper right', fontsize=9)
    ax.grid(alpha=0.15)
    angle = np.linspace(0, 2*np.pi, 801)
    circle.plot(centre+radius*np.cos(angle), radius*np.sin(angle), color='#006e9c', linewidth=2)
    circle.axhline(0, color='0.55', linewidth=0.7)
    circle.axvline(0, color='0.55', linewidth=0.7)
    circle.plot([0, centre+radius+0.3], [ell, ell], ':', color='0.5')
    circle.scatter([ki, kr], [ell, ell], color='black', s=25, zorder=4)
    circle.scatter([centre], [0], marker='+', color='0.4', s=65)
    circle.annotate('Incident\nwestward group', (ki, ell), xytext=(ki-0.1, ell+0.5), ha='center', fontsize=9,
                    arrowprops={'arrowstyle': '->', 'color': '0.25'})
    circle.annotate('Reflected\neastward group', (kr, ell), xytext=(kr+0.05, ell+0.5), ha='center', fontsize=9,
                    arrowprops={'arrowstyle': '->', 'color': '0.25'})
    circle.set(xlabel='$k/a$', ylabel='$l/a$', title=r'Constant frequency: $\omega a/\beta=-0.3$')
    circle.set_xlim(-0.1, centre+radius+0.3)
    circle.set_ylim(-1.55, 1.55)
    circle.set_aspect('equal', adjustable='box')
    circle.grid(alpha=0.15)
    fig.suptitle('Rossby waves: phase travels west, reflected energy travels east', fontsize=12)
    fig.tight_layout(rect=(0, 0, 1, 0.93))
    fig.savefig('paper-79-rossby-dispersion.png', dpi=100, facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()