Hα stick spectrum and broadened profile

Script: examples/example_halpha.py

Side-by-side comparison of the discrete transition stick spectrum (discrete_transitions) and the static Stark-Zeeman broadened profile at B = 0, 3, and 10 T, at two electron densities (\(N_e = 10^{17}\) and \(10^{19}\) m-3), showing the transition from the Zeeman-dominated to the Stark-dominated regime.

Code

  1"""
  2example_halpha.py — H Balmer-alpha (n=3→2) Stark-Zeeman profiles.
  3
  4Shows static profiles for B = 1, 5, 10 T at two electron densities:
  5  • Ne = 10^20 m^-3 : Zeeman-dominated  (±5 meV window)
  6  • Ne = 10^23 m^-3 : Stark-dominated   (±30 meV window)
  7Te = 5 eV throughout.
  8
  9Run:
 10    python example_halpha.py
 11
 12Produces:  example_halpha.png
 13"""
 14
 15import numpy as np
 16import matplotlib.pyplot as plt
 17import sys, os
 18sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
 19
 20from starkzee.static_profile import calculate_static_profile, discrete_transitions
 21from starkzee.utils import reduced_mass_rydberg_ev
 22
 23# ── Parameters ────────────────────────────────────────────────────────────────
 24
 25Z, n_u, n_l = 1, 3, 2
 26A           = 1          # atomic mass (1 = H)
 27Te_ev       = .5
 28Ti_ev       = .5
 29E0 = (Z**2) * reduced_mass_rydberg_ev(Z, A) * (1.0 / n_l**2 - 1.0 / n_u**2)
 30
 31B_VALS  = [0.0, 3.0, 10.0]        # Tesla
 32NE_ROWS = [
 33    (1e17, 1.0),   # (Ne m^-3, detuning half-range meV)
 34    (1e19, 1.0),
 35]
 36NPTS = 500   # Voigt bakes in Doppler; grid only needs to resolve ~Doppler width (~50 ueV)
 37
 38POL_COLOR = {0: "#e74c3c", -1: "#3498db", 1: "#2ecc71"}
 39
 40# ── Precompute stick spectra (field-independent per B) ────────────────────────
 41
 42print("Computing stick spectra …")
 43sticks = {}
 44for B in B_VALS:
 45    sticks[B] = discrete_transitions(
 46        n_u=n_u, n_l=n_l, Z=Z, B=B,
 47        fine_structure=True, min_strength=0
 48    )
 49
 50# ── Figure ────────────────────────────────────────────────────────────────────
 51
 52fig, axes = plt.subplots(2, 3, figsize=(14, 8), constrained_layout=True)
 53
 54for row, (Ne, det_range) in enumerate(NE_ROWS):
 55    ne_exp   = int(np.log10(Ne))
 56    det_mev  = np.linspace(-det_range, det_range, NPTS)
 57    energies = E0 + det_mev * 1e-3
 58
 59    for col, B in enumerate(B_VALS):
 60        ax = axes[row, col]
 61
 62        print(f"  B={B:.0f} T,  Ne=1e{ne_exp} m^-3 ...", flush=True)
 63
 64        pi, sp, sm = calculate_static_profile(
 65            n_u=n_u, n_l=n_l, Z=Z, B=B,
 66            Ne_m3=Ne, Te_ev=Te_ev,
 67            energies_ev=energies,
 68            num_f=25, num_mu=8,
 69            fine_structure=True,
 70            frequency_dependent_width=True,
 71            Ti_ev=Ti_ev, species='H',
 72        )
 73
 74        # 90° transverse: I = I_π + ½(I_σ+ + I_σ−)
 75        transverse = pi + 0.5 * (sp + sm)
 76        norm  = transverse.max() or 1.0
 77
 78        # Centre detuning on the intensity-weighted centroid so that the
 79        # fine-structure blueshift does not break the visual σ+/σ− symmetry.
 80        E_center = float(np.sum(energies * transverse) / np.sum(transverse))
 81        det_plot = (energies - E_center) * 1e3   # meV from FS line center
 82
 83        # ── Profile curves ────────────────────────────────────────────────
 84        ax.fill_between(det_plot, transverse / norm, alpha=0.10, color="k")
 85        ax.plot(det_plot, transverse / norm, "k",       lw=1.8, label="Transverse")
 86        ax.plot(det_plot, pi / norm,         color=POL_COLOR[0],
 87                lw=1.1, ls="--", alpha=0.85, label="π")
 88        ax.plot(det_plot, 0.5*(sp+sm) / norm, color=POL_COLOR[-1],
 89                lw=1.1, ls=":",  alpha=0.85, label="½(σ+σ−)")
 90
 91        # ── Stick spectrum overlay (scaled to 0.45 of plot height) ────────
 92        tr      = sticks[B]
 93        det_stk = (tr["energy_ev"] - E_center) * 1e3
 94        s_stk   = tr["strength"] / tr["strength"].max() * 0.45
 95        for q in [0, -1, 1]:
 96            mask = (tr["q"] == q) & (np.abs(det_stk) <= det_range)
 97            if mask.any():
 98                ax.vlines(det_stk[mask], 0, s_stk[mask],
 99                          colors=POL_COLOR[q], alpha=0.35, lw=1.0)
100                ax.plot(det_stk[mask], s_stk[mask], '.',
101                          color=POL_COLOR[q], alpha=0.35, lw=1.0)
102
103        # ── Axes decoration ───────────────────────────────────────────────
104        ax.set_title(
105            f"B = {B:.0f} T  |  $N_e = 10^{{{ne_exp}}}$ m$^{{-3}}$",
106            fontsize=10
107        )
108        ax.set_xlabel("Detuning from line center (meV)", fontsize=9)
109        ax.set_ylabel("Norm. intensity",                  fontsize=9)
110        ax.set_xlim(-det_range, det_range)
111        ax.set_ylim(0, 1.15)
112        ax.axhline(0, color="grey", lw=0.4)
113        ax.tick_params(labelsize=8)
114
115        if row == 0 and col == 0:
116            ax.legend(fontsize=8, framealpha=0.75, loc="upper right")
117
118print("Done.  Saving example_halpha.png …")
119
120fig.suptitle(
121    f"H Balmer-α  (n=3→2, Z=1)  ·  Static Stark-Zeeman + Doppler  ·"
122    f"  $T_e = T_i$ = {Te_ev:.1f} eV",
123    fontsize=13
124)
125plt.savefig("example_halpha.png", dpi=150, bbox_inches="tight")
126plt.show()

Result

Hα stick spectra and broadened profiles across B and electron density

Transverse profile (with π and ½(σ++σ−) components) and the zero-field stick spectrum overlaid, across a grid of B and \(N_e\) values.