codex/past-exam-of-the-chemistry-course-of-the-year-1-of-the-natural-sciences-course-of-the-university-of-cambridge/2022/paper-1-grignard-ketones.py

past-exam-of-the-chemistry-course-of-the-year-1-of-the-natural-sciences-course-of-the-university-of-cambridge/2022/paper-1-grignard-ketones.py
"""Original solution schematic; Python 3.14, numpy 2.3.5, matplotlib 3.10.7.
Writes its own PNG basename to the current working directory.
"""
from pathlib import Path
import sys
for candidate in Path(__file__).resolve().parents:
    if (candidate / 'tools' / 'chemistry_diagram.py').exists():
        sys.path.insert(0, str(candidate))
        break
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, Circle
from tools.chemistry_diagram import atom, bond, ring, wedge, curly, flow, finish, save, BLUE, ORANGE, GREEN

def txt(ax,x,y,s,size=11,color='0.15',ha='center'):
    ax.text(x,y,s,ha=ha,va='center',fontsize=size,color=color)
def couple(ax,a,b,label,rad=.3,offset=(0,0)):
    ax.add_patch(FancyArrowPatch(a,b,connectionstyle=f'arc3,rad={rad}',arrowstyle='<->',mutation_scale=10,color=BLUE,lw=1.3))
    m=(np.asarray(a)+np.asarray(b))/2+np.asarray(offset)
    txt(ax,*m,label,10,BLUE)
def benzene(ax,c=(0,0),scale=1):
    t=np.deg2rad([30,90,150,210,270,330]);pts=np.column_stack([np.cos(t),np.sin(t)])*scale+np.asarray(c)
    ring(ax,pts,{0:2,2:2,4:2});return pts
def carbonyl(ax,c,left,right,oxy='O',tetra=False,down=None):
    x,y=c;o=(x,y+1);bond(ax,c,o,1 if tetra else 2);atom(ax,o,oxy)
    for q,label in [(left[0],left[1]),(right[0],right[1])]:bond(ax,c,q);atom(ax,q,label)
    if down:bond(ax,c,down[0]);atom(ax,down[0],down[1])
    return o

fig,axs=plt.subplots(3,1,figsize=(12,9))
ax=axs[0]
carbonyl(ax,(1.1,0),((-.1,0),r'$R^1$'),((2.4,0),'OEt'))
atom(ax,(-.2,1.2),r'$R-MgBr$');curly(ax,(.0,1),(1.0,.15),rad=.1);curly(ax,(1.17,.65),(1.15,1.15),rad=-.7)
flow(ax,(2.9,.4),(3.7,.4));atom(ax,(5,.4),r'$R^1C(O^-)(OEt)R$')
flow(ax,(6.5,.4),(7.2,.4),'−EtO⁻');atom(ax,(8.3,.4),r'$R^1COR$')
flow(ax,(9.3,.4),(10.3,.4),'+R, then H⁺');atom(ax,(11.4,.4),r'$R^1C(OH)R_2$')
finish(ax,(-1,13),(-.9,2.1),'Ester: ketone appears before quench and is attacked again')
ax=axs[1]
carbonyl(ax,(1.1,0),((-.1,0),'Me'),((2.4,0),'OAc'))
atom(ax,(-.2,1.2),r'$nBu-MgBr$');curly(ax,(.1,1),(1.0,.15),rad=.1);curly(ax,(1.17,.65),(1.15,1.15),rad=-.7)
flow(ax,(3,.4),(3.8,.4),'cold')
carbonyl(ax,(5.0,0),((3.7,0),'Me'),((6.3,0),'OAc'),oxy=r'$O^-MgBr^+$',tetra=True,down=((5,-.9),'nBu'))
ax.plot([5.1,6.3],[.9,.15],ls=':',color=GREEN)
flow(ax,(7,.4),(8.2,.4),'aqueous work-up');atom(ax,(10,.4),r'$MeC(=O)nBu$')
finish(ax,(-1,13),(-1.4,2.0),'Anhydride: persistent magnesium-bound addition complex delays ketone release')
ax=axs[2]
atom(ax,(.1,.4),'nBu–MgBr');atom(ax,(2.6,.4),r'$Ph-C\equiv N$');curly(ax,(1,.55),(2.65,.55),rad=-.6)
flow(ax,(4,.4),(4.9,.4));atom(ax,(6.2,.4),r'$PhC(nBu)=N^-MgBr^+$')
flow(ax,(7.9,.4),(9,.4),'H⁺, water');atom(ax,(10.5,.4),r'$PhC(=O)nBu$')
txt(ax,6,-.8,'Ketimine salt → iminium → carbinolamine → ketone + ammonium',11)
finish(ax,(-1,13),(-1.4,2),'Nitrile: nitrogen-anionic intermediate; ketone is generated during hydrolysis')
fig.tight_layout()

save(fig, 'paper-1-grignard-ketones.png')