codexpast-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge2023paper-2-optical-forces.py
past-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge/2023/paper-2-optical-forces.py
"""Original explanatory diagram; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7."""
from pathlib import Path
import os
import numpy as np
os.environ.setdefault("MPLCONFIGDIR", "/tmp/cambridge-nano2-matplotlib")
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle, RegularPolygon, FancyArrowPatch, Wedge
plt.rcParams.update({"font.size": 10, "figure.facecolor": "white", "savefig.facecolor": "white"})
fig,ax=plt.subplots(figsize=(8,4.5),layout="constrained");ax.add_patch(Circle((0,0),1,fc="#e7eff4",ec="#246c99",lw=1.5))
ax.axhline(1.18,color=".4",ls="--");ax.text(1.5,1.25,"Optical axis / high intensity")
def refract(d,n,eta):
c=-np.dot(n,d);return eta*d+(eta*c-np.sqrt(1-eta*eta*(1-c*c)))*n
for b,power,col in [(.58,2.2,"#b23a37"),(-.58,1.,"#d19829")]:
entry=np.array([-np.sqrt(1-b*b),b]);di=np.array([1.,0.])
inside=refract(di,entry,1.33/1.6)
exit=entry-2*np.dot(entry,inside)*inside
dout=refract(inside,-exit,1.6/1.33)
end=exit+1.9*dout
ax.plot([-2.7,entry[0],exit[0],end[0]],[b,entry[1],exit[1],end[1]],color=col,lw=power)
ax.annotate("",entry+(exit-entry)*.63,entry+(exit-entry)*.42,arrowprops=dict(arrowstyle="->",color=col))
dp=di-dout
dp=dp/np.linalg.norm(dp)*(.42*power)
start=np.array([.1,-.08 if b>0 else -.2]);ax.annotate("",start+dp,start,arrowprops=dict(arrowstyle="-|>",color=col,lw=2))
ax.text(-2.7,.79,"Stronger ray",color="#b23a37");ax.text(-2.7,-.89,"Weaker ray",color="#d19829")
ax.text(.2,.5,"Larger upward\nreaction",color="#b23a37",fontsize=9)
ax.text(.2,-.6,"Smaller downward\nreaction",color="#aa7110",fontsize=9)
ax.text(-.8,-1.48,"Ray deflection changes light momentum;\nnet particle force points toward the brighter axis.")
ax.set(aspect="equal",xlim=(-2.9,3.2),ylim=(-1.6,1.65));ax.axis("off")
ax.set_title("Local ray tracing near the waist: particle index 1.60, surrounding index 1.33")
fig.savefig(Path(__file__).with_suffix(".png").name,dpi=145)