codexbohr-hydrogen-levels-and-transitions.py
bohr-hydrogen-levels-and-transitions.py
"""Original finite-mass Bohr ladder; run from _media. Python 3.14, matplotlib 3.10."""
from pathlib import Path
import math
import matplotlib.pyplot as plt
def main():
e, h, c = 1.602176634e-19, 6.62607015e-34, 299792458.0
epsilon = 8.8541878128e-12
electron, proton = 9.1093837015e-31, 1.67262192369e-27
mu = electron * proton / (electron + proton)
hbar = h / (2 * math.pi)
kappa = e**2 / (4 * math.pi * epsilon)
binding = mu * kappa**2 / (2 * hbar**2)
def energy(n):
return -binding / (e * n**2)
def wavelength(upper, lower):
return h * c / (binding * (lower**-2 - upper**-2)) * 1e9
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white"})
fig, axes = plt.subplots(1, 2, figsize=(11, 6.2), dpi=110)
fig.subplots_adjust(left=0.075, right=0.98, bottom=0.14, top=0.80, wspace=0.28)
fig.suptitle("Hydrogen: energy differences become emitted light", fontsize=17, y=0.97)
fig.text(0.5, 0.895, "Calculated circular Bohr model · finite proton mass · vacuum wavelengths",
ha="center", color="#444444")
for ax in axes:
ax.set_xlim(0, 5.3)
ax.set_xticks([])
ax.set_ylabel("Internal energy (eV)")
ax.axhline(0, color="#555555", linestyle="--", linewidth=1)
ax.text(0.08, 0.08, "Ionization threshold", fontsize=10,
transform=ax.get_yaxis_transform(), va="bottom")
for n in range(1, 7):
ax.hlines(energy(n), 0.2, 4.45, color="#374c67", linewidth=1.4)
ax.spines["bottom"].set_visible(False)
full, zoom = axes
full.set_ylim(-14.4, 0.75)
full.set_title("Lyman emissions to n = 1", pad=17)
full.text(4.54, energy(1), "n = 1", va="center")
full.text(4.54, energy(2), "n = 2", va="center")
full.annotate("n = 3–6", xy=(4.4, energy(4)), xytext=(3.2, -2.25),
arrowprops={"arrowstyle": "-", "color": "#777777"}, fontsize=10)
for x, upper, color in [(1.2, 2, "#7e57a3"), (2.7, 3, "#4d77a5")]:
full.annotate("", xy=(x, energy(1)), xytext=(x, energy(upper)),
arrowprops={"arrowstyle": "-|>", "color": color, "lw": 2.5,
"mutation_scale": 14})
full.text(x + 0.13, -8.0, f"{upper} → 1\n{wavelength(upper, 1):.3f} nm",
color=color, va="center", fontsize=11)
zoom.set_ylim(-3.8, 0.30)
zoom.set_title("Expanded Balmer emissions to n = 2", pad=17)
for n in range(2, 7):
zoom.text(4.54, energy(n), f"n = {n}", va="center", fontsize=10)
colors = ["#b33b36", "#258a8a", "#4866ae", "#85529c"]
for x, upper, color in zip([0.6, 1.7, 2.8, 3.9], range(3, 7), colors):
zoom.annotate("", xy=(x, energy(2)), xytext=(x, energy(upper)),
arrowprops={"arrowstyle": "-|>", "color": color, "lw": 2.4,
"mutation_scale": 13})
zoom.text(x + 0.10, -2.5, f"{upper} → 2\n{wavelength(upper, 2):.3f} nm",
color=color, rotation=90, va="center", ha="left", fontsize=10)
fig.text(0.5, 0.035, "Arrows show energy loss, not electron paths. Higher levels and line intensities are omitted.",
ha="center", fontsize=10, color="#444444")
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()