codex/past-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge/2022/paper-2-growth-pathways.py

past-exam-of-the-mphil-in-micro-and-nanotechnology-enterprise-course-of-the-university-of-cambridge/2022/paper-2-growth-pathways.py
"""Original nanocrystal mechanism cartoons; Python 3.14/numpy/matplotlib.

Conceptual stages, not digitized microscopy or a quantitative growth model.
Output: PNG basename in the current working directory.
"""
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle, RegularPolygon
import numpy as np

BLUE, ORANGE, PURPLE = '#2d718b', '#b6623e', '#8b6a9d'
fig, axes = plt.subplots(2, 3, figsize=(11, 6.5), layout='constrained')
fig.patch.set_facecolor('white')
def setup(ax, title):
    ax.set(xlim=(0, 6), ylim=(0, 4), aspect='equal')
    ax.axis('off'); ax.set_title(title, fontsize=11)
def arrow(ax, a, b, text=None):
    ax.annotate('', b, a, arrowprops={'arrowstyle':'->', 'color':'.3', 'lw':1.5})
    if text: ax.text((a[0]+b[0])/2, (a[1]+b[1])/2+.18, text, ha='center', fontsize=8)
for ax, title in zip(axes.flat, ['Monomer addition', 'Ostwald ripening', 'Oriented attachment',
                               'One-dimensional growth', 'Two-dimensional growth', 'What the arrows represent']):
    setup(ax, title)
ax=axes[0,0]
ax.add_patch(Circle((3,2), .65, color=BLUE, alpha=.8))
for angle in np.arange(6)*np.pi/3:
    p=np.array([3,2])+1.6*np.array([np.cos(angle),np.sin(angle)])
    ax.add_patch(Circle(p,.09,color=ORANGE))
    arrow(ax,p,p*.0+np.array([3,2])+.8*np.array([np.cos(angle),np.sin(angle)]))
ax.text(3,.35,'Supersaturated solution feeds a seed',ha='center',fontsize=9)
ax=axes[0,1]
for x,r,c in [(1,.18,ORANGE),(2,.3,ORANGE),(4.8,.7,BLUE)]:
    ax.add_patch(Circle((x,2),r,color=c,alpha=.8))
arrow(ax,(2.6,2),(3.8,2),'Dissolve / redeposit')
ax.text(3,.35,'Curvature-dependent chemical potential',ha='center',fontsize=9)
ax=axes[0,2]
for x in [1,2]:
    ax.add_patch(Rectangle((x-.3,1.7),.6,.6,facecolor=BLUE,edgecolor='white'))
    for z in [.2,.4]:ax.plot([x-.3+z]*2,[1.7,2.3],color='white',lw=.7)
arrow(ax,(2.6,2),(3.8,2))
ax.add_patch(Rectangle((4,1.7),1.2,.6,facecolor=BLUE,edgecolor='white'))
ax.text(3,.35,'Aligned crystallites join and heal',ha='center',fontsize=9)
ax=axes[1,0]
centre=(.8,2.2)
ax.add_patch(RegularPolygon(centre,5,radius=.43,facecolor=BLUE,edgecolor='white'))
for angle in np.arange(5)*2*np.pi/5+np.pi/2:
    ax.plot([centre[0],centre[0]+.43*np.cos(angle)],[centre[1],centre[1]+.43*np.sin(angle)],color='white',lw=.7)
arrow(ax,(1.4,2.2),(2.4,2.2))
ax.add_patch(Rectangle((2.6,1.9),2.7,.6,facecolor=BLUE))
for yy in [2.05,2.25]:ax.plot([2.6,5.3],[yy,yy],color='white',lw=.8)
arrow(ax,(5.25,2.2),(5.85,2.2))
ax.text(3,.65,'Twinned seed + protected sides\npreferential axial addition',ha='center',fontsize=9)
ax=axes[1,1]
ax.add_patch(Rectangle((.4,2),.65,.22,facecolor=PURPLE))
ax.plot([.4,1.05],[2.11,2.11],color='white',lw=.8)
arrow(ax,(1.4,2.1),(2.3,2.1))
ax.add_patch(RegularPolygon((4,2.1),6,radius=1.15,orientation=np.pi/6,facecolor=PURPLE))
arrow(ax,(4,3.3),(4,3.8))
arrow(ax,(5.15,2.1),(5.8,2.1))
ax.text(3,.35,'Planar defect + basal-face protection\nlateral growth faster than thickening',ha='center',fontsize=9)
ax=axes[1,2]
ax.text(.2,3.3,'Growth mechanism: how material moves\n\nSymmetry breaking: which directions grow\n\nAnisotropic shape ≠ a new bulk lattice\n\nSpecific ligand and defect pathways vary',fontsize=10,va='top',linespacing=1.5)
fig.savefig('paper-2-growth-pathways.png',dpi=150,facecolor='white')
plt.close(fig)