codex/past-exam-of-the-chemistry-course-of-the-year-3-of-the-natural-sciences-course-of-the-university-of-cambridge/2021/paper-1-ring-transformations.py

past-exam-of-the-chemistry-course-of-the-year-3-of-the-natural-sciences-course-of-the-university-of-cambridge/2021/paper-1-ring-transformations.py
"""Original atom-mapped ring transformations; Python 3.14/numpy/matplotlib.

Outputs only its PNG basename in the current working directory.
"""
import numpy as np
import matplotlib.pyplot as plt

BLUE='#24659a'; ORANGE='#b66a21'; GREEN='#28794b'

def line(ax,a,b,order=1,color='#555',ls='-'):
    a=np.array(a,float);b=np.array(b,float)
    v=b-a;v/=np.linalg.norm(v)
    off=np.array([-v[1],v[0]])*.045
    for d in ([0] if order==1 else [-1,1]):
        u=a+d*off;w=b+d*off
        ax.plot([u[0],w[0]],[u[1],w[1]],color=color,lw=1.6,ls=ls,zorder=1)

def atom(ax,p,label,color=BLUE):
    ax.text(*p,label,ha='center',va='center',fontsize=12,color=color,
            bbox={'facecolor':'white','edgecolor':'none','pad':1},zorder=4)

def polygon(n):
    theta=np.arange(n)*2*np.pi/n+np.pi/2
    return np.column_stack([np.cos(theta),np.sin(theta)])

def skeleton(ax,pts,labels,orders=None):
    orders=orders or {}
    for i in range(len(pts)):line(ax,pts[i],pts[(i+1)%len(pts)],orders.get(i,1))
    for p,l in zip(pts,labels):atom(ax,p,l)

def title(ax,heading,foot):
    ax.set_title(heading,fontsize=11,pad=15)
    ax.text(.5,-.04,foot,ha='center',va='top',fontsize=9,transform=ax.transAxes)
    ax.set(xlim=(-2.2,2.2),ylim=(-1.8,1.9));ax.set_aspect('equal');ax.axis('off')

def draw():
    fig,axs=plt.subplots(2,3,figsize=(13,8.5),layout='constrained')
    p=polygon(6)
    a=axs[0,0];skeleton(a,p,list('ABCDEF'),{5:2})
    for i in [0,5]:
        q=p[i]+np.array([-.25 if i==5 else .25,.55]);line(a,p[i],q);atom(a,q,'Me',ORANGE)
    for i in [2,3]:
        q=p[i]+np.array([0,-.55]);line(a,p[i],q,ls='--');atom(a,q,'CN',GREEN)
    title(a,'Q8: stereospecific cycloaddition','A=F derives from the diene; C–CN and D–CN are cis')
    a=axs[0,1]
    x=np.linspace(-1.9,1.9,6);pts=np.column_stack([x,np.array([.2,-.15,.2,-.15,.2,-.15])])
    for i in range(5):line(a,pts[i],pts[i+1])
    for i,p0 in enumerate(pts):atom(a,p0,list('ABCDEF')[i])
    for i in [0,5]:atom(a,pts[i]+[0,.7],'O=C–Me',ORANGE)
    for i in [2,3]:line(a,pts[i],pts[i]+[0,-.65],ls='--');atom(a,pts[i]+[0,-.75],'CN',GREEN)
    a.annotate('',pts[5]+[0,.2],pts[1]+[0,.2],arrowprops={'arrowstyle':'->','connectionstyle':'arc3,rad=-.65','color':BLUE,'lw':1.6})
    title(a,'Q8: open A=F by reductive ozonolysis','B enolate attacks carbonyl F: form the B–F bond')
    a=axs[0,2];p5=polygon(5);skeleton(a,p5,list('BCDEF'),{4:2})
    for i in [1,2]:
        q=p5[i]+np.array([-.55 if i==1 else -.15,-.45]);line(a,p5[i],q,ls='--');atom(a,q,'CN',GREEN)
    q=p5[0]+[.1,.65];line(a,p5[0],q);atom(a,q,'C(A)(=O)Me',ORANGE)
    q=p5[4]+[.65,.35];line(a,p5[4],q);atom(a,q,'Me',ORANGE)
    title(a,'Q8: dehydration gives B=F','A remains in the acetyl group; no carbon is discarded')
    # The five lowercase atoms are the retained original cyclopentadiene ring.
    pr=polygon(5)*.95
    for col in [0,1]:
        a=axs[1,col];skeleton(a,pr,list('abcde'))
        pa=pr[0];pd=pr[3]
        f=np.array([1.35,.75]);g=np.array([1.25,-.6])
        if col==0:
            line(a,pa,f);line(a,f,g);line(a,g,pd)
            atom(a,f,'f: C=O',ORANGE);atom(a,g,'g: CH₂',ORANGE)
            title(a,'Q12(b): ketone after Nef reaction and H₂','The a–f–g–d bridge lies on one face of ring abcde')
        else:
            o=np.array([.8,1.45])
            line(a,pa,o);line(a,o,f);line(a,f,g);line(a,g,pd)
            atom(a,o,'O',GREEN);atom(a,f,'f: C=O',ORANGE);atom(a,g,'g: CH₂',ORANGE)
            title(a,'Q12(b): Baeyer–Villiger insertion at a–f','O enters the bridge; the five-carbon ring survives')
    a=axs[1,2];skeleton(a,pr,list('abcde'))
    q=pr[0]+[.55,.6];line(a,pr[0],q,ls='--');atom(a,q,'OH',GREEN)
    q=pr[3]+[1.15,-.15];line(a,pr[3],q,ls='--');atom(a,q,'g: CH₂–f: CHO',ORANGE)
    a.text(0,-1.50,'HWE: CHO → CH=CHCO₂R (E)',ha='center',fontsize=10,color=BLUE)
    title(a,'Q12(b): lactol opens to the aldehyde','Same-face attachments at a and d give cis-1,3 substitution')
    fig.savefig('paper-1-ring-transformations.png',dpi=155,facecolor='white');plt.close(fig)

if __name__=='__main__':draw()