codexnuclear-rotor-and-quadrupole-vibrator.py
nuclear-rotor-and-quadrupole-vibrator.py
"""Chosen ideal bands, not measured data; tested with Python 3.14.
Uses NumPy 2.3.5 and Matplotlib 3.10.7. Run from _media to emit the PNG.
Both models are normalized to the same first 2+ excitation energy.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
plt.rcParams.update({"font.size": 12, "figure.facecolor": "white"})
fig, axes = plt.subplots(1, 2, figsize=(10, 5.5), dpi=120, sharey=True)
rotor, vibrator = axes
for ax in axes:
ax.set(xlim=(0, 1), ylim=(-0.35, 7.8), xticks=[])
ax.spines[["top", "right", "bottom"]].set_visible(False)
ax.grid(axis="y", color="#e5e7eb", lw=0.7)
ax.set_yticks(np.arange(0, 8))
rotor.set_ylabel(r"Excitation energy / $E(2_1^+)$")
rotor.set_title(r"Axial rotor: $K=0$" + "\n" + r"$E(I)/E(2)=I(I+1)/6$", pad=14)
vibrator.set_title("Harmonic quadrupole vibrator\n" + r"$E(n)/E(2)=n$", pad=14)
spins = np.array([0, 2, 4, 6])
energies = spins * (spins + 1) / 6
for spin, energy in zip(spins, energies):
rotor.hlines(energy, 0.19, 0.69, color="#127b88", lw=2.5)
rotor.text(0.72, energy, f"{spin}" + r"$^+$", va="center")
for low, high in zip(energies[:-1], energies[1:]):
rotor.annotate("", xy=(0.27, low + 0.06), xytext=(0.27, high - 0.06),
arrowprops={"arrowstyle": "->", "lw": 1.5, "color": "#127b88"})
vibrator.hlines(0, 0.19, 0.69, color="#be6c24", lw=2.5)
vibrator.text(0.72, 0, r"$0^+$", va="center")
vibrator.hlines(1, 0.19, 0.69, color="#be6c24", lw=2.5)
vibrator.text(0.72, 1, r"$2^+$", va="center")
for x, spin in zip([0.20, 0.45, 0.70], [0, 2, 4]):
vibrator.hlines(2, x - 0.09, x + 0.09, color="#be6c24", lw=2.5)
vibrator.text(x, 2.15, f"{spin}" + r"$^+$", ha="center", va="bottom")
vibrator.annotate("", xy=(0.45, 1.07), xytext=(x, 1.93),
arrowprops={"arrowstyle": "->", "lw": 1.3, "color": "#be6c24"})
vibrator.annotate("", xy=(0.45, 0.07), xytext=(0.45, 0.93),
arrowprops={"arrowstyle": "->", "lw": 1.3, "color": "#be6c24"})
vibrator.text(0.45, 3.05, "Two-quantum multiplet:\n" + r"$0^+,\ 2^+,\ 4^+$" +
"\n\nHigher multiplets omitted", ha="center", color="#5f4938")
rotor.text(0.64, 5.0, r"$E(4)/E(2)=10/3$", ha="center", color="#127b88", fontsize=14)
vibrator.text(0.45, 5.0, r"$E(4_1^+)/E(2_1^+)=2$", ha="center", color="#be6c24", fontsize=14)
fig.suptitle("Rotation and vibration leave different energy patterns", fontsize=16, y=0.98)
fig.text(0.5, 0.028, "Original ideal-model levels. Arrows: selected E2 decays; widths do not encode strength.",
ha="center", fontsize=10)
fig.subplots_adjust(left=0.085, right=0.975, top=0.80, bottom=0.105, wspace=0.20)
fig.savefig(Path.cwd() / (Path(__file__).stem + ".png"), facecolor="white",
transparent=False, dpi=120)
plt.close(fig)
if __name__ == "__main__":
main()