codex/past-exam-of-the-soft-matter-and-biological-physics-course-of-the-university-of-cambridge/2014/paper-1-force-spectroscopy.py

past-exam-of-the-soft-matter-and-biological-physics-course-of-the-university-of-cambridge/2014/paper-1-force-spectroscopy.py
"""Original generic-physics figure; Python 3.14, numpy and matplotlib.
PNG basename output to cwd; no source figures or media paths.
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
plt.rcParams.update({'font.size':10,'figure.facecolor':'white','savefig.facecolor':'white','axes.spines.top':False,'axes.spines.right':False})
fig,(a,b)=plt.subplots(1,2,figsize=(9,3.5),layout='constrained')
for x,txt,col in [(0,'stage\nZ','#dadada'),(2,'single synthetic\nchain x','#e8c7a2'),(4,'cantilever\ndeflection d','#b8d2e8')]:a.add_patch(Rectangle((x,0),1.5,.8,facecolor=col,edgecolor='gray'));a.text(x+.75,.4,txt,ha='center',va='center',fontsize=9)
for x in [1.5,3.5]:a.plot([x,x+.5],[.4,.4],color='black')
a.text(2.8,-.6,'Series compliance: Z = x + d; force F = kc d',ha='center',fontsize=10);a.set(xlim=(-.2,5.8),ylim=(-1.2,1.6),title='Measurement chain (schematic)');a.axis('off')
f=np.linspace(.005,8,500);x=1/np.tanh(f)-1/f;d=f/10;b.plot(x,f,label='Molecular extension x/L');b.plot(x+d,f,label='Stage travel Z/L');b.set(xlabel='Scaled displacement',ylabel='Force in chosen thermal units',title='Illustrative freely-jointed-chain response');b.legend(frameon=False,fontsize=8)
fig.savefig('paper-1-force-spectroscopy.png',dpi=150);plt.close(fig)