Spectra & density of states#

The final property family reads the orbital-energy spectrum — the ladder of orbital energies an SCF produces. Broadened into a density of states (DOS) and summarized by the HOMO–LUMO gap, it connects your calculation to electronic structure, optical onset, and chemical hardness. These live in qc.prop.spec.

Theory: from orbital levels to a spectrum#

An SCF returns a set of orbital energies \(\{\varepsilon_i\}\) — the occupied levels up to the HOMO (highest occupied) and the empty levels from the LUMO (lowest unoccupied) up. Two summaries matter:

  • The HOMO–LUMO gap \(\varepsilon_{\text{LUMO}} - \varepsilon_{\text{HOMO}}\) — a first estimate of the excitation energy and a measure of kinetic stability (large gap = hard, inert; small gap = reactive). It is the orbital-energy sibling of the chemical hardness from the reactivity chapter.

  • The density of states (DOS) — the discrete levels broadened into a continuous curve \(g(\varepsilon)\) by convolving each with a Gaussian/Lorentzian. It shows how many states sit near each energy, the natural way to compare a spectrum of levels or to line up computed and photoelectron spectra.

For a molecule the “DOS” is really a broadened stick spectrum of discrete levels; the same machinery underlies band-structure DOS for extended systems.

The HOMO–LUMO gap#

import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()

fl = qc.prop.spec.fermi_level(m)
fl["homo"], fl["lumo"]     # -13.419, 5.049   frontier orbital energies (eV)
fl["gap"]                  # 18.468           the HOMO–LUMO gap (eV)
fl["fermi_level"]          # -4.185           midgap "Fermi" level

Water’s gap of ~18.5 eV is large — consistent with its chemical inertness, and (as expected) equal to the hardness \(\eta\) from the conceptual-DFT chapter, since both are \(I - A\) in the Koopmans picture.

Density of states#

qc.prop.spec.dos broadens the level spectrum into a curve you can plot:

dos = qc.prop.spec.dos(m)
dos["energies"]     # the energy axis (2000 points, eV)
dos["tdos"]         # the total DOS g(ε)
dos["pdos"]         # projected DOS (per atom / angular momentum)
dos["levels"]       # the 24 underlying discrete orbital energies
dos["broadening"], dos["fwhm"]   # the broadening scheme and width

Plotting tdos vs energies gives the broadened spectrum; pdos decomposes it by atom or angular momentum (which atoms contribute states at a given energy). qc.prop.spec also provides band_center (a d-band-center- style first moment, useful in catalysis) and fermi_level.

Worked example#

import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
m = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()

fl = qc.prop.spec.fermi_level(m)
print(f"HOMO {fl['homo']:.2f} eV, LUMO {fl['lumo']:.2f} eV, gap {fl['gap']:.2f} eV")
# HOMO -13.42 eV, LUMO 5.05 eV, gap 18.47 eV

dos = qc.prop.spec.dos(m)
print("DOS points:", len(dos["energies"]), "| levels:", len(dos["levels"]))   # 2000 | 24

Exercise 15

  1. Molecule X has a HOMO–LUMO gap of 1.5 eV, molecule Y of 8 eV. Which is likely more reactive / more deeply coloured, and which is the harder molecule?

  2. Why is the DOS a broadened curve rather than just the list of orbital energies — what does the broadening let you do?

  3. qc.prop.spin.s_squared returns 0.0 for your closed-shell RHF water but 0.76 for a UHF methyl radical. What does the 0.76 (vs the exact 0.75) tell you?

That completes Part III. You can now go from a molecule to an SCF, forces and geometries, correlated energies, solvation and dispersion, pictures, and a full analysis of charges, bonding, topology, aromaticity, reactivity, and spectra — all in one toolkit. Beyond here, Part IV scales these calculations across threads, machines, and GPUs.