codex/past-exam-of-the-fluids-and-waves-course-of-the-university-of-oxford/2014-channel-instability.py

past-exam-of-the-fluids-and-waves-course-of-the-university-of-oxford/2014-channel-instability.py
"""Oxford A10 (2014): growing symmetric-channel vortex-sheet modes.

Writes 2014-channel-instability.png in the process CWD.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Dependencies: repository root pyproject.toml.
"""

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    x=np.linspace(0,2*np.pi,800)
    fig,(ax,bx)=plt.subplots(1,2,figsize=(9,4.0),facecolor="white")
    ax.axhline(1,color="#37474f",lw=2)
    ax.axhline(-1,color="#37474f",lw=2)
    for t,color in [(0,"#7aa6bc"),(1.5,"#176b93"),(3,"#b4591c")]:
        eta=.025*np.exp(t/2)*np.cos(x-t/2)
        ax.plot(x,eta,color=color,lw=1.8,label=f"$t={t:g}$")
    ax.annotate("Upper base flow $U=1$",xy=(5.8,.65),xytext=(.4,.65),
                arrowprops={"arrowstyle":"->","color":"0.2"},va="center",fontsize=10)
    ax.text(.4,-.65,"Lower base flow at rest",fontsize=10)
    ax.set(xlim=(0,2*np.pi),ylim=(-1.15,1.15),xlabel="$x$",ylabel="$y$",
           title="Small growing waves ($k=h=U=1$)")
    ax.legend(loc="lower right",fontsize=9,frameon=False)
    kh=np.linspace(0,4,300)
    bx.plot(kh,kh/2,color="#176b93",lw=2,label="Growing branch")
    bx.plot(kh,-kh/2,color="#b4591c",lw=2,label="Decaying branch")
    bx.axhline(0,color="0.6",lw=.8)
    bx.text(.15,1.65,r"$\mathrm{Re}\,\omega=kU/2$",fontsize=11)
    bx.set(xlim=(0,4),ylim=(-2.2,2.2),xlabel="$kh$",ylabel=r"$\mathrm{Im}\,\omega\ h/U$",
           title="Complex frequencies: growth and decay")
    bx.legend(loc="lower left",fontsize=9,frameon=False)
    for axis in (ax,bx):axis.spines[["top","right"]].set_visible(False)
    fig.tight_layout()
    fig.savefig("2014-channel-instability.png",dpi=155,facecolor="white",transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()