codexpast-exam-of-the-further-mathematical-biology-course-of-the-university-of-oxfordoxford-2023-further-mathematical-biology-pattern-selection.py
past-exam-of-the-further-mathematical-biology-course-of-the-university-of-oxford/oxford-2023-further-mathematical-biology-pattern-selection.py
"""Generate dispersion and early-mode sketches in cwd; Python 3.14, NumPy, Matplotlib."""
import os
os.environ.setdefault('MPLCONFIGDIR', '/tmp/oxford-2023-fmb-matplotlib')
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
# Linear perturbations only; no nonlinear pattern simulation is represented.
db, dn, mu, b0, lam = .002, .04, .5, 2., 2.5
nstar = lam/(mu*(1+b0**2))
eta = lam*(b0**2-1)/(1+b0**2)**2
chis = [.6, .8, 1.5, 3.]
colors = ['#777777', '#2463a4', '#d48228', '#a43c53']
k = np.linspace(0, 40, 1000)
mode = np.arange(1, 17)
km = mode*np.pi
x = np.linspace(0, 1, 800)
def growth(k, chi):
q = k*k
coupling = chi*eta/(1+nstar)**2
return -(1+mu+(db+dn)*q)/2 + np.sqrt((mu-1+(dn-db)*q)**2+4*coupling*q)/2
fig = plt.figure(figsize=(10.5, 6.7), layout='constrained')
grid = fig.add_gridspec(3, 2, height_ratios=[1.4, .75, .75])
ax = fig.add_subplot(grid[0, :])
for chi, color in zip(chis, colors):
ax.plot(k, growth(k, chi), color=color, lw=1.6, label=rf'$\chi_b={chi:g}$')
ax.scatter(km[km<=40], growth(km[km<=40], chi), s=13, color=color, zorder=4)
ax.axhline(0, color='black', lw=.8)
ax.set(xlabel='Wavenumber k (dots: allowed k = mπ)', ylabel='Larger growth rate σ₊',
xlim=(0, 40), ylim=(-1.05, 3.2), title='Increasing chemotaxis changes which spatial mode grows fastest')
ax.legend(loc='upper right', ncol=2)
selected = []
for j, (chi, color) in enumerate(zip(chis, colors)):
a = fig.add_subplot(grid[1+j//2, j%2])
rates = growth(km, chi)
m = int(mode[np.argmax(rates)])
best = float(rates.max())
selected.append({'chi_b':chi, 'm_fast':m if best>0 else None,
'largest_discrete_growth_rate':best})
if best > 0:
q = (m*np.pi)**2
kappa = chi/(1+nstar)**2
ratio = (1+db*q+best)/(kappa*q)
a.plot(x, np.cos(m*np.pi*x), color=color, label='b perturbation')
a.plot(x, ratio*np.cos(m*np.pi*x), color=color, ls='--', label='n perturbation')
title = rf'$\chi_b={chi:g}$: fastest mode $m={m}$, $\sigma_+={best:.3f}$'
else:
a.axhline(0, color=color, lw=2)
title = rf'$\chi_b={chi:g}$: every allowed mode decays'
a.set(xlim=(0, 1), ylim=(-1.4, 1.4), xlabel='x', ylabel='Mode shape', title=title)
a.axhline(0, color='0.7', lw=.5)
a.title.set_fontsize(10)
if best>0:
a.legend(fontsize=7, loc='upper right')
fig.suptitle('Early pattern selection: d_b=0.002, d_n=0.04, μ=0.5, b₀=2, λ=2.5', fontsize=12)
fig.savefig('oxford-2023-further-mathematical-biology-pattern-selection.png', dpi=145, facecolor='white')
if __name__ == '__main__':
import json
print(json.dumps(selected, indent=2))