codex/solid-angle-disk-detector-and-distance.py

solid-angle-disk-detector-and-distance.py
"""Exact geometric acceptance of an on-axis disk; output basename.png in cwd."""
from pathlib import Path
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.patches import Arc
import numpy as np


def main():
    plt.rcParams.update({'font.size':12,'figure.facecolor':'white','axes.facecolor':'white'})
    fig,(geo,ax)=plt.subplots(1,2,figsize=(10.6,4.8),layout='constrained',gridspec_kw={'width_ratios':[1,1.15]})
    geo.plot([0,1],[0,1],color='#ed8b29',lw=2)
    geo.plot([0,1],[0,-1],color='#ed8b29',lw=2)
    geo.plot([0,1.15],[0,0],color='#788a96',ls=':',lw=1.3)
    geo.plot([1,1],[-1,1],color='#1476a5',lw=5)
    geo.scatter([0],[0],color='black',s=28)
    geo.text(-.10,-.19,'O')
    geo.add_patch(Arc((0,0),.75,.75,theta1=0,theta2=45,color='#202020',lw=1.4))
    geo.text(.40,.12,r'$\theta$',fontsize=14)
    geo.annotate('',xy=(1.18,1),xytext=(1.18,0),arrowprops={'arrowstyle':'<->','color':'#1476a5'})
    geo.text(1.25,.45,r'$a$')
    geo.annotate('',xy=(1,-1.28),xytext=(0,-1.28),arrowprops={'arrowstyle':'<->','color':'#535e66'})
    geo.text(.47,-1.50,r'$d$')
    geo.text(.04,1.36,'On-axis circular detector',weight='bold')
    geo.text(-.12,-1.93,r'$\tan\theta=a/d$; disk area $A=\pi a^2$',fontsize=11)
    geo.text(-.12,-2.23,'Meridian section; the blue segment rotates about the axis.',fontsize=10)
    geo.set_aspect('equal');geo.set_xlim(-.25,1.75);geo.set_ylim(-2.4,1.7);geo.axis('off')
    ratio=np.geomspace(.2,12,500)
    exact=.5/(np.sqrt(1+ratio**2)*(np.sqrt(1+ratio**2)+ratio))
    # Stable form of (1-d/sqrt(d²+a²))/2 with a=1.
    ax.plot(ratio,exact,color='#1476a5',lw=2.6,label='Exact disk acceptance')
    far=ratio[ratio>=.72]
    ax.plot(far,1/(4*far**2),color='#c55b28',ls='--',lw=2,label=r'Far-field $a^2/(4d^2)$')
    for x,offset in [(1,(22,28)),(5,(-60,35))]:
        y=.5*(1-x/np.sqrt(x*x+1))
        ax.scatter([x],[y],color='#1476a5',s=32,zorder=5)
        ax.annotate(f'd/a = {x}\n{100*y:.3f}%',(x,y),xytext=offset,textcoords='offset points',
                    fontsize=10,arrowprops={'arrowstyle':'->','color':'#687784'})
    ax.axhline(.5,color='#8d969d',ls=':',lw=1.5)
    ax.text(.22,.507,'Hemisphere limit: 50%',fontsize=10)
    ax.set(xscale='log',xlim=(.2,12),ylim=(0,.55),xlabel=r'Axial distance / radius, $d/a$',
           ylabel=r'Fraction of isotropic emission, $\Omega/(4\pi)$',title='Acceptance saturates near the source')
    ax.set_xticks([.2,.5,1,2,5,10],labels=['0.2','0.5','1','2','5','10'])
    ax.grid(alpha=.18);ax.legend(loc='upper right',fontsize=10)
    fig.savefig(Path(__file__).with_suffix('.png').name,dpi=120,facecolor='white')
    plt.close(fig)


if __name__=='__main__':
    main()