codexpast-exam-of-the-mathematics-and-the-environment-and-mathematical-physiology-course-of-the-university-of-oxford2008-enzyme-phase-plane.py
past-exam-of-the-mathematics-and-the-environment-and-mathematical-physiology-course-of-the-university-of-oxford/2008-enzyme-phase-plane.py
# Python 3.14.4; numpy 2.3.5; matplotlib 3.10.7. Output in current working directory.
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from pathlib import Path
plt.rcParams.update({'figure.facecolor':'white','axes.facecolor':'white','font.size':10})
eps=.05;K=.5;lam=.2
def fun(y):
s,c=y;return np.array([eps*(-s+(s+K-lam)*c),s-(s+K)*c])
y=np.array([1.,0.]);ys=[y.copy()];dt=.025
for _ in range(32000):
a=fun(y);b=fun(y+dt*a/2);c=fun(y+dt*b/2);e=fun(y+dt*c);y=y+dt*(a+2*b+2*c+e)/6;ys.append(y.copy())
ys=np.array(ys);ss=np.linspace(0,1.05,400);fig,ax=plt.subplots(figsize=(7,4.8))
ax.plot(ss,ss/(ss+K),'--',color='#2171b5',label=r'$\dot C=0$');ax.plot(ss,ss/(ss+K-lam),':',color='#606060',lw=2,label=r'$\dot S=0$');ax.plot(ys[:,0],ys[:,1],color='#c34f2d',lw=2,label='kinetic trajectory')
for i in [20,450,1500,5000]:
ax.annotate('',ys[i+35],ys[i],arrowprops={'arrowstyle':'->','color':'#c34f2d','lw':1.8})
ax.plot(1,0,'ko');ax.annotate('initial layer',(ys[30,0],ys[30,1]),(.65,.12),arrowprops={'arrowstyle':'->'},fontsize=9);ax.annotate('slow depletion',(ys[2200,0],ys[2200,1]),(.1,.72),arrowprops={'arrowstyle':'->'},fontsize=9)
ax.set(xlabel='$[S]/S_0$',ylabel='$[C]/E_0$',xlim=(0,1.05),ylim=(0,.85),title=r'Enzyme phase plane: $\epsilon=.05, K=.5, \lambda=.2$');ax.legend(loc='lower left',fontsize=8);ax.grid(alpha=.2)
fig.tight_layout();fig.savefig(Path('2008-enzyme-phase-plane.png'),dpi=150,facecolor='white');plt.close(fig)