Satellite features at B = 1000 T

Script: examples/diag_halpha_satellites.py

Searches for the \(\pm 2\mu_B B\) Stark-Zeeman satellite feature in Hα (n=3→2) at B = 1000 T, at three quadrature resolutions (num_f/num_mu = 20/6, 40/10, 60/12) to check convergence, both with and without the quadratic Zeeman term.

Note

This is an exploratory/diagnostic script, not a validated result: at the plasma conditions used here (\(N_e = 10^{17}\) m-3, \(T_e = 5\) eV) no distinctly resolved satellite peak is found at \(\pm 2\mu_B B\) in the full-resolution profile — see the physical mechanism described in the script’s docstring for the effect it was searching for. A companion script that claimed a specific ~2% satellite for Hβ (n=4→2) was found not to reproduce against the current solver and is intentionally not included here.

Code

  1#!/usr/bin/env python3
  2"""
  3diag_halpha_satellites.py
  4=========================
  5Look for the ±2μ_B×B Stark-Zeeman satellite features in Hα (n=3→2) at B=1000 T.
  6
  7Physical mechanism
  8------------------
  9Transverse Stark field Fx mixes states with Δl=±1, Δml=±1 within the same n shell.
 10The eigenstate near E0_n3 + 2μ_B×B (dominated by |3d,ml=2⟩) acquires an admixture
 11β × |3p,ml=1⟩ due to Fx coupling. This β component can make a σ+ transition to the
 12lower eigenstate near E0_n2 (dominated by |2s,ml=0⟩ or |2p,ml=0⟩) — producing a
 13photon at energy E0 + 2μ_B×B instead of the regular E0 + μ_B×B.
 14
 15β ≈ Fx × ⟨3p|r|3d⟩ × A0 / (μ_B × B) → scales as Fx/B → satellite intensity ∝ ⟨F²⟩/B²
 16
 17For n=3: β_rms ≈ F0 × 10.06a0 × A0 / μ_B×B = 8e6 × 5.32e-10 / 57.9e-3 ≈ 0.074
 18Satellite intensity ~ β² × OS_fraction ~ 0.5%  (weak — that's why it's hard to see)
 19
 20For n=4: β_rms ≈ F0 × 15.9a0 × A0 / μ_B×B ≈ 0.116 → intensity ~ 1.3%  (visible)
 21
 22This script runs with high resolution: num_f=60, num_mu=12 and a ±200 meV window,
 23comparing B=100T, 500T, 1000T.
 24"""
 25
 26import numpy as np
 27import matplotlib.pyplot as plt
 28import sys, os
 29sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
 30
 31from starkzee.static_profile import calculate_static_profile
 32from scipy.constants import e as E_CHARGE, m_e as M_E
 33from starkzee.utils import reduced_mass_rydberg_ev, BOHR_MAGNETON_EV_T, A0
 34
 35Z, n_u, n_l = 1, 3, 2
 36A  = 1
 37Ne, Te = 1e17, 5.0
 38E0 = (Z**2) * reduced_mass_rydberg_ev(Z, A) * (1.0/n_l**2 - 1.0/n_u**2)
 39
 40muB_1000 = BOHR_MAGNETON_EV_T * 1000.0          # ≈ 57.9 meV
 41
 42print(f"Hα: E0 = {E0:.4f} eV")
 43print(f"μ_B × 1000 T = {muB_1000*1e3:.2f} meV")
 44print(f"Satellite expected at ±{2*muB_1000*1e3:.1f} meV from E0")
 45print()
 46
 47# ── Compute profiles at B=1000 T with wide window ─────────────────────────────
 48B = 1000.0
 49muB_B = BOHR_MAGNETON_EV_T * B
 50DET_HALF = 0.20         # ±200 meV window to see both σ± main peaks AND satellites
 51NPTS     = 8000         # ~0.05 meV resolution
 52
 53det = np.linspace(-DET_HALF, DET_HALF, NPTS)
 54energies = E0 + det
 55
 56# Run with different resolution settings to check convergence
 57configs = [
 58    ("Low  (num_f=20, num_mu=6)",  20, 6),
 59    ("Mid  (num_f=40, num_mu=10)", 40, 10),
 60    ("High (num_f=60, num_mu=12)", 60, 12),
 61]
 62
 63results = {}
 64for label, num_f, num_mu in configs:
 65    print(f"B={int(B)}T, {label} …", flush=True)
 66    pi_nq, sp_nq, sm_nq = calculate_static_profile(
 67        n_u, n_l, Z, B, Ne, Te, energies,
 68        num_f=num_f, num_mu=num_mu,
 69        use_screening=True, quadratic_zeeman=False,
 70        frequency_dependent_width=False, Ti_ev=Te)
 71    print("  without QZ done.", flush=True)
 72    pi_yq, sp_yq, sm_yq = calculate_static_profile(
 73        n_u, n_l, Z, B, Ne, Te, energies,
 74        num_f=num_f, num_mu=num_mu,
 75        use_screening=True, quadratic_zeeman=True,
 76        frequency_dependent_width=False, Ti_ev=Te)
 77    print("  with QZ done.", flush=True)
 78    results[label] = dict(pi_nq=pi_nq, sp_nq=sp_nq, sm_nq=sm_nq,
 79                          pi_yq=pi_yq, sp_yq=sp_yq, sm_yq=sm_yq)
 80
 81    # Quick peak analysis for no-QZ total spectrum
 82    total_nq = pi_nq + 0.5*(sp_nq + sm_nq)
 83    peak = total_nq.max()
 84    satellite_region = np.abs(np.abs(det*1e3) - 2*muB_B*1e3) < 15.0  # ±15 meV around ±2μ_BB
 85    main_region      = np.abs(det) < 0.030
 86    sat_peak = total_nq[satellite_region].max() if satellite_region.any() else 0
 87    main_peak = total_nq[main_region].max()
 88    print(f"  Satellite / main peak ratio (no-QZ, ±15meV around ±2μ_B×B): "
 89          f"{sat_peak/main_peak*100:.3f}%")
 90
 91# ── Plot: full spectrum showing satellites ─────────────────────────────────────
 92fig, axes = plt.subplots(3, 1, figsize=(14, 12), sharex=True)
 93fig.suptitle(
 94    r"Hα (n=3→2) full Stark-Zeeman profile, B=1000 T — searching for ±2μ$_B$B satellites"
 95    "\n"
 96    r"$N_e=10^{17}$ m$^{-3}$, $T_e=5$ eV, transverse obs. (I$_\pi$ + ½(I$_{\sigma+}$ + I$_{\sigma-}$))",
 97    fontsize=11)
 98
 99det_meV = det * 1e3
