"""Constructed single-breath tracer curve with a sharp equal-area replacement. Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7. Run in _media; PNG in cwd. No measured patient data: transition spans 100-200 mL; equal-area step is 150 mL. """ from pathlib import Path import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np v=np.linspace(0,500,2001) g=np.clip((v-100)/100,0,1) area=np.trapezoid(g,v) assert abs(area-350)<1e-9 and abs(500-area-150)<1e-9 left=(v>=100)&(v<=150);right=(v>=150)&(v<=200) assert abs(np.trapezoid(g[left],v[left])-12.5)<1e-9 assert abs(np.trapezoid(1-g[right],v[right])-12.5)<1e-9 fig,ax=plt.subplots(figsize=(8,4.5),dpi=100,facecolor='white') ax.plot(v,g,lw=2.5,color='#276c91',label='Constructed tracer curve') ax.plot([0,150,150,500],[0,0,1,1],'--',lw=1.7,color='#a34f28',label='Equal-area step') ax.fill_between(v[left],0,g[left],color='#b7d7e8',label='Equal areas = 12.5 mL each') ax.fill_between(v[right],g[right],1,color='#e8c6aa') ax.axvline(150,ymax=.86,color='#777777',ls=':',lw=1) ax.text(48,1.08,'Phase I',ha='center',fontsize=10) ax.text(150,1.08,'Phase II',ha='center',fontsize=10) ax.text(330,1.08,'Phase III',ha='center',fontsize=10) ax.annotate('First tracer: 100 mL',xy=(100,.01),xytext=(24,.26),fontsize=10, arrowprops=dict(arrowstyle='->',color='#555555')) ax.annotate('Dead-space step: 150 mL',xy=(150,.5),xytext=(245,.54),fontsize=10, arrowprops=dict(arrowstyle='->',color='#555555')) ax.set(xlim=(-5,505),ylim=(-.035,1.17),xlabel='Expired volume (mL)', ylabel='Tracer fraction / alveolar plateau fraction') ax.spines[['top','right']].set_visible(False) ax.legend(loc='lower right',frameon=False,fontsize=9) fig.suptitle('A mixing transition shifts the dead-space boundary',fontsize=13) fig.text(.5,.02,'Ideal illustration: gas fractions and volumes are stipulated, not clinical reference data.',ha='center',fontsize=9) fig.subplots_adjust(left=.12,right=.97,bottom=.17,top=.85) fig.savefig(Path('single-breath-dead-space.png'),facecolor='white',dpi=100) plt.close(fig)