codex/past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2004/iii/paper-75-wave-steepening.py

past-exam-of-the-mathematics-course-of-the-university-of-cambridge/2004/iii/paper-75-wave-steepening.py
"""Original sketch. Tested with Python 3.14, numpy 2.3, matplotlib 3.10.
Writes only its opaque PNG basename in the caller's current directory.
The caller may supply MPLCONFIGDIR; this script preserves it.
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 10})
fig,ax=plt.subplots(1,4,figsize=(13.5,3.7),layout='constrained',facecolor='white')
a=.2
T=2/(3*a)
for s in np.linspace(0,1.5,11):
    t=np.linspace(s,T,150)
    ax[0].plot((1+1.5*a*np.sin(s))*(t-s),t,color=plt.cm.viridis(s/1.5))
ax[0].plot([0,T],[0,T],'k--',lw=1.4,label='leading edge')
ax[0].plot(T,T,'ro',label='first intersection')
ax[0].set(xlabel=r'$x\omega/c_0$',ylabel=r'$\omega t$',title='Outgoing characteristics')
ax[0].legend(fontsize=8,loc='upper left')
for j,t in enumerate([1,2.7]):
    s=np.linspace(0,t,900)
    x=(1+1.5*a*np.sin(s))*(t-s)
    h=(1+.5*a*np.sin(s))**2
    ax[j+1].plot(x,h,color=['#1469a0','#b24716'][j],lw=2)
    ax[j+1].plot([t,t+.7],[1,1],color='gray')
    ax[j+1].axhline(1,color='gray',lw=.7,ls=':')
    ax[j+1].set(xlabel=r'$x\omega/c_0$',ylabel=r'$h/H$',title=rf'Profile at $\omega t={t:g}$',ylim=(.97,1.24))
    ax[j+1].text(.03,.94,'Forward slope steepens',transform=ax[j+1].transAxes,va='top',fontsize=9)
ax[3].plot([0,.5,.9,1.25,1.45,1.45,1.9],[1.07,1.16,1.21,1.17,1.11,1,1],color='#b24716',lw=2)
ax[3].axhline(1,color='gray',ls=':',lw=.7)
ax[3].annotate('Bore',xy=(1.45,1.05),xytext=(.5,1.04),arrowprops={'arrowstyle':'->'})
ax[3].set(xlabel='Distance (schematic)',ylabel=r'$h/H$',title='After smooth-wave failure',ylim=(.97,1.24))
ax[3].set_xticks([])
fig.savefig('paper-75-wave-steepening.png',dpi=120,facecolor='white',transparent=False)