codex/past-exam-of-the-perturbation-methods-and-applied-complex-variables-course-of-the-university-of-oxford/2007-free-streamline-hodograph.py

past-exam-of-the-perturbation-methods-and-applied-complex-variables-course-of-the-university-of-oxford/2007-free-streamline-hodograph.py
"""Generate PNG in CWD; Python 3.14, NumPy, Matplotlib."""
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig,axs=plt.subplots(1,2,figsize=(9,4),facecolor="white",layout="constrained")
ax=axs[0];ax.axhspan(0,1,color="#e0eff7")
ax.axhline(0,color="#b23c41",lw=2);ax.axhline(1,color="#12679b",lw=2)
ax.scatter([0],[0],color="#b23c41")
ax.text(0,-.14,"Corner B: $w=0$",ha="center",fontsize=10)
ax.text(0,1.1,r"Free surface: $\psi=1$",ha="center")
ax.text(-1.35,.08,"Upstream",fontsize=10);ax.text(.6,.08,"Downstream",fontsize=10)
ax.annotate("",xy=(1,.5),xytext=(-1,.5),arrowprops=dict(arrowstyle="->",color="#12679b",lw=2))
ax.set(xlim=(-2,2),ylim=(-.35,1.45),xlabel=r"$\phi$",ylabel=r"$\psi$",title="Potential strip")
ax=axs[1];a=np.linspace(0,np.pi/2,200)
ax.fill([0,*np.cos(a),0],[0,*np.sin(a),0],color="0.92")
ax.fill_between([0,2.3],[2.3,2.3],color="#e0eff7",zorder=-2)
ax.fill([0,*np.cos(a),0],[0,*np.sin(a),0],color="white",zorder=-1)
ax.plot(np.cos(a),np.sin(a),color="#12679b",lw=3)
ax.plot([1,2.3],[0,0],color="#b23c41",lw=3)
ax.plot([0,0],[1,2.3],color="#b23c41",lw=3)
ax.scatter([1,0],[0,1],color="#12679b",s=30)
ax.text(1,-.15,"Upstream: 1",ha="center",fontsize=10)
ax.text(.08,1.03,"Downstream: $i$",fontsize=10)
ax.text(1.2,1.15,r"$|q|>1$",fontsize=13)
ax.text(.14,2.1,r"Corner B: $q\to\infty$",fontsize=10)
ax.annotate("",xy=(1.95,1.95),xytext=(1.5,1.5),arrowprops=dict(arrowstyle="->",lw=1.5,color="#b23c41"))
ax.set(xlim=(-.15,2.4),ylim=(-.3,2.4),aspect="equal",xlabel=r"$\Re q=u$",ylabel=r"$\Im q=-v$",title="Exterior quarter-disc hodograph")
fig.savefig("2007-free-streamline-hodograph.png",dpi=160,facecolor="white")