codexfranck-hertz-collector-threshold.py
franck-hertz-collector-threshold.py
"""Original collector model schematic; Python 3.14, matplotlib 3.10.7.
Run from _media; writes franck-hertz-collector-threshold.png to CWD.
"""
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch
def main():
plt.rcParams.update({"font.size": 11, "figure.facecolor": "white"})
fig, (ax, energy) = plt.subplots(2, 1, figsize=(10, 6.7),
gridspec_kw={"height_ratios": [1.25, 1]})
fig.subplots_adjust(left=.09, right=.97, top=.92, bottom=.11, hspace=.53)
fig.suptitle("Franck–Hertz: an energy loss becomes a collection loss", fontsize=16)
ax.set(xlim=(0, 10), ylim=(-.6, 4.6))
ax.axis("off")
tube = FancyBboxPatch((.3, 1), 9.2, 2.3, boxstyle="round,pad=0.14",
edgecolor="#657781", facecolor="#f4f8fa", linewidth=1.5)
ax.add_patch(tube)
ax.plot([1, 1], [1.45, 2.9], color="#d17c18", lw=7)
ax.text(1, .55, "Heated cathode", ha="center")
ax.text(1, .15, "electron source", ha="center", fontsize=10)
ax.plot([6.6, 6.6], [1.2, 3.1], color="#326f9a", lw=2, ls="--")
ax.text(6.6, .55, "Grid", ha="center")
ax.plot([8.7, 8.7], [1.4, 2.95], color="#326f9a", lw=5)
ax.text(8.7, .55, "Collector", ha="center")
ax.text(8.7, .15, r"current $I(V,V_r)$", ha="center", fontsize=10)
ax.annotate("", (4.2, 2.2), (1.4, 2.2),
arrowprops={"arrowstyle": "->", "lw": 2, "color": "#326f9a"})
ax.text(3.2, 2.6, r"gain $e(V-V_c)$", ha="center")
ax.scatter([4.7], [2.2], s=260, color="#b45283", edgecolor="white", zorder=4)
ax.text(4.7, 1.55, r"one atom takes $\Delta$", ha="center", color="#943e6b")
ax.annotate("", (6.25, 2.2), (5.05, 2.2),
arrowprops={"arrowstyle": "->", "lw": 2, "color": "#326f9a"})
ax.annotate("", (8.35, 2.2), (7, 2.2),
arrowprops={"arrowstyle": "->", "lw": 2, "color": "#326f9a"})
ax.text(7.7, 2.65, r"barrier $eV_r$", ha="center")
ax.text(4.8, 3.65, "Vapor density sets collision opportunities; retarder selects survivors",
ha="center", fontsize=11)
ax.text(.35, -.45, "Functional model schematic • not historical geometry or a scale drawing",
fontsize=10, color="#555555")
energy.set_title("Chosen one-collision example: 5.6 eV input, 4.9 eV loss, probability 0.75",
fontsize=12, pad=14)
energy.set(xlim=(0, 6.2), ylim=(0, 1.05), xlabel="Outgoing kinetic energy E (eV)")
energy.axvspan(0, 1, color="#f9dfd9")
energy.axvspan(1, 6.2, color="#e3f0e9")
energy.vlines([.7, 5.6], [0, 0], [.75, .25], colors=["#a54732", "#217349"], lw=4)
energy.scatter([.7, 5.6], [.75, .25], color=["#a54732", "#217349"], zorder=3)
energy.axvline(1, color="#555555", ls="--", lw=1.4)
energy.text(.7, .82, "75% at 0.7 eV", ha="center")
energy.text(5.6, .32, "25% at 5.6 eV", ha="center")
energy.text(1.13, .91, "1.0 V retarder: only 25% collected", color="#217349")
energy.text(2, .68, "0.5 V retarder: both branches collected", color="#444444")
energy.set_ylabel("Probability mass")
energy.set_yticks([0, .25, .5, .75, 1])
energy.spines[["top", "right"]].set_visible(False)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), dpi=140, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()