codexhomestake-chemical-neutrino-readout.py
homestake-chemical-neutrino-readout.py
"""Original functional Homestake diagram; Python 3.14, matplotlib 3.10, NumPy 2.3.
Run from _media; writes homestake-chemical-neutrino-readout.png to CWD.
Illustrative curves use dimensionless half-life units, not experimental data.
"""
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch
def main():
plt.rcParams.update({'font.size': 11, 'font.family': 'DejaVu Sans'})
fig = plt.figure(figsize=(10, 6.3), facecolor='white')
fig.suptitle('Homestake: chemical memory, then delayed pulses', fontsize=17, y=.97)
ax = fig.add_axes([.025, .53, .95, .36])
ax.set(xlim=(0, 10), ylim=(0, 3)); ax.axis('off')
boxes = [(0.15, '1 Capture', 'Chlorine target\nin liquid'),
(3.6, '2 Extract', 'Helium sweep\nArgon trap + purification'),
(7.05, '3 Count', 'Small proportional counter\nElectron-capture pulses')]
for x, title, label in boxes:
ax.add_patch(FancyBboxPatch((x, .6), 2.8, 1.85, boxstyle='round,pad=0.1',
facecolor='#edf4fa', edgecolor='#22577a', linewidth=1.7))
ax.text(x+1.4, 2.05, title, ha='center', weight='bold', color='#173e59')
ax.text(x+1.4, 1.3, label, ha='center', va='center', fontsize=11)
for x in (3.0, 6.45):
ax.annotate('', xy=(x+.5, 1.5), xytext=(x, 1.5),
arrowprops={'arrowstyle':'->', 'lw':2, 'color':'#22577a'})
ax.text(1.55, .1, r'$\nu_e+{}^{37}\mathrm{Cl}\to{}^{37}\mathrm{Ar}+e^-$', ha='center', fontsize=12)
ax.text(5, .1, 'Stable argon: recovery check', ha='center', fontsize=10)
ax.text(8.45, .1, 'Background counting: pulse check', ha='center', fontsize=10)
plot = fig.add_axes([.11, .13, .52, .33])
t = np.linspace(0, 3, 301)
signal = 2.0**(-t)
background = .12
plot.plot(t, signal+background, color='#22577a', lw=2.5, label='Signal + background')
plot.plot(t, signal, color='#c05a19', lw=2, label='Signal alone')
plot.axhline(background, color='#707070', ls='--', lw=1.5, label='Constant background')
plot.set(xlim=(0, 3), ylim=(0, 1.2), xlabel='Time after counting starts / half-life',
ylabel='Expected pulse rate\n(arbitrary units)', xticks=[0, 1, 2, 3])
plot.spines[['top', 'right']].set_visible(False)
plot.grid(alpha=.17); plot.legend(fontsize=9, loc='upper right', frameon=False)
fig.text(.70, .37, 'Illustrative counting model', weight='bold', fontsize=11)
fig.text(.70, .22, 'After one half-life:\nsignal rate halves;\nconstant background persists.', fontsize=11, linespacing=1.6)
fig.text(.5, .025, 'Functional schematic; not to scale. Curves are not measured data.', ha='center', fontsize=10, color='#4a4a4a')
fig.savefig(Path.cwd() / (Path(__file__).stem+'.png'), dpi=110, facecolor='white', transparent=False)
plt.close(fig)
if __name__ == '__main__':
main()