SPC output
The *_up.spc and *_dn.spc data files are located automatically next to the log file (using input_params.file as the stem). Override either path explicitly when needed:
k-mesh and energy range
bsf = spc.spectral_up
if bsf is not None:
km = bsf.kmesh
print(f"Energy range : {km.energy_min:.4f} – {km.energy_max:.4f} Ry")
print(f"Energy points: {km.n_energy}")
print(f"k-path labels: {km.high_symmetry_indices}") # e.g. {1: '(0 0 0)', 80: '(0 1 0)', ...}
BSF intensity matrix
SpectralFunction.data is a NumPy array of shape (n_energy, n_kpoints). None when n_sym_points == 0 (no k-path was computed).
import numpy as np
if bsf is not None and bsf.data is not None:
print(f"Shape: {bsf.data.shape}") # (n_energy, n_kpoints)
# Energy axis in eV (1 Ry = 13.6057 eV)
energy_Ry = np.linspace(bsf.kmesh.energy_min, bsf.kmesh.energy_max, bsf.kmesh.n_energy)
energy_eV = energy_Ry * 13.6057
# Intensity at the Fermi level (energy closest to 0 Ry)
ef_idx = int(np.argmin(np.abs(energy_Ry)))
print(f"BSF row at E_F: {bsf.data[ef_idx, :]}")
DataFrame export
SPCResult.to_dataframe() returns a tidy long-form frame with one row per (energy, k, spin) point, concatenating both spin channels when present:
df = spc.to_dataframe()
# columns: energy_Ry k spin intensity
# Intensity along k for the energy point closest to E_F, spin up
ef_slice = df.query("spin == 'up'")
ef_slice = ef_slice[ef_slice["energy_Ry"].sub(0.0).abs() == ef_slice["energy_Ry"].sub(0.0).abs().min()]
Channels with no data (None, or an uncomputed k-path) are omitted; to_dataframe() returns an empty frame with the same columns when neither channel has data.
Plotting the spectral function
from akaitools.plotting import plot_bsf
fig = plot_bsf(spc, spin="up", energy_unit="eV")
fig.savefig("bsf.png", dpi=150)
See Plotting for the full set of options (spin filtering, colormap, vmax, and the placeholder behavior when a channel's SpectralFunction.data is None).