The initial guess#
You now have a molecule and a basis. Before the SCF can run, it needs somewhere to start — an approximate density to build the very first Fock matrix from. That starting point is the initial guess. Most of the time you never think about it: qc-rs picks a good default automatically. But understanding it pays off exactly when a calculation is hard to converge — so this short chapter explains what the guess is, why it (usually) does not change your answer, and how to change it when you need to.
Theory: why an SCF needs a guess#
Recall the self-consistent-field loop from Hartree–Fock. The Fock matrix \(\mathbf F\) is built from the density \(\mathbf D\) (through the Coulomb and exchange operators \(\mathbf J[\mathbf D]\), \(\mathbf K[\mathbf D]\)), but the density comes from diagonalizing \(\mathbf F\). That circularity —
is why the SCF is iterative. But it also means the loop cannot take its first step without a density already in hand. The initial guess provides that starting \(\mathbf D_0\) (or a starting set of orbitals).
Here is the key idea. The SCF converges to a fixed point — the self-consistent density — and for a well-behaved system that fixed point does not depend on where you started. So:
Important
The guess changes the path, not the destination A different initial guess gives the same converged energy; it only changes how many cycles the SCF needs (and, for difficult systems, whether it converges at all). A guess close to the final density converges fast; a crude one takes more cycles. You do not trade accuracy for a cheaper guess — only speed and robustness.
We will see this directly in the worked example below: six different guesses for water all land on \(-76.026794\,E_h\), taking between 8 and 12 cycles.
The default: SAD#
qc-rs’s default is the superposition of atomic densities (SAD). The idea is physical and cheap: a molecule’s density is, to first approximation, just its atoms’ densities overlaid. SAD precomputes a spherically-averaged density for each element (from a small atomic SCF) and sums them at the nuclear positions to form \(\mathbf D_0\). It needs no molecular integrals beyond what it already has, is robust across the periodic table, and is usually close enough to converge in a handful of cycles.
You do not have to ask for it. As Core concepts described, run() auto-inserts
a sad guess when an SCF needs a starting state and none exists. (If an atomic density cannot be built — an
untabulated element, or an ECP-only heavy atom — SAD falls back to gwh, then to the bare core Hamiltonian.)
Usage#
The common case is doing nothing — let run() insert SAD. To override, add a guess step before the
SCF (functional or method-chain form, like every other step):
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
# explicit guess (method-chain)
mychk = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").guess("gwh").scf(ref="r").run()
# equivalently, functional form
base = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom")
mychk = qc.scf(qc.guess(base, "gwh"), ref="r").run()
Restarting from a previous result: guess("read")#
guess("read", source=...) imports the orbitals from another checkpoint, projecting them into the current
basis. Two everyday uses:
Restart a calculation from a saved
.qch5— continue, or re-run a property on an existing SCF.Basis stepping — converge cheaply in a small basis, then project that result as the guess for the big-basis SCF, which then needs far fewer expensive cycles:
small = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").scf(ref="r").run()
small.save("water_dz.qch5")
big = qc.chk.new(atom=water, ao="cc-pvtz", unit="angstrom") \
.guess("read", source="water_dz.qch5").scf(ref="r").run()
The read details (MO projection, symmetry/irrep handling) are in the checkpoint reference,
qc-chk.md.
When the guess actually matters#
For routine closed-shell molecules the default just works. The guess earns your attention in three cases:
Hard-to-converge systems — transition metals, near-degeneracies, diradicals. A better guess (or a
readfrom a related calculation) can be the difference between smooth convergence and an SCF that oscillates. Pair it with the convergence tools in the SCF chapter.ECP basis sets — the atomic-superposition guesses (
sad/sap/harris) are all-electron and would put the wrong number of valence electrons on an ECP atom, so qc-rs automatically degrades them togwh(which diagonalizes the valence core Hamiltonian \(\mathbf T + \mathbf V_{\text{nuc}}[Z_{\text{eff}}] + \mathbf V^{\text{ECP}}\)).gwhandcoreare ECP-correct directly.Broken symmetry & excited states — a UHF diradical often needs
guess(..., spin_break="mix")to break spatial symmetry, and a ΔSCF excited state needs a non-Aufbau occupation (guess(..., occupation=...)withmom=True). These are covered in the SCF chapter.
Worked example: same energy, different number of cycles#
This is the whole idea of the chapter in one script — every guess converges to the identical energy, but the count of SCF cycles varies:
import qc
water = "O 0 0 0.117; H 0 0.757 -0.469; H 0 -0.757 -0.469"
for g in ("sad", "minao", "harris", "sap", "core", "gwh"):
chk = qc.chk.new(atom=water, ao="cc-pvdz", unit="angstrom").guess(g).scf(ref="r").run()
print(f"{g:8} E = {chk.scf.energy:.6f} cycles = {chk.scf.ncycle}")
# sad E = -76.026794 cycles = 9
# minao E = -76.026794 cycles = 9
# harris E = -76.026794 cycles = 8
# sap E = -76.026794 cycles = 10
# core E = -76.026794 cycles = 11
# gwh E = -76.026794 cycles = 12
The energies are bit-for-bit identical; only the cycles column moves. That is the guess doing its one job:
setting the starting point, not the answer.
Exercise 3
Without running it, what converged RHF energy do you expect from
guess("core")versusguess("sad")for the same molecule and basis? Why?You must run 50 SCFs on the same molecule at
cc-pvqz(an expensive basis). Sketch a strategy usingguess("read")to make each one converge in fewer cycles.You give a heavy-metal complex an ECP basis and request
guess("harris"). What does qc-rs actually use, and why?
Solution to Exercise 3
The same energy. The SCF converges to the same fixed point regardless of the guess;
corewill just take more cycles to get there (it ignores electron–electron interaction entirely, so it starts further from the truth).Converge once in a cheaper basis (say
cc-pvdz) or at a nearby geometry,save()it, and pass it asguess("read", source=...)for thecc-pvqzruns — each expensive SCF then starts from an already near-converged density and needs far fewer of its costly cycles.It degrades
harristogwh.harris(likesad/sap) is an all-electron atomic-superposition guess, which would assign the wrong valence electron count on an ECP atom;gwhbuilds from the valence-only ECP core Hamiltonian and is correct.
With the molecule, basis, and starting point settled, we can finally turn to the SCF itself — the references, functionals, and the convergence toolkit — in the next chapter.