100satellite_eV = 2 * muB_B  # 115.8 meV at B=1000T
101main_eV = muB_B            # 57.9 meV
102
103for ax, (label, num_f, num_mu), color in zip(
104        axes, configs, ['tab:blue', 'tab:orange', 'tab:red']):
105    r = results[label]
106    total_nq = r['pi_nq'] + 0.5*(r['sp_nq'] + r['sm_nq'])
107    total_yq = r['pi_yq'] + 0.5*(r['sp_yq'] + r['sm_yq'])
108
109    peak = max(total_yq.max(), total_nq.max(), 1e-30)
110
111    ax.semilogy(det_meV, total_nq/peak, 'k--', lw=1.2, alpha=0.75, label='No QZ')
112    ax.semilogy(det_meV, total_yq/peak, color=color, lw=1.8, label='With QZ')
113
114    # Mark expected satellite positions
115    for sign, tag in [(+1, 'σ+'), (-1, 'σ−')]:
116        ax.axvline(sign*satellite_eV*1e3, color='orange', ls=':', lw=1.2,
117                   label=f'±2μ_B×B = ±{satellite_eV*1e3:.0f} meV ({tag} sat)' if sign==1 else '')
118        ax.axvline(sign*main_eV*1e3, color='gray', ls=':', lw=1.0,
119                   label=f'±μ_B×B = ±{main_eV*1e3:.0f} meV (σ±)' if sign==1 else '')
120
121    ax.set_ylim(1e-5, 3.0)
122    ax.set_ylabel('Norm. intensity', fontsize=9)
123    ax.set_title(f'{label}', fontsize=10)
124    ax.legend(fontsize=8, loc='upper right')
125    ax.grid(True, which='both', alpha=0.3)
126
127axes[-1].set_xlabel('Detuning from $E_0$ (meV)', fontsize=11)
128axes[-1].set_xlim(-210, 210)
129
130plt.tight_layout()
131out = 'halpha_satellites_convergence.png'
132plt.savefig(out, dpi=200, bbox_inches='tight')
133print(f"\nSaved {out}")
134
135# ── Zoomed-in plot: just the satellite regions ─────────────────────────────────
136fig2, axes2 = plt.subplots(2, 3, figsize=(15, 9))
137fig2.suptitle(r"Zoomed satellite regions: Hα at B=1000 T", fontsize=12)
138
139windows = [
140    ('+2μ_B×B region (σ+ sat)',  +satellite_eV*1e3, 30),
141    ('+μ_B×B region (σ+ main)', +main_eV*1e3, 30),
142    ('zero  (π main)', 0.0, 30),
143]
144for col, (title, center_meV, hw_meV) in enumerate(windows):
145    mask = np.abs(det_meV - center_meV) < hw_meV
146    for row, (label, num_f, num_mu), color in zip(range(3), configs, ['tab:blue','tab:orange','tab:red']):
147        if col == 0:
148            ax = axes2[0 if row < 2 else 1, col]
149        # actually let me just use 1 row per resolution for comparison
150        pass
151
152    for ax, (label, num_f, num_mu), color in zip([axes2[0,col], axes2[1,col]],
153                                                   configs[:2], ['tab:blue','tab:orange']):
154        r = results[label]
155        total_nq = r['pi_nq'] + 0.5*(r['sp_nq'] + r['sm_nq'])
156        total_yq = r['pi_yq'] + 0.5*(r['sp_yq'] + r['sm_yq'])
157        peak = total_nq.max()
158
159        ax.plot(det_meV[mask], total_nq[mask]/peak*100, 'k--', lw=1.2, alpha=0.8, label='No QZ')
160        ax.plot(det_meV[mask], total_yq[mask]/peak*100, color=color, lw=1.8, label='With QZ')
161        ax.set_title(f'{title}\n{label}', fontsize=8)
162        ax.set_xlabel('Detuning (meV)', fontsize=8)
163        ax.set_ylabel('% of main peak', fontsize=8)
164        ax.legend(fontsize=7)
165        ax.grid(True, alpha=0.3)
166
167plt.tight_layout()
168out2 = 'halpha_satellites_zoom.png'
169plt.savefig(out2, dpi=200, bbox_inches='tight')
170print(f"Saved {out2}")
171
172plt.show()
173print("\nDone.")

Results

Convergence of the Hα Stark-Zeeman profile search for satellite features across quadrature resolutions

Full ±200 meV window at three quadrature resolutions, with and without quadratic Zeeman, marking the naive \(\pm\mu_B B\) and \(\pm 2\mu_B B\) positions.

Zoomed views of the sigma-plus satellite region, main peak region, and pi line center

Zoomed views of the σ+ satellite region, the σ+ main-peak region, and the π line center, for the two lowest quadrature resolutions.